public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* Re: [PATCH v8] c++: implement P2564, consteval needs to propagate up [PR107687]
@ 2023-12-11  8:49 FX Coudert
  2023-12-11 19:02 ` Jason Merrill
  0 siblings, 1 reply; 6+ messages in thread
From: FX Coudert @ 2023-12-11  8:49 UTC (permalink / raw)
  To: GCC Patches; +Cc: Marek Polacek

Hi Marek,

The patch is causing three failures on x86_64-apple-darwin21:

> FAIL: g++.dg/cpp2a/concepts-explicit-inst1.C -std=c++20 scan-assembler _Z1gI1XEvT_
> FAIL: g++.dg/cpp2a/concepts-explicit-inst1.C -std=c++20 scan-assembler _Z1gI1YEvT_
> FAIL: g++.dg/cpp2a/consteval-prop6.C -std=c++20 at line 58 (test for warnings, line 57)

How could I help debug this?

FX

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH v8] c++: implement P2564, consteval needs to propagate up [PR107687]
  2023-12-11  8:49 [PATCH v8] c++: implement P2564, consteval needs to propagate up [PR107687] FX Coudert
@ 2023-12-11 19:02 ` Jason Merrill
  2023-12-11 19:05   ` Marek Polacek
  0 siblings, 1 reply; 6+ messages in thread
From: Jason Merrill @ 2023-12-11 19:02 UTC (permalink / raw)
  To: FX Coudert, GCC Patches; +Cc: Marek Polacek

On 12/11/23 03:49, FX Coudert wrote:
> Hi Marek,
> 
> The patch is causing three failures on x86_64-apple-darwin21:
> 
>> FAIL: g++.dg/cpp2a/concepts-explicit-inst1.C -std=c++20 scan-assembler _Z1gI1XEvT_
>> FAIL: g++.dg/cpp2a/concepts-explicit-inst1.C -std=c++20 scan-assembler _Z1gI1YEvT_

I think these are from my r14-6064-gc3f281a0c1ca50 rather than Marek's 
patch.  The mangling of those two functions changed, but the test 
looking for the old mangling still passed on targets that support ABI 
compatibility aliases.  I'll fix the tests.

Jason

>> FAIL: g++.dg/cpp2a/consteval-prop6.C -std=c++20 at line 58 (test for warnings, line 57)
> 
> How could I help debug this?
> 
> FX
> 


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH v8] c++: implement P2564, consteval needs to propagate up [PR107687]
  2023-12-11 19:02 ` Jason Merrill
@ 2023-12-11 19:05   ` Marek Polacek
  0 siblings, 0 replies; 6+ messages in thread
From: Marek Polacek @ 2023-12-11 19:05 UTC (permalink / raw)
  To: Jason Merrill; +Cc: FX Coudert, GCC Patches

On Mon, Dec 11, 2023 at 02:02:52PM -0500, Jason Merrill wrote:
> On 12/11/23 03:49, FX Coudert wrote:
> > Hi Marek,
> > 
> > The patch is causing three failures on x86_64-apple-darwin21:
> > 
> > > FAIL: g++.dg/cpp2a/concepts-explicit-inst1.C -std=c++20 scan-assembler _Z1gI1XEvT_
> > > FAIL: g++.dg/cpp2a/concepts-explicit-inst1.C -std=c++20 scan-assembler _Z1gI1YEvT_
> 
> I think these are from my r14-6064-gc3f281a0c1ca50 rather than Marek's
> patch.  The mangling of those two functions changed, but the test looking
> for the old mangling still passed on targets that support ABI compatibility
> aliases.  I'll fix the tests.

Sounds plausible, thanks.  If the tests still fail with -fno-immediate-escalation,
it's the mangling changes.
 
> Jason
> 
> > > FAIL: g++.dg/cpp2a/consteval-prop6.C -std=c++20 at line 58 (test for warnings, line 57)
> > 
> > How could I help debug this?
> > 
> > FX
> > 
> 

Marek


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH v8] c++: implement P2564, consteval needs to propagate up [PR107687]
  2023-12-06 11:39                     ` Prathamesh Kulkarni
@ 2023-12-06 14:34                       ` Marek Polacek
  0 siblings, 0 replies; 6+ messages in thread
From: Marek Polacek @ 2023-12-06 14:34 UTC (permalink / raw)
  To: Prathamesh Kulkarni; +Cc: Jason Merrill, GCC Patches

On Wed, Dec 06, 2023 at 05:09:21PM +0530, Prathamesh Kulkarni wrote:
> On Tue, 5 Dec 2023 at 06:18, Marek Polacek <polacek@redhat.com> wrote:
> >
> > On Mon, Dec 04, 2023 at 04:49:29PM -0500, Jason Merrill wrote:
> > > On 12/4/23 15:23, Marek Polacek wrote:
> > > > +/* FN is not a consteval function, but may become one.  Remember to
> > > > +   escalate it after all pending templates have been instantiated.  */
> > > > +
> > > > +void
> > > > +maybe_store_immediate_escalating_fn (tree fn)
> > > > +{
> > > > +  if (unchecked_immediate_escalating_function_p (fn))
> > > > +    remember_escalating_expr (fn);
> > > > +}
> > >
> > > > +++ b/gcc/cp/decl.cc
> > > > @@ -18441,7 +18441,10 @@ finish_function (bool inline_p)
> > > >    if (!processing_template_decl
> > > >        && !DECL_IMMEDIATE_FUNCTION_P (fndecl)
> > > >        && !DECL_OMP_DECLARE_REDUCTION_P (fndecl))
> > > > -    cp_fold_function (fndecl);
> > > > +    {
> > > > +      cp_fold_function (fndecl);
> > > > +      maybe_store_immediate_escalating_fn (fndecl);
> > > > +    }
> > >
> > > I think maybe_store_, and the call to it from finish_function, are unneeded;
> > > we will have already decided whether we need to remember the function during
> > > the call to cp_fold_function.
> >
> > 'Tis true.
> >
> > > OK with that change.
> >
> > Here's what I pushed after another regtest.  Thanks!
> Hi Marek,
> It seems the patch caused following regressions on aarch64:
> 
> Running g++:g++.dg/modules/modules.exp ...
> FAIL: g++.dg/modules/xtreme-header-4_b.C -std=c++2b (internal compiler
> error: tree check: expected class 'type', have 'declaration'
> (template_decl) in get_originating_module_decl, at cp/module.cc:18659)
> FAIL: g++.dg/modules/xtreme-header-5_b.C -std=c++2b (internal compiler
> error: tree check: expected class 'type', have 'declaration'
> (template_decl) in get_originating_module_decl, at cp/module.cc:18659)
> FAIL: g++.dg/modules/xtreme-header_b.C -std=c++2b (internal compiler
> error: tree check: expected class 'type', have 'declaration'
> (template_decl) in get_originating_module_decl, at cp/module.cc:18659)
> 
> Log files: https://ci.linaro.org/job/tcwg_gcc_check--master-aarch64-build/1299/artifact/artifacts/00-sumfiles/

Are you sure it's caused by my patch?  I reckon I've seen that FAIL
many times before.

Marek


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH v8] c++: implement P2564, consteval needs to propagate up [PR107687]
  2023-12-05  0:44                   ` [PATCH v8] " Marek Polacek
@ 2023-12-06 11:39                     ` Prathamesh Kulkarni
  2023-12-06 14:34                       ` Marek Polacek
  0 siblings, 1 reply; 6+ messages in thread
From: Prathamesh Kulkarni @ 2023-12-06 11:39 UTC (permalink / raw)
  To: Marek Polacek; +Cc: Jason Merrill, GCC Patches

On Tue, 5 Dec 2023 at 06:18, Marek Polacek <polacek@redhat.com> wrote:
>
> On Mon, Dec 04, 2023 at 04:49:29PM -0500, Jason Merrill wrote:
> > On 12/4/23 15:23, Marek Polacek wrote:
> > > +/* FN is not a consteval function, but may become one.  Remember to
> > > +   escalate it after all pending templates have been instantiated.  */
> > > +
> > > +void
> > > +maybe_store_immediate_escalating_fn (tree fn)
> > > +{
> > > +  if (unchecked_immediate_escalating_function_p (fn))
> > > +    remember_escalating_expr (fn);
> > > +}
> >
> > > +++ b/gcc/cp/decl.cc
> > > @@ -18441,7 +18441,10 @@ finish_function (bool inline_p)
> > >    if (!processing_template_decl
> > >        && !DECL_IMMEDIATE_FUNCTION_P (fndecl)
> > >        && !DECL_OMP_DECLARE_REDUCTION_P (fndecl))
> > > -    cp_fold_function (fndecl);
> > > +    {
> > > +      cp_fold_function (fndecl);
> > > +      maybe_store_immediate_escalating_fn (fndecl);
> > > +    }
> >
> > I think maybe_store_, and the call to it from finish_function, are unneeded;
> > we will have already decided whether we need to remember the function during
> > the call to cp_fold_function.
>
> 'Tis true.
>
> > OK with that change.
>
> Here's what I pushed after another regtest.  Thanks!
Hi Marek,
It seems the patch caused following regressions on aarch64:

Running g++:g++.dg/modules/modules.exp ...
FAIL: g++.dg/modules/xtreme-header-4_b.C -std=c++2b (internal compiler
error: tree check: expected class 'type', have 'declaration'
(template_decl) in get_originating_module_decl, at cp/module.cc:18659)
FAIL: g++.dg/modules/xtreme-header-5_b.C -std=c++2b (internal compiler
error: tree check: expected class 'type', have 'declaration'
(template_decl) in get_originating_module_decl, at cp/module.cc:18659)
FAIL: g++.dg/modules/xtreme-header_b.C -std=c++2b (internal compiler
error: tree check: expected class 'type', have 'declaration'
(template_decl) in get_originating_module_decl, at cp/module.cc:18659)

Log files: https://ci.linaro.org/job/tcwg_gcc_check--master-aarch64-build/1299/artifact/artifacts/00-sumfiles/

Thanks,
Prathamesh
>
> -- >8 --
> 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/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.
> ---
>  gcc/c-family/c-cppbuiltin.cc                  |   2 +-
>  gcc/c-family/c-opts.cc                        |   5 +
>  gcc/c-family/c.opt                            |   4 +
>  gcc/cp/call.cc                                |   2 +-
>  gcc/cp/constexpr.cc                           |   4 +-
>  gcc/cp/cp-gimplify.cc                         | 348 ++++++++++++++----
>  gcc/cp/cp-tree.h                              |  19 +-
>  gcc/cp/decl2.cc                               |  16 +-
>  gcc/cp/error.cc                               |   2 +-
>  gcc/cp/module.cc                              |   4 +
>  gcc/cp/pt.cc                                  |   2 +-
>  gcc/cp/typeck.cc                              |   6 +-
>  gcc/doc/invoke.texi                           |  34 ++
>  gcc/testsuite/g++.dg/cpp23/consteval-if10.C   |   7 +-
>  gcc/testsuite/g++.dg/cpp23/consteval-if2.C    |  14 +-
>  gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C       |   4 +-
>  gcc/testsuite/g++.dg/cpp26/feat-cxx26.C       |   4 +-
>  gcc/testsuite/g++.dg/cpp2a/consteval-memfn1.C |   3 +
>  gcc/testsuite/g++.dg/cpp2a/consteval-prop1.C  | 169 +++++++++
>  gcc/testsuite/g++.dg/cpp2a/consteval-prop10.C |  41 +++
>  gcc/testsuite/g++.dg/cpp2a/consteval-prop11.C |  49 +++
>  gcc/testsuite/g++.dg/cpp2a/consteval-prop12.C |  30 ++
>  gcc/testsuite/g++.dg/cpp2a/consteval-prop13.C |  23 ++
>  gcc/testsuite/g++.dg/cpp2a/consteval-prop14.C |  78 ++++
>  gcc/testsuite/g++.dg/cpp2a/consteval-prop15.C | 107 ++++++
>  gcc/testsuite/g++.dg/cpp2a/consteval-prop16.C |  73 ++++
>  gcc/testsuite/g++.dg/cpp2a/consteval-prop17.C |  17 +
>  gcc/testsuite/g++.dg/cpp2a/consteval-prop18.C |  20 +
>  gcc/testsuite/g++.dg/cpp2a/consteval-prop19.C |   7 +
>  gcc/testsuite/g++.dg/cpp2a/consteval-prop2.C  |  90 +++++
>  gcc/testsuite/g++.dg/cpp2a/consteval-prop20.C |  21 ++
>  gcc/testsuite/g++.dg/cpp2a/consteval-prop3.C  |  27 ++
>  gcc/testsuite/g++.dg/cpp2a/consteval-prop4.C  |  30 ++
>  gcc/testsuite/g++.dg/cpp2a/consteval-prop5.C  |  27 ++
>  gcc/testsuite/g++.dg/cpp2a/consteval-prop6.C  |  59 +++
>  gcc/testsuite/g++.dg/cpp2a/consteval-prop7.C  |  76 ++++
>  gcc/testsuite/g++.dg/cpp2a/consteval-prop8.C  |  82 +++++
>  gcc/testsuite/g++.dg/cpp2a/consteval-prop9.C  |  67 ++++
>  gcc/testsuite/g++.dg/cpp2a/consteval11.C      |  18 +
>  gcc/testsuite/g++.dg/cpp2a/consteval3.C       |   4 +-
>  gcc/testsuite/g++.dg/cpp2a/consteval34.C      |   8 +
>  gcc/testsuite/g++.dg/cpp2a/consteval36.C      |  26 +-
>  gcc/testsuite/g++.dg/cpp2a/consteval9.C       |   2 +
>  gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C       |   4 +-
>  gcc/testsuite/g++.dg/cpp2a/spaceship-synth9.C |   2 +-
>  .../comparisons/categories/zero_neg.cc        |   1 +
>  .../testsuite/std/format/string_neg.cc        |   2 +-
>  47 files changed, 1525 insertions(+), 115 deletions(-)
>  create mode 100644 gcc/testsuite/g++.dg/cpp2a/consteval-prop1.C
>  create mode 100644 gcc/testsuite/g++.dg/cpp2a/consteval-prop10.C
>  create mode 100644 gcc/testsuite/g++.dg/cpp2a/consteval-prop11.C
>  create mode 100644 gcc/testsuite/g++.dg/cpp2a/consteval-prop12.C
>  create mode 100644 gcc/testsuite/g++.dg/cpp2a/consteval-prop13.C
>  create mode 100644 gcc/testsuite/g++.dg/cpp2a/consteval-prop14.C
>  create mode 100644 gcc/testsuite/g++.dg/cpp2a/consteval-prop15.C
>  create mode 100644 gcc/testsuite/g++.dg/cpp2a/consteval-prop16.C
>  create mode 100644 gcc/testsuite/g++.dg/cpp2a/consteval-prop17.C
>  create mode 100644 gcc/testsuite/g++.dg/cpp2a/consteval-prop18.C
>  create mode 100644 gcc/testsuite/g++.dg/cpp2a/consteval-prop19.C
>  create mode 100644 gcc/testsuite/g++.dg/cpp2a/consteval-prop2.C
>  create mode 100644 gcc/testsuite/g++.dg/cpp2a/consteval-prop20.C
>  create mode 100644 gcc/testsuite/g++.dg/cpp2a/consteval-prop3.C
>  create mode 100644 gcc/testsuite/g++.dg/cpp2a/consteval-prop4.C
>  create mode 100644 gcc/testsuite/g++.dg/cpp2a/consteval-prop5.C
>  create mode 100644 gcc/testsuite/g++.dg/cpp2a/consteval-prop6.C
>  create mode 100644 gcc/testsuite/g++.dg/cpp2a/consteval-prop7.C
>  create mode 100644 gcc/testsuite/g++.dg/cpp2a/consteval-prop8.C
>  create mode 100644 gcc/testsuite/g++.dg/cpp2a/consteval-prop9.C
>
> diff --git a/gcc/c-family/c-cppbuiltin.cc b/gcc/c-family/c-cppbuiltin.cc
> index e536429fa4c..2d1249f29ed 100644
> --- a/gcc/c-family/c-cppbuiltin.cc
> +++ b/gcc/c-family/c-cppbuiltin.cc
> @@ -1059,7 +1059,7 @@ c_cpp_builtins (cpp_reader *pfile)
>             cpp_define (pfile, "__cpp_constexpr=202002L");
>           cpp_define (pfile, "__cpp_constexpr_in_decltype=201711L");
>           cpp_define (pfile, "__cpp_conditional_explicit=201806L");
> -         cpp_define (pfile, "__cpp_consteval=201811L");
> +         cpp_define (pfile, "__cpp_consteval=202211L");
>           cpp_define (pfile, "__cpp_constinit=201907L");
>           cpp_define (pfile, "__cpp_deduction_guides=201907L");
>           cpp_define (pfile, "__cpp_nontype_template_args=201911L");
> diff --git a/gcc/c-family/c-opts.cc b/gcc/c-family/c-opts.cc
> index d7faff10d66..d484ecfdfdb 100644
> --- a/gcc/c-family/c-opts.cc
> +++ b/gcc/c-family/c-opts.cc
> @@ -1139,6 +1139,11 @@ c_common_post_options (const char **pfilename)
>    if (cxx_dialect >= cxx20 || flag_concepts_ts)
>      flag_concepts = 1;
>
> +  /* -fimmediate-escalation has no effect when immediate functions are not
> +     supported.  */
> +  if (flag_immediate_escalation && cxx_dialect < cxx20)
> +    flag_immediate_escalation = 0;
> +
>    if (num_in_fnames > 1)
>      error ("too many filenames given; type %<%s %s%> for usage",
>            progname, "--help");
> diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
> index ab44a6da66a..3706505f8bf 100644
> --- a/gcc/c-family/c.opt
> +++ b/gcc/c-family/c.opt
> @@ -1898,6 +1898,10 @@ fhuge-objects
>  C++ ObjC++ WarnRemoved
>  No longer supported.
>
> +fimmediate-escalation
> +C++ ObjC++ Var(flag_immediate_escalation) Init(1)
> +Implement P2564 for consteval propagation.
> +
>  fimplement-inlines
>  C++ ObjC++ Var(flag_implement_inlines) Init(1)
>  Export functions even if they can be inlined.
> diff --git a/gcc/cp/call.cc b/gcc/cp/call.cc
> index ae0decd87f1..c7efc5b077a 100644
> --- a/gcc/cp/call.cc
> +++ b/gcc/cp/call.cc
> @@ -9742,7 +9742,7 @@ in_immediate_context ()
>  /* Return true if a call to FN with number of arguments NARGS
>     is an immediate invocation.  */
>
> -static bool
> +bool
>  immediate_invocation_p (tree fn)
>  {
>    return (TREE_CODE (fn) == FUNCTION_DECL
> diff --git a/gcc/cp/constexpr.cc b/gcc/cp/constexpr.cc
> index 96c61666470..58187a4fd12 100644
> --- a/gcc/cp/constexpr.cc
> +++ b/gcc/cp/constexpr.cc
> @@ -3128,11 +3128,11 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
>         /* OK */;
>        else if (!DECL_SAVED_TREE (fun))
>         {
> -         /* When at_eof >= 2, cgraph has started throwing away
> +         /* When at_eof >= 3, cgraph has started throwing away
>              DECL_SAVED_TREE, so fail quietly.  FIXME we get here because of
>              late code generation for VEC_INIT_EXPR, which needs to be
>              completely reconsidered.  */
> -         gcc_assert (at_eof >= 2 && ctx->quiet);
> +         gcc_assert (at_eof >= 3 && ctx->quiet);
>           *non_constant_p = true;
>         }
>        else if (tree copy = get_fundef_copy (new_call.fundef))
> diff --git a/gcc/cp/cp-gimplify.cc b/gcc/cp/cp-gimplify.cc
> index 795c811471d..5abb91bbdd3 100644
> --- a/gcc/cp/cp-gimplify.cc
> +++ b/gcc/cp/cp-gimplify.cc
> @@ -43,6 +43,21 @@ along with GCC; see the file COPYING3.  If not see
>  #include "omp-general.h"
>  #include "opts.h"
>
> +/* Keep track of forward references to immediate-escalating functions in
> +   case they become consteval.  This vector contains ADDR_EXPRs and
> +   PTRMEM_CSTs; it also stores FUNCTION_DECLs that had an escalating
> +   function call in them, to check that they can be evaluated to a constant,
> +   and immediate-escalating functions that may become consteval.  */
> +static GTY(()) hash_set<tree> *deferred_escalating_exprs;
> +
> +static void
> +remember_escalating_expr (tree t)
> +{
> +  if (!deferred_escalating_exprs)
> +    deferred_escalating_exprs = hash_set<tree>::create_ggc (37);
> +  deferred_escalating_exprs->add (t);
> +}
> +
>  /* Flags for cp_fold and cp_fold_r.  */
>
>  enum fold_flags {
> @@ -53,8 +68,6 @@ enum fold_flags {
>       definitely not in a manifestly constant-evaluated
>       context.  */
>    ff_mce_false = 1 << 1,
> -  /* Whether we're being called from cp_fold_immediate.  */
> -  ff_fold_immediate = 1 << 2,
>  };
>
>  using fold_flags_t = int;
> @@ -72,6 +85,7 @@ static tree cp_genericize_r (tree *, int *, void *);
>  static tree cp_fold_r (tree *, int *, void *);
>  static void cp_genericize_tree (tree*, bool);
>  static tree cp_fold (tree, fold_flags_t);
> +static tree cp_fold_immediate_r (tree *, int *, void *);
>
>  /* Genericize a TRY_BLOCK.  */
>
> @@ -428,6 +442,104 @@ lvalue_has_side_effects (tree e)
>      return TREE_SIDE_EFFECTS (e);
>  }
>
> +/* Return true if FN is an immediate-escalating function.  */
> +
> +static bool
> +immediate_escalating_function_p (tree fn)
> +{
> +  if (!fn || !flag_immediate_escalation)
> +    return false;
> +
> +  gcc_checking_assert (TREE_CODE (fn) == FUNCTION_DECL);
> +
> +  if (DECL_IMMEDIATE_FUNCTION_P (fn))
> +    return false;
> +
> +  /* An immediate-escalating function is
> +      -- the call operator of a lambda that is not declared with the consteval
> +        specifier  */
> +  if (LAMBDA_FUNCTION_P (fn))
> +    return true;
> +  /* -- a defaulted special member function that is not declared with the
> +       consteval specifier  */
> +  special_function_kind sfk = special_memfn_p (fn);
> +  if (sfk != sfk_none && DECL_DEFAULTED_FN (fn))
> +    return true;
> +  /* -- a function that results from the instantiation of a templated entity
> +       defined with the constexpr specifier.  */
> +  return is_instantiation_of_constexpr (fn);
> +}
> +
> +/* Return true if FN is an immediate-escalating function that has not been
> +   checked for escalating expressions..  */
> +
> +static bool
> +unchecked_immediate_escalating_function_p (tree fn)
> +{
> +  return (immediate_escalating_function_p (fn)
> +         && !DECL_ESCALATION_CHECKED_P (fn));
> +}
> +
> +/* Promote FN to an immediate function, including its clones.  */
> +
> +static void
> +promote_function_to_consteval (tree fn)
> +{
> +  SET_DECL_IMMEDIATE_FUNCTION_P (fn);
> +  DECL_ESCALATION_CHECKED_P (fn) = true;
> +  tree clone;
> +  FOR_EACH_CLONE (clone, fn)
> +    {
> +      SET_DECL_IMMEDIATE_FUNCTION_P (clone);
> +      DECL_ESCALATION_CHECKED_P (clone) = true;
> +    }
> +}
> +
> +/* A wrapper around cp_fold_immediate_r.  Return a non-null tree if
> +   we found a non-constant immediate function, or taking the address
> +   of an immediate function.  */
> +
> +tree
> +cp_fold_immediate (tree *tp, mce_value manifestly_const_eval,
> +                  tree decl /*= current_function_decl*/)
> +{
> +  if (cxx_dialect <= cxx17)
> +    return NULL_TREE;
> +
> +  temp_override<tree> cfd (current_function_decl, decl);
> +
> +  fold_flags_t flags = ff_none;
> +  if (manifestly_const_eval == mce_false)
> +    flags |= ff_mce_false;
> +
> +  cp_fold_data data (flags);
> +  int save_errorcount = errorcount;
> +  tree r = cp_walk_tree_without_duplicates (tp, cp_fold_immediate_r, &data);
> +  if (errorcount > save_errorcount)
> +    return integer_one_node;
> +  return r;
> +}
> +
> +/* Maybe say that FN (a function decl with DECL_IMMEDIATE_FUNCTION_P set)
> +   was initially not an immediate function, but was promoted to one because
> +   its body contained an immediate-escalating expression or conversion.  */
> +
> +static void
> +maybe_explain_promoted_consteval (location_t loc, tree fn)
> +{
> +  if (DECL_ESCALATION_CHECKED_P (fn))
> +    {
> +      /* See if we can figure out what made the function consteval.  */
> +      tree x = cp_fold_immediate (&DECL_SAVED_TREE (fn), mce_unknown, NULL_TREE);
> +      if (x)
> +       inform (cp_expr_loc_or_loc (x, loc),
> +               "%qD was promoted to an immediate function because its "
> +               "body contains an immediate-escalating expression %qE", fn, x);
> +      else
> +       inform (loc, "%qD was promoted to an immediate function", fn);
> +    }
> +}
> +
>  /* Gimplify *EXPR_P as rvalue into an expression that can't be modified
>     by expressions with side-effects in other operands.  */
>
> @@ -746,7 +858,9 @@ cp_gimplify_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p)
>        if (ret != GS_ERROR)
>         {
>           tree decl = cp_get_callee_fndecl_nofold (*expr_p);
> -         if (decl && fndecl_built_in_p (decl, BUILT_IN_FRONTEND))
> +         if (!decl)
> +           break;
> +         if (fndecl_built_in_p (decl, BUILT_IN_FRONTEND))
>             switch (DECL_FE_FUNCTION_CODE (decl))
>               {
>               case CP_BUILT_IN_IS_CONSTANT_EVALUATED:
> @@ -771,10 +885,12 @@ cp_gimplify_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p)
>               default:
>                 break;
>               }
> -         else if (decl
> -                  && fndecl_built_in_p (decl, BUILT_IN_CLZG, BUILT_IN_CTZG))
> +         else if (fndecl_built_in_p (decl, BUILT_IN_CLZG, BUILT_IN_CTZG))
>             ret = (enum gimplify_status) c_gimplify_expr (expr_p, pre_p,
>                                                           post_p);
> +         else
> +           /* All consteval functions should have been processed by now.  */
> +           gcc_checking_assert (!immediate_invocation_p (decl));
>         }
>        break;
>
> @@ -1035,6 +1151,20 @@ struct cp_genericize_data
>    bool handle_invisiref_parm_p;
>  };
>
> +/* Emit an error about taking the address of an immediate function.
> +   EXPR is the whole expression; DECL is the immediate function.  */
> +
> +static void
> +taking_address_of_imm_fn_error (tree expr, tree decl)
> +{
> +  auto_diagnostic_group d;
> +  const location_t loc = (TREE_CODE (expr) == PTRMEM_CST
> +                         ? PTRMEM_CST_LOCATION (expr)
> +                         : EXPR_LOCATION (expr));
> +  error_at (loc, "taking address of an immediate function %qD", decl);
> +  maybe_explain_promoted_consteval (loc, decl);
> +}
> +
>  /* A subroutine of cp_fold_r to handle immediate functions.  */
>
>  static tree
> @@ -1045,90 +1175,128 @@ cp_fold_immediate_r (tree *stmt_p, int *walk_subtrees, void *data_)
>    /* The purpose of this is not to emit errors for mce_unknown.  */
>    const tsubst_flags_t complain = (data->flags & ff_mce_false
>                                    ? tf_error : tf_none);
> +  const tree_code code = TREE_CODE (stmt);
>
>    /* No need to look into types or unevaluated operands.
>       NB: This affects cp_fold_r as well.  */
> -  if (TYPE_P (stmt) || unevaluated_p (TREE_CODE (stmt)))
> +  if (TYPE_P (stmt) || unevaluated_p (code) || in_immediate_context ())
>      {
>        *walk_subtrees = 0;
>        return NULL_TREE;
>      }
>
> -  switch (TREE_CODE (stmt))
> -    {
> -    case PTRMEM_CST:
> -      if (TREE_CODE (PTRMEM_CST_MEMBER (stmt)) == FUNCTION_DECL
> -         && DECL_IMMEDIATE_FUNCTION_P (PTRMEM_CST_MEMBER (stmt)))
> -       {
> -         if (!data->pset.add (stmt) && (complain & tf_error))
> -           {
> -             error_at (PTRMEM_CST_LOCATION (stmt),
> -                       "taking address of an immediate function %qD",
> -                       PTRMEM_CST_MEMBER (stmt));
> -             *stmt_p = build_zero_cst (TREE_TYPE (stmt));
> -           }
> -         return error_mark_node;
> -       }
> -      break;
> +  tree decl = NULL_TREE;
> +  bool call_p = false;
>
> -    /* Expand immediate invocations.  */
> +  /* We are looking for &fn or fn().  */
> +  switch (code)
> +    {
>      case CALL_EXPR:
>      case AGGR_INIT_EXPR:
>        if (tree fn = cp_get_callee (stmt))
>         if (TREE_CODE (fn) != ADDR_EXPR || ADDR_EXPR_DENOTES_CALL_P (fn))
> -         if (tree fndecl = cp_get_fndecl_from_callee (fn, /*fold*/false))
> -           if (DECL_IMMEDIATE_FUNCTION_P (fndecl))
> -             {
> -               stmt = cxx_constant_value (stmt, complain);
> -               if (stmt == error_mark_node)
> -                 {
> -                   if (complain & tf_error)
> -                     *stmt_p = error_mark_node;
> -                   return error_mark_node;
> -                 }
> -               *stmt_p = stmt;
> -             }
> +         decl = cp_get_fndecl_from_callee (fn, /*fold*/false);
> +      call_p = true;
> +      break;
> +    case PTRMEM_CST:
> +      decl = PTRMEM_CST_MEMBER (stmt);
>        break;
> -
>      case ADDR_EXPR:
> -      if (TREE_CODE (TREE_OPERAND (stmt, 0)) == FUNCTION_DECL
> -         && DECL_IMMEDIATE_FUNCTION_P (TREE_OPERAND (stmt, 0))
> -         && !ADDR_EXPR_DENOTES_CALL_P (stmt))
> -       {
> -         if (complain & tf_error)
> -           {
> -             error_at (EXPR_LOCATION (stmt),
> -                       "taking address of an immediate function %qD",
> -                       TREE_OPERAND (stmt, 0));
> -             *stmt_p = build_zero_cst (TREE_TYPE (stmt));
> -           }
> -         return error_mark_node;
> -       }
> +      if (!ADDR_EXPR_DENOTES_CALL_P (stmt))
> +       decl = TREE_OPERAND (stmt, 0);
>        break;
> -
>      default:
> -      break;
> +      return NULL_TREE;
>      }
>
> -  return NULL_TREE;
> -}
> +  if (!decl || TREE_CODE (decl) != FUNCTION_DECL)
> +    return NULL_TREE;
>
> -/* A wrapper around cp_fold_immediate_r.  Return true if we found
> -   a non-constant immediate function, or taking the address of an
> -   immediate function.  */
> +  /* Fully escalate once all templates have been instantiated.  What we're
> +     calling is not a consteval function but it may become one.  This
> +     requires recursing; DECL may be promoted to consteval because it
> +     contains an escalating expression E, but E itself may have to be
> +     promoted first, etc.  */
> +  if (at_eof > 1 && unchecked_immediate_escalating_function_p (decl))
> +    {
> +      /* Set before the actual walk to avoid endless recursion.  */
> +      DECL_ESCALATION_CHECKED_P (decl) = true;
> +      /* We're only looking for the first escalating expression.  Let us not
> +        walk more trees than necessary, hence mce_unknown.  */
> +      cp_fold_immediate (&DECL_SAVED_TREE (decl), mce_unknown, decl);
> +    }
>
> -bool
> -cp_fold_immediate (tree *tp, mce_value manifestly_const_eval)
> -{
> -  if (cxx_dialect <= cxx17)
> -    return false;
> +  /* [expr.const]p16 "An expression or conversion is immediate-escalating if
> +     it is not initially in an immediate function context and it is either
> +     -- an immediate invocation that is not a constant expression and is not
> +     a subexpression of an immediate invocation."
>
> -  fold_flags_t flags = ff_fold_immediate;
> -  if (manifestly_const_eval == mce_false)
> -    flags |= ff_mce_false;
> +     If we are in an immediate-escalating function, the immediate-escalating
> +     expression or conversion makes it an immediate function.  So STMT does
> +     not need to produce a constant expression.  */
> +  if (DECL_IMMEDIATE_FUNCTION_P (decl))
> +    {
> +      tree e = cxx_constant_value (stmt, tf_none);
> +      if (e == error_mark_node)
> +       {
> +         /* This takes care of, e.g.,
> +             template <typename T>
> +             constexpr int f(T t)
> +             {
> +               return id(t);
> +             }
> +           where id (consteval) causes f<int> to be promoted.  */
> +         if (immediate_escalating_function_p (current_function_decl))
> +           promote_function_to_consteval (current_function_decl);
> +         else if (complain & tf_error)
> +           {
> +             if (call_p)
> +               {
> +                 auto_diagnostic_group d;
> +                 location_t loc = cp_expr_loc_or_input_loc (stmt);
> +                 error_at (loc, "call to consteval function %qE is "
> +                           "not a constant expression", stmt);
> +                 /* Explain why it's not a constant expression.  */
> +                 *stmt_p = cxx_constant_value (stmt, complain);
> +                 maybe_explain_promoted_consteval (loc, decl);
> +               }
> +             else if (!data->pset.add (stmt))
> +               {
> +                 taking_address_of_imm_fn_error (stmt, decl);
> +                 *stmt_p = build_zero_cst (TREE_TYPE (stmt));
> +               }
> +             /* If we're giving hard errors, continue the walk rather than
> +                bailing out after the first error.  */
> +             return NULL_TREE;
> +           }
> +         *walk_subtrees = 0;
> +         return stmt;
> +       }
> +      /* We've evaluated the consteval function call.  */
> +      if (call_p)
> +       *stmt_p = e;
> +    }
> +  /* We've encountered a function call that may turn out to be consteval
> +     later.  Store its caller so that we can ensure that the call is
> +     a constant expression.  */
> +  else if (unchecked_immediate_escalating_function_p (decl))
> +    {
> +      /* Make sure we're not inserting new elements while walking
> +        the deferred_escalating_exprs hash table; if we are, it's
> +        likely that a function wasn't properly marked checked for
> +        i-e expressions.  */
> +      gcc_checking_assert (at_eof <= 1);
> +      if (current_function_decl)
> +       remember_escalating_expr (current_function_decl);
> +      /* auto p = &f<int>; in the global scope won't be ensconced in
> +        a function we could store for later at this point.  (If there's
> +        no c_f_d at this point and we're dealing with a call, we should
> +        see the call when cp_fold_function __static_i_and_d.)  */
> +      else if (!call_p)
> +       remember_escalating_expr (stmt);
> +    }
>
> -  cp_fold_data data (flags);
> -  return !!cp_walk_tree_without_duplicates (tp, cp_fold_immediate_r, &data);
> +  return NULL_TREE;
>  }
>
>  /* Perform any pre-gimplification folding of C++ front end trees to
> @@ -1178,11 +1346,19 @@ cp_fold_r (tree *stmt_p, int *walk_subtrees, void *data_)
>           *walk_subtrees = 0;
>           /* Don't return yet, still need the cp_fold below.  */
>         }
> -      cp_fold_immediate_r (stmt_p, walk_subtrees, data);
> +      else
> +       cp_fold_immediate_r (stmt_p, walk_subtrees, data);
>      }
>
>    *stmt_p = stmt = cp_fold (*stmt_p, data->flags);
>
> +  /* For certain trees, like +foo(), the cp_fold above will remove the +,
> +     and the subsequent tree walk would go straight down to the CALL_EXPR's
> +     operands, meaning that cp_fold_immediate_r would never see the
> +     CALL_EXPR.  Ew :(.  */
> +  if (TREE_CODE (stmt) == CALL_EXPR && code != CALL_EXPR)
> +    cp_fold_immediate_r (stmt_p, walk_subtrees, data);
> +
>    if (data->pset.add (stmt))
>      {
>        /* Don't walk subtrees of stmts we've already walked once, otherwise
> @@ -1304,6 +1480,44 @@ cp_fold_function (tree fndecl)
>       pass ff_mce_false.  */
>    cp_fold_data data (ff_genericize | ff_mce_false);
>    cp_walk_tree (&DECL_SAVED_TREE (fndecl), cp_fold_r, &data, NULL);
> +
> +  /* This is merely an optimization: if FNDECL has no i-e expressions,
> +     we'll not save c_f_d, and we can safely say that FNDECL will not
> +     be promoted to consteval.  */
> +  if (deferred_escalating_exprs
> +      && !deferred_escalating_exprs->contains (current_function_decl))
> +    DECL_ESCALATION_CHECKED_P (fndecl) = true;
> +}
> +
> +/* We've stashed immediate-escalating functions.  Now see if they indeed
> +   ought to be promoted to consteval.  */
> +
> +void
> +process_and_check_pending_immediate_escalating_fns ()
> +{
> +  /* This will be null for -fno-immediate-escalation.  */
> +  if (!deferred_escalating_exprs)
> +    return;
> +
> +  for (auto e : *deferred_escalating_exprs)
> +    if (TREE_CODE (e) == FUNCTION_DECL && !DECL_ESCALATION_CHECKED_P (e))
> +      cp_fold_immediate (&DECL_SAVED_TREE (e), mce_false, e);
> +
> +  /* We've escalated every function that could have been promoted to
> +     consteval.  Check that we are not taking the address of a consteval
> +     function.  */
> +  for (auto e : *deferred_escalating_exprs)
> +    {
> +      if (TREE_CODE (e) == FUNCTION_DECL)
> +       continue;
> +      tree decl = (TREE_CODE (e) == PTRMEM_CST
> +                  ? PTRMEM_CST_MEMBER (e)
> +                  : TREE_OPERAND (e, 0));
> +      if (DECL_IMMEDIATE_FUNCTION_P (decl))
> +       taking_address_of_imm_fn_error (e, decl);
> +    }
> +
> +  deferred_escalating_exprs = nullptr;
>  }
>
>  /* Turn SPACESHIP_EXPR EXPR into GENERIC.  */
> diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
> index 6b3ce9d87da..fef80816c5f 100644
> --- a/gcc/cp/cp-tree.h
> +++ b/gcc/cp/cp-tree.h
> @@ -2946,8 +2946,9 @@ struct GTY(()) lang_decl_fn {
>    unsigned maybe_deleted : 1;
>    unsigned coroutine_p : 1;
>    unsigned implicit_constexpr : 1;
> +  unsigned escalated_p : 1;
>
> -  unsigned spare : 9;
> +  unsigned spare : 8;
>
>    /* 32-bits padding on 64-bit host.  */
>
> @@ -3399,6 +3400,14 @@ struct GTY(()) lang_decl {
>  #define DECL_MAYBE_DELETED(NODE) \
>    (LANG_DECL_FN_CHECK (NODE)->maybe_deleted)
>
> +/* Nonzero for FUNCTION_DECL means that this function's body has been
> +   checked for immediate-escalating expressions and maybe promoted.  It
> +   does *not* mean the function is consteval.  It must not be set in
> +   a function that was marked consteval by the user, so that we can
> +   distinguish between explicitly consteval functions and promoted consteval
> +   functions.  */
> +#define DECL_ESCALATION_CHECKED_P(NODE) (LANG_DECL_FN_CHECK (NODE)->escalated_p)
> +
>  /* True (in a FUNCTION_DECL) if NODE is a virtual function that is an
>     invalid overrider for a function from a base class.  Once we have
>     complained about an invalid overrider we avoid complaining about it
> @@ -5882,7 +5891,8 @@ extern GTY(()) vec<tree, va_gc> *keyed_classes;
>
>
>  /* Nonzero if we're done parsing and into end-of-file activities.
> -   Two if we're done with front-end processing.  */
> +   2 if all templates have been instantiated.
> +   3 if we're done with front-end processing.  */
>
>  extern int at_eof;
>
> @@ -6774,6 +6784,7 @@ extern tree perform_direct_initialization_if_possible (tree, tree, bool,
>  extern vec<tree,va_gc> *resolve_args (vec<tree,va_gc>*, tsubst_flags_t);
>  extern tree in_charge_arg_for_name             (tree);
>  extern bool in_immediate_context               ();
> +extern bool immediate_invocation_p             (tree);
>  extern tree build_cxx_call                     (tree, int, tree *,
>                                                  tsubst_flags_t,
>                                                  tree = NULL_TREE);
> @@ -8415,7 +8426,9 @@ extern tree process_stmt_assume_attribute (tree, tree, location_t);
>  extern bool simple_empty_class_p               (tree, tree, tree_code);
>  extern tree fold_builtin_source_location       (const_tree);
>  extern tree get_source_location_impl_type      ();
> -extern bool cp_fold_immediate                  (tree *, mce_value);
> +extern tree cp_fold_immediate                  (tree *, mce_value,
> +                                                tree = current_function_decl);
> +extern void process_and_check_pending_immediate_escalating_fns ();
>
>  /* in name-lookup.cc */
>  extern tree strip_using_decl                    (tree);
> diff --git a/gcc/cp/decl2.cc b/gcc/cp/decl2.cc
> index 9e666e5eece..bee84879023 100644
> --- a/gcc/cp/decl2.cc
> +++ b/gcc/cp/decl2.cc
> @@ -169,7 +169,9 @@ typedef hash_map<unsigned/*Priority*/, tree/*List*/,
>     one for init.  The fini table is only ever used when !cxa_atexit.  */
>  static GTY(()) priority_map_t *static_init_fini_fns[2];
>
> -/* Nonzero if we're done parsing and into end-of-file activities.  */
> +/* Nonzero if we're done parsing and into end-of-file activities.
> +   2 if all templates have been instantiated.
> +   3 if we're done with front-end processing.  */
>
>  int at_eof;
>
> @@ -4987,6 +4989,7 @@ c_parse_final_cleanups (void)
>    tree decl;
>
>    locus_at_end_of_parsing = input_location;
> +  /* We're done parsing.  */
>    at_eof = 1;
>
>    /* Bad parse errors.  Just forget about it.  */
> @@ -5252,6 +5255,9 @@ c_parse_final_cleanups (void)
>         reconsider = true;
>      }
>
> +  /* All templates have been instantiated.  */
> +  at_eof = 2;
> +
>    void *module_cookie = finish_module_processing (parse_in);
>
>    lower_var_init ();
> @@ -5294,7 +5300,11 @@ c_parse_final_cleanups (void)
>    if (static_init_fini_fns[true])
>      for (auto iter : *static_init_fini_fns[true])
>        iter.second = nreverse (iter.second);
> -
> +
> +  /* Now we've instantiated all templates.  Now we can escalate the functions
> +     we squirreled away earlier.  */
> +  process_and_check_pending_immediate_escalating_fns ();
> +
>    /* Then, do the Objective-C stuff.  This is where all the
>       Objective-C module stuff gets generated (symtab,
>       class/protocol/selector lists etc).  This must be done after C++
> @@ -5376,7 +5386,7 @@ c_parse_final_cleanups (void)
>    timevar_start (TV_PHASE_PARSING);
>
>    /* Indicate that we're done with front end processing.  */
> -  at_eof = 2;
> +  at_eof = 3;
>  }
>
>  /* Perform any post compilation-proper cleanups for the C++ front-end.
> diff --git a/gcc/cp/error.cc b/gcc/cp/error.cc
> index 785909c362a..3b1b5de5ea4 100644
> --- a/gcc/cp/error.cc
> +++ b/gcc/cp/error.cc
> @@ -478,7 +478,7 @@ dump_template_bindings (cxx_pretty_printer *pp, tree parms, tree args,
>
>    /* Don't try to do this once cgraph starts throwing away front-end
>       information.  */
> -  if (at_eof >= 2)
> +  if (at_eof >= 3)
>      return;
>
>    FOR_EACH_VEC_SAFE_ELT (typenames, i, t)
> diff --git a/gcc/cp/module.cc b/gcc/cp/module.cc
> index 33fcf396875..1b57fbe2124 100644
> --- a/gcc/cp/module.cc
> +++ b/gcc/cp/module.cc
> @@ -5683,6 +5683,8 @@ trees_out::lang_decl_bools (tree t)
>        WB (lang->u.fn.has_dependent_explicit_spec_p);
>        WB (lang->u.fn.immediate_fn_p);
>        WB (lang->u.fn.maybe_deleted);
> +      WB (lang->u.fn.escalated_p);
> +      /* We do not stream lang->u.fn.implicit_constexpr.  */
>        goto lds_min;
>
>      case lds_decomp:  /* lang_decl_decomp.  */
> @@ -5751,6 +5753,8 @@ trees_in::lang_decl_bools (tree t)
>        RB (lang->u.fn.has_dependent_explicit_spec_p);
>        RB (lang->u.fn.immediate_fn_p);
>        RB (lang->u.fn.maybe_deleted);
> +      RB (lang->u.fn.escalated_p);
> +      /* We do not stream lang->u.fn.implicit_constexpr.  */
>        goto lds_min;
>
>      case lds_decomp:  /* lang_decl_decomp.  */
> diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
> index 7265201e036..924a20973b4 100644
> --- a/gcc/cp/pt.cc
> +++ b/gcc/cp/pt.cc
> @@ -11107,7 +11107,7 @@ push_tinst_level_loc (tree tldcl, tree targs, location_t loc)
>    if (tinst_depth >= max_tinst_depth)
>      {
>        /* Tell error.cc not to try to instantiate any templates.  */
> -      at_eof = 2;
> +      at_eof = 3;
>        fatal_error (input_location,
>                    "template instantiation depth exceeds maximum of %d"
>                    " (use %<-ftemplate-depth=%> to increase the maximum)",
> diff --git a/gcc/cp/typeck.cc b/gcc/cp/typeck.cc
> index bf8ffaa7e75..8e4cfae08aa 100644
> --- a/gcc/cp/typeck.cc
> +++ b/gcc/cp/typeck.cc
> @@ -7269,11 +7269,9 @@ cp_build_addr_expr_1 (tree arg, bool strict_lvalue, tsubst_flags_t complain)
>                               complain);
>      }
>
> -  /* For addresses of immediate functions ensure we have EXPR_LOCATION
> -     set for possible later diagnostics.  */
> +  /* Ensure we have EXPR_LOCATION set for possible later diagnostics.  */
>    if (TREE_CODE (val) == ADDR_EXPR
> -      && TREE_CODE (TREE_OPERAND (val, 0)) == FUNCTION_DECL
> -      && DECL_IMMEDIATE_FUNCTION_P (TREE_OPERAND (val, 0)))
> +      && TREE_CODE (TREE_OPERAND (val, 0)) == FUNCTION_DECL)
>      SET_EXPR_LOCATION (val, input_location);
>
>    return val;
> diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
> index 2b51ff304f6..681e3f3f466 100644
> --- a/gcc/doc/invoke.texi
> +++ b/gcc/doc/invoke.texi
> @@ -219,6 +219,7 @@ in the following sections.
>  -fno-elide-constructors
>  -fno-enforce-eh-specs
>  -fno-gnu-keywords
> +-fno-immediate-escalation
>  -fno-implicit-templates
>  -fno-implicit-inline-templates
>  -fno-implement-inlines
> @@ -3386,6 +3387,39 @@ word as an identifier.  You can use the keyword @code{__typeof__} instead.
>  This option is implied by the strict ISO C++ dialects: @option{-ansi},
>  @option{-std=c++98}, @option{-std=c++11}, etc.
>
> +@opindex fno-immediate-escalation
> +@opindex fimmediate-escalation
> +@item -fno-immediate-escalation
> +Do not enable immediate function escalation whereby certain functions
> +can be promoted to consteval, as specified in P2564R3.  For example:
> +
> +@example
> +consteval int id(int i) @{ return i; @}
> +
> +constexpr int f(auto t)
> +@{
> +  return t + id(t); // id causes f<int> to be promoted to consteval
> +@}
> +
> +void g(int i)
> +@{
> +  f (3);
> +@}
> +@end example
> +
> +compiles in C++20: @code{f} is an immediate-escalating function (due to
> +the @code{auto} it is a function template and is declared @code{constexpr})
> +and @code{id(t)} is an immediate-escalating expression, so @code{f} is
> +promoted to @code{consteval}.  Consequently, the call to @code{id(t)}
> +is in an immediate context, so doesn't have to produce a constant (that
> +is the mechanism allowing consteval function composition).  However,
> +with @option{-fno-immediate-escalation}, @code{f} is not promoted to
> +@code{consteval}, and since the call to consteval function @code{id(t)}
> +is not a constant expression, the compiler rejects the code.
> +
> +This option is turned on by default; it is only effective in C++20 mode
> +or later.
> +
>  @opindex fimplicit-constexpr
>  @item -fimplicit-constexpr
>  Make inline functions implicitly constexpr, if they satisfy the
> diff --git a/gcc/testsuite/g++.dg/cpp23/consteval-if10.C b/gcc/testsuite/g++.dg/cpp23/consteval-if10.C
> index 4c0523fe1d0..b8709beba85 100644
> --- a/gcc/testsuite/g++.dg/cpp23/consteval-if10.C
> +++ b/gcc/testsuite/g++.dg/cpp23/consteval-if10.C
> @@ -2,6 +2,9 @@
>  // { dg-do compile { target c++20 } }
>  // { dg-options "" }
>
> +// We used to give errors but the lambdas are now promoted to consteval
> +// and are in a immediate function context, so no errors.
> +
>  consteval int foo (int x) { return x; }
>
>  constexpr int
> @@ -10,7 +13,7 @@ bar (int x)
>    int r = 0;
>    if consteval         // { dg-warning "'if consteval' only available with" "" { target c++20_only } }
>      {
> -      auto y = [=] { foo (x); };       // { dg-error "'x' is not a constant expression" }
> +      auto y = [=] { foo (x); };
>        y ();
>      }
>    return r;
> @@ -23,7 +26,7 @@ baz (T x)
>    T r = 0;
>    if consteval         // { dg-warning "'if consteval' only available with" "" { target c++20_only } }
>      {
> -      auto y = [=] { foo (x); };       // { dg-error "'x' is not a constant expression" }
> +      auto y = [=] { foo (x); };
>        y ();
>      }
>    return r;
> diff --git a/gcc/testsuite/g++.dg/cpp23/consteval-if2.C b/gcc/testsuite/g++.dg/cpp23/consteval-if2.C
> index b2c5472b7de..3b258711ce6 100644
> --- a/gcc/testsuite/g++.dg/cpp23/consteval-if2.C
> +++ b/gcc/testsuite/g++.dg/cpp23/consteval-if2.C
> @@ -33,7 +33,7 @@ baz (int x)
>    int r = 0;
>    if not consteval     // { dg-warning "'if consteval' only available with" "" { target c++20_only } }
>      {
> -      r += foo (x);    // { dg-error "'x' is not a constant expression" }
> +      r += foo (x);    // { dg-error "not a constant expression" }
>      }
>    else
>      {
> @@ -45,11 +45,11 @@ baz (int x)
>      }
>    else
>      {
> -      r += foo (8 * x);        // { dg-error "'x' is not a constant expression" }
> +      r += foo (8 * x);        // { dg-error "is not a constant expression" }
>      }
>    if ! consteval       // { dg-warning "'if consteval' only available with" "" { target c++20_only } }
>      {
> -      r += foo (32 * x);// { dg-error "'x' is not a constant expression" }
> +      r += foo (32 * x);// { dg-error "not a constant expression" }
>      }
>    if consteval         // { dg-warning "'if consteval' only available with" "" { target c++20_only } }
>      {
> @@ -98,7 +98,7 @@ corge (T x)
>    T r = 0;
>    if not consteval     // { dg-warning "'if consteval' only available with" "" { target c++20_only } }
>      {
> -      r += foo (x);    // { dg-error "'x' is not a constant expression" }
> +      r += foo (x);
>      }
>    else
>      {
> @@ -110,11 +110,11 @@ corge (T x)
>      }
>    else
>      {
> -      r += foo (8 * x);        // { dg-error "is not a constant expression" }
> +      r += foo (8 * x);
>      }
>    if ! consteval       // { dg-warning "'if consteval' only available with" "" { target c++20_only } }
>      {
> -      r += foo (32 * x);// { dg-error "is not a constant expression" }
> +      r += foo (32 * x);
>      }
>    if consteval         // { dg-warning "'if consteval' only available with" "" { target c++20_only } }
>      {
> @@ -126,5 +126,5 @@ corge (T x)
>  int
>  garply (int x)
>  {
> -  return corge (x);
> +  return corge (x); // { dg-error "is not a constant expression" }
>  }
> diff --git a/gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C b/gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C
> index 9e29b01adc1..2b21bd1bc0d 100644
> --- a/gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C
> +++ b/gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C
> @@ -480,8 +480,8 @@
>
>  #ifndef __cpp_consteval
>  #  error "__cpp_consteval"
> -#elif __cpp_consteval != 201811
> -#  error "__cpp_consteval != 201811"
> +#elif __cpp_consteval != 202211L
> +#  error "__cpp_consteval != 202211L"
>  #endif
>
>  #ifndef __cpp_concepts
> diff --git a/gcc/testsuite/g++.dg/cpp26/feat-cxx26.C b/gcc/testsuite/g++.dg/cpp26/feat-cxx26.C
> index 6244f8fdfe4..4507ea07d1c 100644
> --- a/gcc/testsuite/g++.dg/cpp26/feat-cxx26.C
> +++ b/gcc/testsuite/g++.dg/cpp26/feat-cxx26.C
> @@ -480,8 +480,8 @@
>
>  #ifndef __cpp_consteval
>  #  error "__cpp_consteval"
> -#elif __cpp_consteval != 201811
> -#  error "__cpp_consteval != 201811"
> +#elif __cpp_consteval != 202211L
> +#  error "__cpp_consteval != 202211L"
>  #endif
>
>  #ifndef __cpp_concepts
> diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval-memfn1.C b/gcc/testsuite/g++.dg/cpp2a/consteval-memfn1.C
> index 46eed13446d..ca923519f98 100644
> --- a/gcc/testsuite/g++.dg/cpp2a/consteval-memfn1.C
> +++ b/gcc/testsuite/g++.dg/cpp2a/consteval-memfn1.C
> @@ -20,10 +20,13 @@ template<class>
>  void VerifyHash(fixed_string s) {
>    s.size(0); // { dg-bogus "" }
>    s.size(-1); // { dg-message "expansion of" }
> +// { dg-error "call to consteval function" "" { target *-*-* } .-1 }
>    s.size_static(0); // { dg-bogus "" }
>    s.size_static(-1); // { dg-message "expansion of" }
> +// { dg-error "call to consteval function" "" { target *-*-* } .-1 }
>    fixed_string::size_static(0); // { dg-bogus "" }
>    fixed_string::size_static(-1); // { dg-message "expansion of" }
> +// { dg-error "call to consteval function" "" { target *-*-* } .-1 }
>    s(); // { dg-bogus "" }
>  }
>
> diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval-prop1.C b/gcc/testsuite/g++.dg/cpp2a/consteval-prop1.C
> new file mode 100644
> index 00000000000..5e7b208113f
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp2a/consteval-prop1.C
> @@ -0,0 +1,169 @@
> +// P2564R3
> +// { dg-do compile { target c++20 } }
> +// Some of these were cribbed from clang's cxx2b-consteval-propagate.cpp.
> +
> +consteval int id(int i) { return i; }
> +
> +template <typename T>
> +constexpr int
> +f0 (T t)
> +{
> +  // OK, f0<int> promoted to consteval.
> +  return id (t); // { dg-message "immediate-escalating expression .id\\(t\\)." }
> +}
> +
> +constexpr auto a0 = f0 (3);
> +
> +// As a consequence of f0<int> being promoted to an immediate function, we
> +// can't take its address.
> +auto p0 = &f0<int>; // { dg-error "taking address of an immediate function" }
> +
> +template <typename T>
> +constexpr int
> +f1 (T t)
> +{
> +  // OK, f1<int> promoted to consteval.
> +  return t + id (t); // { dg-message "immediate-escalating expression .id\\(t\\)." }
> +}
> +
> +constexpr auto a1 = f1 (3);
> +
> +// As a consequence of f1<int> being promoted to an immediate function, we
> +// can't take its address.
> +auto p1 = &f1<int>; // { dg-error "taking address of an immediate function" }
> +
> +template <typename T>
> +constexpr int
> +f2 (T)
> +{
> +  // This produces a constant; f2 *not* promoted to consteval.
> +  return id (42);
> +}
> +
> +// ... so we can take its address.
> +auto p2 = &f2<int>;
> +
> +constexpr int
> +f3 (int i)
> +{
> +  // f3 isn't a function template and those don't get upgraded to consteval.
> +  return id (i); // { dg-error "not a constant expression" }
> +}
> +
> +auto p3 = &f3;
> +
> +template<typename T>
> +constexpr int
> +f4 (T t)
> +{
> +  auto p = id; // { dg-message "immediate-escalating expression .id." }
> +  (void) p;
> +  return t;
> +}
> +
> +auto p6 = &f4<int>; // { dg-error "taking address of an immediate function" }
> +
> +static_assert (f4 (42) == 42);
> +
> +// Constructors.
> +consteval int zero (int)
> +{
> +  return 0;
> +}
> +
> +struct A {
> +  // A::A(auto) promoted to consteval.
> +  constexpr A(auto i) { zero (i); }
> +};
> +
> +constexpr void
> +f5 (auto i)
> +{
> +  A a{i};
> +}
> +
> +constexpr void
> +f5_nt (int i)
> +{
> +  A a{i}; // { dg-error "call to consteval function|not a constant" }
> +}
> +
> +void
> +f6 ()
> +{
> +  f5 (0);
> +}
> +
> +struct B {
> +  constexpr B(int) { }
> +};
> +
> +B b1(f0<int>((f1<int>(7))));
> +
> +template<typename T>
> +constexpr int cid(T t) { return t; }
> +
> +auto p4 = &cid<int>;
> +auto p5 = &cid<char>;
> +
> +int g = 7; // { dg-message ".int g. is not const" }
> +
> +B b2(f0<int>(cid<int>(g))); // { dg-error "call to consteval function|not usable" }
> +
> +struct C {
> +  consteval C (int) {};
> +};
> +
> +constexpr int
> +f7 (auto t)
> +{
> +  C c(t); // { dg-message "immediate-escalating expression .c.C::C\\(t\\)." }
> +  return 0;
> +}
> +
> +int i1 = f7 (g); // { dg-error "call to consteval function|not usable" }
> +
> +struct Y {
> +  int y;
> +  int x = id (y);
> +  consteval Y (int i) : y (id (i)) {}
> +};
> +
> +Y y1(1);
> +Y y2(g); // { dg-error "call to consteval function|not usable" }
> +
> +struct Y2 {
> +  int y;
> +  int x = id (y);
> +  constexpr Y2 (auto i) : y (id (i)) {}
> +};
> +
> +Y2 y3(1);
> +Y2 y4(g); // { dg-error "call to consteval function|not usable" }
> +
> +auto l1 = [](int i) constexpr {
> +  int t = id (i);
> +  return id (0);
> +};
> +
> +int (*pl1)(int) = l1; // { dg-error "call to consteval function|returns address of immediate function" }
> +
> +auto l2 = [](int i) {
> +  int t = id (i);
> +  return id (0);
> +};
> +
> +int (*pl2)(int) = l2; // { dg-error "call to consteval function|returns address of immediate function" }
> +
> +// Not defined = won't produce a constant expression.
> +consteval int undef (); // { dg-warning "used but never defined" }
> +
> +struct S {
> +  int a = [] { return undef (); }();
> +};
> +
> +struct S2 {  // { dg-error "used before its definition" }
> +  int a = [] (int u = undef ()) {
> +    return u;
> +  }();
> +} s2; // { dg-error "call to consteval function" }
> diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval-prop10.C b/gcc/testsuite/g++.dg/cpp2a/consteval-prop10.C
> new file mode 100644
> index 00000000000..4e33e6e3d0e
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp2a/consteval-prop10.C
> @@ -0,0 +1,41 @@
> +// P2564R3
> +// { dg-do compile { target c++20 } }
> +// Test default arguments.
> +
> +consteval int id (int i) { return i; }
> +
> +template<typename>
> +constexpr int
> +f1 (int i = id (42))
> +{
> +  return i;
> +}
> +
> +int non_const; // { dg-message ".int non_const. is not const" }
> +
> +template<typename>
> +constexpr int
> +f2 (int i = id (non_const))
> +{
> +  return i;
> +}
> +
> +constexpr int
> +f3 (auto)
> +{
> +  return f2<int>(); // { dg-message "contains an immediate-escalating expression .id\\(non_const\\)." }
> +}
> +
> +auto a = &f3<int>; // { dg-error "taking address of an immediate function" }
> +
> +void
> +g (int i)
> +{
> +  f1<int> (42);
> +  f1<int> (i);
> +  f1<int> ();
> +  f2<int> (42);
> +  f2<int> (i);
> +  f2<int> (); // { dg-error "call to consteval function .id\\(non_const\\). is not a constant expression" }
> +// { dg-error ".non_const. is not usable in a constant expression" "" { target *-*-* } .-1 }
> +}
> diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval-prop11.C b/gcc/testsuite/g++.dg/cpp2a/consteval-prop11.C
> new file mode 100644
> index 00000000000..aca9675cd53
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp2a/consteval-prop11.C
> @@ -0,0 +1,49 @@
> +// P2564R3
> +// { dg-do compile { target c++20 } }
> +// { dg-options "-fdiagnostics-show-caret" }
> +// Test diagnostic.
> +
> +consteval int id (int i) { return i; }
> +constexpr int foo (int i ) { return i; }
> +
> +constexpr int
> +foobar (auto i)
> +{
> +  return i + id (i);
> +  /* { dg-begin-multiline-output "" }
> +   return i + id (i);
> +              ~~~^~~
> +     { dg-end-multiline-output "" } */
> +}
> +
> +void
> +g (int x)
> +{
> +  foobar (x); // { dg-error "10:call to consteval function .foobar<int>\\(x\\). is not a constant expression" }
> +// { dg-error ".x. is not a constant expression" "" { target *-*-* } .-1 }
> +  /* { dg-begin-multiline-output "" }
> +foobar (x);
> +   ~~~~~~~^~~
> +     { dg-end-multiline-output "" } */
> +}
> +
> +constexpr int
> +f2 (auto i)
> +{
> +  auto p = &id;
> +  /* { dg-begin-multiline-output "" }
> +   auto p = &id;
> +            ^~~
> +     { dg-end-multiline-output "" } */
> +  return p (i);
> +}
> +
> +void
> +g2 (int x)
> +{
> +  f2 (x); // { dg-error "6:call to consteval function .f2<int>\\(x\\). is not a constant expression|not a constant expression" }
> +  /* { dg-begin-multiline-output "" }
> +f2 (x);
> +   ~~~^~~
> +     { dg-end-multiline-output "" } */
> +}
> diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval-prop12.C b/gcc/testsuite/g++.dg/cpp2a/consteval-prop12.C
> new file mode 100644
> index 00000000000..2949ab83af8
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp2a/consteval-prop12.C
> @@ -0,0 +1,30 @@
> +// P2564R3
> +// { dg-do compile { target c++20 } }
> +
> +consteval int
> +zero (int)
> +{
> +  return 0;
> +}
> +
> +constexpr int
> +f (auto i)
> +{
> +  return zero (i);
> +}
> +
> +constexpr int
> +g (auto)
> +{
> +  // This call is a constant expression, so don't promote g.
> +  return f (42);
> +}
> +
> +void
> +do_test ()
> +{
> +  g (2);
> +}
> +
> +// Must work.
> +auto q = &g<int>;
> diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval-prop13.C b/gcc/testsuite/g++.dg/cpp2a/consteval-prop13.C
> new file mode 100644
> index 00000000000..6c20b98a87c
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp2a/consteval-prop13.C
> @@ -0,0 +1,23 @@
> +// P2564R3
> +// { dg-do compile { target c++20 } }
> +// Verify we don't recurse endlessly while determining whether a function
> +// should be propagated to consteval.
> +
> +consteval int id (int i) { return i; }
> +
> +constexpr int f2 (auto);
> +
> +constexpr int
> +f1 (auto i)
> +{
> +  return f2 (i);
> +}
> +
> +constexpr int
> +f2 (auto i)
> +{
> +  return f1 (i);
> +}
> +
> +auto p = &f1<int>;
> +auto q = &f2<int>;
> diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval-prop14.C b/gcc/testsuite/g++.dg/cpp2a/consteval-prop14.C
> new file mode 100644
> index 00000000000..cdc1f6dc862
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp2a/consteval-prop14.C
> @@ -0,0 +1,78 @@
> +// P2564R3
> +// { dg-do compile { target c++20 } }
> +// Test more CALL_EXPRs in a function, some of which are escalating.
> +
> +consteval int id (int i) { return i; }
> +constexpr int neg (int i) { return -i; }
> +constexpr int foo (auto i) { return id (i); }
> +
> +constexpr int
> +f1 (auto i)
> +{
> +  auto x = id (i);  // { dg-message "promoted to an immediate function because its body contains an immediate-escalating expression .id\\(i\\)." }
> +  auto y = neg (i);
> +  return x + y;
> +}
> +
> +constexpr int
> +f2 (auto i)
> +{
> +  return neg (id (i)); // { dg-message "promoted to an immediate function because its body contains an immediate-escalating expression .id\\(i\\)." }
> +}
> +
> +constexpr int
> +f3 (auto i)
> +{
> +  auto x = i + neg (neg (neg (id (neg (neg (i)))))); // { dg-message "promoted to an immediate function because its body contains an immediate-escalating expression .id\\(neg\\(neg\\(i\\)\\)\\)." }
> +  return x;
> +}
> +
> +constexpr int
> +f4 (auto i)
> +{
> +  return i + neg ((id (2 * i) + neg (i)) / 2); // { dg-message "promoted to an immediate function because its body contains an immediate-escalating expression .id\\(\\(i \\* 2\\)\\)." }
> +}
> +
> +constexpr int
> +f5 (auto i)
> +{
> +  (void) neg (i);
> +  (void) neg (i);
> +  (void) neg (i);
> +  (void) neg (i);
> +  (void) neg (i);
> +  (void) neg (i);
> +  (void) +id (i); // { dg-message "promoted to an immediate function because its body contains an immediate-escalating expression .id\\(i\\)." }
> +  (void) neg (i);
> +  return i;
> +}
> +
> +constexpr int
> +f6 (auto i)
> +{
> +  auto x = neg (i + foo (i)); // { dg-message "promoted to an immediate function because its body contains an immediate-escalating expression .foo<int>\\(i\\)." }
> +  return x;
> +}
> +
> +void
> +g (int i)
> +{
> +  f1 (i); // { dg-error "call to consteval function .f1<int>\\(i\\). is not a constant expression" }
> +// { dg-error ".i. is not a constant expression" "" { target *-*-* } .-1 }
> +  f1 (42);
> +  f2 (i); // { dg-error "call to consteval function .f2<int>\\(i\\). is not a constant expression" }
> +// { dg-error ".i. is not a constant expression" "" { target *-*-* } .-1 }
> +  f2 (42);
> +  f3 (i); // { dg-error "call to consteval function .f3<int>\\(i\\). is not a constant expression" }
> +// { dg-error ".i. is not a constant expression" "" { target *-*-* } .-1 }
> +  f3 (42);
> +  f4 (i); // { dg-error "call to consteval function .f4<int>\\(i\\). is not a constant expression" }
> +// { dg-error ".i. is not a constant expression" "" { target *-*-* } .-1 }
> +  f4 (42);
> +  f5 (i); // { dg-error "call to consteval function .f5<int>\\(i\\). is not a constant expression" }
> +// { dg-error ".i. is not a constant expression" "" { target *-*-* } .-1 }
> +  f5 (42);
> +  f6 (i); // { dg-error "call to consteval function .f6<int>\\(i\\). is not a constant expression" }
> +// { dg-error ".i. is not a constant expression" "" { target *-*-* } .-1 }
> +  f6 (42);
> +}
> diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval-prop15.C b/gcc/testsuite/g++.dg/cpp2a/consteval-prop15.C
> new file mode 100644
> index 00000000000..3341c510a9f
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp2a/consteval-prop15.C
> @@ -0,0 +1,107 @@
> +// P2564R3
> +// { dg-do compile { target c++20 } }
> +// { dg-options "-Wno-c++23-extensions" }
> +
> +consteval int id (int i) { return i; }
> +
> +constexpr int
> +f1 (auto i)
> +{
> +  auto p = &id; // { dg-message "promoted to an immediate function because its body contains an immediate-escalating expression .id." }
> +  (void) p;
> +  return i;
> +}
> +
> +constexpr int
> +f2 (auto i)
> +{
> +  return f1 (i);
> +}
> +
> +constexpr int
> +f3 (auto i)
> +{
> +  return f2 (i);
> +}
> +
> +constexpr int
> +f4 (auto i)
> +{
> +  return f3 (i);
> +}
> +
> +constexpr int
> +f5 (auto i)
> +{
> +  return f4 (i); // { dg-message "promoted to an immediate function because its body contains an immediate-escalating expression .f4<int>\\(i\\)." }
> +}
> +
> +constexpr int
> +f6 (auto)
> +{
> +  // This call is a constant expression, so don't promote f6.
> +  return f4 (42);
> +}
> +
> +constexpr int
> +f7 (auto i)
> +{
> +  if consteval {
> +    auto p = &id;
> +    (void) p;
> +  }
> +  return i;
> +}
> +
> +constexpr int
> +f8 (auto i)
> +{
> +  if not consteval {
> +    (void) 0;
> +  } else {
> +    auto p = &id;
> +    (void) p;
> +  }
> +  return i;
> +}
> +
> +constexpr int
> +f9 (auto i)
> +{
> +  if consteval {
> +    return id(i);
> +  }
> +  return i;
> +}
> +
> +constexpr int
> +f10 (auto i)
> +{
> +  if not consteval {
> +    (void) 0;
> +  } else {
> +    return id(i);
> +  }
> +  return i;
> +}
> +
> +void
> +g (int non_const)
> +{
> +  f1 (42);
> +  f1 (non_const); // { dg-error "call to consteval function .f1<int>\\(non_const\\). is not a constant expression" }
> +// { dg-error ".non_const. is not a constant expression" "" { target *-*-* } .-1 }
> +  f5 (42);
> +  f5 (non_const); // { dg-error "call to consteval function .f5<int>\\(non_const\\). is not a constant expression" }
> +// { dg-error ".non_const. is not a constant expression" "" { target *-*-* } .-1 }
> +  f6 (42);
> +  f6 (non_const);
> +  f7 (42);
> +  f7 (non_const);
> +  f8 (42);
> +  f8 (non_const);
> +  f9 (42);
> +  f9 (non_const);
> +  f10 (42);
> +  f10 (non_const);
> +}
> diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval-prop16.C b/gcc/testsuite/g++.dg/cpp2a/consteval-prop16.C
> new file mode 100644
> index 00000000000..7952d495d8b
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp2a/consteval-prop16.C
> @@ -0,0 +1,73 @@
> +// P2564R3
> +// { dg-do compile { target c++20 } }
> +// Test unevaluated operands.
> +
> +consteval int id (int i) { return i; }
> +
> +constexpr int
> +f1 (auto i)
> +{
> +  // Unevaluated operand -> don't promote.
> +  auto p = sizeof (&id);
> +  (void) p;
> +  return i;
> +}
> +
> +constexpr int
> +f2 (auto i)
> +{
> +  // Unevaluated operand -> don't promote.
> +  auto p = noexcept (id);
> +  (void) p;
> +  return i;
> +}
> +
> +constexpr int
> +f3 (auto i)
> +{
> +  // Unevaluated operand -> don't promote.
> +  auto p = noexcept (id (i));
> +  (void) p;
> +  return i;
> +}
> +
> +constexpr int
> +f4 (auto i)
> +{
> +  // Unevaluated operand -> don't promote.
> +  decltype(id) p;
> +  (void) p;
> +  return i;
> +}
> +
> +constexpr int
> +f5 (auto i)
> +{
> +  // Unevaluated operand -> don't promote.
> +  __extension__ auto p = alignof (id (i));
> +  (void) p;
> +  return i;
> +}
> +
> +constexpr int
> +f6 (auto i) requires requires { id (i); }
> +{
> +  return i;
> +}
> +
> +void
> +g (int non_const)
> +{
> +  f1 (42);
> +  f1 (non_const);
> +  f2 (42);
> +  f2 (non_const);
> +  f3 (42);
> +  f3 (non_const);
> +  f4 (42);
> +  f4 (non_const);
> +  f5 (42);
> +  f5 (non_const);
> +  f6 (42);
> +  f6 (non_const);
> +}
> diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval-prop17.C b/gcc/testsuite/g++.dg/cpp2a/consteval-prop17.C
> new file mode 100644
> index 00000000000..47ec9b60b6c
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp2a/consteval-prop17.C
> @@ -0,0 +1,17 @@
> +// P2564R3
> +// { dg-do compile { target c++20 } }
> +// { dg-options "-fno-immediate-escalation" }
> +
> +consteval int id(int i) { return i; }
> +
> +constexpr int
> +f (auto i)
> +{
> +  return id (i); // { dg-error "not a constant expression" }
> +}
> +
> +int
> +g ()
> +{
> +  return f (42);
> +}
> diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval-prop18.C b/gcc/testsuite/g++.dg/cpp2a/consteval-prop18.C
> new file mode 100644
> index 00000000000..a18106f8e0f
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp2a/consteval-prop18.C
> @@ -0,0 +1,20 @@
> +// P2564R3
> +// { dg-do compile { target c++20 } }
> +
> +consteval int id(int i) { return i; }
> +
> +constexpr int
> +f (auto t)
> +{
> +  return t + id (t);
> +}
> +
> +constexpr int
> +f2 (auto t)
> +{
> +  return t + f(t); // { dg-message "immediate-escalating expression .f<int>\\(t\\)." }
> +}
> +
> +int z; // { dg-message "not const" }
> +auto y1 = f2 (42);
> +auto y2 = f2 (z); // { dg-error "value of .z. is not usable in a constant expression|call to consteval function" }
> diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval-prop19.C b/gcc/testsuite/g++.dg/cpp2a/consteval-prop19.C
> new file mode 100644
> index 00000000000..3ceb05e41f4
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp2a/consteval-prop19.C
> @@ -0,0 +1,7 @@
> +// P2564R3
> +// { dg-do compile { target c++20 } }
> +
> +consteval int g(int p) { return p; }
> +template<typename T> constexpr auto f(T) { return g; }
> +int r = f(1)(2);      // proposed ok
> +int s = f(1)(2) + r;  // { dg-error "call to consteval function|returns address of immediate function" }
> diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval-prop2.C b/gcc/testsuite/g++.dg/cpp2a/consteval-prop2.C
> new file mode 100644
> index 00000000000..30129a4a266
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp2a/consteval-prop2.C
> @@ -0,0 +1,90 @@
> +// P2564R3
> +// { dg-do compile { target c++20 } }
> +// Testcase from P2564R3.
> +
> +consteval int id(int i) { return i; }
> +constexpr char id(char c) { return c; }
> +
> +template<class T>
> +constexpr int f(T t) {
> +  return t + id(t);            // { dg-message "immediate-escalating expression .id\\(t\\)." }
> +}
> +
> +auto a = &f<char>;              // OK, f<char> is not an immediate function
> +auto b = &f<int>;               // { dg-error "taking address of an immediate function" }
> +
> +static_assert(f(3) == 6);       // OK
> +
> +template<class T>
> +constexpr int g(T t) {          // g<int> is not an immediate function
> +  return t + id(42);            // because id(42) is already a constant
> +}
> +
> +template<class T, class F>
> +constexpr bool is_not(T t, F f) {
> +  return not f(t);
> +}
> +
> +consteval bool is_even(int i) { return i % 2 == 0; }
> +
> +static_assert(is_not(5, is_even));      // OK
> +
> +int x = 0;
> +
> +template<class T>
> +constexpr T h(T t = id(x)) {    // h<int> is not an immediate function
> +    return t;
> +}
> +
> +template<class T>
> +constexpr T hh() {              // hh<int> is an immediate function
> +  return h<T>();               // { dg-error "the value of .x. is not usable in a constant expression" }
> +// { dg-message "immediate-escalating expression .id\\(x\\)." "" { target *-*-* } .-1 }
> +}
> +
> +int i = hh<int>();              // { dg-error "call to consteval function|called in a constant expression" }
> +                               // error: hh<int>() is an immediate-escalating expression
> +                                // outside of an immediate-escalating function
> +struct A {
> +  int x;
> +  int y = id(x);
> +};
> +
> +// [expr.const]#example-9 says:
> +//   k<int> is not an immediate function because A(42) is a
> +//   constant expression and thus not immediate-escalating
> +// In the evaluation of A(42), the member x has just been initialized
> +// to constant 42.  And A(42) is constant-evaluated because "An aggregate
> +// initialization is an immediate invocation if it evaluates a default
> +// member initializer that has a subexpression that is an
> +// immediate-escalating expression."
> +template<class T>
> +constexpr int k(int) {
> +  return A(42).y;
> +}
> +
> +int
> +test (int i)
> +{
> +  int r = g (42) + g(i);
> +  int t = k<int>(42)
> +           + k<int>(i); // { dg-bogus "call to|constant" "" { xfail *-*-* } }
> +  return r + t;
> +}
> +
> +// Just like above, but make the call to id(x) actually a constant.
> +struct A2 {
> +  static constexpr int x = 42;
> +  int y = id(x);
> +};
> +
> +template<class T>
> +constexpr int k2(int) {
> +  return A2(42).y;
> +}
> +
> +int
> +test2 (int i)
> +{
> +  return k2<int>(42) + k2<int>(i);
> +}
> diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval-prop20.C b/gcc/testsuite/g++.dg/cpp2a/consteval-prop20.C
> new file mode 100644
> index 00000000000..f1bb08e2dba
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp2a/consteval-prop20.C
> @@ -0,0 +1,21 @@
> +// P2564R3
> +// { dg-do compile { target c++20 } }
> +// { dg-options "-Wno-c++23-extensions" }
> +
> +consteval int id(int i) { return i; }
> +
> +constexpr int
> +f (auto i)
> +{
> +  return id (i);
> +}
> +
> +void
> +g ()
> +{
> +  auto p = &f<int>; // { dg-error "taking address" }
> +  decltype(&f<int>) x;
> +  if consteval {
> +    auto q = &f<int>;
> +  }
> +}
> diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval-prop3.C b/gcc/testsuite/g++.dg/cpp2a/consteval-prop3.C
> new file mode 100644
> index 00000000000..f181cb32942
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp2a/consteval-prop3.C
> @@ -0,0 +1,27 @@
> +// P2564R3
> +// { dg-do compile { target c++20 } }
> +// Cribbed from clang's cxx2b-consteval-propagate.cpp.
> +
> +consteval int id(int i) { return i; }
> +
> +template <typename T>
> +constexpr int f(T t);
> +
> +auto a1 = &f<char>;
> +auto b1 = &f<int>;
> +
> +template <typename T>
> +constexpr int f(T t) {
> +    return id(0);
> +}
> +
> +template <typename T>
> +constexpr int f2(T);
> +
> +auto a2 = &f2<char>; // { dg-error "taking address" }
> +auto b2 = &f2<int>; // { dg-error "taking address" }
> +
> +template <typename T>
> +constexpr int f2(T t) {
> +    return id(t);
> +}
> diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval-prop4.C b/gcc/testsuite/g++.dg/cpp2a/consteval-prop4.C
> new file mode 100644
> index 00000000000..3a2e09b17b0
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp2a/consteval-prop4.C
> @@ -0,0 +1,30 @@
> +// P2564R3
> +// { dg-do compile { target c++20 } }
> +// From clang's cxx2b-consteval-propagate.cpp.  This test ICEd when I worked on
> +// P2564.
> +
> +consteval int f (int);
> +
> +struct S {
> +  int a = 0;
> +  int b = f (a);
> +};
> +
> +constexpr bool
> +g (auto i)
> +{
> +  S s{i};
> +  return s.b == 2 *i;
> +}
> +
> +consteval int
> +f (int i)
> +{
> +  return 2 * i;
> +}
> +
> +void
> +test ()
> +{
> +  static_assert(g(42));
> +}
> diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval-prop5.C b/gcc/testsuite/g++.dg/cpp2a/consteval-prop5.C
> new file mode 100644
> index 00000000000..3bd1b9d1674
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp2a/consteval-prop5.C
> @@ -0,0 +1,27 @@
> +// P2564R3
> +// { dg-do compile { target c++20 } }
> +
> +consteval int f (int i) { return i; }
> +
> +struct S {
> +  int x = f(42);
> +};
> +
> +constexpr S
> +immediate (auto)
> +{
> +  return S{};
> +}
> +
> +void
> +g ()
> +{
> +  immediate (0);
> +}
> +
> +consteval void
> +test ()
> +{
> +  constexpr S s = immediate(0);
> +  static_assert(s.x == 42);
> +}
> diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval-prop6.C b/gcc/testsuite/g++.dg/cpp2a/consteval-prop6.C
> new file mode 100644
> index 00000000000..93ed398d9bf
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp2a/consteval-prop6.C
> @@ -0,0 +1,59 @@
> +// P2564R3
> +// { dg-do compile { target c++20 } }
> +// From cxx2b-consteval-propagate.cpp.
> +
> +void side_effect();
> +
> +consteval int
> +f (int x)
> +{
> +  if (!x)
> +    side_effect(); // { dg-error "call to non-.constexpr. function" }
> +  return x;
> +}
> +
> +struct SS {
> +  int y = f(1);
> +  int x = f(0);
> +  SS();
> +};
> +SS::SS(){} // { dg-error "call to consteval function" }
> +
> +consteval int
> +f2 (int x)
> +{
> +  if (!__builtin_is_constant_evaluated ())
> +    side_effect();
> +  return x;
> +}
> +
> +struct S2 {
> +  int x = f2(0);
> +  constexpr S2();
> +};
> +
> +constexpr S2::S2(){}
> +S2 s = {};
> +constinit S2 s2 = {};
> +
> +struct S3 {
> +  int x = f2(0);
> +  S3();
> +};
> +S3::S3(){}
> +
> +consteval int undef (int x); // { dg-warning "never defined" }
> +
> +struct X {
> +  int a = sizeof(undef(0));
> +  int x = undef(0);
> +
> +  X() = default; // { dg-error "modification of .x. is not a constant expression" }
> +};
> +
> +void
> +test ()
> +{
> +  [[maybe_unused]] X x; // { dg-error "call to consteval function" }
> +// { dg-message "promoted to an immediate function" "" { target *-*-* } .-1 }
> +}
> diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval-prop7.C b/gcc/testsuite/g++.dg/cpp2a/consteval-prop7.C
> new file mode 100644
> index 00000000000..118cf576f14
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp2a/consteval-prop7.C
> @@ -0,0 +1,76 @@
> +// P2564R3
> +// { dg-do compile { target c++20 } }
> +// The problem here was that while parsing, we first process calling
> +// 'f' from 'g' but only when instantiating 'f<int>' do we promote 'f'
> +// to consteval.  When the var we're initializing is marked constexpr,
> +// store_init_value detects the problem that we're calling a consteval
> +// function with non-const argument.
> +
> +consteval int id(int i) { return i; }
> +
> +// Don't let the instantiations confuse us, e.g. instantiating a fn
> +// prior to entering 'g'.
> +template <typename T>
> +constexpr int f1(T t) { return id (t); }
> +
> +template <typename T>
> +constexpr int f2(T t) { return id (t); }
> +
> +template <typename T>
> +constexpr int f3(T t) { return id (t); }
> +
> +template <typename T>
> +constexpr int f4(T t) { return id (t); }
> +
> +template <typename T>
> +constexpr int f5(T t) { return id (t); }
> +
> +template <typename T>
> +constexpr int f6(T t) { return id (t); }
> +
> +template <typename T>
> +constexpr int f7(T t) { return id (t); }
> +
> +template <typename T>
> +constexpr int f8(T t) { return id (t); }
> +
> +template <typename T>
> +constexpr int f9(T t) { return id (t); }
> +
> +template <typename T>
> +constexpr int f10(T t) { return id (t); }
> +
> +template <typename T>
> +constexpr int g1(T t) { auto p = id; return p (t); }
> +
> +int non_const;
> +
> +auto a1 = f1 (non_const); // { dg-error "call to consteval function|not usable" }
> +constexpr auto a2 = f2 (non_const); // { dg-error "not a constant|not usable" }
> +auto a3 = f3 (42);
> +constexpr auto a4 = f4 (42);
> +
> +void
> +g ()
> +{
> +   auto a5 = f5 (non_const); // { dg-error "not a constant|not usable" }
> +   constexpr auto a6 = f6 (non_const); // { dg-error "not usable" }
> +   auto a7 = f7 (42);
> +   constexpr auto a8 = f8 (42);
> +   (void) f9 (non_const); // { dg-error "not a constant|not usable" }
> +   (void) f10 (42);
> +   (void) g1 (non_const); // { dg-error "not a constant|not usable" }
> +}
> +
> +struct S {
> +    int y;
> +    int x = id (y);
> +    // Promoted to consteval.
> +    template<typename T>
> +    constexpr S(T t) : y (id (t)) {}
> +};
> +
> +S s1(1);
> +S s2(non_const); // { dg-error "call to consteval function|not usable" }
> +constexpr S s3(1);
> +constexpr S s4(non_const); // { dg-error "not usable" }
> diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval-prop8.C b/gcc/testsuite/g++.dg/cpp2a/consteval-prop8.C
> new file mode 100644
> index 00000000000..080fc76f26e
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp2a/consteval-prop8.C
> @@ -0,0 +1,82 @@
> +// P2564R3
> +// { dg-do compile { target c++20 } }
> +// { dg-options "-Wno-c++23-extensions" }
> +
> +consteval int zero (int)
> +{
> +  return 0;
> +}
> +
> +struct A {
> +  // A::A(auto) promoted to consteval.
> +  constexpr A(auto i) { zero (i); }
> +};
> +
> +// 'f1<int>' is an immediate function because its body contains a call to an
> +// immediate constructor 'A<int>' and that call is not a constant expression
> +constexpr void
> +f1 (auto i)
> +{
> +  A a{i};
> +}
> +
> +// 'f2<int>' is an immediate function because its body contains a call to an
> +// immediate constructor 'A<int>' and that call is not a constant expression
> +constexpr void
> +f2 (auto i)
> +{
> +  A a{i};
> +}
> +
> +void
> +f3 (int i)
> +{
> +  A a{i}; // { dg-error "not a constant expression" }
> +}
> +
> +inline void
> +f7 (int i)
> +{
> +  A a{i}; // { dg-error "not a constant expression" }
> +}
> +
> +constexpr void
> +f8 (int i)
> +{
> +  A a{i}; // { dg-error "not a constant expression" }
> +}
> +
> +/* "An expression or conversion is immediate-escalating if it is not initially
> +   in an immediate function context" but this one is, so we do *not* promote
> +   f4 to consteval.  */
> +constexpr void
> +f4 (auto i)
> +{
> +  if consteval {
> +    A a{i};
> +  }
> +}
> +
> +constexpr void
> +f5 (auto i)
> +{
> +  if not consteval {
> +    (void) 0;
> +  } else {
> +    A a{i};
> +  }
> +}
> +
> +void
> +f6 (int x)
> +{
> +  f1 (0);
> +  f1 (x); // { dg-error "not a constant expression" }
> +  f2 (0);
> +  f2 (x); // { dg-error "not a constant expression" }
> +  f3 (0);
> +  f4 (x);
> +  f4 (0);
> +  f5 (x);
> +  f5 (0);
> +}
> diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval-prop9.C b/gcc/testsuite/g++.dg/cpp2a/consteval-prop9.C
> new file mode 100644
> index 00000000000..9c4a23389ce
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp2a/consteval-prop9.C
> @@ -0,0 +1,67 @@
> +// P2564R3
> +// { dg-do compile { target c++20 } }
> +
> +consteval int
> +zero (int)
> +{
> +  return 0;
> +}
> +
> +constexpr int
> +f1 (auto i)
> +{
> +  return zero (i);
> +}
> +
> +constexpr int
> +f2 (auto i)
> +{
> +  return f1 (i);
> +}
> +
> +constexpr int
> +f3 (auto i)
> +{
> +  return f2 (i);
> +}
> +
> +constexpr int
> +f4 (auto i)
> +{
> +  return f3 (i);
> +}
> +
> +constexpr int
> +f5 (auto i)
> +{
> +  return f4 (i);
> +}
> +
> +constexpr int
> +f6 (auto)
> +{
> +  // This call is a constant expression, so don't promote f6.
> +  return f5 (42);
> +}
> +
> +constexpr int
> +f7 (auto)
> +{
> +  // This call is a constant expression, so don't promote f7.
> +  return zero (42);
> +}
> +
> +auto p1 = &f5<int>; // { dg-error "taking address" }
> +static auto p2 = &f4<int>; // { dg-error "taking address" }
> +auto p3 = &f6<int>;
> +static auto p4 = &f6<int>;
> +auto p5 = &f7<int>;
> +static auto p6 = &f7<int>;
> +
> +void
> +g ()
> +{
> +  static auto q1 = &f4<int>; // { dg-error "taking address" }
> +  static auto q2 = &f6<int>;
> +  static auto q3 = &f7<int>;
> +}
> diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval11.C b/gcc/testsuite/g++.dg/cpp2a/consteval11.C
> index 05cecea4502..c2ee3c7a82a 100644
> --- a/gcc/testsuite/g++.dg/cpp2a/consteval11.C
> +++ b/gcc/testsuite/g++.dg/cpp2a/consteval11.C
> @@ -8,9 +8,11 @@ constexpr int a = bar (1);
>  constexpr int b = bar (2);             // { dg-message "in 'constexpr' expansion of" }
>  constexpr int c = 0 ? bar (3) : 1;
>  const int d = bar (4);                 // { dg-message "in 'constexpr' expansion of" }
> +// { dg-error "call to consteval function" "" { target *-*-* } .-1 }
>  const int e = 0 ? bar (5) : 1;
>  int f = bar (1);
>  int g = bar (6);                       // { dg-message "in 'constexpr' expansion of" }
> +// { dg-error "call to consteval function" "" { target *-*-* } .-1 }
>  int h = 0 ? bar (7) : 1;
>
>  void
> @@ -20,25 +22,35 @@ foo ()
>    constexpr int b = bar (2);           // { dg-message "in 'constexpr' expansion of" }
>    constexpr int c = 0 ? bar (3) : 1;
>    const int d = bar (4);               // { dg-message "in 'constexpr' expansion of" }
> +// { dg-error "call to consteval function" "" { target *-*-* } .-1 }
>    const int e = 0 ? bar (5) : 1;
>    int f = bar (1);
>    int g = bar (6);                     // { dg-message "in 'constexpr' expansion of" }
> +// { dg-error "call to consteval function" "" { target *-*-* } .-1 }
>    int h = 0 ? bar (7) : 1;             // { dg-message "in 'constexpr' expansion of" }
> +// { dg-error "call to consteval function" "" { target *-*-* } .-1 }
>    h += 0 ? bar (8) : 1;                        // { dg-message "in 'constexpr' expansion of" }
> +// { dg-error "call to consteval function" "" { target *-*-* } .-1 }
>    if (0)
>      bar (9);                           // { dg-message "in 'constexpr' expansion of" }
> +// { dg-error "call to consteval function" "" { target *-*-* } .-1 }
>    else
>      bar (10);                          // { dg-message "in 'constexpr' expansion of" }
> +// { dg-error "call to consteval function" "" { target *-*-* } .-1 }
>    if (1)
>      bar (11);                          // { dg-message "in 'constexpr' expansion of" }
> +// { dg-error "call to consteval function" "" { target *-*-* } .-1 }
>    else
>      bar (12);                          // { dg-message "in 'constexpr' expansion of" }
> +// { dg-error "call to consteval function" "" { target *-*-* } .-1 }
>    if constexpr (0)
>      bar (13);
>    else
>      bar (14);                          // { dg-message "in 'constexpr' expansion of" }
> +// { dg-error "call to consteval function" "" { target *-*-* } .-1 }
>    if constexpr (1)
>      bar (15);                          // { dg-message "in 'constexpr' expansion of" }
> +// { dg-error "call to consteval function" "" { target *-*-* } .-1 }
>    else
>      bar (16);
>  }
> @@ -121,18 +133,24 @@ quux ()
>  {
>    if (0)
>      bar ((T) 2);                               // { dg-message "in 'constexpr' expansion of" }
> +// { dg-error "call to consteval function" "" { target *-*-* } .-1 }
>    else
>      bar ((T) 3);                               // { dg-message "in 'constexpr' expansion of" }
> +// { dg-error "call to consteval function" "" { target *-*-* } .-1 }
>    if (1)
>      bar ((T) 4);                               // { dg-message "in 'constexpr' expansion of" }
> +// { dg-error "call to consteval function" "" { target *-*-* } .-1 }
>    else
>      bar ((T) 5);                               // { dg-message "in 'constexpr' expansion of" }
> +// { dg-error "call to consteval function" "" { target *-*-* } .-1 }
>    if constexpr (0)
>      bar ((T) 6);
>    else
>      bar ((T) 7);                               // { dg-message "in 'constexpr' expansion of" }
> +// { dg-error "call to consteval function" "" { target *-*-* } .-1 }
>    if constexpr (1)
>      bar ((T) 8);                               // { dg-message "in 'constexpr' expansion of" }
> +// { dg-error "call to consteval function" "" { target *-*-* } .-1 }
>    else
>      bar ((T) 9);
>  }
> diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval3.C b/gcc/testsuite/g++.dg/cpp2a/consteval3.C
> index 9efac8c8eae..1199e9db623 100644
> --- a/gcc/testsuite/g++.dg/cpp2a/consteval3.C
> +++ b/gcc/testsuite/g++.dg/cpp2a/consteval3.C
> @@ -16,8 +16,8 @@ consteval auto [ b, c ] = S ();               // { dg-error "structured binding declaration c
>  int f5 (consteval int x) { return x; } // { dg-error "a parameter cannot be declared 'consteval'" }
>  consteval int f6 (int x) { return x; }
>  int d = 6;             // { dg-message "'int d' is not const" }
> -int e = f6 (d);                // { dg-error "the value of 'd' is not usable in a constant expression" }
> -constexpr int f7 (int x) { return f6 (x); }    // { dg-error "'x' is not a constant expression" }
> +int e = f6 (d);                // { dg-error "the value of 'd' is not usable in a constant expression|call to consteval function" }
> +constexpr int f7 (int x) { return f6 (x); }    // { dg-error "'x' is not a constant expression|call to consteval function" }
>  constexpr int f = f7 (5);
>  using fnptr = int (int);
>  fnptr *g = f6;         // { dg-error "taking address of an immediate function 'consteval int f6\\(int\\)'" }
> diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval34.C b/gcc/testsuite/g++.dg/cpp2a/consteval34.C
> index 068827ba516..7562f403f74 100644
> --- a/gcc/testsuite/g++.dg/cpp2a/consteval34.C
> +++ b/gcc/testsuite/g++.dg/cpp2a/consteval34.C
> @@ -7,6 +7,7 @@ constexpr int
>  foo (bool b)
>  {
>    return b ? bar (3) : 2; // { dg-message "in .constexpr. expansion" }
> +// { dg-error "call to consteval function" "" { target *-*-* } .-1 }
>  }
>
>  static_assert (foo (false) == 2);
> @@ -22,13 +23,20 @@ void
>  g ()
>  {
>    __extension__ int a1[bar(3)]; // { dg-message "in .constexpr. expansion" }
> +// { dg-error "call to consteval function" "" { target *-*-* } .-1 }
>    int a2[sizeof (bar(3))];
>
>    int a3 = false ? (1 + bar (8)) : 1; // { dg-message "in .constexpr. expansion" }
> +// { dg-error "call to consteval function" "" { target *-*-* } .-1 }
>    a3 += false ? (1 + bar (8)) : 1; // { dg-message "in .constexpr. expansion" }
> +// { dg-error "call to consteval function" "" { target *-*-* } .-1 }
>
>    __extension__ int a4 = false ?: (1 + bar (8)); // { dg-message "in .constexpr. expansion" }
> +// { dg-error "call to consteval function" "" { target *-*-* } .-1 }
>    __extension__ int a5 = true ?: (1 + bar (8)); // { dg-message "in .constexpr. expansion" }
> +// { dg-error "call to consteval function" "" { target *-*-* } .-1 }
>    int a6 = bar (2) ? 1 : 2; // { dg-message "in .constexpr. expansion" }
> +// { dg-error "call to consteval function" "" { target *-*-* } .-1 }
>    int a7 = bar (2) - 1 ? 1 : 2; // { dg-message "in .constexpr. expansion" }
> +// { dg-error "call to consteval function" "" { target *-*-* } .-1 }
>  }
> diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval36.C b/gcc/testsuite/g++.dg/cpp2a/consteval36.C
> index 9c470e4b7d7..8e27f2e33c6 100644
> --- a/gcc/testsuite/g++.dg/cpp2a/consteval36.C
> +++ b/gcc/testsuite/g++.dg/cpp2a/consteval36.C
> @@ -6,17 +6,17 @@ consteval int id (int i) { return i; }
>  void
>  g (int i)
>  {
> -  1 ? 1 : ((1 ? 1 : 1), id (i)); // { dg-error "'i' is not a constant expression" }
> -  1 ? 1 : ((1 ? 1 : 1), id (i), 1); // { dg-error "'i' is not a constant expression" }
> -  1 ? 1 : ((i ? 1 : 1), id (i), 1); // { dg-error "'i' is not a constant expression" }
> -  1 ? 1 : ((1 ? i : 1), id (i), 1); // { dg-error "'i' is not a constant expression" }
> -  1 ? 1 : ((1 ? 1 : i), id (i), 1); // { dg-error "'i' is not a constant expression" }
> -  1 ? 1 : ((i ? -i : i), id (i), 1); // { dg-error "'i' is not a constant expression" }
> -  1 ? 1 : ((1 ? 1 : id (i)), id (42), 1); // { dg-error "'i' is not a constant expression" }
> -  1 ? 1 : ((1 ? 1 : id (42)), id (i)); // { dg-error "'i' is not a constant expression" }
> -  1 ? 1 : ((1 ? 1 : id (42)), id (i), 1); // { dg-error "'i' is not a constant expression" }
> -  id (i) ? 1 : ((1 ? 1 : 1), id (i)); // { dg-error "'i' is not a constant expression" }
> -  1 ? 1 : ((1 ? 1 : id (i)), id (i)); // { dg-error "'i' is not a constant expression" }
> -  1 ? id (i) : ((1 ? 1 : id (i)), id (i)); // { dg-error "'i' is not a constant expression" }
> -  1 ? 1 : ((id (i) ? 1 : 1), id (i)); // { dg-error "'i' is not a constant expression" }
> +  1 ? 1 : ((1 ? 1 : 1), id (i)); // { dg-error "call to consteval function|'i' is not a constant expression" }
> +  1 ? 1 : ((1 ? 1 : 1), id (i), 1); // { dg-error "call to consteval function|'i' is not a constant expression" }
> +  1 ? 1 : ((i ? 1 : 1), id (i), 1); // { dg-error "call to consteval function|'i' is not a constant expression" }
> +  1 ? 1 : ((1 ? i : 1), id (i), 1); // { dg-error "call to consteval function|'i' is not a constant expression" }
> +  1 ? 1 : ((1 ? 1 : i), id (i), 1); // { dg-error "call to consteval function|'i' is not a constant expression" }
> +  1 ? 1 : ((i ? -i : i), id (i), 1); // { dg-error "call to consteval function|'i' is not a constant expression" }
> +  1 ? 1 : ((1 ? 1 : id (i)), id (42), 1); // { dg-error "call to consteval function|'i' is not a constant expression" }
> +  1 ? 1 : ((1 ? 1 : id (42)), id (i)); // { dg-error "call to consteval function|'i' is not a constant expression" }
> +  1 ? 1 : ((1 ? 1 : id (42)), id (i), 1); // { dg-error "call to consteval function|'i' is not a constant expression" }
> +  id (i) ? 1 : ((1 ? 1 : 1), id (i)); // { dg-error "call to consteval function|'i' is not a constant expression" }
> +  1 ? 1 : ((1 ? 1 : id (i)), id (i)); // { dg-error "call to consteval function|'i' is not a constant expression" }
> +  1 ? id (i) : ((1 ? 1 : id (i)), id (i)); // { dg-error "call to consteval function|'i' is not a constant expression" }
> +  1 ? 1 : ((id (i) ? 1 : 1), id (i)); // { dg-error "call to consteval function|'i' is not a constant expression" }
>  }
> diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval9.C b/gcc/testsuite/g++.dg/cpp2a/consteval9.C
> index 051a3d4e355..ad882d51c9b 100644
> --- a/gcc/testsuite/g++.dg/cpp2a/consteval9.C
> +++ b/gcc/testsuite/g++.dg/cpp2a/consteval9.C
> @@ -14,6 +14,7 @@ template <int N>
>  void qux ()
>  {
>    int a = bar (N);     // { dg-message "in 'constexpr' expansion of 'bar\\(2\\)'" }
> +// { dg-error "call to consteval function" "" { target *-*-* } .-1 }
>  }
>
>  // This function is not instantiated so NDR.
> @@ -31,3 +32,4 @@ baz ()
>  }
>
>  int a = bar (2);       // { dg-message "in 'constexpr' expansion of 'bar\\(2\\)'" }
> +// { dg-error "call to consteval function" "" { target *-*-* } .-1 }
> diff --git a/gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C b/gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C
> index 16bc0b85395..fc268d44e1a 100644
> --- a/gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C
> +++ b/gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C
> @@ -480,8 +480,8 @@
>
>  #ifndef __cpp_consteval
>  #  error "__cpp_consteval"
> -#elif __cpp_consteval != 201811
> -#  error "__cpp_consteval != 201811"
> +#elif __cpp_consteval != 202211L
> +#  error "__cpp_consteval != 202211L"
>  #endif
>
>  #ifndef __cpp_concepts
> diff --git a/gcc/testsuite/g++.dg/cpp2a/spaceship-synth9.C b/gcc/testsuite/g++.dg/cpp2a/spaceship-synth9.C
> index 33b547d2b50..ecb46b016a6 100644
> --- a/gcc/testsuite/g++.dg/cpp2a/spaceship-synth9.C
> +++ b/gcc/testsuite/g++.dg/cpp2a/spaceship-synth9.C
> @@ -22,6 +22,6 @@ struct Z: Y<int>
>  int main()
>  {
>    X<char>() == X<char>();      // { dg-error "no match" }
> -  X<int> x; x == x;            // { dg-error "x' is not usable in a constant expression" }
> +  X<int> x; x == x;            // { dg-error "x' is not usable in a constant expression|call to consteval function" }
>    Y<int>()  == Y<int>();       // { dg-warning "nodiscard" }
>  }
> diff --git a/libstdc++-v3/testsuite/18_support/comparisons/categories/zero_neg.cc b/libstdc++-v3/testsuite/18_support/comparisons/categories/zero_neg.cc
> index 9d2115b3f4f..82f7cd54fba 100644
> --- a/libstdc++-v3/testsuite/18_support/comparisons/categories/zero_neg.cc
> +++ b/libstdc++-v3/testsuite/18_support/comparisons/categories/zero_neg.cc
> @@ -52,3 +52,4 @@ test01()
>
>  // { dg-prune-output "reinterpret_cast.* is not a constant expression" }
>  // { dg-prune-output "cast from 'void.' is not allowed" }
> +// { dg-prune-output "not a constant expression" }
> diff --git a/libstdc++-v3/testsuite/std/format/string_neg.cc b/libstdc++-v3/testsuite/std/format/string_neg.cc
> index 7a60ef8cf0e..69bcc736cff 100644
> --- a/libstdc++-v3/testsuite/std/format/string_neg.cc
> +++ b/libstdc++-v3/testsuite/std/format/string_neg.cc
> @@ -2,5 +2,5 @@
>
>  #include <format>
>
> -auto s = std::format(" {9} ");
> +auto s = std::format(" {9} "); // { dg-error "call to consteval function" }
>  // { dg-error "invalid.arg.id" "" { target *-*-* } 0 }
>
> base-commit: 606f7201c066b840ea43ab62fcf47042b81e54d4
> --
> 2.43.0
>

^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH v8] c++: implement P2564, consteval needs to propagate up [PR107687]
  2023-12-04 21:49                 ` Jason Merrill
@ 2023-12-05  0:44                   ` Marek Polacek
  2023-12-06 11:39                     ` Prathamesh Kulkarni
  0 siblings, 1 reply; 6+ messages in thread
From: Marek Polacek @ 2023-12-05  0:44 UTC (permalink / raw)
  To: Jason Merrill; +Cc: GCC Patches

On Mon, Dec 04, 2023 at 04:49:29PM -0500, Jason Merrill wrote:
> On 12/4/23 15:23, Marek Polacek wrote:
> > +/* FN is not a consteval function, but may become one.  Remember to
> > +   escalate it after all pending templates have been instantiated.  */
> > +
> > +void
> > +maybe_store_immediate_escalating_fn (tree fn)
> > +{
> > +  if (unchecked_immediate_escalating_function_p (fn))
> > +    remember_escalating_expr (fn);
> > +}
> 
> > +++ b/gcc/cp/decl.cc
> > @@ -18441,7 +18441,10 @@ finish_function (bool inline_p)
> >    if (!processing_template_decl
> >        && !DECL_IMMEDIATE_FUNCTION_P (fndecl)
> >        && !DECL_OMP_DECLARE_REDUCTION_P (fndecl))
> > -    cp_fold_function (fndecl);
> > +    {
> > +      cp_fold_function (fndecl);
> > +      maybe_store_immediate_escalating_fn (fndecl);
> > +    }
> 
> I think maybe_store_, and the call to it from finish_function, are unneeded;
> we will have already decided whether we need to remember the function during
> the call to cp_fold_function.

'Tis true.
 
> OK with that change.

Here's what I pushed after another regtest.  Thanks!

-- >8 --
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/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.
---
 gcc/c-family/c-cppbuiltin.cc                  |   2 +-
 gcc/c-family/c-opts.cc                        |   5 +
 gcc/c-family/c.opt                            |   4 +
 gcc/cp/call.cc                                |   2 +-
 gcc/cp/constexpr.cc                           |   4 +-
 gcc/cp/cp-gimplify.cc                         | 348 ++++++++++++++----
 gcc/cp/cp-tree.h                              |  19 +-
 gcc/cp/decl2.cc                               |  16 +-
 gcc/cp/error.cc                               |   2 +-
 gcc/cp/module.cc                              |   4 +
 gcc/cp/pt.cc                                  |   2 +-
 gcc/cp/typeck.cc                              |   6 +-
 gcc/doc/invoke.texi                           |  34 ++
 gcc/testsuite/g++.dg/cpp23/consteval-if10.C   |   7 +-
 gcc/testsuite/g++.dg/cpp23/consteval-if2.C    |  14 +-
 gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C       |   4 +-
 gcc/testsuite/g++.dg/cpp26/feat-cxx26.C       |   4 +-
 gcc/testsuite/g++.dg/cpp2a/consteval-memfn1.C |   3 +
 gcc/testsuite/g++.dg/cpp2a/consteval-prop1.C  | 169 +++++++++
 gcc/testsuite/g++.dg/cpp2a/consteval-prop10.C |  41 +++
 gcc/testsuite/g++.dg/cpp2a/consteval-prop11.C |  49 +++
 gcc/testsuite/g++.dg/cpp2a/consteval-prop12.C |  30 ++
 gcc/testsuite/g++.dg/cpp2a/consteval-prop13.C |  23 ++
 gcc/testsuite/g++.dg/cpp2a/consteval-prop14.C |  78 ++++
 gcc/testsuite/g++.dg/cpp2a/consteval-prop15.C | 107 ++++++
 gcc/testsuite/g++.dg/cpp2a/consteval-prop16.C |  73 ++++
 gcc/testsuite/g++.dg/cpp2a/consteval-prop17.C |  17 +
 gcc/testsuite/g++.dg/cpp2a/consteval-prop18.C |  20 +
 gcc/testsuite/g++.dg/cpp2a/consteval-prop19.C |   7 +
 gcc/testsuite/g++.dg/cpp2a/consteval-prop2.C  |  90 +++++
 gcc/testsuite/g++.dg/cpp2a/consteval-prop20.C |  21 ++
 gcc/testsuite/g++.dg/cpp2a/consteval-prop3.C  |  27 ++
 gcc/testsuite/g++.dg/cpp2a/consteval-prop4.C  |  30 ++
 gcc/testsuite/g++.dg/cpp2a/consteval-prop5.C  |  27 ++
 gcc/testsuite/g++.dg/cpp2a/consteval-prop6.C  |  59 +++
 gcc/testsuite/g++.dg/cpp2a/consteval-prop7.C  |  76 ++++
 gcc/testsuite/g++.dg/cpp2a/consteval-prop8.C  |  82 +++++
 gcc/testsuite/g++.dg/cpp2a/consteval-prop9.C  |  67 ++++
 gcc/testsuite/g++.dg/cpp2a/consteval11.C      |  18 +
 gcc/testsuite/g++.dg/cpp2a/consteval3.C       |   4 +-
 gcc/testsuite/g++.dg/cpp2a/consteval34.C      |   8 +
 gcc/testsuite/g++.dg/cpp2a/consteval36.C      |  26 +-
 gcc/testsuite/g++.dg/cpp2a/consteval9.C       |   2 +
 gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C       |   4 +-
 gcc/testsuite/g++.dg/cpp2a/spaceship-synth9.C |   2 +-
 .../comparisons/categories/zero_neg.cc        |   1 +
 .../testsuite/std/format/string_neg.cc        |   2 +-
 47 files changed, 1525 insertions(+), 115 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/cpp2a/consteval-prop1.C
 create mode 100644 gcc/testsuite/g++.dg/cpp2a/consteval-prop10.C
 create mode 100644 gcc/testsuite/g++.dg/cpp2a/consteval-prop11.C
 create mode 100644 gcc/testsuite/g++.dg/cpp2a/consteval-prop12.C
 create mode 100644 gcc/testsuite/g++.dg/cpp2a/consteval-prop13.C
 create mode 100644 gcc/testsuite/g++.dg/cpp2a/consteval-prop14.C
 create mode 100644 gcc/testsuite/g++.dg/cpp2a/consteval-prop15.C
 create mode 100644 gcc/testsuite/g++.dg/cpp2a/consteval-prop16.C
 create mode 100644 gcc/testsuite/g++.dg/cpp2a/consteval-prop17.C
 create mode 100644 gcc/testsuite/g++.dg/cpp2a/consteval-prop18.C
 create mode 100644 gcc/testsuite/g++.dg/cpp2a/consteval-prop19.C
 create mode 100644 gcc/testsuite/g++.dg/cpp2a/consteval-prop2.C
 create mode 100644 gcc/testsuite/g++.dg/cpp2a/consteval-prop20.C
 create mode 100644 gcc/testsuite/g++.dg/cpp2a/consteval-prop3.C
 create mode 100644 gcc/testsuite/g++.dg/cpp2a/consteval-prop4.C
 create mode 100644 gcc/testsuite/g++.dg/cpp2a/consteval-prop5.C
 create mode 100644 gcc/testsuite/g++.dg/cpp2a/consteval-prop6.C
 create mode 100644 gcc/testsuite/g++.dg/cpp2a/consteval-prop7.C
 create mode 100644 gcc/testsuite/g++.dg/cpp2a/consteval-prop8.C
 create mode 100644 gcc/testsuite/g++.dg/cpp2a/consteval-prop9.C

diff --git a/gcc/c-family/c-cppbuiltin.cc b/gcc/c-family/c-cppbuiltin.cc
index e536429fa4c..2d1249f29ed 100644
--- a/gcc/c-family/c-cppbuiltin.cc
+++ b/gcc/c-family/c-cppbuiltin.cc
@@ -1059,7 +1059,7 @@ c_cpp_builtins (cpp_reader *pfile)
 	    cpp_define (pfile, "__cpp_constexpr=202002L");
 	  cpp_define (pfile, "__cpp_constexpr_in_decltype=201711L");
 	  cpp_define (pfile, "__cpp_conditional_explicit=201806L");
-	  cpp_define (pfile, "__cpp_consteval=201811L");
+	  cpp_define (pfile, "__cpp_consteval=202211L");
 	  cpp_define (pfile, "__cpp_constinit=201907L");
 	  cpp_define (pfile, "__cpp_deduction_guides=201907L");
 	  cpp_define (pfile, "__cpp_nontype_template_args=201911L");
diff --git a/gcc/c-family/c-opts.cc b/gcc/c-family/c-opts.cc
index d7faff10d66..d484ecfdfdb 100644
--- a/gcc/c-family/c-opts.cc
+++ b/gcc/c-family/c-opts.cc
@@ -1139,6 +1139,11 @@ c_common_post_options (const char **pfilename)
   if (cxx_dialect >= cxx20 || flag_concepts_ts)
     flag_concepts = 1;
 
+  /* -fimmediate-escalation has no effect when immediate functions are not
+     supported.  */
+  if (flag_immediate_escalation && cxx_dialect < cxx20)
+    flag_immediate_escalation = 0;
+
   if (num_in_fnames > 1)
     error ("too many filenames given; type %<%s %s%> for usage",
 	   progname, "--help");
diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
index ab44a6da66a..3706505f8bf 100644
--- a/gcc/c-family/c.opt
+++ b/gcc/c-family/c.opt
@@ -1898,6 +1898,10 @@ fhuge-objects
 C++ ObjC++ WarnRemoved
 No longer supported.
 
+fimmediate-escalation
+C++ ObjC++ Var(flag_immediate_escalation) Init(1)
+Implement P2564 for consteval propagation.
+
 fimplement-inlines
 C++ ObjC++ Var(flag_implement_inlines) Init(1)
 Export functions even if they can be inlined.
diff --git a/gcc/cp/call.cc b/gcc/cp/call.cc
index ae0decd87f1..c7efc5b077a 100644
--- a/gcc/cp/call.cc
+++ b/gcc/cp/call.cc
@@ -9742,7 +9742,7 @@ in_immediate_context ()
 /* Return true if a call to FN with number of arguments NARGS
    is an immediate invocation.  */
 
-static bool
+bool
 immediate_invocation_p (tree fn)
 {
   return (TREE_CODE (fn) == FUNCTION_DECL
diff --git a/gcc/cp/constexpr.cc b/gcc/cp/constexpr.cc
index 96c61666470..58187a4fd12 100644
--- a/gcc/cp/constexpr.cc
+++ b/gcc/cp/constexpr.cc
@@ -3128,11 +3128,11 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
 	/* OK */;
       else if (!DECL_SAVED_TREE (fun))
 	{
-	  /* When at_eof >= 2, cgraph has started throwing away
+	  /* When at_eof >= 3, cgraph has started throwing away
 	     DECL_SAVED_TREE, so fail quietly.  FIXME we get here because of
 	     late code generation for VEC_INIT_EXPR, which needs to be
 	     completely reconsidered.  */
-	  gcc_assert (at_eof >= 2 && ctx->quiet);
+	  gcc_assert (at_eof >= 3 && ctx->quiet);
 	  *non_constant_p = true;
 	}
       else if (tree copy = get_fundef_copy (new_call.fundef))
diff --git a/gcc/cp/cp-gimplify.cc b/gcc/cp/cp-gimplify.cc
index 795c811471d..5abb91bbdd3 100644
--- a/gcc/cp/cp-gimplify.cc
+++ b/gcc/cp/cp-gimplify.cc
@@ -43,6 +43,21 @@ along with GCC; see the file COPYING3.  If not see
 #include "omp-general.h"
 #include "opts.h"
 
+/* Keep track of forward references to immediate-escalating functions in
+   case they become consteval.  This vector contains ADDR_EXPRs and
+   PTRMEM_CSTs; it also stores FUNCTION_DECLs that had an escalating
+   function call in them, to check that they can be evaluated to a constant,
+   and immediate-escalating functions that may become consteval.  */
+static GTY(()) hash_set<tree> *deferred_escalating_exprs;
+
+static void
+remember_escalating_expr (tree t)
+{
+  if (!deferred_escalating_exprs)
+    deferred_escalating_exprs = hash_set<tree>::create_ggc (37);
+  deferred_escalating_exprs->add (t);
+}
+
 /* Flags for cp_fold and cp_fold_r.  */
 
 enum fold_flags {
@@ -53,8 +68,6 @@ enum fold_flags {
      definitely not in a manifestly constant-evaluated
      context.  */
   ff_mce_false = 1 << 1,
-  /* Whether we're being called from cp_fold_immediate.  */
-  ff_fold_immediate = 1 << 2,
 };
 
 using fold_flags_t = int;
@@ -72,6 +85,7 @@ static tree cp_genericize_r (tree *, int *, void *);
 static tree cp_fold_r (tree *, int *, void *);
 static void cp_genericize_tree (tree*, bool);
 static tree cp_fold (tree, fold_flags_t);
+static tree cp_fold_immediate_r (tree *, int *, void *);
 
 /* Genericize a TRY_BLOCK.  */
 
@@ -428,6 +442,104 @@ lvalue_has_side_effects (tree e)
     return TREE_SIDE_EFFECTS (e);
 }
 
+/* Return true if FN is an immediate-escalating function.  */
+
+static bool
+immediate_escalating_function_p (tree fn)
+{
+  if (!fn || !flag_immediate_escalation)
+    return false;
+
+  gcc_checking_assert (TREE_CODE (fn) == FUNCTION_DECL);
+
+  if (DECL_IMMEDIATE_FUNCTION_P (fn))
+    return false;
+
+  /* An immediate-escalating function is
+      -- the call operator of a lambda that is not declared with the consteval
+	 specifier  */
+  if (LAMBDA_FUNCTION_P (fn))
+    return true;
+  /* -- a defaulted special member function that is not declared with the
+	consteval specifier  */
+  special_function_kind sfk = special_memfn_p (fn);
+  if (sfk != sfk_none && DECL_DEFAULTED_FN (fn))
+    return true;
+  /* -- a function that results from the instantiation of a templated entity
+	defined with the constexpr specifier.  */
+  return is_instantiation_of_constexpr (fn);
+}
+
+/* Return true if FN is an immediate-escalating function that has not been
+   checked for escalating expressions..  */
+
+static bool
+unchecked_immediate_escalating_function_p (tree fn)
+{
+  return (immediate_escalating_function_p (fn)
+	  && !DECL_ESCALATION_CHECKED_P (fn));
+}
+
+/* Promote FN to an immediate function, including its clones.  */
+
+static void
+promote_function_to_consteval (tree fn)
+{
+  SET_DECL_IMMEDIATE_FUNCTION_P (fn);
+  DECL_ESCALATION_CHECKED_P (fn) = true;
+  tree clone;
+  FOR_EACH_CLONE (clone, fn)
+    {
+      SET_DECL_IMMEDIATE_FUNCTION_P (clone);
+      DECL_ESCALATION_CHECKED_P (clone) = true;
+    }
+}
+
+/* A wrapper around cp_fold_immediate_r.  Return a non-null tree if
+   we found a non-constant immediate function, or taking the address
+   of an immediate function.  */
+
+tree
+cp_fold_immediate (tree *tp, mce_value manifestly_const_eval,
+		   tree decl /*= current_function_decl*/)
+{
+  if (cxx_dialect <= cxx17)
+    return NULL_TREE;
+
+  temp_override<tree> cfd (current_function_decl, decl);
+
+  fold_flags_t flags = ff_none;
+  if (manifestly_const_eval == mce_false)
+    flags |= ff_mce_false;
+
+  cp_fold_data data (flags);
+  int save_errorcount = errorcount;
+  tree r = cp_walk_tree_without_duplicates (tp, cp_fold_immediate_r, &data);
+  if (errorcount > save_errorcount)
+    return integer_one_node;
+  return r;
+}
+
+/* Maybe say that FN (a function decl with DECL_IMMEDIATE_FUNCTION_P set)
+   was initially not an immediate function, but was promoted to one because
+   its body contained an immediate-escalating expression or conversion.  */
+
+static void
+maybe_explain_promoted_consteval (location_t loc, tree fn)
+{
+  if (DECL_ESCALATION_CHECKED_P (fn))
+    {
+      /* See if we can figure out what made the function consteval.  */
+      tree x = cp_fold_immediate (&DECL_SAVED_TREE (fn), mce_unknown, NULL_TREE);
+      if (x)
+	inform (cp_expr_loc_or_loc (x, loc),
+		"%qD was promoted to an immediate function because its "
+		"body contains an immediate-escalating expression %qE", fn, x);
+      else
+	inform (loc, "%qD was promoted to an immediate function", fn);
+    }
+}
+
 /* Gimplify *EXPR_P as rvalue into an expression that can't be modified
    by expressions with side-effects in other operands.  */
 
@@ -746,7 +858,9 @@ cp_gimplify_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p)
       if (ret != GS_ERROR)
 	{
 	  tree decl = cp_get_callee_fndecl_nofold (*expr_p);
-	  if (decl && fndecl_built_in_p (decl, BUILT_IN_FRONTEND))
+	  if (!decl)
+	    break;
+	  if (fndecl_built_in_p (decl, BUILT_IN_FRONTEND))
 	    switch (DECL_FE_FUNCTION_CODE (decl))
 	      {
 	      case CP_BUILT_IN_IS_CONSTANT_EVALUATED:
@@ -771,10 +885,12 @@ cp_gimplify_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p)
 	      default:
 		break;
 	      }
-	  else if (decl
-		   && fndecl_built_in_p (decl, BUILT_IN_CLZG, BUILT_IN_CTZG))
+	  else if (fndecl_built_in_p (decl, BUILT_IN_CLZG, BUILT_IN_CTZG))
 	    ret = (enum gimplify_status) c_gimplify_expr (expr_p, pre_p,
 							  post_p);
+	  else
+	    /* All consteval functions should have been processed by now.  */
+	    gcc_checking_assert (!immediate_invocation_p (decl));
 	}
       break;
 
@@ -1035,6 +1151,20 @@ struct cp_genericize_data
   bool handle_invisiref_parm_p;
 };
 
+/* Emit an error about taking the address of an immediate function.
+   EXPR is the whole expression; DECL is the immediate function.  */
+
+static void
+taking_address_of_imm_fn_error (tree expr, tree decl)
+{
+  auto_diagnostic_group d;
+  const location_t loc = (TREE_CODE (expr) == PTRMEM_CST
+			  ? PTRMEM_CST_LOCATION (expr)
+			  : EXPR_LOCATION (expr));
+  error_at (loc, "taking address of an immediate function %qD", decl);
+  maybe_explain_promoted_consteval (loc, decl);
+}
+
 /* A subroutine of cp_fold_r to handle immediate functions.  */
 
 static tree
@@ -1045,90 +1175,128 @@ cp_fold_immediate_r (tree *stmt_p, int *walk_subtrees, void *data_)
   /* The purpose of this is not to emit errors for mce_unknown.  */
   const tsubst_flags_t complain = (data->flags & ff_mce_false
 				   ? tf_error : tf_none);
+  const tree_code code = TREE_CODE (stmt);
 
   /* No need to look into types or unevaluated operands.
      NB: This affects cp_fold_r as well.  */
-  if (TYPE_P (stmt) || unevaluated_p (TREE_CODE (stmt)))
+  if (TYPE_P (stmt) || unevaluated_p (code) || in_immediate_context ())
     {
       *walk_subtrees = 0;
       return NULL_TREE;
     }
 
-  switch (TREE_CODE (stmt))
-    {
-    case PTRMEM_CST:
-      if (TREE_CODE (PTRMEM_CST_MEMBER (stmt)) == FUNCTION_DECL
-	  && DECL_IMMEDIATE_FUNCTION_P (PTRMEM_CST_MEMBER (stmt)))
-	{
-	  if (!data->pset.add (stmt) && (complain & tf_error))
-	    {
-	      error_at (PTRMEM_CST_LOCATION (stmt),
-			"taking address of an immediate function %qD",
-			PTRMEM_CST_MEMBER (stmt));
-	      *stmt_p = build_zero_cst (TREE_TYPE (stmt));
-	    }
-	  return error_mark_node;
-	}
-      break;
+  tree decl = NULL_TREE;
+  bool call_p = false;
 
-    /* Expand immediate invocations.  */
+  /* We are looking for &fn or fn().  */
+  switch (code)
+    {
     case CALL_EXPR:
     case AGGR_INIT_EXPR:
       if (tree fn = cp_get_callee (stmt))
 	if (TREE_CODE (fn) != ADDR_EXPR || ADDR_EXPR_DENOTES_CALL_P (fn))
-	  if (tree fndecl = cp_get_fndecl_from_callee (fn, /*fold*/false))
-	    if (DECL_IMMEDIATE_FUNCTION_P (fndecl))
-	      {
-		stmt = cxx_constant_value (stmt, complain);
-		if (stmt == error_mark_node)
-		  {
-		    if (complain & tf_error)
-		      *stmt_p = error_mark_node;
-		    return error_mark_node;
-		  }
-		*stmt_p = stmt;
-	      }
+	  decl = cp_get_fndecl_from_callee (fn, /*fold*/false);
+      call_p = true;
+      break;
+    case PTRMEM_CST:
+      decl = PTRMEM_CST_MEMBER (stmt);
       break;
-
     case ADDR_EXPR:
-      if (TREE_CODE (TREE_OPERAND (stmt, 0)) == FUNCTION_DECL
-	  && DECL_IMMEDIATE_FUNCTION_P (TREE_OPERAND (stmt, 0))
-	  && !ADDR_EXPR_DENOTES_CALL_P (stmt))
-	{
-	  if (complain & tf_error)
-	    {
-	      error_at (EXPR_LOCATION (stmt),
-			"taking address of an immediate function %qD",
-			TREE_OPERAND (stmt, 0));
-	      *stmt_p = build_zero_cst (TREE_TYPE (stmt));
-	    }
-	  return error_mark_node;
-	}
+      if (!ADDR_EXPR_DENOTES_CALL_P (stmt))
+	decl = TREE_OPERAND (stmt, 0);
       break;
-
     default:
-      break;
+      return NULL_TREE;
     }
 
-  return NULL_TREE;
-}
+  if (!decl || TREE_CODE (decl) != FUNCTION_DECL)
+    return NULL_TREE;
 
-/* A wrapper around cp_fold_immediate_r.  Return true if we found
-   a non-constant immediate function, or taking the address of an
-   immediate function.  */
+  /* Fully escalate once all templates have been instantiated.  What we're
+     calling is not a consteval function but it may become one.  This
+     requires recursing; DECL may be promoted to consteval because it
+     contains an escalating expression E, but E itself may have to be
+     promoted first, etc.  */
+  if (at_eof > 1 && unchecked_immediate_escalating_function_p (decl))
+    {
+      /* Set before the actual walk to avoid endless recursion.  */
+      DECL_ESCALATION_CHECKED_P (decl) = true;
+      /* We're only looking for the first escalating expression.  Let us not
+	 walk more trees than necessary, hence mce_unknown.  */
+      cp_fold_immediate (&DECL_SAVED_TREE (decl), mce_unknown, decl);
+    }
 
-bool
-cp_fold_immediate (tree *tp, mce_value manifestly_const_eval)
-{
-  if (cxx_dialect <= cxx17)
-    return false;
+  /* [expr.const]p16 "An expression or conversion is immediate-escalating if
+     it is not initially in an immediate function context and it is either
+     -- an immediate invocation that is not a constant expression and is not
+     a subexpression of an immediate invocation."
 
-  fold_flags_t flags = ff_fold_immediate;
-  if (manifestly_const_eval == mce_false)
-    flags |= ff_mce_false;
+     If we are in an immediate-escalating function, the immediate-escalating
+     expression or conversion makes it an immediate function.  So STMT does
+     not need to produce a constant expression.  */
+  if (DECL_IMMEDIATE_FUNCTION_P (decl))
+    {
+      tree e = cxx_constant_value (stmt, tf_none);
+      if (e == error_mark_node)
+	{
+	  /* This takes care of, e.g.,
+	      template <typename T>
+	      constexpr int f(T t)
+	      {
+		return id(t);
+	      }
+	    where id (consteval) causes f<int> to be promoted.  */
+	  if (immediate_escalating_function_p (current_function_decl))
+	    promote_function_to_consteval (current_function_decl);
+	  else if (complain & tf_error)
+	    {
+	      if (call_p)
+		{
+		  auto_diagnostic_group d;
+		  location_t loc = cp_expr_loc_or_input_loc (stmt);
+		  error_at (loc, "call to consteval function %qE is "
+			    "not a constant expression", stmt);
+		  /* Explain why it's not a constant expression.  */
+		  *stmt_p = cxx_constant_value (stmt, complain);
+		  maybe_explain_promoted_consteval (loc, decl);
+		}
+	      else if (!data->pset.add (stmt))
+		{
+		  taking_address_of_imm_fn_error (stmt, decl);
+		  *stmt_p = build_zero_cst (TREE_TYPE (stmt));
+		}
+	      /* If we're giving hard errors, continue the walk rather than
+		 bailing out after the first error.  */
+	      return NULL_TREE;
+	    }
+	  *walk_subtrees = 0;
+	  return stmt;
+	}
+      /* We've evaluated the consteval function call.  */
+      if (call_p)
+	*stmt_p = e;
+    }
+  /* We've encountered a function call that may turn out to be consteval
+     later.  Store its caller so that we can ensure that the call is
+     a constant expression.  */
+  else if (unchecked_immediate_escalating_function_p (decl))
+    {
+      /* Make sure we're not inserting new elements while walking
+	 the deferred_escalating_exprs hash table; if we are, it's
+	 likely that a function wasn't properly marked checked for
+	 i-e expressions.  */
+      gcc_checking_assert (at_eof <= 1);
+      if (current_function_decl)
+	remember_escalating_expr (current_function_decl);
+      /* auto p = &f<int>; in the global scope won't be ensconced in
+	 a function we could store for later at this point.  (If there's
+	 no c_f_d at this point and we're dealing with a call, we should
+	 see the call when cp_fold_function __static_i_and_d.)  */
+      else if (!call_p)
+	remember_escalating_expr (stmt);
+    }
 
-  cp_fold_data data (flags);
-  return !!cp_walk_tree_without_duplicates (tp, cp_fold_immediate_r, &data);
+  return NULL_TREE;
 }
 
 /* Perform any pre-gimplification folding of C++ front end trees to
@@ -1178,11 +1346,19 @@ cp_fold_r (tree *stmt_p, int *walk_subtrees, void *data_)
 	  *walk_subtrees = 0;
 	  /* Don't return yet, still need the cp_fold below.  */
 	}
-      cp_fold_immediate_r (stmt_p, walk_subtrees, data);
+      else
+	cp_fold_immediate_r (stmt_p, walk_subtrees, data);
     }
 
   *stmt_p = stmt = cp_fold (*stmt_p, data->flags);
 
+  /* For certain trees, like +foo(), the cp_fold above will remove the +,
+     and the subsequent tree walk would go straight down to the CALL_EXPR's
+     operands, meaning that cp_fold_immediate_r would never see the
+     CALL_EXPR.  Ew :(.  */
+  if (TREE_CODE (stmt) == CALL_EXPR && code != CALL_EXPR)
+    cp_fold_immediate_r (stmt_p, walk_subtrees, data);
+
   if (data->pset.add (stmt))
     {
       /* Don't walk subtrees of stmts we've already walked once, otherwise
@@ -1304,6 +1480,44 @@ cp_fold_function (tree fndecl)
      pass ff_mce_false.  */
   cp_fold_data data (ff_genericize | ff_mce_false);
   cp_walk_tree (&DECL_SAVED_TREE (fndecl), cp_fold_r, &data, NULL);
+
+  /* This is merely an optimization: if FNDECL has no i-e expressions,
+     we'll not save c_f_d, and we can safely say that FNDECL will not
+     be promoted to consteval.  */
+  if (deferred_escalating_exprs
+      && !deferred_escalating_exprs->contains (current_function_decl))
+    DECL_ESCALATION_CHECKED_P (fndecl) = true;
+}
+
+/* We've stashed immediate-escalating functions.  Now see if they indeed
+   ought to be promoted to consteval.  */
+
+void
+process_and_check_pending_immediate_escalating_fns ()
+{
+  /* This will be null for -fno-immediate-escalation.  */
+  if (!deferred_escalating_exprs)
+    return;
+
+  for (auto e : *deferred_escalating_exprs)
+    if (TREE_CODE (e) == FUNCTION_DECL && !DECL_ESCALATION_CHECKED_P (e))
+      cp_fold_immediate (&DECL_SAVED_TREE (e), mce_false, e);
+
+  /* We've escalated every function that could have been promoted to
+     consteval.  Check that we are not taking the address of a consteval
+     function.  */
+  for (auto e : *deferred_escalating_exprs)
+    {
+      if (TREE_CODE (e) == FUNCTION_DECL)
+	continue;
+      tree decl = (TREE_CODE (e) == PTRMEM_CST
+		   ? PTRMEM_CST_MEMBER (e)
+		   : TREE_OPERAND (e, 0));
+      if (DECL_IMMEDIATE_FUNCTION_P (decl))
+	taking_address_of_imm_fn_error (e, decl);
+    }
+
+  deferred_escalating_exprs = nullptr;
 }
 
 /* Turn SPACESHIP_EXPR EXPR into GENERIC.  */
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 6b3ce9d87da..fef80816c5f 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -2946,8 +2946,9 @@ struct GTY(()) lang_decl_fn {
   unsigned maybe_deleted : 1;
   unsigned coroutine_p : 1;
   unsigned implicit_constexpr : 1;
+  unsigned escalated_p : 1;
 
-  unsigned spare : 9;
+  unsigned spare : 8;
 
   /* 32-bits padding on 64-bit host.  */
 
@@ -3399,6 +3400,14 @@ struct GTY(()) lang_decl {
 #define DECL_MAYBE_DELETED(NODE) \
   (LANG_DECL_FN_CHECK (NODE)->maybe_deleted)
 
+/* Nonzero for FUNCTION_DECL means that this function's body has been
+   checked for immediate-escalating expressions and maybe promoted.  It
+   does *not* mean the function is consteval.  It must not be set in
+   a function that was marked consteval by the user, so that we can
+   distinguish between explicitly consteval functions and promoted consteval
+   functions.  */
+#define DECL_ESCALATION_CHECKED_P(NODE) (LANG_DECL_FN_CHECK (NODE)->escalated_p)
+
 /* True (in a FUNCTION_DECL) if NODE is a virtual function that is an
    invalid overrider for a function from a base class.  Once we have
    complained about an invalid overrider we avoid complaining about it
@@ -5882,7 +5891,8 @@ extern GTY(()) vec<tree, va_gc> *keyed_classes;
 
 \f
 /* Nonzero if we're done parsing and into end-of-file activities.
-   Two if we're done with front-end processing.  */
+   2 if all templates have been instantiated.
+   3 if we're done with front-end processing.  */
 
 extern int at_eof;
 
@@ -6774,6 +6784,7 @@ extern tree perform_direct_initialization_if_possible (tree, tree, bool,
 extern vec<tree,va_gc> *resolve_args (vec<tree,va_gc>*, tsubst_flags_t);
 extern tree in_charge_arg_for_name		(tree);
 extern bool in_immediate_context		();
+extern bool immediate_invocation_p		(tree);
 extern tree build_cxx_call			(tree, int, tree *,
 						 tsubst_flags_t,
 						 tree = NULL_TREE);
@@ -8415,7 +8426,9 @@ extern tree process_stmt_assume_attribute	(tree, tree, location_t);
 extern bool simple_empty_class_p		(tree, tree, tree_code);
 extern tree fold_builtin_source_location	(const_tree);
 extern tree get_source_location_impl_type	();
-extern bool cp_fold_immediate			(tree *, mce_value);
+extern tree cp_fold_immediate			(tree *, mce_value,
+						 tree = current_function_decl);
+extern void process_and_check_pending_immediate_escalating_fns ();
 
 /* in name-lookup.cc */
 extern tree strip_using_decl                    (tree);
diff --git a/gcc/cp/decl2.cc b/gcc/cp/decl2.cc
index 9e666e5eece..bee84879023 100644
--- a/gcc/cp/decl2.cc
+++ b/gcc/cp/decl2.cc
@@ -169,7 +169,9 @@ typedef hash_map<unsigned/*Priority*/, tree/*List*/,
    one for init.  The fini table is only ever used when !cxa_atexit.  */
 static GTY(()) priority_map_t *static_init_fini_fns[2];
 
-/* Nonzero if we're done parsing and into end-of-file activities.  */
+/* Nonzero if we're done parsing and into end-of-file activities.
+   2 if all templates have been instantiated.
+   3 if we're done with front-end processing.  */
 
 int at_eof;
 
@@ -4987,6 +4989,7 @@ c_parse_final_cleanups (void)
   tree decl;
 
   locus_at_end_of_parsing = input_location;
+  /* We're done parsing.  */
   at_eof = 1;
 
   /* Bad parse errors.  Just forget about it.  */
@@ -5252,6 +5255,9 @@ c_parse_final_cleanups (void)
 	reconsider = true;
     }
 
+  /* All templates have been instantiated.  */
+  at_eof = 2;
+
   void *module_cookie = finish_module_processing (parse_in);
 
   lower_var_init ();
@@ -5294,7 +5300,11 @@ c_parse_final_cleanups (void)
   if (static_init_fini_fns[true])
     for (auto iter : *static_init_fini_fns[true])
       iter.second = nreverse (iter.second);
-  
+
+  /* Now we've instantiated all templates.  Now we can escalate the functions
+     we squirreled away earlier.  */
+  process_and_check_pending_immediate_escalating_fns ();
+
   /* Then, do the Objective-C stuff.  This is where all the
      Objective-C module stuff gets generated (symtab,
      class/protocol/selector lists etc).  This must be done after C++
@@ -5376,7 +5386,7 @@ c_parse_final_cleanups (void)
   timevar_start (TV_PHASE_PARSING);
 
   /* Indicate that we're done with front end processing.  */
-  at_eof = 2;
+  at_eof = 3;
 }
 
 /* Perform any post compilation-proper cleanups for the C++ front-end.
diff --git a/gcc/cp/error.cc b/gcc/cp/error.cc
index 785909c362a..3b1b5de5ea4 100644
--- a/gcc/cp/error.cc
+++ b/gcc/cp/error.cc
@@ -478,7 +478,7 @@ dump_template_bindings (cxx_pretty_printer *pp, tree parms, tree args,
 
   /* Don't try to do this once cgraph starts throwing away front-end
      information.  */
-  if (at_eof >= 2)
+  if (at_eof >= 3)
     return;
 
   FOR_EACH_VEC_SAFE_ELT (typenames, i, t)
diff --git a/gcc/cp/module.cc b/gcc/cp/module.cc
index 33fcf396875..1b57fbe2124 100644
--- a/gcc/cp/module.cc
+++ b/gcc/cp/module.cc
@@ -5683,6 +5683,8 @@ trees_out::lang_decl_bools (tree t)
       WB (lang->u.fn.has_dependent_explicit_spec_p);
       WB (lang->u.fn.immediate_fn_p);
       WB (lang->u.fn.maybe_deleted);
+      WB (lang->u.fn.escalated_p);
+      /* We do not stream lang->u.fn.implicit_constexpr.  */
       goto lds_min;
 
     case lds_decomp:  /* lang_decl_decomp.  */
@@ -5751,6 +5753,8 @@ trees_in::lang_decl_bools (tree t)
       RB (lang->u.fn.has_dependent_explicit_spec_p);
       RB (lang->u.fn.immediate_fn_p);
       RB (lang->u.fn.maybe_deleted);
+      RB (lang->u.fn.escalated_p);
+      /* We do not stream lang->u.fn.implicit_constexpr.  */
       goto lds_min;
 
     case lds_decomp:  /* lang_decl_decomp.  */
diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
index 7265201e036..924a20973b4 100644
--- a/gcc/cp/pt.cc
+++ b/gcc/cp/pt.cc
@@ -11107,7 +11107,7 @@ push_tinst_level_loc (tree tldcl, tree targs, location_t loc)
   if (tinst_depth >= max_tinst_depth)
     {
       /* Tell error.cc not to try to instantiate any templates.  */
-      at_eof = 2;
+      at_eof = 3;
       fatal_error (input_location,
 		   "template instantiation depth exceeds maximum of %d"
 		   " (use %<-ftemplate-depth=%> to increase the maximum)",
diff --git a/gcc/cp/typeck.cc b/gcc/cp/typeck.cc
index bf8ffaa7e75..8e4cfae08aa 100644
--- a/gcc/cp/typeck.cc
+++ b/gcc/cp/typeck.cc
@@ -7269,11 +7269,9 @@ cp_build_addr_expr_1 (tree arg, bool strict_lvalue, tsubst_flags_t complain)
 			      complain);
     }
 
-  /* For addresses of immediate functions ensure we have EXPR_LOCATION
-     set for possible later diagnostics.  */
+  /* Ensure we have EXPR_LOCATION set for possible later diagnostics.  */
   if (TREE_CODE (val) == ADDR_EXPR
-      && TREE_CODE (TREE_OPERAND (val, 0)) == FUNCTION_DECL
-      && DECL_IMMEDIATE_FUNCTION_P (TREE_OPERAND (val, 0)))
+      && TREE_CODE (TREE_OPERAND (val, 0)) == FUNCTION_DECL)
     SET_EXPR_LOCATION (val, input_location);
 
   return val;
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 2b51ff304f6..681e3f3f466 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -219,6 +219,7 @@ in the following sections.
 -fno-elide-constructors
 -fno-enforce-eh-specs
 -fno-gnu-keywords
+-fno-immediate-escalation
 -fno-implicit-templates
 -fno-implicit-inline-templates
 -fno-implement-inlines
@@ -3386,6 +3387,39 @@ word as an identifier.  You can use the keyword @code{__typeof__} instead.
 This option is implied by the strict ISO C++ dialects: @option{-ansi},
 @option{-std=c++98}, @option{-std=c++11}, etc.
 
+@opindex fno-immediate-escalation
+@opindex fimmediate-escalation
+@item -fno-immediate-escalation
+Do not enable immediate function escalation whereby certain functions
+can be promoted to consteval, as specified in P2564R3.  For example:
+
+@example
+consteval int id(int i) @{ return i; @}
+
+constexpr int f(auto t)
+@{
+  return t + id(t); // id causes f<int> to be promoted to consteval
+@}
+
+void g(int i)
+@{
+  f (3);
+@}
+@end example
+
+compiles in C++20: @code{f} is an immediate-escalating function (due to
+the @code{auto} it is a function template and is declared @code{constexpr})
+and @code{id(t)} is an immediate-escalating expression, so @code{f} is
+promoted to @code{consteval}.  Consequently, the call to @code{id(t)}
+is in an immediate context, so doesn't have to produce a constant (that
+is the mechanism allowing consteval function composition).  However,
+with @option{-fno-immediate-escalation}, @code{f} is not promoted to
+@code{consteval}, and since the call to consteval function @code{id(t)}
+is not a constant expression, the compiler rejects the code.
+
+This option is turned on by default; it is only effective in C++20 mode
+or later.
+
 @opindex fimplicit-constexpr
 @item -fimplicit-constexpr
 Make inline functions implicitly constexpr, if they satisfy the
diff --git a/gcc/testsuite/g++.dg/cpp23/consteval-if10.C b/gcc/testsuite/g++.dg/cpp23/consteval-if10.C
index 4c0523fe1d0..b8709beba85 100644
--- a/gcc/testsuite/g++.dg/cpp23/consteval-if10.C
+++ b/gcc/testsuite/g++.dg/cpp23/consteval-if10.C
@@ -2,6 +2,9 @@
 // { dg-do compile { target c++20 } }
 // { dg-options "" }
 
+// We used to give errors but the lambdas are now promoted to consteval
+// and are in a immediate function context, so no errors.
+
 consteval int foo (int x) { return x; }
 
 constexpr int
@@ -10,7 +13,7 @@ bar (int x)
   int r = 0;
   if consteval		// { dg-warning "'if consteval' only available with" "" { target c++20_only } }
     {
-      auto y = [=] { foo (x); };	// { dg-error "'x' is not a constant expression" }
+      auto y = [=] { foo (x); };
       y ();
     }
   return r;
@@ -23,7 +26,7 @@ baz (T x)
   T r = 0;
   if consteval		// { dg-warning "'if consteval' only available with" "" { target c++20_only } }
     {
-      auto y = [=] { foo (x); };	// { dg-error "'x' is not a constant expression" }
+      auto y = [=] { foo (x); };
       y ();
     }
   return r;
diff --git a/gcc/testsuite/g++.dg/cpp23/consteval-if2.C b/gcc/testsuite/g++.dg/cpp23/consteval-if2.C
index b2c5472b7de..3b258711ce6 100644
--- a/gcc/testsuite/g++.dg/cpp23/consteval-if2.C
+++ b/gcc/testsuite/g++.dg/cpp23/consteval-if2.C
@@ -33,7 +33,7 @@ baz (int x)
   int r = 0;
   if not consteval	// { dg-warning "'if consteval' only available with" "" { target c++20_only } }
     {
-      r += foo (x);	// { dg-error "'x' is not a constant expression" }
+      r += foo (x);	// { dg-error "not a constant expression" }
     }
   else
     {
@@ -45,11 +45,11 @@ baz (int x)
     }
   else
     {
-      r += foo (8 * x);	// { dg-error "'x' is not a constant expression" }
+      r += foo (8 * x);	// { dg-error "is not a constant expression" }
     }
   if ! consteval	// { dg-warning "'if consteval' only available with" "" { target c++20_only } }
     {
-      r += foo (32 * x);// { dg-error "'x' is not a constant expression" }
+      r += foo (32 * x);// { dg-error "not a constant expression" }
     }
   if consteval		// { dg-warning "'if consteval' only available with" "" { target c++20_only } }
     {
@@ -98,7 +98,7 @@ corge (T x)
   T r = 0;
   if not consteval	// { dg-warning "'if consteval' only available with" "" { target c++20_only } }
     {
-      r += foo (x);	// { dg-error "'x' is not a constant expression" }
+      r += foo (x);
     }
   else
     {
@@ -110,11 +110,11 @@ corge (T x)
     }
   else
     {
-      r += foo (8 * x);	// { dg-error "is not a constant expression" }
+      r += foo (8 * x);
     }
   if ! consteval	// { dg-warning "'if consteval' only available with" "" { target c++20_only } }
     {
-      r += foo (32 * x);// { dg-error "is not a constant expression" }
+      r += foo (32 * x);
     }
   if consteval		// { dg-warning "'if consteval' only available with" "" { target c++20_only } }
     {
@@ -126,5 +126,5 @@ corge (T x)
 int
 garply (int x)
 {
-  return corge (x);
+  return corge (x); // { dg-error "is not a constant expression" }
 }
diff --git a/gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C b/gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C
index 9e29b01adc1..2b21bd1bc0d 100644
--- a/gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C
+++ b/gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C
@@ -480,8 +480,8 @@
 
 #ifndef __cpp_consteval
 #  error "__cpp_consteval"
-#elif __cpp_consteval != 201811
-#  error "__cpp_consteval != 201811"
+#elif __cpp_consteval != 202211L
+#  error "__cpp_consteval != 202211L"
 #endif
 
 #ifndef __cpp_concepts
diff --git a/gcc/testsuite/g++.dg/cpp26/feat-cxx26.C b/gcc/testsuite/g++.dg/cpp26/feat-cxx26.C
index 6244f8fdfe4..4507ea07d1c 100644
--- a/gcc/testsuite/g++.dg/cpp26/feat-cxx26.C
+++ b/gcc/testsuite/g++.dg/cpp26/feat-cxx26.C
@@ -480,8 +480,8 @@
 
 #ifndef __cpp_consteval
 #  error "__cpp_consteval"
-#elif __cpp_consteval != 201811
-#  error "__cpp_consteval != 201811"
+#elif __cpp_consteval != 202211L
+#  error "__cpp_consteval != 202211L"
 #endif
 
 #ifndef __cpp_concepts
diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval-memfn1.C b/gcc/testsuite/g++.dg/cpp2a/consteval-memfn1.C
index 46eed13446d..ca923519f98 100644
--- a/gcc/testsuite/g++.dg/cpp2a/consteval-memfn1.C
+++ b/gcc/testsuite/g++.dg/cpp2a/consteval-memfn1.C
@@ -20,10 +20,13 @@ template<class>
 void VerifyHash(fixed_string s) {
   s.size(0); // { dg-bogus "" }
   s.size(-1); // { dg-message "expansion of" }
+// { dg-error "call to consteval function" "" { target *-*-* } .-1 }
   s.size_static(0); // { dg-bogus "" }
   s.size_static(-1); // { dg-message "expansion of" }
+// { dg-error "call to consteval function" "" { target *-*-* } .-1 }
   fixed_string::size_static(0); // { dg-bogus "" }
   fixed_string::size_static(-1); // { dg-message "expansion of" }
+// { dg-error "call to consteval function" "" { target *-*-* } .-1 }
   s(); // { dg-bogus "" }
 }
 
diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval-prop1.C b/gcc/testsuite/g++.dg/cpp2a/consteval-prop1.C
new file mode 100644
index 00000000000..5e7b208113f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/consteval-prop1.C
@@ -0,0 +1,169 @@
+// P2564R3
+// { dg-do compile { target c++20 } }
+// Some of these were cribbed from clang's cxx2b-consteval-propagate.cpp.
+
+consteval int id(int i) { return i; }
+
+template <typename T>
+constexpr int
+f0 (T t)
+{
+  // OK, f0<int> promoted to consteval.
+  return id (t); // { dg-message "immediate-escalating expression .id\\(t\\)." }
+}
+
+constexpr auto a0 = f0 (3);
+
+// As a consequence of f0<int> being promoted to an immediate function, we
+// can't take its address.
+auto p0 = &f0<int>; // { dg-error "taking address of an immediate function" }
+
+template <typename T>
+constexpr int
+f1 (T t)
+{
+  // OK, f1<int> promoted to consteval.
+  return t + id (t); // { dg-message "immediate-escalating expression .id\\(t\\)." }
+}
+
+constexpr auto a1 = f1 (3);
+
+// As a consequence of f1<int> being promoted to an immediate function, we
+// can't take its address.
+auto p1 = &f1<int>; // { dg-error "taking address of an immediate function" }
+
+template <typename T>
+constexpr int
+f2 (T)
+{
+  // This produces a constant; f2 *not* promoted to consteval.
+  return id (42);
+}
+
+// ... so we can take its address.
+auto p2 = &f2<int>;
+
+constexpr int
+f3 (int i)
+{
+  // f3 isn't a function template and those don't get upgraded to consteval.
+  return id (i); // { dg-error "not a constant expression" }
+}
+
+auto p3 = &f3;
+
+template<typename T>
+constexpr int
+f4 (T t)
+{
+  auto p = id; // { dg-message "immediate-escalating expression .id." }
+  (void) p;
+  return t;
+}
+
+auto p6 = &f4<int>; // { dg-error "taking address of an immediate function" }
+
+static_assert (f4 (42) == 42);
+
+// Constructors.
+consteval int zero (int)
+{
+  return 0;
+}
+
+struct A {
+  // A::A(auto) promoted to consteval.
+  constexpr A(auto i) { zero (i); }
+};
+
+constexpr void
+f5 (auto i)
+{
+  A a{i};
+}
+
+constexpr void
+f5_nt (int i)
+{
+  A a{i}; // { dg-error "call to consteval function|not a constant" }
+}
+
+void
+f6 ()
+{
+  f5 (0);
+}
+
+struct B {
+  constexpr B(int) { }
+};
+
+B b1(f0<int>((f1<int>(7))));
+
+template<typename T>
+constexpr int cid(T t) { return t; }
+
+auto p4 = &cid<int>;
+auto p5 = &cid<char>;
+
+int g = 7; // { dg-message ".int g. is not const" }
+
+B b2(f0<int>(cid<int>(g))); // { dg-error "call to consteval function|not usable" }
+
+struct C {
+  consteval C (int) {};
+};
+
+constexpr int
+f7 (auto t)
+{
+  C c(t); // { dg-message "immediate-escalating expression .c.C::C\\(t\\)." }
+  return 0;
+}
+
+int i1 = f7 (g); // { dg-error "call to consteval function|not usable" }
+
+struct Y {
+  int y;
+  int x = id (y);
+  consteval Y (int i) : y (id (i)) {}
+};
+
+Y y1(1);
+Y y2(g); // { dg-error "call to consteval function|not usable" }
+
+struct Y2 {
+  int y;
+  int x = id (y);
+  constexpr Y2 (auto i) : y (id (i)) {}
+};
+
+Y2 y3(1);
+Y2 y4(g); // { dg-error "call to consteval function|not usable" }
+
+auto l1 = [](int i) constexpr {
+  int t = id (i);
+  return id (0);
+};
+
+int (*pl1)(int) = l1; // { dg-error "call to consteval function|returns address of immediate function" }
+
+auto l2 = [](int i) {
+  int t = id (i);
+  return id (0);
+};
+
+int (*pl2)(int) = l2; // { dg-error "call to consteval function|returns address of immediate function" }
+
+// Not defined = won't produce a constant expression.
+consteval int undef (); // { dg-warning "used but never defined" }
+
+struct S {
+  int a = [] { return undef (); }();
+};
+
+struct S2 {  // { dg-error "used before its definition" }
+  int a = [] (int u = undef ()) {
+    return u;
+  }();
+} s2; // { dg-error "call to consteval function" }
diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval-prop10.C b/gcc/testsuite/g++.dg/cpp2a/consteval-prop10.C
new file mode 100644
index 00000000000..4e33e6e3d0e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/consteval-prop10.C
@@ -0,0 +1,41 @@
+// P2564R3
+// { dg-do compile { target c++20 } }
+// Test default arguments.
+
+consteval int id (int i) { return i; }
+
+template<typename>
+constexpr int
+f1 (int i = id (42))
+{
+  return i;
+}
+
+int non_const; // { dg-message ".int non_const. is not const" }
+
+template<typename>
+constexpr int
+f2 (int i = id (non_const))
+{
+  return i;
+}
+
+constexpr int
+f3 (auto)
+{
+  return f2<int>(); // { dg-message "contains an immediate-escalating expression .id\\(non_const\\)." }
+}
+
+auto a = &f3<int>; // { dg-error "taking address of an immediate function" }
+
+void
+g (int i)
+{
+  f1<int> (42);
+  f1<int> (i);
+  f1<int> ();
+  f2<int> (42);
+  f2<int> (i);
+  f2<int> (); // { dg-error "call to consteval function .id\\(non_const\\). is not a constant expression" }
+// { dg-error ".non_const. is not usable in a constant expression" "" { target *-*-* } .-1 }
+}
diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval-prop11.C b/gcc/testsuite/g++.dg/cpp2a/consteval-prop11.C
new file mode 100644
index 00000000000..aca9675cd53
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/consteval-prop11.C
@@ -0,0 +1,49 @@
+// P2564R3
+// { dg-do compile { target c++20 } }
+// { dg-options "-fdiagnostics-show-caret" }
+// Test diagnostic.
+
+consteval int id (int i) { return i; }
+constexpr int foo (int i ) { return i; }
+
+constexpr int
+foobar (auto i)
+{
+  return i + id (i);
+  /* { dg-begin-multiline-output "" }
+   return i + id (i);
+              ~~~^~~
+     { dg-end-multiline-output "" } */
+}
+
+void
+g (int x)
+{
+  foobar (x); // { dg-error "10:call to consteval function .foobar<int>\\(x\\). is not a constant expression" }
+// { dg-error ".x. is not a constant expression" "" { target *-*-* } .-1 }
+  /* { dg-begin-multiline-output "" }
+foobar (x);
+   ~~~~~~~^~~
+     { dg-end-multiline-output "" } */
+}
+
+constexpr int
+f2 (auto i)
+{
+  auto p = &id;
+  /* { dg-begin-multiline-output "" }
+   auto p = &id;
+            ^~~
+     { dg-end-multiline-output "" } */
+  return p (i);
+}
+
+void
+g2 (int x)
+{
+  f2 (x); // { dg-error "6:call to consteval function .f2<int>\\(x\\). is not a constant expression|not a constant expression" }
+  /* { dg-begin-multiline-output "" }
+f2 (x);
+   ~~~^~~
+     { dg-end-multiline-output "" } */
+}
diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval-prop12.C b/gcc/testsuite/g++.dg/cpp2a/consteval-prop12.C
new file mode 100644
index 00000000000..2949ab83af8
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/consteval-prop12.C
@@ -0,0 +1,30 @@
+// P2564R3
+// { dg-do compile { target c++20 } }
+
+consteval int
+zero (int)
+{
+  return 0;
+}
+
+constexpr int
+f (auto i)
+{
+  return zero (i);
+}
+
+constexpr int
+g (auto)
+{
+  // This call is a constant expression, so don't promote g.
+  return f (42);
+}
+
+void
+do_test ()
+{
+  g (2);
+}
+
+// Must work.
+auto q = &g<int>;
diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval-prop13.C b/gcc/testsuite/g++.dg/cpp2a/consteval-prop13.C
new file mode 100644
index 00000000000..6c20b98a87c
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/consteval-prop13.C
@@ -0,0 +1,23 @@
+// P2564R3
+// { dg-do compile { target c++20 } }
+// Verify we don't recurse endlessly while determining whether a function
+// should be propagated to consteval.
+
+consteval int id (int i) { return i; }
+
+constexpr int f2 (auto);
+
+constexpr int
+f1 (auto i)
+{
+  return f2 (i);
+}
+
+constexpr int
+f2 (auto i)
+{
+  return f1 (i);
+}
+
+auto p = &f1<int>;
+auto q = &f2<int>;
diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval-prop14.C b/gcc/testsuite/g++.dg/cpp2a/consteval-prop14.C
new file mode 100644
index 00000000000..cdc1f6dc862
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/consteval-prop14.C
@@ -0,0 +1,78 @@
+// P2564R3
+// { dg-do compile { target c++20 } }
+// Test more CALL_EXPRs in a function, some of which are escalating.
+
+consteval int id (int i) { return i; }
+constexpr int neg (int i) { return -i; }
+constexpr int foo (auto i) { return id (i); }
+
+constexpr int
+f1 (auto i)
+{
+  auto x = id (i);  // { dg-message "promoted to an immediate function because its body contains an immediate-escalating expression .id\\(i\\)." }
+  auto y = neg (i);
+  return x + y;
+}
+
+constexpr int
+f2 (auto i)
+{
+  return neg (id (i)); // { dg-message "promoted to an immediate function because its body contains an immediate-escalating expression .id\\(i\\)." }
+}
+
+constexpr int
+f3 (auto i)
+{
+  auto x = i + neg (neg (neg (id (neg (neg (i)))))); // { dg-message "promoted to an immediate function because its body contains an immediate-escalating expression .id\\(neg\\(neg\\(i\\)\\)\\)." }
+  return x;
+}
+
+constexpr int
+f4 (auto i)
+{
+  return i + neg ((id (2 * i) + neg (i)) / 2); // { dg-message "promoted to an immediate function because its body contains an immediate-escalating expression .id\\(\\(i \\* 2\\)\\)." }
+}
+
+constexpr int
+f5 (auto i)
+{
+  (void) neg (i);
+  (void) neg (i);
+  (void) neg (i);
+  (void) neg (i);
+  (void) neg (i);
+  (void) neg (i);
+  (void) +id (i); // { dg-message "promoted to an immediate function because its body contains an immediate-escalating expression .id\\(i\\)." }
+  (void) neg (i);
+  return i;
+}
+
+constexpr int
+f6 (auto i)
+{
+  auto x = neg (i + foo (i)); // { dg-message "promoted to an immediate function because its body contains an immediate-escalating expression .foo<int>\\(i\\)." }
+  return x;
+}
+
+void
+g (int i)
+{
+  f1 (i); // { dg-error "call to consteval function .f1<int>\\(i\\). is not a constant expression" }
+// { dg-error ".i. is not a constant expression" "" { target *-*-* } .-1 }
+  f1 (42);
+  f2 (i); // { dg-error "call to consteval function .f2<int>\\(i\\). is not a constant expression" }
+// { dg-error ".i. is not a constant expression" "" { target *-*-* } .-1 }
+  f2 (42);
+  f3 (i); // { dg-error "call to consteval function .f3<int>\\(i\\). is not a constant expression" }
+// { dg-error ".i. is not a constant expression" "" { target *-*-* } .-1 }
+  f3 (42);
+  f4 (i); // { dg-error "call to consteval function .f4<int>\\(i\\). is not a constant expression" }
+// { dg-error ".i. is not a constant expression" "" { target *-*-* } .-1 }
+  f4 (42);
+  f5 (i); // { dg-error "call to consteval function .f5<int>\\(i\\). is not a constant expression" }
+// { dg-error ".i. is not a constant expression" "" { target *-*-* } .-1 }
+  f5 (42);
+  f6 (i); // { dg-error "call to consteval function .f6<int>\\(i\\). is not a constant expression" }
+// { dg-error ".i. is not a constant expression" "" { target *-*-* } .-1 }
+  f6 (42);
+}
diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval-prop15.C b/gcc/testsuite/g++.dg/cpp2a/consteval-prop15.C
new file mode 100644
index 00000000000..3341c510a9f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/consteval-prop15.C
@@ -0,0 +1,107 @@
+// P2564R3
+// { dg-do compile { target c++20 } }
+// { dg-options "-Wno-c++23-extensions" }
+
+consteval int id (int i) { return i; }
+
+constexpr int
+f1 (auto i)
+{
+  auto p = &id; // { dg-message "promoted to an immediate function because its body contains an immediate-escalating expression .id." }
+  (void) p;
+  return i;
+}
+
+constexpr int
+f2 (auto i)
+{
+  return f1 (i);
+}
+
+constexpr int
+f3 (auto i)
+{
+  return f2 (i);
+}
+
+constexpr int
+f4 (auto i)
+{
+  return f3 (i);
+}
+
+constexpr int
+f5 (auto i)
+{
+  return f4 (i); // { dg-message "promoted to an immediate function because its body contains an immediate-escalating expression .f4<int>\\(i\\)." }
+}
+
+constexpr int
+f6 (auto)
+{
+  // This call is a constant expression, so don't promote f6.
+  return f4 (42);
+}
+
+constexpr int
+f7 (auto i)
+{
+  if consteval {
+    auto p = &id;
+    (void) p;
+  }
+  return i;
+}
+
+constexpr int
+f8 (auto i)
+{
+  if not consteval {
+    (void) 0;
+  } else {
+    auto p = &id;
+    (void) p;
+  }
+  return i;
+}
+
+constexpr int
+f9 (auto i)
+{
+  if consteval {
+    return id(i);
+  }
+  return i;
+}
+
+constexpr int
+f10 (auto i)
+{
+  if not consteval {
+    (void) 0;
+  } else {
+    return id(i);
+  }
+  return i;
+}
+
+void
+g (int non_const)
+{
+  f1 (42);
+  f1 (non_const); // { dg-error "call to consteval function .f1<int>\\(non_const\\). is not a constant expression" }
+// { dg-error ".non_const. is not a constant expression" "" { target *-*-* } .-1 }
+  f5 (42);
+  f5 (non_const); // { dg-error "call to consteval function .f5<int>\\(non_const\\). is not a constant expression" }
+// { dg-error ".non_const. is not a constant expression" "" { target *-*-* } .-1 }
+  f6 (42);
+  f6 (non_const);
+  f7 (42);
+  f7 (non_const);
+  f8 (42);
+  f8 (non_const);
+  f9 (42);
+  f9 (non_const);
+  f10 (42);
+  f10 (non_const);
+}
diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval-prop16.C b/gcc/testsuite/g++.dg/cpp2a/consteval-prop16.C
new file mode 100644
index 00000000000..7952d495d8b
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/consteval-prop16.C
@@ -0,0 +1,73 @@
+// P2564R3
+// { dg-do compile { target c++20 } }
+// Test unevaluated operands.
+
+consteval int id (int i) { return i; }
+
+constexpr int
+f1 (auto i)
+{
+  // Unevaluated operand -> don't promote.
+  auto p = sizeof (&id);
+  (void) p;
+  return i;
+}
+
+constexpr int
+f2 (auto i)
+{
+  // Unevaluated operand -> don't promote.
+  auto p = noexcept (id);
+  (void) p;
+  return i;
+}
+
+constexpr int
+f3 (auto i)
+{
+  // Unevaluated operand -> don't promote.
+  auto p = noexcept (id (i));
+  (void) p;
+  return i;
+}
+
+constexpr int
+f4 (auto i)
+{
+  // Unevaluated operand -> don't promote.
+  decltype(id) p;
+  (void) p;
+  return i;
+}
+
+constexpr int
+f5 (auto i)
+{
+  // Unevaluated operand -> don't promote.
+  __extension__ auto p = alignof (id (i));
+  (void) p;
+  return i;
+}
+
+constexpr int
+f6 (auto i) requires requires { id (i); }
+{
+  return i;
+}
+
+void
+g (int non_const)
+{
+  f1 (42);
+  f1 (non_const);
+  f2 (42);
+  f2 (non_const);
+  f3 (42);
+  f3 (non_const);
+  f4 (42);
+  f4 (non_const);
+  f5 (42);
+  f5 (non_const);
+  f6 (42);
+  f6 (non_const);
+}
diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval-prop17.C b/gcc/testsuite/g++.dg/cpp2a/consteval-prop17.C
new file mode 100644
index 00000000000..47ec9b60b6c
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/consteval-prop17.C
@@ -0,0 +1,17 @@
+// P2564R3
+// { dg-do compile { target c++20 } }
+// { dg-options "-fno-immediate-escalation" }
+
+consteval int id(int i) { return i; }
+
+constexpr int
+f (auto i)
+{
+  return id (i); // { dg-error "not a constant expression" }
+}
+
+int
+g ()
+{
+  return f (42);
+}
diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval-prop18.C b/gcc/testsuite/g++.dg/cpp2a/consteval-prop18.C
new file mode 100644
index 00000000000..a18106f8e0f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/consteval-prop18.C
@@ -0,0 +1,20 @@
+// P2564R3
+// { dg-do compile { target c++20 } }
+
+consteval int id(int i) { return i; }
+
+constexpr int
+f (auto t)
+{
+  return t + id (t);
+}
+
+constexpr int
+f2 (auto t)
+{
+  return t + f(t); // { dg-message "immediate-escalating expression .f<int>\\(t\\)." }
+}
+
+int z; // { dg-message "not const" }
+auto y1 = f2 (42);
+auto y2 = f2 (z); // { dg-error "value of .z. is not usable in a constant expression|call to consteval function" }
diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval-prop19.C b/gcc/testsuite/g++.dg/cpp2a/consteval-prop19.C
new file mode 100644
index 00000000000..3ceb05e41f4
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/consteval-prop19.C
@@ -0,0 +1,7 @@
+// P2564R3
+// { dg-do compile { target c++20 } }
+
+consteval int g(int p) { return p; }
+template<typename T> constexpr auto f(T) { return g; }
+int r = f(1)(2);      // proposed ok
+int s = f(1)(2) + r;  // { dg-error "call to consteval function|returns address of immediate function" }
diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval-prop2.C b/gcc/testsuite/g++.dg/cpp2a/consteval-prop2.C
new file mode 100644
index 00000000000..30129a4a266
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/consteval-prop2.C
@@ -0,0 +1,90 @@
+// P2564R3
+// { dg-do compile { target c++20 } }
+// Testcase from P2564R3.
+
+consteval int id(int i) { return i; }
+constexpr char id(char c) { return c; }
+
+template<class T>
+constexpr int f(T t) {
+  return t + id(t);		// { dg-message "immediate-escalating expression .id\\(t\\)." }
+}
+
+auto a = &f<char>;              // OK, f<char> is not an immediate function
+auto b = &f<int>;               // { dg-error "taking address of an immediate function" }
+
+static_assert(f(3) == 6);       // OK
+
+template<class T>
+constexpr int g(T t) {          // g<int> is not an immediate function
+  return t + id(42);            // because id(42) is already a constant
+}
+
+template<class T, class F>
+constexpr bool is_not(T t, F f) {
+  return not f(t);
+}
+
+consteval bool is_even(int i) { return i % 2 == 0; }
+
+static_assert(is_not(5, is_even));      // OK
+
+int x = 0;
+
+template<class T>
+constexpr T h(T t = id(x)) {    // h<int> is not an immediate function
+    return t;
+}
+
+template<class T>
+constexpr T hh() {              // hh<int> is an immediate function
+  return h<T>();		// { dg-error "the value of .x. is not usable in a constant expression" }
+// { dg-message "immediate-escalating expression .id\\(x\\)." "" { target *-*-* } .-1 }
+}
+
+int i = hh<int>();              // { dg-error "call to consteval function|called in a constant expression" }
+				// error: hh<int>() is an immediate-escalating expression
+                                // outside of an immediate-escalating function
+struct A {
+  int x;
+  int y = id(x);
+};
+
+// [expr.const]#example-9 says:
+//   k<int> is not an immediate function because A(42) is a
+//   constant expression and thus not immediate-escalating
+// In the evaluation of A(42), the member x has just been initialized
+// to constant 42.  And A(42) is constant-evaluated because "An aggregate
+// initialization is an immediate invocation if it evaluates a default
+// member initializer that has a subexpression that is an
+// immediate-escalating expression."
+template<class T>
+constexpr int k(int) {
+  return A(42).y;
+}
+
+int
+test (int i)
+{
+  int r = g (42) + g(i);
+  int t = k<int>(42)
+	    + k<int>(i); // { dg-bogus "call to|constant" "" { xfail *-*-* } }
+  return r + t;
+}
+
+// Just like above, but make the call to id(x) actually a constant.
+struct A2 {
+  static constexpr int x = 42;
+  int y = id(x);
+};
+
+template<class T>
+constexpr int k2(int) {
+  return A2(42).y;
+}
+
+int
+test2 (int i)
+{
+  return k2<int>(42) + k2<int>(i);
+}
diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval-prop20.C b/gcc/testsuite/g++.dg/cpp2a/consteval-prop20.C
new file mode 100644
index 00000000000..f1bb08e2dba
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/consteval-prop20.C
@@ -0,0 +1,21 @@
+// P2564R3
+// { dg-do compile { target c++20 } }
+// { dg-options "-Wno-c++23-extensions" }
+
+consteval int id(int i) { return i; }
+
+constexpr int
+f (auto i)
+{
+  return id (i);
+}
+
+void
+g ()
+{
+  auto p = &f<int>; // { dg-error "taking address" }
+  decltype(&f<int>) x;
+  if consteval {
+    auto q = &f<int>;
+  }
+}
diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval-prop3.C b/gcc/testsuite/g++.dg/cpp2a/consteval-prop3.C
new file mode 100644
index 00000000000..f181cb32942
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/consteval-prop3.C
@@ -0,0 +1,27 @@
+// P2564R3
+// { dg-do compile { target c++20 } }
+// Cribbed from clang's cxx2b-consteval-propagate.cpp.
+
+consteval int id(int i) { return i; }
+
+template <typename T>
+constexpr int f(T t);
+
+auto a1 = &f<char>;
+auto b1 = &f<int>;
+
+template <typename T>
+constexpr int f(T t) {
+    return id(0);
+}
+
+template <typename T>
+constexpr int f2(T);
+
+auto a2 = &f2<char>; // { dg-error "taking address" }
+auto b2 = &f2<int>; // { dg-error "taking address" }
+
+template <typename T>
+constexpr int f2(T t) {
+    return id(t);
+}
diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval-prop4.C b/gcc/testsuite/g++.dg/cpp2a/consteval-prop4.C
new file mode 100644
index 00000000000..3a2e09b17b0
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/consteval-prop4.C
@@ -0,0 +1,30 @@
+// P2564R3
+// { dg-do compile { target c++20 } }
+// From clang's cxx2b-consteval-propagate.cpp.  This test ICEd when I worked on
+// P2564.
+
+consteval int f (int);
+
+struct S {
+  int a = 0;
+  int b = f (a);
+};
+
+constexpr bool
+g (auto i)
+{
+  S s{i};
+  return s.b == 2 *i;
+}
+
+consteval int
+f (int i)
+{
+  return 2 * i;
+}
+
+void
+test ()
+{
+  static_assert(g(42));
+}
diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval-prop5.C b/gcc/testsuite/g++.dg/cpp2a/consteval-prop5.C
new file mode 100644
index 00000000000..3bd1b9d1674
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/consteval-prop5.C
@@ -0,0 +1,27 @@
+// P2564R3
+// { dg-do compile { target c++20 } }
+
+consteval int f (int i) { return i; }
+
+struct S {
+  int x = f(42);
+};
+
+constexpr S
+immediate (auto)
+{
+  return S{};
+}
+
+void
+g ()
+{
+  immediate (0);
+}
+
+consteval void
+test ()
+{
+  constexpr S s = immediate(0);
+  static_assert(s.x == 42);
+}
diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval-prop6.C b/gcc/testsuite/g++.dg/cpp2a/consteval-prop6.C
new file mode 100644
index 00000000000..93ed398d9bf
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/consteval-prop6.C
@@ -0,0 +1,59 @@
+// P2564R3
+// { dg-do compile { target c++20 } }
+// From cxx2b-consteval-propagate.cpp.
+
+void side_effect();
+
+consteval int
+f (int x)
+{
+  if (!x)
+    side_effect(); // { dg-error "call to non-.constexpr. function" }
+  return x;
+}
+
+struct SS {
+  int y = f(1);
+  int x = f(0);
+  SS();
+};
+SS::SS(){} // { dg-error "call to consteval function" }
+
+consteval int
+f2 (int x)
+{
+  if (!__builtin_is_constant_evaluated ())
+    side_effect();
+  return x;
+}
+
+struct S2 {
+  int x = f2(0);
+  constexpr S2();
+};
+
+constexpr S2::S2(){}
+S2 s = {};
+constinit S2 s2 = {};
+
+struct S3 {
+  int x = f2(0);
+  S3();
+};
+S3::S3(){}
+
+consteval int undef (int x); // { dg-warning "never defined" }
+
+struct X {
+  int a = sizeof(undef(0));
+  int x = undef(0);
+
+  X() = default; // { dg-error "modification of .x. is not a constant expression" }
+};
+
+void
+test ()
+{
+  [[maybe_unused]] X x; // { dg-error "call to consteval function" }
+// { dg-message "promoted to an immediate function" "" { target *-*-* } .-1 }
+}
diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval-prop7.C b/gcc/testsuite/g++.dg/cpp2a/consteval-prop7.C
new file mode 100644
index 00000000000..118cf576f14
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/consteval-prop7.C
@@ -0,0 +1,76 @@
+// P2564R3
+// { dg-do compile { target c++20 } }
+// The problem here was that while parsing, we first process calling
+// 'f' from 'g' but only when instantiating 'f<int>' do we promote 'f'
+// to consteval.  When the var we're initializing is marked constexpr,
+// store_init_value detects the problem that we're calling a consteval
+// function with non-const argument.
+
+consteval int id(int i) { return i; }
+
+// Don't let the instantiations confuse us, e.g. instantiating a fn
+// prior to entering 'g'.
+template <typename T>
+constexpr int f1(T t) { return id (t); }
+
+template <typename T>
+constexpr int f2(T t) { return id (t); }
+
+template <typename T>
+constexpr int f3(T t) { return id (t); }
+
+template <typename T>
+constexpr int f4(T t) { return id (t); }
+
+template <typename T>
+constexpr int f5(T t) { return id (t); }
+
+template <typename T>
+constexpr int f6(T t) { return id (t); }
+
+template <typename T>
+constexpr int f7(T t) { return id (t); }
+
+template <typename T>
+constexpr int f8(T t) { return id (t); }
+
+template <typename T>
+constexpr int f9(T t) { return id (t); }
+
+template <typename T>
+constexpr int f10(T t) { return id (t); }
+
+template <typename T>
+constexpr int g1(T t) { auto p = id; return p (t); }
+
+int non_const;
+
+auto a1 = f1 (non_const); // { dg-error "call to consteval function|not usable" }
+constexpr auto a2 = f2 (non_const); // { dg-error "not a constant|not usable" }
+auto a3 = f3 (42);
+constexpr auto a4 = f4 (42);
+
+void
+g ()
+{
+   auto a5 = f5 (non_const); // { dg-error "not a constant|not usable" }
+   constexpr auto a6 = f6 (non_const); // { dg-error "not usable" }
+   auto a7 = f7 (42);
+   constexpr auto a8 = f8 (42);
+   (void) f9 (non_const); // { dg-error "not a constant|not usable" }
+   (void) f10 (42);
+   (void) g1 (non_const); // { dg-error "not a constant|not usable" }
+}
+
+struct S {
+    int y;
+    int x = id (y);
+    // Promoted to consteval.
+    template<typename T>
+    constexpr S(T t) : y (id (t)) {}
+};
+
+S s1(1);
+S s2(non_const); // { dg-error "call to consteval function|not usable" }
+constexpr S s3(1);
+constexpr S s4(non_const); // { dg-error "not usable" }
diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval-prop8.C b/gcc/testsuite/g++.dg/cpp2a/consteval-prop8.C
new file mode 100644
index 00000000000..080fc76f26e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/consteval-prop8.C
@@ -0,0 +1,82 @@
+// P2564R3
+// { dg-do compile { target c++20 } }
+// { dg-options "-Wno-c++23-extensions" }
+
+consteval int zero (int)
+{
+  return 0;
+}
+
+struct A {
+  // A::A(auto) promoted to consteval.
+  constexpr A(auto i) { zero (i); }
+};
+
+// 'f1<int>' is an immediate function because its body contains a call to an
+// immediate constructor 'A<int>' and that call is not a constant expression
+constexpr void
+f1 (auto i)
+{
+  A a{i};
+}
+
+// 'f2<int>' is an immediate function because its body contains a call to an
+// immediate constructor 'A<int>' and that call is not a constant expression
+constexpr void
+f2 (auto i)
+{
+  A a{i};
+}
+
+void
+f3 (int i)
+{
+  A a{i}; // { dg-error "not a constant expression" }
+}
+
+inline void
+f7 (int i)
+{
+  A a{i}; // { dg-error "not a constant expression" }
+}
+
+constexpr void
+f8 (int i)
+{
+  A a{i}; // { dg-error "not a constant expression" }
+}
+
+/* "An expression or conversion is immediate-escalating if it is not initially
+   in an immediate function context" but this one is, so we do *not* promote
+   f4 to consteval.  */
+constexpr void
+f4 (auto i)
+{
+  if consteval {
+    A a{i};
+  }
+}
+
+constexpr void
+f5 (auto i)
+{
+  if not consteval {
+    (void) 0;
+  } else {
+    A a{i};
+  }
+}
+
+void
+f6 (int x)
+{
+  f1 (0);
+  f1 (x); // { dg-error "not a constant expression" }
+  f2 (0);
+  f2 (x); // { dg-error "not a constant expression" }
+  f3 (0);
+  f4 (x);
+  f4 (0);
+  f5 (x);
+  f5 (0);
+}
diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval-prop9.C b/gcc/testsuite/g++.dg/cpp2a/consteval-prop9.C
new file mode 100644
index 00000000000..9c4a23389ce
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/consteval-prop9.C
@@ -0,0 +1,67 @@
+// P2564R3
+// { dg-do compile { target c++20 } }
+
+consteval int
+zero (int)
+{
+  return 0;
+}
+
+constexpr int
+f1 (auto i)
+{
+  return zero (i);
+}
+
+constexpr int
+f2 (auto i)
+{
+  return f1 (i);
+}
+
+constexpr int
+f3 (auto i)
+{
+  return f2 (i);
+}
+
+constexpr int
+f4 (auto i)
+{
+  return f3 (i);
+}
+
+constexpr int
+f5 (auto i)
+{
+  return f4 (i);
+}
+
+constexpr int
+f6 (auto)
+{
+  // This call is a constant expression, so don't promote f6.
+  return f5 (42);
+}
+
+constexpr int
+f7 (auto)
+{
+  // This call is a constant expression, so don't promote f7.
+  return zero (42);
+}
+
+auto p1 = &f5<int>; // { dg-error "taking address" }
+static auto p2 = &f4<int>; // { dg-error "taking address" }
+auto p3 = &f6<int>;
+static auto p4 = &f6<int>;
+auto p5 = &f7<int>;
+static auto p6 = &f7<int>;
+
+void
+g ()
+{
+  static auto q1 = &f4<int>; // { dg-error "taking address" }
+  static auto q2 = &f6<int>;
+  static auto q3 = &f7<int>;
+}
diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval11.C b/gcc/testsuite/g++.dg/cpp2a/consteval11.C
index 05cecea4502..c2ee3c7a82a 100644
--- a/gcc/testsuite/g++.dg/cpp2a/consteval11.C
+++ b/gcc/testsuite/g++.dg/cpp2a/consteval11.C
@@ -8,9 +8,11 @@ constexpr int a = bar (1);
 constexpr int b = bar (2);		// { dg-message "in 'constexpr' expansion of" }
 constexpr int c = 0 ? bar (3) : 1;
 const int d = bar (4);			// { dg-message "in 'constexpr' expansion of" }
+// { dg-error "call to consteval function" "" { target *-*-* } .-1 }
 const int e = 0 ? bar (5) : 1;
 int f = bar (1);
 int g = bar (6);			// { dg-message "in 'constexpr' expansion of" }
+// { dg-error "call to consteval function" "" { target *-*-* } .-1 }
 int h = 0 ? bar (7) : 1;
 
 void
@@ -20,25 +22,35 @@ foo ()
   constexpr int b = bar (2);		// { dg-message "in 'constexpr' expansion of" }
   constexpr int c = 0 ? bar (3) : 1;
   const int d = bar (4);		// { dg-message "in 'constexpr' expansion of" }
+// { dg-error "call to consteval function" "" { target *-*-* } .-1 }
   const int e = 0 ? bar (5) : 1;
   int f = bar (1);
   int g = bar (6);			// { dg-message "in 'constexpr' expansion of" }
+// { dg-error "call to consteval function" "" { target *-*-* } .-1 }
   int h = 0 ? bar (7) : 1;		// { dg-message "in 'constexpr' expansion of" }
+// { dg-error "call to consteval function" "" { target *-*-* } .-1 }
   h += 0 ? bar (8) : 1;			// { dg-message "in 'constexpr' expansion of" }
+// { dg-error "call to consteval function" "" { target *-*-* } .-1 }
   if (0)
     bar (9);				// { dg-message "in 'constexpr' expansion of" }
+// { dg-error "call to consteval function" "" { target *-*-* } .-1 }
   else
     bar (10);				// { dg-message "in 'constexpr' expansion of" }
+// { dg-error "call to consteval function" "" { target *-*-* } .-1 }
   if (1)
     bar (11);				// { dg-message "in 'constexpr' expansion of" }
+// { dg-error "call to consteval function" "" { target *-*-* } .-1 }
   else
     bar (12);				// { dg-message "in 'constexpr' expansion of" }
+// { dg-error "call to consteval function" "" { target *-*-* } .-1 }
   if constexpr (0)
     bar (13);
   else
     bar (14);				// { dg-message "in 'constexpr' expansion of" }
+// { dg-error "call to consteval function" "" { target *-*-* } .-1 }
   if constexpr (1)
     bar (15);				// { dg-message "in 'constexpr' expansion of" }
+// { dg-error "call to consteval function" "" { target *-*-* } .-1 }
   else
     bar (16);
 }
@@ -121,18 +133,24 @@ quux ()
 {
   if (0)
     bar ((T) 2);				// { dg-message "in 'constexpr' expansion of" }
+// { dg-error "call to consteval function" "" { target *-*-* } .-1 }
   else
     bar ((T) 3);				// { dg-message "in 'constexpr' expansion of" }
+// { dg-error "call to consteval function" "" { target *-*-* } .-1 }
   if (1)
     bar ((T) 4);				// { dg-message "in 'constexpr' expansion of" }
+// { dg-error "call to consteval function" "" { target *-*-* } .-1 }
   else
     bar ((T) 5);				// { dg-message "in 'constexpr' expansion of" }
+// { dg-error "call to consteval function" "" { target *-*-* } .-1 }
   if constexpr (0)
     bar ((T) 6);
   else
     bar ((T) 7);				// { dg-message "in 'constexpr' expansion of" }
+// { dg-error "call to consteval function" "" { target *-*-* } .-1 }
   if constexpr (1)
     bar ((T) 8);				// { dg-message "in 'constexpr' expansion of" }
+// { dg-error "call to consteval function" "" { target *-*-* } .-1 }
   else
     bar ((T) 9);
 }
diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval3.C b/gcc/testsuite/g++.dg/cpp2a/consteval3.C
index 9efac8c8eae..1199e9db623 100644
--- a/gcc/testsuite/g++.dg/cpp2a/consteval3.C
+++ b/gcc/testsuite/g++.dg/cpp2a/consteval3.C
@@ -16,8 +16,8 @@ consteval auto [ b, c ] = S ();		// { dg-error "structured binding declaration c
 int f5 (consteval int x) { return x; }	// { dg-error "a parameter cannot be declared 'consteval'" }
 consteval int f6 (int x) { return x; }
 int d = 6;		// { dg-message "'int d' is not const" }
-int e = f6 (d);		// { dg-error "the value of 'd' is not usable in a constant expression" }
-constexpr int f7 (int x) { return f6 (x); }	// { dg-error "'x' is not a constant expression" }
+int e = f6 (d);		// { dg-error "the value of 'd' is not usable in a constant expression|call to consteval function" }
+constexpr int f7 (int x) { return f6 (x); }	// { dg-error "'x' is not a constant expression|call to consteval function" }
 constexpr int f = f7 (5);
 using fnptr = int (int);
 fnptr *g = f6;		// { dg-error "taking address of an immediate function 'consteval int f6\\(int\\)'" }
diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval34.C b/gcc/testsuite/g++.dg/cpp2a/consteval34.C
index 068827ba516..7562f403f74 100644
--- a/gcc/testsuite/g++.dg/cpp2a/consteval34.C
+++ b/gcc/testsuite/g++.dg/cpp2a/consteval34.C
@@ -7,6 +7,7 @@ constexpr int
 foo (bool b)
 {
   return b ? bar (3) : 2; // { dg-message "in .constexpr. expansion" }
+// { dg-error "call to consteval function" "" { target *-*-* } .-1 }
 }
 
 static_assert (foo (false) == 2);
@@ -22,13 +23,20 @@ void
 g ()
 {
   __extension__ int a1[bar(3)]; // { dg-message "in .constexpr. expansion" }
+// { dg-error "call to consteval function" "" { target *-*-* } .-1 }
   int a2[sizeof (bar(3))];
 
   int a3 = false ? (1 + bar (8)) : 1; // { dg-message "in .constexpr. expansion" }
+// { dg-error "call to consteval function" "" { target *-*-* } .-1 }
   a3 += false ? (1 + bar (8)) : 1; // { dg-message "in .constexpr. expansion" }
+// { dg-error "call to consteval function" "" { target *-*-* } .-1 }
 
   __extension__ int a4 = false ?: (1 + bar (8)); // { dg-message "in .constexpr. expansion" }
+// { dg-error "call to consteval function" "" { target *-*-* } .-1 }
   __extension__ int a5 = true ?: (1 + bar (8)); // { dg-message "in .constexpr. expansion" }
+// { dg-error "call to consteval function" "" { target *-*-* } .-1 }
   int a6 = bar (2) ? 1 : 2; // { dg-message "in .constexpr. expansion" }
+// { dg-error "call to consteval function" "" { target *-*-* } .-1 }
   int a7 = bar (2) - 1 ? 1 : 2; // { dg-message "in .constexpr. expansion" }
+// { dg-error "call to consteval function" "" { target *-*-* } .-1 }
 }
diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval36.C b/gcc/testsuite/g++.dg/cpp2a/consteval36.C
index 9c470e4b7d7..8e27f2e33c6 100644
--- a/gcc/testsuite/g++.dg/cpp2a/consteval36.C
+++ b/gcc/testsuite/g++.dg/cpp2a/consteval36.C
@@ -6,17 +6,17 @@ consteval int id (int i) { return i; }
 void
 g (int i)
 {
-  1 ? 1 : ((1 ? 1 : 1), id (i)); // { dg-error "'i' is not a constant expression" }
-  1 ? 1 : ((1 ? 1 : 1), id (i), 1); // { dg-error "'i' is not a constant expression" }
-  1 ? 1 : ((i ? 1 : 1), id (i), 1); // { dg-error "'i' is not a constant expression" }
-  1 ? 1 : ((1 ? i : 1), id (i), 1); // { dg-error "'i' is not a constant expression" }
-  1 ? 1 : ((1 ? 1 : i), id (i), 1); // { dg-error "'i' is not a constant expression" }
-  1 ? 1 : ((i ? -i : i), id (i), 1); // { dg-error "'i' is not a constant expression" }
-  1 ? 1 : ((1 ? 1 : id (i)), id (42), 1); // { dg-error "'i' is not a constant expression" }
-  1 ? 1 : ((1 ? 1 : id (42)), id (i)); // { dg-error "'i' is not a constant expression" }
-  1 ? 1 : ((1 ? 1 : id (42)), id (i), 1); // { dg-error "'i' is not a constant expression" }
-  id (i) ? 1 : ((1 ? 1 : 1), id (i)); // { dg-error "'i' is not a constant expression" }
-  1 ? 1 : ((1 ? 1 : id (i)), id (i)); // { dg-error "'i' is not a constant expression" }
-  1 ? id (i) : ((1 ? 1 : id (i)), id (i)); // { dg-error "'i' is not a constant expression" }
-  1 ? 1 : ((id (i) ? 1 : 1), id (i)); // { dg-error "'i' is not a constant expression" }
+  1 ? 1 : ((1 ? 1 : 1), id (i)); // { dg-error "call to consteval function|'i' is not a constant expression" }
+  1 ? 1 : ((1 ? 1 : 1), id (i), 1); // { dg-error "call to consteval function|'i' is not a constant expression" }
+  1 ? 1 : ((i ? 1 : 1), id (i), 1); // { dg-error "call to consteval function|'i' is not a constant expression" }
+  1 ? 1 : ((1 ? i : 1), id (i), 1); // { dg-error "call to consteval function|'i' is not a constant expression" }
+  1 ? 1 : ((1 ? 1 : i), id (i), 1); // { dg-error "call to consteval function|'i' is not a constant expression" }
+  1 ? 1 : ((i ? -i : i), id (i), 1); // { dg-error "call to consteval function|'i' is not a constant expression" }
+  1 ? 1 : ((1 ? 1 : id (i)), id (42), 1); // { dg-error "call to consteval function|'i' is not a constant expression" }
+  1 ? 1 : ((1 ? 1 : id (42)), id (i)); // { dg-error "call to consteval function|'i' is not a constant expression" }
+  1 ? 1 : ((1 ? 1 : id (42)), id (i), 1); // { dg-error "call to consteval function|'i' is not a constant expression" }
+  id (i) ? 1 : ((1 ? 1 : 1), id (i)); // { dg-error "call to consteval function|'i' is not a constant expression" }
+  1 ? 1 : ((1 ? 1 : id (i)), id (i)); // { dg-error "call to consteval function|'i' is not a constant expression" }
+  1 ? id (i) : ((1 ? 1 : id (i)), id (i)); // { dg-error "call to consteval function|'i' is not a constant expression" }
+  1 ? 1 : ((id (i) ? 1 : 1), id (i)); // { dg-error "call to consteval function|'i' is not a constant expression" }
 }
diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval9.C b/gcc/testsuite/g++.dg/cpp2a/consteval9.C
index 051a3d4e355..ad882d51c9b 100644
--- a/gcc/testsuite/g++.dg/cpp2a/consteval9.C
+++ b/gcc/testsuite/g++.dg/cpp2a/consteval9.C
@@ -14,6 +14,7 @@ template <int N>
 void qux ()
 {
   int a = bar (N);	// { dg-message "in 'constexpr' expansion of 'bar\\(2\\)'" }
+// { dg-error "call to consteval function" "" { target *-*-* } .-1 }
 }
 
 // This function is not instantiated so NDR.
@@ -31,3 +32,4 @@ baz ()
 }
 
 int a = bar (2);	// { dg-message "in 'constexpr' expansion of 'bar\\(2\\)'" }
+// { dg-error "call to consteval function" "" { target *-*-* } .-1 }
diff --git a/gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C b/gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C
index 16bc0b85395..fc268d44e1a 100644
--- a/gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C
+++ b/gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C
@@ -480,8 +480,8 @@
 
 #ifndef __cpp_consteval
 #  error "__cpp_consteval"
-#elif __cpp_consteval != 201811
-#  error "__cpp_consteval != 201811"
+#elif __cpp_consteval != 202211L
+#  error "__cpp_consteval != 202211L"
 #endif
 
 #ifndef __cpp_concepts
diff --git a/gcc/testsuite/g++.dg/cpp2a/spaceship-synth9.C b/gcc/testsuite/g++.dg/cpp2a/spaceship-synth9.C
index 33b547d2b50..ecb46b016a6 100644
--- a/gcc/testsuite/g++.dg/cpp2a/spaceship-synth9.C
+++ b/gcc/testsuite/g++.dg/cpp2a/spaceship-synth9.C
@@ -22,6 +22,6 @@ struct Z: Y<int>
 int main()
 {
   X<char>() == X<char>();	// { dg-error "no match" }
-  X<int> x; x == x;		// { dg-error "x' is not usable in a constant expression" }
+  X<int> x; x == x;		// { dg-error "x' is not usable in a constant expression|call to consteval function" }
   Y<int>()  == Y<int>();	// { dg-warning "nodiscard" }
 }
diff --git a/libstdc++-v3/testsuite/18_support/comparisons/categories/zero_neg.cc b/libstdc++-v3/testsuite/18_support/comparisons/categories/zero_neg.cc
index 9d2115b3f4f..82f7cd54fba 100644
--- a/libstdc++-v3/testsuite/18_support/comparisons/categories/zero_neg.cc
+++ b/libstdc++-v3/testsuite/18_support/comparisons/categories/zero_neg.cc
@@ -52,3 +52,4 @@ test01()
 
 // { dg-prune-output "reinterpret_cast.* is not a constant expression" }
 // { dg-prune-output "cast from 'void.' is not allowed" }
+// { dg-prune-output "not a constant expression" }
diff --git a/libstdc++-v3/testsuite/std/format/string_neg.cc b/libstdc++-v3/testsuite/std/format/string_neg.cc
index 7a60ef8cf0e..69bcc736cff 100644
--- a/libstdc++-v3/testsuite/std/format/string_neg.cc
+++ b/libstdc++-v3/testsuite/std/format/string_neg.cc
@@ -2,5 +2,5 @@
 
 #include <format>
 
-auto s = std::format(" {9} ");
+auto s = std::format(" {9} "); // { dg-error "call to consteval function" }
 // { dg-error "invalid.arg.id" "" { target *-*-* } 0 }

base-commit: 606f7201c066b840ea43ab62fcf47042b81e54d4
-- 
2.43.0


^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2023-12-11 19:05 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-12-11  8:49 [PATCH v8] c++: implement P2564, consteval needs to propagate up [PR107687] FX Coudert
2023-12-11 19:02 ` Jason Merrill
2023-12-11 19:05   ` Marek Polacek
  -- strict thread matches above, loose matches on Subject: below --
2023-11-02 15:28 [PATCH v3] " Marek Polacek
2023-11-03 17:51 ` Jason Merrill
2023-11-06 22:34   ` [PATCH v4] " Marek Polacek
2023-11-14  2:06     ` Jason Merrill
2023-11-23 16:46       ` [PATCH v5] " Marek Polacek
2023-11-30 23:34         ` Jason Merrill
2023-12-01 23:37           ` [PATCH v6] " Marek Polacek
2023-12-02  0:43             ` Jason Merrill
2023-12-04 20:23               ` [PATCH v7] " Marek Polacek
2023-12-04 21:49                 ` Jason Merrill
2023-12-05  0:44                   ` [PATCH v8] " Marek Polacek
2023-12-06 11:39                     ` Prathamesh Kulkarni
2023-12-06 14:34                       ` Marek Polacek

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).