https://gcc.gnu.org/g:d1c6023698dbf575af4affa05bb1dc9e90502a33

commit r16-7718-gd1c6023698dbf575af4affa05bb1dc9e90502a33
Author: Marek Polacek <[email protected]>
Date:   Mon Feb 23 14:16:13 2026 -0500

    c++/reflection: fix wrong "consteval-only" error [PR123662]
    
    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.
    
    Rather than convincing check_out_of_consteval_use that we are in an
    immediate context, we should only call check_out_of_consteval_use on the
    outermost statement, not sub-statements like the ++D.67757 above.  This
    is not a complete fix though, see the FIXME.
    
            PR c++/123662
            PR c++/123611
    
    gcc/cp/ChangeLog:
    
            * cvt.cc (convert_to_void): Only call check_out_of_consteval_use
            when stmts_are_full_exprs_p.
    
    gcc/testsuite/ChangeLog:
    
            * g++.dg/reflect/reflect_constant_array5.C: New test.
            * g++.dg/reflect/reflect_constant_array6.C: New test.
    
    Reviewed-by: Jason Merrill <[email protected]>

Diff:
---
 gcc/cp/cvt.cc                                      |   6 +-
 .../g++.dg/reflect/reflect_constant_array5.C       |  16 ++++
 .../g++.dg/reflect/reflect_constant_array6.C       | 104 +++++++++++++++++++++
 3 files changed, 124 insertions(+), 2 deletions(-)

diff --git a/gcc/cp/cvt.cc b/gcc/cp/cvt.cc
index 4042938da5e9..b1176317d996 100644
--- a/gcc/cp/cvt.cc
+++ b/gcc/cp/cvt.cc
@@ -1215,8 +1215,10 @@ convert_to_void (tree expr, impl_conv_void implicit, 
tsubst_flags_t complain)
 
   /* Detect using expressions of consteval-only types outside manifestly
      constant-evaluated contexts.  We are going to discard this expression,
-     so we can't wait till cp_fold_immediate_r.  */
-  if (check_out_of_consteval_use (expr))
+     so we can't wait till cp_fold_immediate_r.  FIXME This is too early;
+     code like "int i = (^^i, 42);" is OK.  We should stop discarding
+     expressions here (PR124249).  */
+  if (stmts_are_full_exprs_p () && check_out_of_consteval_use (expr))
     return error_mark_node;
 
   if (VOID_TYPE_P (TREE_TYPE (expr)))
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 000000000000..9e52f408d5a0
--- /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 000000000000..918b07ffb881
--- /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) { }
+}

Reply via email to