Hi!

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.

Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?

2026-09-23  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.

--- a/gcc/cp/constexpr.cc       2026-09-21 14:31:23.000000000 +0200
+++ b/gcc/cp/constexpr.cc       2026-09-22 15:52:09.649180741 +0200
@@ -8635,7 +8635,7 @@ cxx_eval_store_expression (const constex
   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;
@@ -8984,7 +8984,7 @@ cxx_eval_store_expression (const constex
     }
   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;
--- a/gcc/testsuite/g++.dg/cpp26/within-lifetime14.C    2026-09-22 
16:24:27.672059837 +0200
+++ b/gcc/testsuite/g++.dg/cpp26/within-lifetime14.C    2026-09-22 
17:57:58.477173326 +0200
@@ -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);

        Jakub

Reply via email to