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);
