[Bug c++/121068] Placement new of array element is rejected at compile-time
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=121068 --- Comment #21 from GCC Commits --- The trunk branch has been updated by Jason Merrill : https://gcc.gnu.org/g:70f33ad677e6350a724b56d4cb766480ed8367fc commit r16--g70f33ad677e6350a724b56d4cb766480ed8367fc Author: Jason Merrill Date: Thu Aug 21 13:52:25 2025 -0400 c++: constexpr clobber of const [PR121068] Since r16-3022, 20_util/variant/102912.cc was failing in C++20 and above due to wrong errors about destruction modifying a const object; destruction is OK. PR c++/121068 gcc/cp/ChangeLog: * constexpr.cc (cxx_eval_store_expression): Allow clobber of a const object. gcc/testsuite/ChangeLog: * g++.dg/cpp2a/constexpr-dtor18.C: New test.
[Bug c++/121068] Placement new of array element is rejected at compile-time
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=121068 Jason Merrill changed: What|Removed |Added Status|NEW |ASSIGNED --- Comment #20 from Jason Merrill --- (In reply to Jonathan Wakely from comment #19) > FAIL: 20_util/variant/102912.cc -std=gnu++20 (test for excess errors) Ah, thanks, I was only running that test in 17 mode (the default). I'll fix that as well.
[Bug c++/121068] Placement new of array element is rejected at compile-time
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=121068 --- Comment #19 from Jonathan Wakely --- I'm seeing a new libstdc++ testsuite failure since r16-3022-gbc42128330c0ea FAIL: 20_util/variant/102912.cc -std=gnu++20 (test for excess errors) FAIL: 20_util/variant/102912.cc -std=gnu++23 (test for excess errors) FAIL: 20_util/variant/102912.cc -std=gnu++26 (test for excess errors) The error is: in 'constexpr' expansion of 'std::destroy_at(__pointer)' /home/jwakely/gcc/15/include/c++/15.1.1/bits/stl_construct.h:164:22: 164 | std::destroy_at(__pointer); | ~~~^~~ /home/jwakely/gcc/15/include/c++/15.1.1/bits/stl_construct.h:88:9: error: modifying a const object '* __location' is not allowed in a constant expression 88 | __location->~_Tp(); | ^~ /home/jwakely/gcc/15/include/c++/15.1.1/variant:246:13: note: originally declared 'const' here 246 | _Type _M_storage; | ^~
[Bug c++/121068] Placement new of array element is rejected at compile-time
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=121068 --- Comment #18 from GCC Commits --- The trunk branch has been updated by Jason Merrill : https://gcc.gnu.org/g:bc42128330c0ea70a015b74b655cb8c48b6a8c06 commit r16-3022-gbc42128330c0ea70a015b74b655cb8c48b6a8c06 Author: Jason Merrill Date: Tue Aug 5 15:16:50 2025 -0700 c++: clobber object on placement new [PR121068] My r16-2432 patch addressed the original testcase involving an array of scalars, but not this additional testcase involving an array of classes. This patch addresses the issue more thoroughly, by having placement new first clobber the new object, and improving cxx_eval_store_expression to implement initial clobbers as well. My earlier attempt to do this clobbered the array as a whole, which broke construct_at after the resolution of LWG3436 due to trying to create a multidimensional array over the top of a single-dimensional array. To side-step that issue, this patch instead clobbers the individual elements of an array, taking advantage of the earlier change to let that activate the array member of a union. PR c++/121068 gcc/cp/ChangeLog: * constexpr.cc (cxx_eval_store_expression): Handle clobbers. (potential_constant_expression_1): Handle clobbers more. * decl.cc (build_clobber_this): Use INIT_EXPR for initial clobber. * init.cc (build_new_1): Clobber on placement new. (build_vec_init): Don't clean up after clobber. gcc/testsuite/ChangeLog: * g++.dg/cpp26/constexpr-new5.C: New test.
[Bug c++/121068] Placement new of array element is rejected at compile-time
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=121068 --- Comment #17 from Jason Merrill --- OK, the issue is that we currently don't represent trivial initialization at all, so the initial placement new has no effect. Then the trivial destructors are represented by clobbers, but constant evaluation doesn't currently do anything with them (before my WIP patch). Then the individual construction starts with a clobber that the evaluator ignores. Then the constructor starts to initialize the individual member, but that's not directly initializing the union member, so it gets rejected. As in the WIP patch I think the solution is to represent trivial initialization by clobbering the object instead of doing nothing.
[Bug c++/121068] Placement new of array element is rejected at compile-time
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=121068
--- Comment #16 from Tomasz Kamiński ---
Ah sorry, I was sure I posted the function before:
```
#include
struct S
{
constexpr S() = default;
constexpr S(int x) : s(x) {}
constexpr S(S&& x) : s(x.s) {}
constexpr S& operator=(S&& x) { s = x.s; return *this; }
unsigned char s;
};
constexpr
int foo()
{
union { S a[20]; };
new (&a) S[20](); // OK
for (int i = 0; i < 20; ++i)
a[i].~S();
auto* sf = ::new(&a[2]) S(11);
return 1;
}
static_assert(foo());
constexpr
int foo2()
{
union { S a[20]; };
new (&a) S[20]; // ILL-FORMED
for (int i = 0; i < 20; ++i)
a[i].~S();
auto* sf = ::new(&a[2]) S(11);
return 1;
}
static_assert(foo2());
```
https://godbolt.org/z/cKbEsdzEh
[Bug c++/121068] Placement new of array element is rejected at compile-time
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=121068 --- Comment #15 from Jason Merrill --- (In reply to Tomasz Kamiński from comment #14) Please provide complete testcases, this snippet isn't enough to reproduce.
[Bug c++/121068] Placement new of array element is rejected at compile-time
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=121068
--- Comment #14 from Tomasz Kamiński ---
Not sure if this is expected, so noting this here.
On trunk (with r16-2432-gfdbc5ff61b471076cc9c758fb6c30d62f7ef1c56), if I value
initialize the array (like in inplace_vector), then the code works:
union { S a[20]; };
new (&a) S[20](); // OK
for (int i = 0; i < 20; ++i)
a[i].~S();
auto* sf = ::new(&a[2]) S(11);
See also: https://gcc.gnu.org/pipermail/libstdc++/2025-July/062752.html
But after changing the placement new to perform default initialization `new
(&a) S`, the above code no longer works. This is not a blocker on any library
feature.
[Bug c++/121068] Placement new of array element is rejected at compile-time
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=121068 Jason Merrill changed: What|Removed |Added Attachment #61891|0 |1 is obsolete|| --- Comment #13 from Jason Merrill --- Created attachment 61942 --> https://gcc.gnu.org/bugzilla/attachment.cgi?id=61942&action=edit revised clobber patch Here's the current state of the patch to clobber arrays to start their lifetime.
[Bug c++/121068] Placement new of array element is rejected at compile-time
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=121068 --- Comment #12 from GCC Commits --- The trunk branch has been updated by Jason Merrill : https://gcc.gnu.org/g:fdbc5ff61b471076cc9c758fb6c30d62f7ef1c56 commit r16-2432-gfdbc5ff61b471076cc9c758fb6c30d62f7ef1c56 Author: Jason Merrill Date: Wed Jul 16 11:52:45 2025 -0400 c++: constexpr union placement new [PR121068] The note and example in [class.union] p6 think that placement new can be used to change the active member of a union, but we didn't support that for array members in constant-evaluation even after implementing P1330 and P2747. First I tried to address this by introducing a CLOBBER_BEGIN_OBJECT for the entire array, but that broke the resolution of LWG3436, which invokes 'new T[1]' for an array T, and trying to clobber a multidimensional array when the actual object is single-dimensional breaks. So I've raised that issue with the committee. Until that is resolved, this patch takes a simpler approach: allow initialization of an element of an array to make the array the active member of a union. PR c++/121068 gcc/cp/ChangeLog: * constexpr.cc (cxx_eval_store_expression): Allow ARRAY_REFs when activating an array member of a union. gcc/testsuite/ChangeLog: * g++.dg/cpp2a/constexpr-union6.C: Expect x5 to work. * g++.dg/cpp26/constexpr-new4.C: New test.
[Bug c++/121068] Placement new of array element is rejected at compile-time
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=121068 --- Comment #11 from Jason Merrill --- (In reply to Tomasz Kamiński from comment #9) > I remember that LWG3436 was discussed in core in Varna > (https://wiki.edg.com/bin/view/Wg21varna/CoreWorkingGroup#LWG3436) and the > current wording is result from there. Ah, right. > I was always suspicious about creating Tp[1] there, as in case when we are > creating a member (or any other object that is not transparently > replaceable), we will simply reuse storage and destroy the original object. > I do not think there is UB caused by lack of multidimensional array at that > location, but the new call is for sure not constant for the same reason. I guess the Tp[1] idea was justified by https://eel.is/c++draft/basic#compound-3 "an object of type T that is not an array element is considered to belong to an array with one element of type T" but that's qualified by "For purposes of pointer arithmetic and comparison", I don't see that it applies to the whole object model. My argument that this is UB is based on https://eel.is/c++draft/basic.lval#11 , thinking that int[3] is not accessible through int[3][1]. But that section also makes the point that access is based on scalars, so perhaps it doesn't apply. Actually, it's unclear to me what actually prevents us from creating a struct of one int overlaying an int > I agree that your proposed change seem to be much better direction, but as > far as I can see it does not match direction of > https://cplusplus.github.io/LWG/issue3436. Am I looking at wrong place? Ah, no, I was; https://www.open-std.org/jtc1/sc22/wg21/docs/lwg-active.html seems to be out of date. I sent mail to the core reflector.
[Bug c++/121068] Placement new of array element is rejected at compile-time
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=121068
--- Comment #10 from Tomasz Kamiński ---
If the object pointed by __location is not transparently replaceable, for
example if I would create an object inside std::byte array that is an member,
then does std::launder(__location) produce pointer to new object? __location
does not automatically point there per (https://eel.is/c++draft/basic.life#10),
by we meet precondition for launder (https://eel.is/c++draft/ptr.launder#2).
This cannot happen for compile-time evaluation, as calling construct_at such
way would require equivalent of reinterpret_cast, so we could do, but I am not
sure if that is necessary.
+#if __cpp_lib_launder
+ if consteval
+{
+ return std::launder(__location);
+}
+ else
+{
+ return std::launder(static_cast<_Tp*>(__loc));
+}
+#else
+ return static_cast<_Tp*>(__loc);
+#endif
[Bug c++/121068] Placement new of array element is rejected at compile-time
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=121068 --- Comment #9 from Tomasz Kamiński --- I remember that LWG3436 was discussed in core in Varna (https://wiki.edg.com/bin/view/Wg21varna/CoreWorkingGroup#LWG3436) and the current wording is result from there. I was always suspicious about creating Tp[1] there, as in case when we are creating a member (or any other object that is not transparently replaceable), we will simply reuse storage and destroy the original object. I do not think there is UB caused by lack of multidimensional array at that location, but the new call is for sure not constant for the same reason. I agree that your proposed change seem to be much better direction, but as far as I can see it does not match direction of https://cplusplus.github.io/LWG/issue3436. Am I looking at wrong place?
[Bug c++/121068] Placement new of array element is rejected at compile-time
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=121068 Jason Merrill changed: What|Removed |Added Ever confirmed|0 |1 Status|UNCONFIRMED |NEW Last reconfirmed||2025-07-21 Assignee|unassigned at gcc dot gnu.org |jason at gcc dot gnu.org --- Comment #8 from Jason Merrill --- (In reply to Tomasz Kamiński from comment #6) > I believed that `_member` being active after new should directly fall from > the definition of active member in > https://eel.is/c++draft/class.union#general-2: > > In a union, a non-static data member is active if its name refers to an > > object whose lifetime has begun and has not ended ([basic.life]). > > So if new operations, starts lifetime of member of the union (regardless of > how) it becomes active member of the union, until it's lifetime is ended. I > do not think that syntax how does it happen matters. Sure, that matches this comment in cxx_eval_store_expression: /* An INIT_EXPR of the last member in an access chain is always OK, so I tried changing the clobber to happen regardless of the syntax of the placement argument. This mostly worked well, including fixing an xfail in one of the constexpr new tests. But it also ran into trouble on the current libstdc++ construct_at, which when told to construct a _Tp where _Tp is an array type, instead tries to construct a _Tp[1], which seems undefined to me; there is no such multidimensional array at that location. This is related to LWG issue 3436. The proposed resolution there seems reasonable, so I'm trying this library change: index 217a0416d42..1ba9740e90d 100644 --- a/libstdc++-v3/include/bits/stl_construct.h +++ b/libstdc++-v3/include/bits/stl_construct.h @@ -104,7 +104,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION static_assert(sizeof...(_Args) == 0, "std::construct_at for array " "types must not use any arguments to initialize the " "array"); - return ::new(__loc) _Tp[1](); + __loc = ::new(__loc) _Tp(); +#if __cpp_lib_launder + return std::launder(__location); +#else + return static_cast<_Tp*>(__loc); +#endif
[Bug c++/121068] Placement new of array element is rejected at compile-time
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=121068
--- Comment #7 from Tomasz Kamiński ---
Or in other words, I believe my example is equivalent to implementation of
optional,
where we have:
union {
T val;
};
And then call:
new(static_cast(addressof(val))) T(...);
It is just version were T is array.
[Bug c++/121068] Placement new of array element is rejected at compile-time
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=121068 --- Comment #6 from Tomasz Kamiński --- I believed that `_member` being active after new should directly fall from the definition of active member in https://eel.is/c++draft/class.union#general-2: > In a union, a non-static data member is active if its name refers to an > object whose lifetime has begun and has not ended ([basic.life]). So if new operations, starts lifetime of member of the union (regardless of how) it becomes active member of the union, until it's lifetime is ended. I do not think that syntax how does it happen matters. The unclear case, would be if creating element of array member, should start the union, i.e. something like: new (&union_.member[2]) T;
[Bug c++/121068] Placement new of array element is rejected at compile-time
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=121068 --- Comment #5 from Jason Merrill --- The intent of the patch was to support new (&union_.member) T syntax like union_.member = T() for setting the active member, as in https://eel.is/c++draft/class.union#general-example-3 but adding the library fluff obscures that syntax, and so is less clear that it ought to work.
[Bug c++/121068] Placement new of array element is rejected at compile-time
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=121068 --- Comment #4 from Tomasz Kamiński --- Hi, the original example works, but when I start to add library fluff, I get the same error. I mean cases like: // passing address to actual member new(&arr) T[3]; new(std::addressof(arr)) T[3]; // Disable overload op& // Disabling ADL new(static_cast(arr)) T[3]; new(static_cast(&arr)) T[3]; new(static_cast(std::addressof(arr))) T[3];
[Bug c++/121068] Placement new of array element is rejected at compile-time
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=121068 --- Comment #3 from Jason Merrill --- Created attachment 61891 --> https://gcc.gnu.org/bugzilla/attachment.cgi?id=61891&action=edit fix Let me know how this works for you.
[Bug c++/121068] Placement new of array element is rejected at compile-time
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=121068
--- Comment #2 from Tomasz Kamiński ---
That is indeed very surprising, as it would mean that if I have:
```
struct S {
union {
int x;
};
};
constexpr S test()
{
S s;
new(&s.x) int;
is_active_member(s.x); // this is false
}
```
One we have ability to ask for active member, we can observe if new marked it
active.
However, marking the member as active is also breaking, as following will no
longer compiler:
```
constexpr S create()
{
S s;
new(&s.x) int;
return s;
}
constexpr S g = create(); // OK now, x is not active,
// ILL-FORMED if new will start lifetime of s.x
```
But I think this should be ILL-FORMED.
[Bug c++/121068] Placement new of array element is rejected at compile-time
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=121068 Jason Merrill changed: What|Removed |Added CC||jason at gcc dot gnu.org --- Comment #1 from Jason Merrill --- Ah, the actual bug is that we don't represent the first array new because it's trivial default initialization, and we don't represent the trivial pseudo-destructor calls, so we don't see any access to arr until we get to actually trying to give it a value. The error is correct under the current standard, but I want to make it well-formed by making the first array new make arr the active member.
