https://gcc.gnu.org/g:368f72c019d68fec8b496bb4c964cce0d5a0865d
commit r16-9830-g368f72c019d68fec8b496bb4c964cce0d5a0865d Author: Marek Polacek <[email protected]> Date: Thu Oct 1 13:53:32 2026 -0400 c++/reflection: fix extract-ref [PR127232] A couple of problems here: [meta.reflection.extract]/5.3 says "If r represents a variable, then either that variable is usable in constant expressions or its lifetime began within the core constant expression currently under evaluation." but I was wrongly using is_constant_expression. That led to a crash because we passed a type to build_address. For "is usable..." we want decl_constant_var_p and for the "its lifetime began..." I'm adding a new function. Using cxx_eval_constant_expression for non-reference variables wouldn't work since evaluating a variable as a glvalue always succeeds, and as a prvalue fails for e.g. an uninitialized variable whose lifetime did begin. With this patch, a variable that isn't usable in constant expressions throws. Thus the extract2.C and extract3.C changes; one can use object_of to get the object instead. Also, DECL_INITIAL of a PARM_DECL is not an initializer but DECL_ARG_TYPE. So we need cxx_eval_constant_expression to handle e.g.: int g; consteval void h (const int &p) { std::meta::extract<const int &> (^^p); } consteval { h (g); } where we need to get g from the PARM_DECL p, and return "(const int &) &g". With cxx_eval_constant_expression with vc_glvalue, the maybe_get_reference_referent call is no longer necessary. The result should be wrapped in a NOP_EXPR otherwise e.g. template <const int &> struct X { }; same_type<X<g>, X<extract<const int &> (object_of (^^g))>> s1; fails: we returned a pointer rather than a value of reference type T. This adds a new XFAIL because we don't have P2686 yet. It seems that object_of will need similar tweaks but that's for another patch. PR c++/127232 gcc/cp/ChangeLog: * constexpr.cc (constexpr_global_ctx::in_eval_lifetime_p): New. (cxx_constexpr_in_eval_lifetime_p): New. * cp-tree.h (cxx_constexpr_in_eval_lifetime_p): Declare. * reflect.cc (extract_ref): Check decl_constant_var_p and cxx_constexpr_in_eval_lifetime_p instead of is_constant_expression. Use cxx_eval_constant_expression instead of DECL_INITIAL for references. Adjust the checking assert. Drop the maybe_get_reference_referent call. Build a NOP_EXPR around the result. gcc/testsuite/ChangeLog: * g++.dg/reflect/extract2.C: Adjust dg-error. * g++.dg/reflect/extract3.C: Add dg-error. Add tests using object_of. * g++.dg/reflect/extract13.C: New test. * g++.dg/reflect/extract14.C: New test. Reviewed-by: Jason Merrill <[email protected]> (cherry picked from commit 947ff08bff31425cdcb02b445f135a1ece869873) Diff: --- gcc/cp/constexpr.cc | 20 ++++++++- gcc/cp/cp-tree.h | 1 + gcc/cp/reflect.cc | 17 +++++--- gcc/testsuite/g++.dg/reflect/extract13.C | 75 ++++++++++++++++++++++++++++++++ gcc/testsuite/g++.dg/reflect/extract14.C | 22 ++++++++++ gcc/testsuite/g++.dg/reflect/extract2.C | 2 +- gcc/testsuite/g++.dg/reflect/extract3.C | 21 ++++++--- 7 files changed, 144 insertions(+), 14 deletions(-) diff --git a/gcc/cp/constexpr.cc b/gcc/cp/constexpr.cc index e812067feb79..ac61be5783ae 100644 --- a/gcc/cp/constexpr.cc +++ b/gcc/cp/constexpr.cc @@ -1242,7 +1242,16 @@ public: return true; return false; } - tree get_value (tree t) + /* This differs from !is_outside_lifetime in that it returns false if + T is not in the map. */ + bool in_eval_lifetime_p (tree t) + { + if (tree *p = values.get (t)) + if (*p != void_node && *p != void_list_node) + return true; + return false; + } + tree get_value (tree t) { if (tree *p = values.get (t)) if (*p != void_node) @@ -1383,6 +1392,15 @@ cxx_constexpr_consteval_block (const constexpr_ctx *ctx) return ctx->global->consteval_block; } +/* Return true if T's lifetime began within the core constant expression + currently under evaluation. */ + +bool +cxx_constexpr_in_eval_lifetime_p (const constexpr_ctx *ctx, tree t) +{ + return ctx->global->in_eval_lifetime_p (t); +} + /* Predicates for the meaning of *jump_target. */ static bool diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 42da025674ee..db602cb01f8c 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -9462,6 +9462,7 @@ extern bool cxx_constexpr_quiet_p (const constexpr_ctx *); extern mce_value cxx_constexpr_manifestly_const_eval (const constexpr_ctx *); extern tree cxx_constexpr_caller (const constexpr_ctx *); extern tree cxx_constexpr_consteval_block (const constexpr_ctx *); +extern bool cxx_constexpr_in_eval_lifetime_p (const constexpr_ctx *, tree); /* An RAII sentinel used to restrict constexpr evaluation so that it doesn't do anything that causes extra DECL_UID generation. */ diff --git a/gcc/cp/reflect.cc b/gcc/cp/reflect.cc index 45be0013f902..591216171f5f 100644 --- a/gcc/cp/reflect.cc +++ b/gcc/cp/reflect.cc @@ -7642,18 +7642,25 @@ extract_ref (location_t loc, const constexpr_ctx *ctx, tree T, tree r, if (adju != error_mark_node && adjt != error_mark_node && is_convertible (adju, adjt) - && (!var_p || is_constant_expression (r))) + && (!var_p + || decl_constant_var_p (r) + || cxx_constexpr_in_eval_lifetime_p (ctx, r))) { if (TYPE_REF_P (TREE_TYPE (r))) { - r = DECL_INITIAL (r); - r = maybe_get_reference_referent (r); - gcc_checking_assert (!TYPE_REF_P (TREE_TYPE (r))); + bool nc = false, ovf = false; + tree jt = NULL_TREE; + r = cxx_eval_constant_expression (ctx, convert_from_reference (r), + vc_glvalue, &nc, &ovf, &jt); + if (nc || jt) + goto throw_; + gcc_checking_assert (!TYPE_P (r)); } - return build_address (r); + return build_nop (T, build_address (r)); } } +throw_: return throw_exception (loc, ctx, "value cannot be extracted", fun, non_constant_p, jump_target); } diff --git a/gcc/testsuite/g++.dg/reflect/extract13.C b/gcc/testsuite/g++.dg/reflect/extract13.C new file mode 100644 index 000000000000..a771a6d32416 --- /dev/null +++ b/gcc/testsuite/g++.dg/reflect/extract13.C @@ -0,0 +1,75 @@ +// PR c++/127232 +// { dg-do compile { target c++26 } } +// { dg-additional-options "-freflection" } + +#include <meta> + +template<typename, typename> struct same_type; +template<typename T> struct same_type<T, T> { }; + +void +foo (int const& x) +{ + constexpr std::meta::info y = ^^x; + constexpr auto e = extract<int const&>(y); // { dg-error "value cannot be extracted" } +} + +consteval bool +throws_p (std::meta::info r) +{ + try { extract<const int &> (r); } + catch (std::meta::exception &) { return true; } + return false; +} + +int g; +constexpr const int &cr = g; + +void +f (const int &x, int y) +{ + static_assert (throws_p (^^x)); + static_assert (throws_p (^^y)); + const int &lx = x; + static_assert (throws_p (^^lx)); + int &lg = g; + static_assert (!throws_p (^^lg)); + static_assert (throws_p (^^g)); + int a = 42; + int &la = a; + static_assert (!throws_p (^^la)); // { dg-bogus "" "P2686" { xfail *-*-* } } +} + +consteval bool +lifetime () +{ + int i = 0; + int &ri = i; + return &extract<int &> (^^ri) == &i; +} +static_assert (lifetime ()); + +consteval void +h (const int &p) +{ + int i = 42; + const int &ri = i; + extract<const int &> (^^i); + extract<const int &> (^^ri); + extract<const int &> (^^p); +} + +consteval { h (g); } + +consteval bool +erroneous () +{ + int u; + int &ru = u; + return &extract<int &> (^^u) == &u && &extract<int &> (^^ru) == &u; +} +static_assert (erroneous ()); + +template <const int &> struct X { }; +same_type<X<g>, X<extract<const int &> (object_of (^^g))>> s1; +same_type<X<g>, X<extract<int &> (object_of (^^g))>> s2; diff --git a/gcc/testsuite/g++.dg/reflect/extract14.C b/gcc/testsuite/g++.dg/reflect/extract14.C new file mode 100644 index 000000000000..614065117e9c --- /dev/null +++ b/gcc/testsuite/g++.dg/reflect/extract14.C @@ -0,0 +1,22 @@ +// PR c++/127232 +// { dg-do compile { target c++26 } } +// { dg-additional-options "-freflection" } + +#include <meta> + +// Check that reflections of locals/parameters refer to the current frame in +// recursive calls. +consteval bool +foo (int n, int *outer) +{ + int i = n; + if (&extract<int &> (^^i) != &i || &extract<int &> (^^n) != &n) + return false; + if (outer && &extract<int &> (^^i) == outer) + return false; + int &ri = i; + if (&extract<int &> (^^ri) != &i) + return false; + return n == 0 || foo (n - 1, &i); +} +static_assert (foo (2, nullptr)); diff --git a/gcc/testsuite/g++.dg/reflect/extract2.C b/gcc/testsuite/g++.dg/reflect/extract2.C index 9de23d1e931d..2d5f758ea96b 100644 --- a/gcc/testsuite/g++.dg/reflect/extract2.C +++ b/gcc/testsuite/g++.dg/reflect/extract2.C @@ -93,7 +93,7 @@ static_assert (!can_extract<const int &>(reflect_constant (i))); static_assert (!can_extract<int &>(^^i)); int j; -static_assert (extract<int &>(^^j)); // { dg-error "non-constant|not usable" } +constexpr auto e = extract<int &>(^^j); // { dg-error "cannot be extracted" } consteval info bar () diff --git a/gcc/testsuite/g++.dg/reflect/extract3.C b/gcc/testsuite/g++.dg/reflect/extract3.C index f8c173c02086..42e06fac3423 100644 --- a/gcc/testsuite/g++.dg/reflect/extract3.C +++ b/gcc/testsuite/g++.dg/reflect/extract3.C @@ -33,12 +33,16 @@ static_assert (extract<const int &>(^^rj) == j); static_assert (&extract<const int &>(^^rj) == &j); int k = 1; -static_assert (&extract<int &>(^^k) == &k); -static_assert (&extract<const int &>(^^k) == &k); +constexpr auto e1 = extract<int &>(^^k); // { dg-error "cannot be extracted" } +constexpr auto e2 = extract<const int &>(^^k); // { dg-error "cannot be extracted" } +static_assert (&extract<int &> (object_of (^^k)) == &k); +static_assert (&extract<const int &> (object_of (^^k)) == &k); int arr[5] {}; -static_assert (&extract<int (&)[5]>(^^arr) == &arr); -static_assert (&extract<const int (&)[5]>(^^arr) == &arr); +constexpr auto e3 = extract<int (&)[5]>(^^arr); // { dg-error "cannot be extracted" } +constexpr auto e4 = extract<const int (&)[5]>(^^arr); // { dg-error "cannot be extracted" } +static_assert (&extract<int (&)[5]>(object_of (^^arr)) == &arr); +static_assert (&extract<const int (&)[5]>(object_of (^^arr)) == &arr); struct S { int m; }; constexpr S s{42}; @@ -54,9 +58,12 @@ same_type<decltype(extract<const S &>(reflect_constant (S{42}))), const S &> st7 same_type<decltype(&extract<const S &>(reflect_constant (S{42}))), const S *> st8; S sr{42}; -static_assert (&extract<S&>(^^sr) == &sr); -static_assert (&extract<const S&>(^^sr) == &sr); -static_assert (&extract<S&>(^^sr).m == &sr.m); +constexpr auto e5 = extract<S&>(^^sr); // { dg-error "cannot be extracted" } +constexpr auto e6 = extract<const S&>(^^sr); // { dg-error "cannot be extracted" } +constexpr auto e7 = extract<S&>(^^sr).m; // { dg-error "cannot be extracted" } +static_assert (&extract<S&>(object_of (^^sr)) == &sr); +static_assert (&extract<const S&>(object_of (^^sr)) == &sr); +static_assert (&extract<S&>(object_of (^^sr)).m == &sr.m); int &srm = sr.m; static_assert (&extract<int&>(^^srm) == &sr.m);
