[PATCH] c++, libstdc++, v2: Implement C++26 P3074R7, P3726R0 and CWG3189 - trivial unions [PR119059]

Tomasz Kaminski tkaminsk@redhat.com
Thu May 21 09:01:57 GMT 2026


On Thu, May 21, 2026 at 10:59 AM Tomasz Kaminski <tkaminsk@redhat.com>
wrote:

>
>
> On Thu, May 21, 2026 at 10:51 AM Jakub Jelinek <jakub@redhat.com> wrote:
>
>> On Thu, May 21, 2026 at 10:31:25AM +0200, Tomasz Kaminski wrote:
>> > Even with this patch, I still see a difference between the foo and bar
>> > functions.
>> > as defined below:
>> > #include <memory>
>> > #include <cassert>
>> > #include <string>
>> >
>> > template <typename T, size_t N>
>> > struct FixedVector {
>> >   union { T storage[N]; };
>> >   size_t size = 0;
>> >
>> >   constexpr FixedVector () { std::start_lifetime (storage); }
>> >
>> >   constexpr ~FixedVector() { std::destroy(storage, storage + size); }
>> >
>> >   constexpr FixedVector(std::initializer_list<T> il)
>> >   {
>> >     assert(il.size() < N);
>> >     std::start_lifetime (storage);
>> >     std::uninitialized_copy(il.begin(), il.end(), storage);
>> >     size = il.size();
>> >   }
>> >
>> >   constexpr void push_back(T const& v) { ::new (storage + size) T(v);
>> > ++size; }
>> > };
>> >
>> > FixedVector<char, 4> foo()
>> > {
>> >   return {'a', 'b', 'c'};
>> > }
>> >
>> > FixedVector<char, 4> bar()
>> > {
>> >   constexpr FixedVector<char, 4> res{'a', 'b', 'c'};
>> >   return res;
>> > }
>> >
>> > After compiling the above with -O2 I see:
>>
>> This boils down to the differences in C with -O2 on:
>> struct S { unsigned char a[4]; unsigned long b; };
>>
>> struct S foo () { static const unsigned char c[3] = { 97, 98, 99 };
>> struct S r; __builtin_memcpy (&r.a, &c, 3); r.b = 3; return r; }
>> struct S bar () { struct S r; r.a[0] = 97; r.a[1] = 98; r.a[2] = 99; r.b
>> = 3; return r; }
>> struct S baz () { static const unsigned char c[4] = { 97, 98, 99, 100 };
>> struct S r; __builtin_memcpy (&r.a, &c, 4); r.b = 4; return r; }
>> struct S qux () { struct S r; r.a[0] = 97; r.a[1] = 98; r.a[2] = 99;
>> r.a[3] = 100; r.b = 4; return r; }
>>
>> Note, baz and qux emit the same
>>         movl    $4, %edx
>>         movl    $1684234849, %eax
>> while
>>         movzbl  .LC0+1(%rip), %edx
>>         movzbl  .LC0(%rip), %eax
>>         salq    $8, %rdx
>>         orq     %rax, %rdx
>>         movzbl  .LC0+2(%rip), %eax
>>         salq    $16, %rax
>>         orq     %rdx, %rax
>>         movl    $3, %edx
>>         ret
>> vs.
>>         movl    $3, %edx
>>         movl    $6513249, %eax
>>         ret
>> I guess worth filing a PR, but I'm afraid we have plenty of prior PRs
>> about this too (and some attempt to improve it but not really successful;
>> it is about the returning of uninitialized bits in registers).
>>
> That what I assumed with current inplace_vector, that zeroes all elements
> of array at compile time. But with the start_lifetime, constexpr
> FixedVector<char, 4> res{'a', 'b', 'c'};
> It also contains the same out of lifetime element, as return {'a', 'b',
> 'c'} would.
>
> I think the expecation from
> https://gcc.gnu.org/bugzilla/show_bug.cgi?id=124121
> is that `return FixedVector<char, 4>{'a', 'b', 'c'}` would constant-fold
> creation of
> fixed vector, and be equivalent to `constexpr FixedVector<char, 4>
> res{'a', 'b', 'c'}; return res;`.
>
That expectation may be misplaced. I have no intuition about when
constant-folding occurs for
compilers.

>
>
>>
>>         Jakub
>>
>>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://gcc.gnu.org/pipermail/libstdc++/attachments/20260521/a61e597e/attachment-0001.htm>


More information about the Libstdc++ mailing list