On 2/12/26 5:15 PM, Jakub Jelinek wrote:
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.

OK.

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