mirror of
https://github.com/gcc-mirror/gcc.git
synced 2026-05-06 14:59:39 +02:00
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 <jason@redhat.com>
This commit is contained in:
@@ -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)))
|
||||
|
||||
16
gcc/testsuite/g++.dg/reflect/reflect_constant_array5.C
Normal file
16
gcc/testsuite/g++.dg/reflect/reflect_constant_array5.C
Normal file
@@ -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) :];
|
||||
}
|
||||
104
gcc/testsuite/g++.dg/reflect/reflect_constant_array6.C
Normal file
104
gcc/testsuite/g++.dg/reflect/reflect_constant_array6.C
Normal file
@@ -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) { }
|
||||
}
|
||||
Reference in New Issue
Block a user