On 10/1/26 9:55 PM, Marek Polacek wrote:
Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk?
-- >8 --
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 "(const int &) &g" from PARM_DECL p.
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::within_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.
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.
---
gcc/cp/constexpr.cc | 20 +++++++-
gcc/cp/cp-tree.h | 1 +
gcc/cp/reflect.cc | 15 ++++--
gcc/testsuite/g++.dg/reflect/extract13.C | 59 ++++++++++++++++++++++++
gcc/testsuite/g++.dg/reflect/extract14.C | 23 +++++++++
gcc/testsuite/g++.dg/reflect/extract2.C | 2 +-
gcc/testsuite/g++.dg/reflect/extract3.C | 21 ++++++---
7 files changed, 129 insertions(+), 12 deletions(-)
create mode 100644 gcc/testsuite/g++.dg/reflect/extract13.C
create mode 100644 gcc/testsuite/g++.dg/reflect/extract14.C
diff --git a/gcc/cp/constexpr.cc b/gcc/cp/constexpr.cc
index 31ef5bf1111..cf31c803be6 100644
--- a/gcc/cp/constexpr.cc
+++ b/gcc/cp/constexpr.cc
@@ -1247,7 +1247,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. */
The names are too similar for this subtle difference. Why not
_eval_lifetime_ like the other function?
+ bool within_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 && *p != void_list_node)
@@ -1404,6 +1413,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->within_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 947bf62ff98..83a287471a5 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -9486,6 +9486,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 8bf518df64f..8a4c5490bbf 100644
--- a/gcc/cp/reflect.cc
+++ b/gcc/cp/reflect.cc
@@ -7686,18 +7686,27 @@ 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);
+ bool nc = false, ovf = false;
+ tree jt = NULL_TREE;
+ r = cxx_eval_constant_expression (ctx, r, vc_prvalue,
vc_prvalue seems wrong if we're trying to get a reference to r; there's
no need to fetch its value. Specifically, it should be OK if r has
erroneous value as long as it's within lifetime.
+ &nc, &ovf, &jt);
+ if (nc || jt)
+ goto throw_;
r = maybe_get_reference_referent (r);
- gcc_checking_assert (!TYPE_REF_P (TREE_TYPE (r)));
+ gcc_checking_assert (r && !TYPE_P (r)
+ && !TYPE_REF_P (TREE_TYPE (r)));
}
return build_address (r);
Incidentally, how is it OK that this returns a pointer rather than a
reference?
}
}
+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 00000000000..8b9ecdd10bf
--- /dev/null
+++ b/gcc/testsuite/g++.dg/reflect/extract13.C
@@ -0,0 +1,59 @@
+// PR c++/127232
+// { dg-do compile { target c++26 } }
+// { dg-additional-options "-freflection" }
+
+#include <meta>
+
+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); }
diff --git a/gcc/testsuite/g++.dg/reflect/extract14.C
b/gcc/testsuite/g++.dg/reflect/extract14.C
new file mode 100644
index 00000000000..ede1a5b3c92
--- /dev/null
+++ b/gcc/testsuite/g++.dg/reflect/extract14.C
@@ -0,0 +1,23 @@
+// 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 (&std::meta::extract<int &> (^^i) != &i
+ || &std::meta::extract<int &> (^^n) != &n)
+ return false;
+ if (outer && &std::meta::extract<int &> (^^i) == outer)
+ return false;
+ int &ri = i;
+ if (&std::meta::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 9de23d1e931..2d5f758ea96 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 f8c173c0208..42e06fac342 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);
base-commit: b24418a22128668b21179dfcb73fa54ee3929ecf