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