]> git.ipfire.org Git - thirdparty/gcc.git/commit
c++: implement P2564, consteval needs to propagate up [PR107687]
authorMarek Polacek <polacek@redhat.com>
Tue, 19 Sep 2023 20:31:17 +0000 (16:31 -0400)
committerMarek Polacek <polacek@redhat.com>
Tue, 5 Dec 2023 00:42:09 +0000 (19:42 -0500)
commit1f1c432226cf3db399b2a2a627e3c5720b02b1d6
tree92824e9a520ee39666015bec30b32e90f28ab986
parent606f7201c066b840ea43ab62fcf47042b81e54d4
c++: implement P2564, consteval needs to propagate up [PR107687]

This patch implements P2564, described at <wg21.link/p2564>, whereby
certain functions are promoted to consteval.  For example:

  consteval int id(int i) { return i; }

  template <typename T>
  constexpr int f(T t)
  {
    return t + id(t); // id causes f<int> to be promoted to consteval
  }

  void g(int i)
  {
    f (3);
  }

now compiles.  Previously the code was ill-formed: we would complain
that 't' in 'f' is not a constant expression.  Since 'f' is now
consteval, it means that the call to id(t) is in an immediate context,
so doesn't have to produce a constant -- this is how we allow consteval
functions composition.  But making 'f<int>' consteval also means that
the call to 'f' in 'g' must yield a constant; failure to do so results
in an error.  I made the effort to have cc1plus explain to us what's
going on.  For example, calling f(i) produces this neat diagnostic:

w.C:11:11: error: call to consteval function 'f<int>(i)' is not a constant expression
   11 |         f (i);
      |         ~~^~~
w.C:11:11: error: 'i' is not a constant expression
w.C:6:22: note: 'constexpr int f(T) [with T = int]' was promoted to an immediate function because its body contains an immediate-escalating expression 'id(t)'
    6 |         return t + id(t); // id causes f<int> to be promoted to consteval
      |                    ~~^~~

which hopefully makes it clear what's going on.

Implementing this proposal has been tricky.  One problem was delayed
instantiation: instantiating a function can set off a domino effect
where one call promotes a function to consteval but that then means
that another function should also be promoted, etc.

In v1, I addressed the delayed instantiation problem by instantiating
trees early, so that we can escalate functions right away.  That caused
a number of problems, and in certain cases, like consteval-prop3.C, it
can't work, because we need to wait till EOF to see the definition of
the function anyway.  Overeager instantiation tends to cause diagnostic
problems too.

In v2, I attempted to move the escalation to the gimplifier, at which
point all templates have been instantiated.  That attempt flopped,
however, because once we've gimplified a function, its body is discarded
and as a consequence, you can no longer evaluate a call to that function
which is required for escalating, which needs to decide if a call is
a constant expression or not.

Therefore, we have to perform the escalation before gimplifying, but
after instantiate_pending_templates.  That's not easy because we have
no way to walk all the trees.  In the v2 patch, I use two vectors: one
to store function decls that may become consteval, and another to
remember references to immediate-escalating functions.  Unfortunately
the latter must also stash functions that call immediate-escalating
functions.  Consider:

  int g(int i)
  {
    f<int>(i); // f is immediate-escalating
  }

where g itself is not immediate-escalating, but we have to make sure
that if f gets promoted to consteval, we give an error.

A new option, -fno-immediate-escalation, is provided to suppress
escalating functions.

v2 also adds a new flag, DECL_ESCALATION_CHECKED_P, so that we don't
escalate a function multiple times, and so that we can distinguish between
explicitly consteval functions and functions that have been promoted
to consteval.

In v3, I removed one of the new vectors and changed the other one
to a hash set.  This version also contains numerous cleanups.

v4 merges find_escalating_expr_r into cp_fold_immediate_r.  It also
adds a new optimization in cp_fold_function.

v5 greatly simplifies the code.

v6 simplifies the code further and removes an ff_ flag.

v7 removes maybe_promote_function_to_consteval and further simplifies
cp_fold_immediate_r logic.

v8 removes maybe_store_immediate_escalating_fn.

PR c++/107687
PR c++/110997

gcc/c-family/ChangeLog:

* c-cppbuiltin.cc (c_cpp_builtins): Update __cpp_consteval.
* c-opts.cc (c_common_post_options): Pre-C++20, unset
flag_immediate_escalation.
* c.opt (fimmediate-escalation): New option.

gcc/cp/ChangeLog:

* call.cc (in_immediate_context): No longer static.
* constexpr.cc (cxx_eval_call_expression): Adjust assert.
* cp-gimplify.cc (deferred_escalating_exprs): New vec.
(remember_escalating_expr): New.
(enum fold_flags): Remove ff_fold_immediate.
(immediate_escalating_function_p): New.
(unchecked_immediate_escalating_function_p): New.
(promote_function_to_consteval): New.
(cp_fold_immediate): Move above.  Return non-null if any errors were
emitted.
(maybe_explain_promoted_consteval): New.
(cp_gimplify_expr) <case CALL_EXPR>: Assert we've handled all
immediate invocations.
(taking_address_of_imm_fn_error): New.
(cp_fold_immediate_r): Merge ADDR_EXPR and PTRMEM_CST cases.  Implement
P2564 - promoting functions to consteval.
<case CALL_EXPR>: Implement P2564 - promoting functions to consteval.
(cp_fold_r): If an expression turns into a CALL_EXPR after cp_fold,
call cp_fold_immediate_r on the CALL_EXPR.
(cp_fold_function): Set DECL_ESCALATION_CHECKED_P if
deferred_escalating_exprs does not contain current_function_decl.
(process_and_check_pending_immediate_escalating_fns): New.
* cp-tree.h (struct lang_decl_fn): Add escalated_p bit-field.
(DECL_ESCALATION_CHECKED_P): New.
(immediate_invocation_p): Declare.
(process_pending_immediate_escalating_fns): Likewise.
* decl2.cc (c_parse_final_cleanups): Set at_eof to 2 after all
templates have been instantiated; and to 3 at the end of the function.
Call process_pending_immediate_escalating_fns.
* error.cc (dump_template_bindings): Check at_eof against an updated
value.
* module.cc (trees_out::lang_decl_bools): Stream escalated_p.
(trees_in::lang_decl_bools): Likewise.
* pt.cc (push_tinst_level_loc): Set at_eof to 3, not 2.
* typeck.cc (cp_build_addr_expr_1): Don't check
DECL_IMMEDIATE_FUNCTION_P.

gcc/ChangeLog:

* doc/invoke.texi: Document -fno-immediate-escalation.

libstdc++-v3/ChangeLog:

* testsuite/18_support/comparisons/categories/zero_neg.cc: Add
dg-prune-output.
* testsuite/std/format/string_neg.cc: Add dg-error.

gcc/testsuite/ChangeLog:

* g++.dg/cpp23/consteval-if10.C: Remove dg-error.
* g++.dg/cpp23/consteval-if2.C: Likewise.
* g++.dg/cpp23/feat-cxx2b.C: Adjust expected value of __cpp_consteval.
* g++.dg/cpp26/feat-cxx26.C: Likewise.
* g++.dg/cpp2a/consteval-memfn1.C: Add dg-error.
* g++.dg/cpp2a/consteval11.C: Likewise.
* g++.dg/cpp2a/consteval3.C: Adjust dg-error.
* g++.dg/cpp2a/consteval34.C: Add dg-error.
* g++.dg/cpp2a/consteval36.C: Likewise.
* g++.dg/cpp2a/consteval9.C: Likewise.
* g++.dg/cpp2a/feat-cxx2a.C: Adjust expected value of __cpp_consteval.
* g++.dg/cpp2a/spaceship-synth9.C: Adjust dg-error.
* g++.dg/cpp2a/consteval-prop1.C: New test.
* g++.dg/cpp2a/consteval-prop10.C: New test.
* g++.dg/cpp2a/consteval-prop11.C: New test.
* g++.dg/cpp2a/consteval-prop12.C: New test.
* g++.dg/cpp2a/consteval-prop13.C: New test.
* g++.dg/cpp2a/consteval-prop14.C: New test.
* g++.dg/cpp2a/consteval-prop15.C: New test.
* g++.dg/cpp2a/consteval-prop16.C: New test.
* g++.dg/cpp2a/consteval-prop17.C: New test.
* g++.dg/cpp2a/consteval-prop18.C: New test.
* g++.dg/cpp2a/consteval-prop19.C: New test.
* g++.dg/cpp2a/consteval-prop20.C: New test.
* g++.dg/cpp2a/consteval-prop2.C: New test.
* g++.dg/cpp2a/consteval-prop3.C: New test.
* g++.dg/cpp2a/consteval-prop4.C: New test.
* g++.dg/cpp2a/consteval-prop5.C: New test.
* g++.dg/cpp2a/consteval-prop6.C: New test.
* g++.dg/cpp2a/consteval-prop7.C: New test.
* g++.dg/cpp2a/consteval-prop8.C: New test.
* g++.dg/cpp2a/consteval-prop9.C: New test.
47 files changed:
gcc/c-family/c-cppbuiltin.cc
gcc/c-family/c-opts.cc
gcc/c-family/c.opt
gcc/cp/call.cc
gcc/cp/constexpr.cc
gcc/cp/cp-gimplify.cc
gcc/cp/cp-tree.h
gcc/cp/decl2.cc
gcc/cp/error.cc
gcc/cp/module.cc
gcc/cp/pt.cc
gcc/cp/typeck.cc
gcc/doc/invoke.texi
gcc/testsuite/g++.dg/cpp23/consteval-if10.C
gcc/testsuite/g++.dg/cpp23/consteval-if2.C
gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C
gcc/testsuite/g++.dg/cpp26/feat-cxx26.C
gcc/testsuite/g++.dg/cpp2a/consteval-memfn1.C
gcc/testsuite/g++.dg/cpp2a/consteval-prop1.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp2a/consteval-prop10.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp2a/consteval-prop11.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp2a/consteval-prop12.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp2a/consteval-prop13.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp2a/consteval-prop14.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp2a/consteval-prop15.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp2a/consteval-prop16.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp2a/consteval-prop17.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp2a/consteval-prop18.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp2a/consteval-prop19.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp2a/consteval-prop2.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp2a/consteval-prop20.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp2a/consteval-prop3.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp2a/consteval-prop4.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp2a/consteval-prop5.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp2a/consteval-prop6.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp2a/consteval-prop7.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp2a/consteval-prop8.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp2a/consteval-prop9.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp2a/consteval11.C
gcc/testsuite/g++.dg/cpp2a/consteval3.C
gcc/testsuite/g++.dg/cpp2a/consteval34.C
gcc/testsuite/g++.dg/cpp2a/consteval36.C
gcc/testsuite/g++.dg/cpp2a/consteval9.C
gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C
gcc/testsuite/g++.dg/cpp2a/spaceship-synth9.C
libstdc++-v3/testsuite/18_support/comparisons/categories/zero_neg.cc
libstdc++-v3/testsuite/std/format/string_neg.cc