This is prohibited at https://eel.is/c++draft/basic.life#7.1. See https://godbolt.org/z/exKnnT935. ```C++ #include <memory> struct X { int x; }; int main() { []() consteval { { X x{}; x.~X(); ++x.x; std::construct_at(&x); } { union { X x{}; }; x.~X(); ++x.x; } }(); } ```
There's also https://eel.is/c++draft/basic.life#9.sentence-1 to consider. See https://godbolt.org/z/P97Kaqhv8. ```C++ struct X { int x; }; int main() { []() consteval { X x{}; x.~X(); }(); } ```
Fixed that: https://godbolt.org/z/YGf4GTP5P. ```C++ struct X { constexpr ~X() { } }; int main() { []() consteval { X x{}; x.~X(); }(); } ```
I think there might be a dup of this bug.
If there is, I confirmed it's not in https://gcc.gnu.org/bugzilla/show_bug.cgi?id=55004.
Confirmed.
Another example. Simplified: https://godbolt.org/z/z781c56PM. ```C++ struct ratio { int numerator; }; template<auto> struct constant; template<auto F> concept constant_invocable = requires { typename constant<(F(), 0)>; }; static_assert(not constant_invocable<[] { ratio r; (void)int{r.*&ratio::numerator}; }>); ``` ``` <source>:9:15: error: static assertion failed 9 | static_assert(not constant_invocable<[] { | ^~~~~~~~~~~~~~~~~~~~~~~~~~~ 10 | ratio r; | ~~~~~~~~ 11 | (void)int{r.*&ratio::numerator}; | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 12 | }>); | ~~ Compiler returned: 1 ``` Found while opening https://github.com/llvm/llvm-project/issues/57958: https://godbolt.org/z/KWfvEnaae. ```C++ #include <concepts> #include <cstdint> #include <functional> #include <type_traits> struct ratio { std::intmax_t numerator; std::intmax_t denominator{1}; }; template<auto F> concept constant_invocable = requires { typename std::integral_constant<int, (F(), 0)>; }; template<auto P> requires std::is_member_object_pointer_v<decltype(P)> inline constexpr bool default_initialization_leaves_uninitialized = []<class T, class M>(M T::*) { return not constant_invocable<[]() { T v; (void)M{v.*P}; }>; }(P); static_assert(default_initialization_leaves_uninitialized<&ratio::numerator>); static_assert(not default_initialization_leaves_uninitialized<&ratio::denominator>); ``` ``` <source>:22:15: error: static assertion failed 22 | static_assert(default_initialization_leaves_uninitialized<&ratio::numerator>); | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Compiler returned: 1 ```
This may be related. Assigning to another member doesn't end the lifetime of the active one: https://godbolt.org/z/eMGY5ehnb. ```C++ #include <memory> struct symbol { }; constexpr symbol one{}; struct unit { const struct symbol* symbol = &one; struct not_one_t { }; union to_the { not_one_t not_one = {}; // Omits ¹ from NTTP. int i; to_the() = default; constexpr to_the(int i) : i{i} { if (i == 1) { // std::destroy_at(&this->i); // On GCC, doesn't help. not_one = {}; // std::construct_at(¬_one); // On GCC, initializes `not_one`, doesn't end the lifetime of `i`. } } } exponent = {}; constexpr unit(const struct symbol& s) : symbol{&s} { } constexpr unit(const struct symbol& s, auto e) : symbol{&s}, exponent{e} { } }; template<auto> void print() { } int main() { constexpr auto U = unit{one, 1}; print<U>(); print<unit{one}>(); static_assert(&U.exponent.not_one); // static_assert(U.exponent.i == 1); // Works on GCC (and MSVC). } ```
*** Bug 117255 has been marked as a duplicate of this bug. ***
I think this probably needs to be fixed for std::is_within_lifetime (Bug 110367) to work correctly.
This also makes following ill-formed: ``` struct A { union { int x = 10; }; }; using Int = int; constexpr A create1() { A a; a.x.~Int(); return a; } constexpr A a1 = create1(); ```
The master branch has been updated by Jonathan Wakely <redi@gcc.gnu.org>: https://gcc.gnu.org/g:a63b663c7bf77e38d90cebe993a3de8a548f8d66 commit r16-2256-ga63b663c7bf77e38d90cebe993a3de8a548f8d66 Author: Jonathan Wakely <jwakely@redhat.com> Date: Thu Jul 10 14:12:44 2025 +0100 libstdc++: Ensure that ranges::destroy destroys in constexpr [PR121024] The new test is currently marked as XFAIL because PR c++/102284 means that GCC doesn't notice that the lifetimes have ended. libstdc++-v3/ChangeLog: PR libstdc++/121024 * include/bits/ranges_uninitialized.h (ranges::destroy): Do not optimize away trivial destructors during constant evaluation. (ranges::destroy_n): Likewise. * testsuite/20_util/specialized_algorithms/destroy/121024.cc: New test. Reviewed-by: Tomasz KamiÅski <tkaminsk@redhat.com>