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.

Rather than convincing check_out_of_consteval_use that we are in an
immediate context, I think we should only call check_out_of_consteval_use
on the outermost statement, not sub-statements like the ++D.67757 above.

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.  And
cp_finish_decl calls check_out_of_consteval_use so we should still
detect any real problems there.

        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.
---
 gcc/cp/cvt.cc                                 |   2 +-
 .../g++.dg/reflect/reflect_constant_array5.C  |  16 +++
 .../g++.dg/reflect/reflect_constant_array6.C  | 104 ++++++++++++++++++
 3 files changed, 121 insertions(+), 1 deletion(-)
 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/cvt.cc b/gcc/cp/cvt.cc
index 4042938da5e..c869cbfe22e 100644
--- a/gcc/cp/cvt.cc
+++ b/gcc/cp/cvt.cc
@@ -1216,7 +1216,7 @@ 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))
+  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 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: 82cc94e5fb69d1c45a386f83798251de5bff9339
-- 
2.53.0

Reply via email to