https://gcc.gnu.org/g:07b05ad02478dc3291a234ebd2030f49e0259673

commit r17-4659-g07b05ad02478dc3291a234ebd2030f49e0259673
Author: Jakub Jelinek <[email protected]>
Date:   Fri Sep 25 17:55:51 2026 +0200

    c++: Represent aggregate subobjects after std::destroy_at as void_node 
[PR127385]
    
    The following testcase misbehaves, because for std::destroy_at of aggregates
    (i.e. CLOBBER with CLOBBER_OBJECT_END) we record during constant evaluation
    just a normal CONSTRUCTOR, so for std::is_within_lifetime it appears as if
    it is normally within lifetime, even when it is not.
    
    For scalar subobjects and complete objects we use void_node, this patch
    uses void_node also for the aggregates (but doesn't do that on
    CLOBBER_OBJECT_START and CLOBBER_STORAGE_START, that breaks too much stuff
    and the lifetime is just beginning there, not ending).  I've just had
    to tweak cxx_eval_store_expression so that it is able to handle stores
    into previously outside of lifetime subobjects by making those within
    lifetime.
    
    2026-09-25  Jakub Jelinek  <[email protected]>
    
            PR c++/127385
            * constexpr.cc (cxx_eval_store_expression): Handle the case
            of *valp == void_node like if *valp is NULL.
            (cxx_eval_store_expression): For CLOBBER_OBJECT_END or above
            store *valp = void_node even for aggregate types.
    
            * g++.dg/cpp26/within-lifetime14.C: New test.
    
    Reviewed-by: Jason Merrill <[email protected]>

Diff:
---
 gcc/cp/constexpr.cc                            |   4 +-
 gcc/testsuite/g++.dg/cpp26/within-lifetime14.C | 195 +++++++++++++++++++++++++
 2 files changed, 197 insertions(+), 2 deletions(-)

diff --git a/gcc/cp/constexpr.cc b/gcc/cp/constexpr.cc
index e90ca1018a7d..e1b64880a883 100644
--- a/gcc/cp/constexpr.cc
+++ b/gcc/cp/constexpr.cc
@@ -8649,7 +8649,7 @@ cxx_eval_store_expression (const constexpr_ctx *ctx, tree 
t,
   bool empty_base = false;
   while (!refs->is_empty ())
     {
-      if (*valp == NULL_TREE)
+      if (*valp == NULL_TREE || *valp == void_node)
        {
          *valp = build_constructor (type, NULL);
          CONSTRUCTOR_NO_CLEARING (*valp) = no_zero_init;
@@ -8998,7 +8998,7 @@ cxx_eval_store_expression (const constexpr_ctx *ctx, tree 
t,
     }
   else if (TREE_CLOBBER_P (init))
     {
-      if (AGGREGATE_TYPE_P (type))
+      if (AGGREGATE_TYPE_P (type) && CLOBBER_KIND (init) < CLOBBER_OBJECT_END)
        {
          if (*valp && TREE_CODE (*valp) == CONSTRUCTOR)
            CONSTRUCTOR_ELTS (*valp) = nullptr;
diff --git a/gcc/testsuite/g++.dg/cpp26/within-lifetime14.C 
b/gcc/testsuite/g++.dg/cpp26/within-lifetime14.C
new file mode 100644
index 000000000000..d4cd7fd94429
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp26/within-lifetime14.C
@@ -0,0 +1,195 @@
+// P3450R1 - Extend std::is_within_lifetime
+// { dg-do compile { target c++20 } }
+
+#include "../cpp2a/construct_at.h"
+
+namespace std {
+  template <class T>
+  consteval bool
+  is_within_lifetime (const T *p) noexcept
+  {
+    return __builtin_is_within_lifetime (p);
+  }
+
+  template <class T>
+  constexpr void
+  start_lifetime (T &r) noexcept
+  {
+    __builtin_start_lifetime (__builtin_addressof (r));
+  }
+}
+
+struct A { int a, b; };
+struct B { A c, d[2]; };
+union C { B e; int f; };
+struct D { C g; B h; };
+struct E { D i[3]; };
+
+consteval int
+foo ()
+{
+  E j;
+  if (!std::is_within_lifetime (&j.i[1]))
+    return __LINE__;
+  if (!std::is_within_lifetime (&j.i[1].g))
+    return __LINE__;
+  if (std::is_within_lifetime (&j.i[1].g.e))
+    return __LINE__;
+  if (std::is_within_lifetime (&j.i[1].g.f))
+    return __LINE__;
+  if (std::is_within_lifetime (&j.i[1].g.e.d[1].b))
+    return __LINE__;
+  if (!std::is_within_lifetime (&j.i[1].h))
+    return __LINE__;
+  if (!std::is_within_lifetime (&j.i[1].h.d[0].a))
+    return __LINE__;
+  std::destroy_at (&j.i[1]);
+  if (std::is_within_lifetime (&j.i[1]))
+    return __LINE__;
+  if (std::is_within_lifetime (&j.i[1].g))
+    return __LINE__;
+  if (std::is_within_lifetime (&j.i[1].g.e))
+    return __LINE__;
+  if (std::is_within_lifetime (&j.i[1].g.f))
+    return __LINE__;
+  if (std::is_within_lifetime (&j.i[1].g.e.d[1].b))
+    return __LINE__;
+  if (std::is_within_lifetime (&j.i[1].h))
+    return __LINE__;
+  if (std::is_within_lifetime (&j.i[1].h.d[0].a))
+    return __LINE__;
+  std::start_lifetime (j.i[1]);
+  if (!std::is_within_lifetime (&j.i[1]))
+    return __LINE__;
+  if (std::is_within_lifetime (&j.i[1].g))
+    return __LINE__;
+  if (std::is_within_lifetime (&j.i[1].g.e))
+    return __LINE__;
+  if (std::is_within_lifetime (&j.i[1].g.f))
+    return __LINE__;
+  if (std::is_within_lifetime (&j.i[1].g.e.d[1].b))
+    return __LINE__;
+  if (std::is_within_lifetime (&j.i[1].h))
+    return __LINE__;
+  if (std::is_within_lifetime (&j.i[1].h.d[0].a))
+    return __LINE__;
+  std::start_lifetime (j.i[1].g);
+  if (!std::is_within_lifetime (&j.i[1]))
+    return __LINE__;
+  if (!std::is_within_lifetime (&j.i[1].g))
+    return __LINE__;
+  if (std::is_within_lifetime (&j.i[1].g.e))
+    return __LINE__;
+  if (std::is_within_lifetime (&j.i[1].g.f))
+    return __LINE__;
+  if (std::is_within_lifetime (&j.i[1].g.e.d[1].b))
+    return __LINE__;
+  if (std::is_within_lifetime (&j.i[1].h))
+    return __LINE__;
+  if (std::is_within_lifetime (&j.i[1].h.d[0].a))
+    return __LINE__;
+  std::start_lifetime (j.i[1].h);
+  if (!std::is_within_lifetime (&j.i[1]))
+    return __LINE__;
+  if (!std::is_within_lifetime (&j.i[1].g))
+    return __LINE__;
+  if (std::is_within_lifetime (&j.i[1].g.e))
+    return __LINE__;
+  if (std::is_within_lifetime (&j.i[1].g.f))
+    return __LINE__;
+  if (std::is_within_lifetime (&j.i[1].g.e.d[1].b))
+    return __LINE__;
+  if (!std::is_within_lifetime (&j.i[1].h))
+    return __LINE__;
+  if (std::is_within_lifetime (&j.i[1].h.d[0].a))
+    return __LINE__;
+  std::start_lifetime (j.i[1].h.d);
+  std::start_lifetime (j.i[1].h.d[0]);
+  if (std::is_within_lifetime (&j.i[1].h.d[0].a))
+    return __LINE__;
+  j.i[1].h.d[0].a = 42;
+  if (!std::is_within_lifetime (&j.i[1].h))
+    return __LINE__;
+  if (!std::is_within_lifetime (&j.i[1].h.d[0].a))
+    return __LINE__;
+  std::start_lifetime (j.i[1].g.e);
+  std::start_lifetime (j.i[1].g.e.d);
+  std::start_lifetime (j.i[1].g.e.d[1]);
+  if (!std::is_within_lifetime (&j.i[1].g.e))
+    return __LINE__;
+  if (std::is_within_lifetime (&j.i[1].g.f))
+    return __LINE__;
+  if (!std::is_within_lifetime (&j.i[1].g.e.d[1]))
+    return __LINE__;
+  if (std::is_within_lifetime (&j.i[1].g.e.d[1].b))
+    return __LINE__;
+  j.i[1].g.e.d[1].b = 42;
+  if (!std::is_within_lifetime (&j.i[1].g.e.d[1]))
+    return __LINE__;
+  if (!std::is_within_lifetime (&j.i[1].g.e.d[1].b))
+    return __LINE__;
+  std::start_lifetime (j.i[1].g);
+  if (!std::is_within_lifetime (&j.i[1]))
+    return __LINE__;
+  if (!std::is_within_lifetime (&j.i[1].g))
+    return __LINE__;
+  if (!std::is_within_lifetime (&j.i[1].g.e))
+    return __LINE__;
+  if (std::is_within_lifetime (&j.i[1].g.f))
+    return __LINE__;
+  if (!std::is_within_lifetime (&j.i[1].g.e.d[1].b))
+    return __LINE__;
+  if (!std::is_within_lifetime (&j.i[1].h))
+    return __LINE__;
+  if (!std::is_within_lifetime (&j.i[1].h.d[0].a))
+    return __LINE__;
+  std::start_lifetime (j.i[1].g);
+  j.i[1].g.f = 42;
+  if (!std::is_within_lifetime (&j.i[1]))
+    return __LINE__;
+  if (!std::is_within_lifetime (&j.i[1].g))
+    return __LINE__;
+  if (std::is_within_lifetime (&j.i[1].g.e))
+    return __LINE__;
+  if (!std::is_within_lifetime (&j.i[1].g.f))
+    return __LINE__;
+  if (std::is_within_lifetime (&j.i[1].g.e.d[1].b))
+    return __LINE__;
+  if (!std::is_within_lifetime (&j.i[1].h))
+    return __LINE__;
+  if (!std::is_within_lifetime (&j.i[1].h.d[0].a))
+    return __LINE__;
+  std::destroy_at (&j.i[1]);
+  if (std::is_within_lifetime (&j.i[1]))
+    return __LINE__;
+  if (std::is_within_lifetime (&j.i[1].g))
+    return __LINE__;
+  if (std::is_within_lifetime (&j.i[1].g.e))
+    return __LINE__;
+  if (std::is_within_lifetime (&j.i[1].g.f))
+    return __LINE__;
+  if (std::is_within_lifetime (&j.i[1].g.e.d[1].b))
+    return __LINE__;
+  if (std::is_within_lifetime (&j.i[1].h))
+    return __LINE__;
+  if (std::is_within_lifetime (&j.i[1].h.d[0].a))
+    return __LINE__;
+  std::construct_at (&j.i[1]);
+  if (!std::is_within_lifetime (&j.i[1]))
+    return __LINE__;
+  if (!std::is_within_lifetime (&j.i[1].g))
+    return __LINE__;
+  if (!std::is_within_lifetime (&j.i[1].g.e))
+    return __LINE__;
+  if (std::is_within_lifetime (&j.i[1].g.f))
+    return __LINE__;
+  if (!std::is_within_lifetime (&j.i[1].g.e.d[1].b))
+    return __LINE__;
+  if (!std::is_within_lifetime (&j.i[1].h))
+    return __LINE__;
+  if (!std::is_within_lifetime (&j.i[1].h.d[0].a))
+    return __LINE__;
+  return 0;
+}
+
+static_assert (foo () == 0);

Reply via email to