[PATCH 2/2] c++: is_const_eval init folding inside lambda scope [PR126483]

Patrick Palka <[email protected]> Thu, 6 Aug 2026 10:58:35 -0400
Newsgroups gmane.comp.gcc.patches
Message-ID <[email protected]>
Bootstrapped and regtested on x86_64-pc-linux-gnu, does this look
OK for trunk/backports?

-- >8 --

Here when processing a's copy-initialization we overeagerly fold its
initializer as if the lambda scope is not constexpr, and thus assume
is_const_eval() is false.  Subsequent constant evaluation of the lambda
gives the wrong answer due to this premature folding.

The culprit cp_fully_fold_init call is guarded by DECL_DECLARED_CONSTEXPR_P
but that isn't set on maybe-constexpr lambdas until the lambda is fully
parsed and its body is checked as a whole for constexpr suitability.
The call needs to instead be guarded by maybe_constexpr_fn which considers
such lambdas (although it considers all lambdas to be maybe-constexpr
even fully parsed ones that have been deemed non-constexpr).

This however means we no longer fold a's copy-init even for the runtime
version of the lambda body, hence the is_constant_evaluated3b xfail.
Making maybe_constexpr_fn distinguish between fully parsed constexpr and
non-constexpr lambdas doesn't help because we don't hit this code path
again and cp_fold_function can't seem to fold the copy-init form:

struct A a1;
<<cleanup_point <<< Unknown tree: expr_stmt
  (void) (a1 = <<< Unknown tree: aggr_init_expr
    5
    __ct_comp
    D.3052
    (struct A *) <<< Unknown tree: void_cst >>>
    42 >>>) >>>>>;

whereas (after r14-6506) it does fold direct-init etc which is expressed
as:

struct A a3;
<<cleanup_point <<< Unknown tree: expr_stmt
  A::A (&a3, NON_LVALUE_EXPR <42>) >>>>>;

Marek's prvalue folding patch r15-6052 doesn't seem to help because
naturally it looks for TARGET_EXPR and we don't have one here.  It
should be possible to have cp_fold_function fold this but I haven't
looked into it.  It would fix the xfail in is_constant_evaluated3{a,b}.

	PR c++/126483

gcc/cp/ChangeLog:

	* typeck2.cc (store_init_value): Check maybe_constexpr_fn
	instead of DECL_DECLARED_CONSTEXPR_P.

gcc/testsuite/ChangeLog:

	* gcc/testsuite/g++.dg/opt/is_constant_evaluated3b.C: xfail
	a1 initializer folding.
	* g++.dg/cpp2a/is-constant-evaluated16.C: New test.
---
 gcc/cp/typeck2.cc                             |  2 +-
 .../g++.dg/cpp2a/is-constant-evaluated16.C    | 20 +++++++++++++++++++
 .../g++.dg/opt/is_constant_evaluated3b.C      |  2 +-
 3 files changed, 22 insertions(+), 2 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/cpp2a/is-constant-evaluated16.C

diff --git a/gcc/cp/typeck2.cc b/gcc/cp/typeck2.cc
index 6c4ed50cb94e..62f583dcf405 100644
--- a/gcc/cp/typeck2.cc
+++ b/gcc/cp/typeck2.cc
@@ -1129,7 +1129,7 @@ store_init_value (tree decl, tree init, vec<tree, va_gc>** cleanups, int flags)
      that might fold away something that needs to be diagnosed at constexpr
      evaluation time.  */
   if (!current_function_decl
-      || !DECL_DECLARED_CONSTEXPR_P (current_function_decl)
+      || !maybe_constexpr_fn (current_function_decl)
       || TREE_STATIC (decl))
     value = cp_fully_fold_init (value);
 
diff --git a/gcc/testsuite/g++.dg/cpp2a/is-constant-evaluated16.C b/gcc/testsuite/g++.dg/cpp2a/is-constant-evaluated16.C
new file mode 100644
index 000000000000..c727967673e3
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/is-constant-evaluated16.C
@@ -0,0 +1,20 @@
+// PR c++/126483
+// { dg-do compile { target c++17 } }
+
+struct A {
+  int m;
+  constexpr A(int n) : m(__builtin_is_constant_evaluated() ? 42 : n) { }
+};
+
+constexpr auto v = [] {
+  A a = 0;
+  return a.m;
+};
+static_assert(v() == 42);
+
+template<class>
+constexpr auto vt = [] {
+  A a = 0;
+  return a.m;
+};
+static_assert(vt<void>() == 42);
diff --git a/gcc/testsuite/g++.dg/opt/is_constant_evaluated3b.C b/gcc/testsuite/g++.dg/opt/is_constant_evaluated3b.C
index ff708e23832b..ac6d21d632e1 100644
--- a/gcc/testsuite/g++.dg/opt/is_constant_evaluated3b.C
+++ b/gcc/testsuite/g++.dg/opt/is_constant_evaluated3b.C
@@ -17,7 +17,7 @@ auto f = [] {
   A a5{};
 };
 
-// { dg-final { scan-tree-dump "a1 = {\\.n=42, \\.m=0}" "original" } }
+// { dg-final { scan-tree-dump "a1 = {\\.n=42, \\.m=0}" "original" { xfail *-*-* } } }
 // { dg-final { scan-tree-dump "a2 = {\\.n=42, \\.m=0}" "original" } }
 // { dg-final { scan-tree-dump "a3 = {\\.n=42, \\.m=0}" "original" } }
 // { dg-final { scan-tree-dump "a4 = {\\.n=42, \\.m=0}" "original" } }
-- 
2.55.0.481.ga97fcc37c2