https://gcc.gnu.org/bugzilla/show_bug.cgi?id=127385
Bug ID: 127385
Summary: std::is_within_lifetime vs. destroyed aggregates
Product: gcc
Version: 17.0
Status: UNCONFIRMED
Severity: normal
Priority: P3
Component: c++
Assignee: unassigned at gcc dot gnu.org
Reporter: jakub at gcc dot gnu.org
Target Milestone: ---
--- a/gcc/testsuite/g++.dg/cpp26/within-lifetime10.C 2026-09-04
14:49:58.820820520 +0200
+++ b/gcc/testsuite/g++.dg/cpp26/within-lifetime10.C 2026-09-04
15:21:57.025628662 +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);
should IMHO pass, but it FAILs. The problem is that std::destroy_at stores
void_node (which the lifetime tracking uses for not within lifetime) only for
scalars going out of lifetime, but not for aggregates. If those are destroyed,
it stores there an empty CONSTRUCTOR with CONSTRUCTOR_NO_CLEARING. That is
generally handled as within lifetime, so we either should find out if we can
also use void_node for not within lifetime aggregates, or need a new flag on
CONSTRUCTORs which will say this isn't within lifetime, neither its subjects
nor the object itself.