On Wed, Feb 11, 2026 at 02:15:54PM +0100, Jakub Jelinek wrote:
> On Wed, Feb 11, 2026 at 08:58:26PM +0900, Jason Merrill wrote:
> > We might replace that check with something separate in cp_finish_decl, but
> > your approach seems reasonable.
> > 
> > I'd drop constinit, that doesn't seem to qualify under
> > [basic.types.general]/12.1.
> 
> You're right.  Seems constinit was still in P2996R9 but P2996R10
> has removed it except for one occurrence in the revision history (that
> is ok, but surprisingly the P2996R10 change that removed it is not
> mentioned).
> 
> So what about this updated patch then?

Marek made me to grep further and I found one more constinit reference
in a comment, so here is another version of the patch with that fixed,
this time bootstrapped/regtested on x86_64-linux and i686-linux
successfully.

2026-02-12  Jakub Jelinek  <[email protected]>

        PR c++/124012
        * reflect.cc (check_out_of_consteval_use_r): New function.
        (check_out_of_consteval_use): Use it instead of lambda.  Don't ignore
        constexpr/constinit vars in the walker and walk DECL_VALUE_EXPR of
        vars which have it.  Ignore expr equal to constexpr VAR_DECL.  In
        diagnostics only complain about missing constexpr on VAR_DECLs without
        that flag and never suggest constinit.  Remove constinit traces from
        function comment.

        * g++.dg/reflect/pr124012.C: New test.
        * g++.dg/reflect/init1.C (r): Change constinit to constexpr.
        (p): Change constinit to constexpr const.
        * g++.dg/reflect/init6.C: Expect diagnostics for constinit
        consteval-only vars.
        * g++.dg/reflect/init7.C: Likewise.
        * g++.dg/reflect/init10.C: Likewise.
        * g++.dg/reflect/diag3.C: Likewise.  Don't expect suggestion to
        add constinit.

--- gcc/cp/reflect.cc.jj        2026-02-11 11:21:41.800645517 +0100
+++ gcc/cp/reflect.cc   2026-02-11 16:10:47.583625789 +0100
@@ -8092,8 +8092,79 @@ consteval_only_p (tree t)
   return !!cp_walk_tree (&t, consteval_only_type_r, &visited, &visited);
 }
 
+/* A walker for check_out_of_consteval_use_r.  It cannot be a lambda, because
+   we have to call this recursively.  */
+
+static tree
+check_out_of_consteval_use_r (tree *tp, int *walk_subtrees, void *pset)
+{
+  tree t = *tp;
+
+  /* No need to look into types or unevaluated operands.  */
+  if (TYPE_P (t)
+      || unevaluated_p (TREE_CODE (t))
+      /* Don't walk INIT_EXPRs, because we'd emit bogus errors about
+        member initializers.  */
+      || TREE_CODE (t) == INIT_EXPR
+      /* Don't walk BIND_EXPR_VARS.  */
+      || TREE_CODE (t) == BIND_EXPR
+      /* And don't recurse on DECL_EXPRs.  */
+      || TREE_CODE (t) == DECL_EXPR)
+    {
+      *walk_subtrees = false;
+      return NULL_TREE;
+    }
+
+  /* A subexpression of a manifestly constant-evaluated expression is
+     an immediate function context.  For example,
+
+      consteval void foo (std::meta::info) { }
+      void g() { foo (^^void); }
+
+      is all good.  */
+  if (tree decl = cp_get_callee_fndecl_nofold (t))
+    if (immediate_invocation_p (decl))
+      {
+       *walk_subtrees = false;
+       return NULL_TREE;
+      }
+
+  if (VAR_P (t) && DECL_HAS_VALUE_EXPR_P (t))
+    {
+      tree vexpr = DECL_VALUE_EXPR (t);
+      if (tree ret = cp_walk_tree (&vexpr, check_out_of_consteval_use_r, pset,
+                                  (hash_set<tree> *) pset))
+       return ret;
+    }
+
+  /* Now check the type to see if we are dealing with a consteval-only
+     expression.  */
+  if (!consteval_only_p (t))
+    return NULL_TREE;
+
+  /* Already escalated?  */
+  if (current_function_decl
+      && DECL_IMMEDIATE_FUNCTION_P (current_function_decl))
+    {
+      *walk_subtrees = false;
+      return NULL_TREE;
+    }
+
+  /* We might have to escalate if we are in an immediate-escalating
+     function.  */
+  if (immediate_escalating_function_p (current_function_decl))
+    {
+      promote_function_to_consteval (current_function_decl);
+      *walk_subtrees = false;
+      return NULL_TREE;
+    }
+
+  *walk_subtrees = false;
+  return t;
+}
+
 /* Detect if a consteval-only expression EXPR or a consteval-only
-   variable EXPR not declared constexpr/constinit is used outside
+   variable EXPR not declared constexpr is used outside
    a manifestly constant-evaluated context.  E.g.:
 
      void f() {
@@ -8115,88 +8186,24 @@ consteval_only_p (tree t)
 bool
 check_out_of_consteval_use (tree expr, bool complain/*=true*/)
 {
-  if (!flag_reflection || in_immediate_context ())
+  if (!flag_reflection || in_immediate_context () || expr == NULL_TREE)
     return false;
 
-  auto walker = [](tree *tp, int *walk_subtrees, void *) -> tree
-    {
-      tree t = *tp;
-
-      /* No need to look into types or unevaluated operands.  */
-      if (TYPE_P (t)
-         || unevaluated_p (TREE_CODE (t))
-         /* Don't walk INIT_EXPRs, because we'd emit bogus errors about
-            member initializers.  */
-         || TREE_CODE (t) == INIT_EXPR
-         /* Don't walk BIND_EXPR_VARS.  */
-         || TREE_CODE (t) == BIND_EXPR
-         /* And don't recurse on DECL_EXPRs.  */
-         || TREE_CODE (t) == DECL_EXPR)
-       {
-         *walk_subtrees = false;
-         return NULL_TREE;
-       }
-
-      /* A subexpression of a manifestly constant-evaluated expression is
-        an immediate function context.  For example,
-
-          consteval void foo (std::meta::info) { }
-          void g() { foo (^^void); }
-
-        is all good.  */
-      if (tree decl = cp_get_callee_fndecl_nofold (t))
-       if (immediate_invocation_p (decl))
-         {
-           *walk_subtrees = false;
-           return NULL_TREE;
-         }
-
-      if (VAR_P (t)
-         && (DECL_DECLARED_CONSTEXPR_P (t) || DECL_DECLARED_CONSTINIT_P (t)))
-       /* This is fine, don't bother checking the type.  */
-       return NULL_TREE;
-
-      /* Now check the type to see if we are dealing with a consteval-only
-        expression.  */
-      if (!consteval_only_p (t))
-       return NULL_TREE;
-
-      /* Already escalated?  */
-      if (current_function_decl
-         && DECL_IMMEDIATE_FUNCTION_P (current_function_decl))
-       {
-         *walk_subtrees = false;
-         return NULL_TREE;
-       }
-
-      /* We might have to escalate if we are in an immediate-escalating
-        function.  */
-      if (immediate_escalating_function_p (current_function_decl))
-       {
-         promote_function_to_consteval (current_function_decl);
-         *walk_subtrees = false;
-         return NULL_TREE;
-       }
-
-      *walk_subtrees = false;
-      return t;
-    };
+  if (VAR_P (expr) && DECL_DECLARED_CONSTEXPR_P (expr))
+    return false;
 
-  if (tree t = cp_walk_tree_without_duplicates (&expr, walker, nullptr))
+  hash_set<tree> pset;
+  if (tree t = cp_walk_tree (&expr, check_out_of_consteval_use_r, &pset, 
&pset))
     {
       if (complain)
        {
-         if (VAR_P (t))
+         if (VAR_P (t) && !DECL_DECLARED_CONSTEXPR_P (t))
            {
              auto_diagnostic_group d;
              error_at (cp_expr_loc_or_input_loc (t),
                        "consteval-only variable %qD not declared %<constexpr%> 
"
                        "used outside a constant-evaluated context", t);
-             if (TREE_STATIC (t) || CP_DECL_THREAD_LOCAL_P (t))
-               inform (DECL_SOURCE_LOCATION (t), "add %<constexpr%> or "
-                       "%<constinit%>");
-             else
-               inform (DECL_SOURCE_LOCATION (t), "add %<constexpr%>");
+             inform (DECL_SOURCE_LOCATION (t), "add %<constexpr%>");
            }
          else
            error_at (cp_expr_loc_or_input_loc (t),
--- gcc/testsuite/g++.dg/reflect/pr124012.C.jj  2026-02-11 13:41:43.184381452 
+0100
+++ gcc/testsuite/g++.dg/reflect/pr124012.C     2026-02-11 13:41:43.184381452 
+0100
@@ -0,0 +1,44 @@
+// PR c++/124012
+// { dg-do compile { target c++26 } }
+// { dg-additional-options "-freflection" }
+
+void foo (char);
+void corge (const char *);
+struct A { char a; decltype (^^::) b; };
+
+void
+bar ()
+{
+  constexpr auto [a, b] = A {};
+  foo (a);                     // { dg-error "consteval-only expressions are 
only allowed in a constant-evaluated context" }
+}
+
+void
+baz ()
+{
+  constexpr auto a = A {};
+  foo (a.a);                   // { dg-error "consteval-only expressions are 
only allowed in a constant-evaluated context" }
+}
+
+void
+qux ()
+{
+  constexpr auto a = A {};
+  corge (&a.a);                        // { dg-error "consteval-only 
expressions are only allowed in a constant-evaluated context" }
+}
+
+void
+garply ()
+{
+  constexpr auto [a, b] = A {};
+  corge (&a);                  // { dg-error "consteval-only expressions are 
only allowed in a constant-evaluated context" }
+}
+
+void
+fred ()
+{
+  constexpr auto [a, b] = A {};
+  constexpr auto c = a;
+  foo (c);
+  corge (&c);
+}
--- gcc/testsuite/g++.dg/reflect/init1.C.jj     2026-01-15 16:33:47.009097925 
+0100
+++ gcc/testsuite/g++.dg/reflect/init1.C        2026-02-11 13:58:21.056264055 
+0100
@@ -22,8 +22,8 @@ struct W {
   decltype(^^::) i = ^^W::i;
 };
 
-constinit info r = ^^int;
-constinit info *p = &r;
+constexpr info r = ^^int;
+constexpr const info *p = &r;
 
 consteval void
 f ()
--- gcc/testsuite/g++.dg/reflect/init6.C.jj     2026-01-15 16:33:47.009097925 
+0100
+++ gcc/testsuite/g++.dg/reflect/init6.C        2026-02-11 13:53:32.472078878 
+0100
@@ -12,11 +12,11 @@ struct N {
 };
 
 S s1;  // { dg-error "consteval-only variable" }
-constinit S s2{};
+constinit S s2{};  // { dg-error "consteval-only variable" }
 constexpr S s3{^^int};
 
 N n1;  // { dg-error "consteval-only variable" }
-constinit N n2;
+constinit N n2;  // { dg-error "consteval-only variable" }
 constexpr N n3;
 
 template<typename T>
@@ -25,7 +25,7 @@ struct X {
 };
 
 X<info> x1;  // { dg-error "consteval-only variable" }
-constinit X<info> x2{};
+constinit X<info> x2{};  // { dg-error "consteval-only variable" }
 constexpr X<info> x3{^^int};
 
 void
--- gcc/testsuite/g++.dg/reflect/init7.C.jj     2026-01-15 16:33:47.009097925 
+0100
+++ gcc/testsuite/g++.dg/reflect/init7.C        2026-02-11 13:52:51.484762719 
+0100
@@ -9,7 +9,7 @@ info r1 = ^^int;  // { dg-error "constev
 const info r2 = ^^int;  // { dg-error "consteval-only variable .r2. not 
declared .constexpr. used outside a constant-evaluated context" }
 
 constexpr info r3 = ^^int;
-constinit info r4 = ^^int;
+constinit info r4 = ^^int;  // { dg-error "consteval-only variable .r4. not 
declared .constexpr. used outside a constant-evaluated context" }
 const info *const p1 = &r3;  // { dg-error "consteval-only variable .p1. not 
declared .constexpr. used outside a constant-evaluated context" }
 info *p2;  // { dg-error "consteval-only variable .p2. not declared 
.constexpr. used outside a constant-evaluated context" }
 const info &q = r3;  // { dg-error "consteval-only variable .q. not declared 
.constexpr. used outside a constant-evaluated context" }
@@ -23,7 +23,7 @@ g ()
   static info l4 = ^^int;  // { dg-error "consteval-only variable .l4. not 
declared .constexpr. used outside a constant-evaluated context" }
   static const info l5 = ^^int;  // { dg-error "consteval-only variable .l5. 
not declared .constexpr. used outside a constant-evaluated context" }
   static constexpr info l6 = ^^int;
-  static constinit info l7 = ^^int;
+  static constinit info l7 = ^^int;  // { dg-error "consteval-only variable 
.l7. not declared .constexpr. used outside a constant-evaluated context" }
 }
 
 consteval void
--- gcc/testsuite/g++.dg/reflect/init10.C.jj    2026-01-15 16:33:47.009097925 
+0100
+++ gcc/testsuite/g++.dg/reflect/init10.C       2026-02-11 13:54:06.441512118 
+0100
@@ -10,7 +10,7 @@ struct A {
 };
 
 A a1;  // { dg-error "consteval-only variable .a1." }
-constinit A a2;
+constinit A a2;  // { dg-error "consteval-only variable .a2." }
 constexpr A a3;
 
 struct B {
@@ -20,5 +20,5 @@ struct B {
 };
 
 B b1;  // { dg-error "consteval-only variable .b1." }
-constinit B b2;
+constinit B b2;  // { dg-error "consteval-only variable .b2." }
 constexpr B b3;
--- gcc/testsuite/g++.dg/reflect/diag3.C.jj     2026-01-15 16:33:47.007097959 
+0100
+++ gcc/testsuite/g++.dg/reflect/diag3.C        2026-02-11 13:56:51.692755021 
+0100
@@ -1,14 +1,16 @@
 // { dg-do compile { target c++26 } }
 // { dg-additional-options "-freflection" }
-// Test that we suggest adding "constexpr" or "constinit" (where allowed).
+// Test that we suggest adding "constexpr" (where allowed).
 
 auto foo = ^^int;  // { dg-error "consteval-only variable .foo." }
-// { dg-message "add .constexpr. or .constinit." "" { target *-*-* } .-1 }
-constinit auto foo_ = ^^int;
+// { dg-message "add .constexpr." "" { target *-*-* } .-1 }
+constinit auto foo_ = ^^int; // { dg-error "consteval-only variable .foo_." }
+// { dg-message "add .constexpr." "" { target *-*-* } .-1 }
 constexpr auto foo__ = ^^int;
 thread_local auto tfoo = ^^int;  // { dg-error "consteval-only variable 
.tfoo." }
-// { dg-message "add .constexpr. or .constinit." "" { target *-*-* } .-1 }
-thread_local constinit auto tfoo_ = ^^int;
+// { dg-message "add .constexpr." "" { target *-*-* } .-1 }
+thread_local constinit auto tfoo_ = ^^int; // { dg-error "consteval-only 
variable .tfoo_." }
+// { dg-message "add .constexpr." "" { target *-*-* } .-1 }
 thread_local constexpr auto tfoo__ = ^^int;
 
 void
@@ -18,11 +20,13 @@ f ()
 // { dg-message "add .constexpr." "" { target *-*-* } .-1 }
   constexpr auto ref_ = ^^int;
   static auto sref = ^^int;  // { dg-error "consteval-only variable .sref." }
-// { dg-message "add .constexpr. or .constinit." "" { target *-*-* } .-1 }
-  static auto constinit sref_ = ^^int;
+// { dg-message "add .constexpr." "" { target *-*-* } .-1 }
+  static auto constinit sref_ = ^^int; // { dg-error "consteval-only variable 
.sref_." }
+// { dg-message "add .constexpr." "" { target *-*-* } .-1 }
   static auto constexpr sref__ = ^^int;
   thread_local auto tref = ^^int; // { dg-error "consteval-only variable 
.tref." }
-// { dg-message "add .constexpr. or .constinit." "" { target *-*-* } .-1 }
-  thread_local constinit auto tref_ = ^^int;
+// { dg-message "add .constexpr." "" { target *-*-* } .-1 }
+  thread_local constinit auto tref_ = ^^int; // { dg-error "consteval-only 
variable .tref_." }
+// { dg-message "add .constexpr." "" { target *-*-* } .-1 }
   thread_local constexpr auto tref__ = ^^int;
 }


        Jakub

Reply via email to