On Tue, Feb 24, 2026 at 10:54:55PM +0900, Jason Merrill wrote:
> On 2/24/26 5:44 AM, Marek Polacek wrote:
> > Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk?
> >
> > -- >8 --
> > This patch fixes a very annoying problem where we emit a bogus
> > check_out_of_consteval_use error. The error is provoked while
> > processing
> >
> > [: std::meta::reflect_constant_array (data) :]
> >
> > The argument of a [: :] is a constant-expression = a manifestly
> > constant-evaluated context, so any consteval-only exprs in it
> > are OK. But in eval_reflect_constant_array we do get_template_parm_object
> > which does push_to_top_level -- so we have no scope_chain, therefore
> > any in_consteval_if_p and current_function_decl are cleared, so we're
> > not in an immediate context.
>
> Since we're initializing a constexpr variable (the template parameter
> object), we should be in MCE for that regardless of the splice expression.
> Maybe check_initializer should set that up?
Yeah, that sounds right, thanks.
I think that my change is also right, but not necessary anymore.
Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk?
-- >8 --
This patch fixes a very annoying problem where we emit a bogus
check_out_of_consteval_use error. The error is provoked while
processing
[: std::meta::reflect_constant_array (data) :]
The argument of a [: :] is a constant-expression = a manifestly
constant-evaluated context, so any consteval-only exprs in it
are OK. But in eval_reflect_constant_array we do get_template_parm_object
which does push_to_top_level -- so we have no scope_chain, therefore
any in_consteval_if_p and current_function_decl are cleared, so we're
not in an immediate context. As part of this get_template_parm_object,
we call cp_finish_decl -> check_initializer -> build_aggr_init ->
-> build_vec_init.
Here in build_vec_init try_const is true, but we still generate code
for the initializer like
<<< Unknown tree: expr_stmt
(void) ++D.67757 >>>;
<<< Unknown tree: expr_stmt
(void) --D.67758 >>>;
etc. We add ++D.67757 with finish_expr_stmt which calls
convert_to_void -> check_out_of_consteval_use which causes the error
because ++D.67757's type is consteval-only. Note that what we end up using
is the simple
_ZTAX... = {{.name=<<< Unknown tree: reflect_expr _ZTAXtlA2_KcLS_95EEE >>>,
.none=1}}
because we didn't see anything non-const in the initializer.
When initializing a constexpr variable, we are in a manifestly
constant-evaluated context, so fix check_initializer to that effect.
PR c++/123662
PR c++/123611
gcc/cp/ChangeLog:
* decl.cc (check_initializer): Set in_consteval_if_p when initializing
a constexpr variable.
gcc/testsuite/ChangeLog:
* g++.dg/reflect/reflect_constant_array5.C: New test.
* g++.dg/reflect/reflect_constant_array6.C: New test.
---
gcc/cp/decl.cc | 4 +
.../g++.dg/reflect/reflect_constant_array5.C | 16 +++
.../g++.dg/reflect/reflect_constant_array6.C | 104 ++++++++++++++++++
3 files changed, 124 insertions(+)
create mode 100644 gcc/testsuite/g++.dg/reflect/reflect_constant_array5.C
create mode 100644 gcc/testsuite/g++.dg/reflect/reflect_constant_array6.C
diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
index 6b210a30b6a..5580dc87e9a 100644
--- a/gcc/cp/decl.cc
+++ b/gcc/cp/decl.cc
@@ -8574,6 +8574,10 @@ check_initializer (tree decl, tree init, int flags,
vec<tree, va_gc> **cleanups)
|| type_has_extended_temps (type))))
|| (DECL_DECOMPOSITION_P (decl) && TREE_CODE (type) == ARRAY_TYPE))
{
+ /* If we're initializing a constexpr variable, we are in
+ a manifestly constant-evaluated context. */
+ in_consteval_if_p_temp_override icip;
+ in_consteval_if_p |= DECL_DECLARED_CONSTEXPR_P (decl);
init_code = build_aggr_init_full_exprs (decl, init, flags);
/* A constructor call is a non-trivial initializer even if
diff --git a/gcc/testsuite/g++.dg/reflect/reflect_constant_array5.C
b/gcc/testsuite/g++.dg/reflect/reflect_constant_array5.C
new file mode 100644
index 00000000000..9e52f408d5a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/reflect/reflect_constant_array5.C
@@ -0,0 +1,16 @@
+// PR c++/123662
+// { dg-do compile { target c++26 } }
+// { dg-additional-options "-freflection" }
+
+#include <meta>
+
+struct arg {
+ std::meta::info name;
+ bool none = true;
+};
+
+template<size_t...Is>
+void test1() {
+ constexpr auto py_arg_data =
std::array{arg{std::meta::reflect_constant_string("_")}};
+ constexpr auto short_py_arg_data = [:
std::meta::reflect_constant_array(py_arg_data) :];
+}
diff --git a/gcc/testsuite/g++.dg/reflect/reflect_constant_array6.C
b/gcc/testsuite/g++.dg/reflect/reflect_constant_array6.C
new file mode 100644
index 00000000000..918b07ffb88
--- /dev/null
+++ b/gcc/testsuite/g++.dg/reflect/reflect_constant_array6.C
@@ -0,0 +1,104 @@
+// PR c++/123611
+// { dg-do compile { target c++26 } }
+// { dg-additional-options "-freflection" }
+
+#include <string>
+#include <meta>
+#include <algorithm>
+#include <ranges>
+#include <sstream>
+
+namespace clap {
+ struct Flags {
+ bool use_short;
+ bool use_long;
+ };
+
+ template <typename T, Flags flags>
+ struct Option {
+ std::optional<T> initializer;
+
+ Option() = default;
+ Option(T t) : initializer(t) { }
+
+ static constexpr bool use_short = flags.use_short;
+ static constexpr bool use_long = flags.use_long;
+ };
+
+ consteval auto spec_to_opts(std::meta::info opts, std::meta::info spec) ->
std::meta::info {
+ std::vector<std::meta::info> new_members;
+ for (auto member :
+ nonstatic_data_members_of(spec,
std::meta::access_context::current())) {
+ auto new_type = template_arguments_of(type_of(member))[0];
+ new_members.push_back(data_member_spec(new_type,
{.name=identifier_of(member)}));
+ }
+ return define_aggregate(opts, new_members);
+ }
+
+ struct Clap {
+ template <typename Spec>
+ auto parse(this Spec const& spec, int argc, const char** argv) {
+ std::vector<std::string_view> cmdline(argv + 1, argv + argc);
+
+ struct Opts;
+ consteval {
+ spec_to_opts(^^Opts, ^^Spec);
+ }
+ Opts opts;
+
+ constexpr auto ctx = std::meta::access_context::current();
+ template for (constexpr auto Pair :
+ std::define_static_array(
+ std::views::zip(nonstatic_data_members_of(^^Spec, ctx),
+ nonstatic_data_members_of(^^Opts, ctx)) |
+ std::views::transform([](auto z) { return
std::pair(get<0>(z), get<1>(z)); }))) {
+ constexpr auto sm = Pair.first;
+ constexpr auto om = Pair.second;
+
+ auto& cur = spec.[:sm:];
+ constexpr auto type = type_of(om);
+
+ auto it = std::find_if(cmdline.begin(), cmdline.end(),
+ [&](std::string_view arg) {
+ return (cur.use_short && arg.size() == 2 && arg[0] == '-' &&
+ arg[1] == identifier_of(sm)[0])
+ || (cur.use_long && arg.starts_with("--") &&
+ arg.substr(2) == identifier_of(sm));
+ });
+
+ if (it == cmdline.end()) {
+ if constexpr (has_template_arguments(type) &&
+ template_of(type) == ^^std::optional) {
+ continue;
+ } else if (cur.initializer) {
+ opts.[:om:] = *cur.initializer;
+ continue;
+ } else {
+ std::exit(EXIT_FAILURE);
+ }
+ } else if (it + 1 == cmdline.end()) {
+ std::exit(EXIT_FAILURE);
+ }
+
+ std::stringstream iss;
+ iss << it[1];
+ if (iss >> opts.[:om:]; !iss) {
+ std::exit(EXIT_FAILURE);
+ }
+ }
+
+ return opts;
+ }
+ };
+}
+
+using namespace clap;
+struct Args : Clap {
+ Option<std::string, Flags{.use_short=true, .use_long=true}> name;
+ Option<int, Flags{.use_short=true, .use_long=true}> count = 1;
+};
+
+int main(int argc, const char** argv) {
+ auto opts = Args{}.parse(argc, argv);
+ for (int i = 0; i < opts.count; ++i) { }
+}
base-commit: 33b856931c78661944f27b60457365ef2b451b54
--
2.53.0