https://gcc.gnu.org/g:947ff08bff31425cdcb02b445f135a1ece869873

commit r17-5520-g947ff08bff31425cdcb02b445f135a1ece869873
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]>

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 31ef5bf1111a..953f1d038eca 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.  */
+  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 && *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->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 01df92d7e095..fbcd0d21328b 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 be3f29b9507f..7dcd56c4f564 100644
--- a/gcc/cp/reflect.cc
+++ b/gcc/cp/reflect.cc
@@ -7686,18 +7686,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);

Reply via email to