<div dir="ltr"><div dir="ltr"><br></div><br><div class="gmail_quote gmail_quote_container"><div dir="ltr" class="gmail_attr">On Thu, Mar 5, 2026 at 7:35 AM Tomasz Kamiński <<a href="mailto:tkaminsk@redhat.com">tkaminsk@redhat.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">The _Arg_value::_M_set method, initialized the union member, by<br>
assigning to reference to that member produced by _M_get(*this).<br>
However, per language rules, such assignment has undefined behavior,<br>
if alternative was not already active, same as for any object not<br>
within it's lifetime.<br>
<br>
To address above, we modify _M_set to use placement new for the class<br>
types, and invoke _S_access with two arguments for all other types.<br>
The _S_access (rename of _S_get) is modified to assign the value of<br>
the second parameter (if provided) to the union member. Such direct<br>
assignments are treated specially in the language (see N5032<br>
[class.union.general] p5), and will start lifetime of trivially default<br>
constructible alternative.<br>
<br>
libstdc++-v3/ChangeLog:<br>
<br>
* include/std/format (_Arg_value::_M_get): Rename to...<br>
(_Arg_value::_M_access): Modified to accept optional<br>
second parameter that is assigned to value.<br>
(_Arg_value::_M_get): Handle rename.<br>
(_Arg_value::_M_set): Use construct_at for basic_string_view,<br>
handle, and two-argument _S_access for other types.<br>
<br>
Signed-off-by: Tomasz Kamiński <<a href="mailto:tkaminsk@redhat.com" target="_blank">tkaminsk@redhat.com</a>><br>
Signed-off-by: Ivan Lazaric <<a href="mailto:ivan.lazaric1@gmail.com" target="_blank">ivan.lazaric1@gmail.com</a>><br>
Co-authored-by: Ivan Lazaric <<a href="mailto:ivan.lazaric1@gmail.com" target="_blank">ivan.lazaric1@gmail.com</a>><br>
---<br>
v2:<br>
- fixes typos and add mention to [class.union.general] p5 in commit<br>
description<br>
- add comments to _M_set function <br>
<br>
Testing on x86_64-linux. All *format* test passed.<br>
OK for trunk when all test passes?<br>
<br>
libstdc++-v3/include/std/format | 62 +++++++++++++++++++--------------<br>
1 file changed, 35 insertions(+), 27 deletions(-)<br>
<br>
diff --git a/libstdc++-v3/include/std/format b/libstdc++-v3/include/std/format<br>
index b014936a21e..9b5d5d499f5 100644<br>
--- a/libstdc++-v3/include/std/format<br>
+++ b/libstdc++-v3/include/std/format<br>
@@ -4212,67 +4212,70 @@ namespace __format<br>
{ _S_get<_Tp>() = __val; }<br>
#endif<br>
<br>
- template<typename _Tp, typename _Self><br>
+ // Returns reference to the _Arg_value member with the type _Tp.<br>
+ // Value of second argument (if provided), is assigned to that member. <br>
+ template<typename _Tp, typename _Self, typename... _Value><br>
[[__gnu__::__always_inline__]]<br>
static auto&<br>
- _S_get(_Self& __u) noexcept<br>
+ _S_access(_Self& __u, _Value... __value) noexcept<br>
{<br>
+ static_assert(sizeof...(_Value) <= 1);<br>
if constexpr (is_same_v<_Tp, bool>)<br>
- return __u._M_bool;<br>
+ return (__u._M_bool = ... = __value);<br>
else if constexpr (is_same_v<_Tp, _CharT>)<br>
- return __u._M_c;<br>
+ return (__u._M_c = ... = __value);<br>
else if constexpr (is_same_v<_Tp, int>)<br>
- return __u._M_i;<br>
+ return (__u._M_i = ... = __value);<br>
else if constexpr (is_same_v<_Tp, unsigned>)<br>
- return __u._M_u;<br>
+ return (__u._M_u = ... = __value);<br>
else if constexpr (is_same_v<_Tp, long long>)<br>
- return __u._M_ll;<br>
+ return (__u._M_ll = ... = __value);<br>
else if constexpr (is_same_v<_Tp, unsigned long long>)<br>
- return __u._M_ull;<br>
+ return (__u._M_ull = ... = __value);<br>
else if constexpr (is_same_v<_Tp, float>)<br>
- return __u._M_flt;<br>
+ return (__u._M_flt = ... = __value);<br>
else if constexpr (is_same_v<_Tp, double>)<br>
- return __u._M_dbl;<br>
+ return (__u._M_dbl = ... = __value);<br>
#ifndef _GLIBCXX_LONG_DOUBLE_ALT128_COMPAT<br>
else if constexpr (is_same_v<_Tp, long double>)<br>
- return __u._M_ldbl;<br>
+ return (__u._M_ldbl = ... = __value);<br>
#else<br>
else if constexpr (is_same_v<_Tp, __ibm128>)<br>
- return __u._M_ibm128;<br>
+ return (__u._M_ibm128 = ... = __value);<br>
else if constexpr (is_same_v<_Tp, __ieee128>)<br>
- return __u._M_ieee128;<br>
+ return (__u._M_ieee128 = ... = __value);<br>
#endif<br>
#ifdef __SIZEOF_FLOAT128__<br>
else if constexpr (is_same_v<_Tp, __float128>)<br>
- return __u._M_float128;<br>
+ return (__u._M_float128 = ... = __value);<br>
#endif<br>
else if constexpr (is_same_v<_Tp, const _CharT*>)<br>
- return __u._M_str;<br>
+ return (__u._M_str = ... = __value);<br>
else if constexpr (is_same_v<_Tp, basic_string_view<_CharT>>)<br>
- return __u._M_sv;<br>
+ return (__u._M_sv = ... = __value);<br>
else if constexpr (is_same_v<_Tp, const void*>)<br>
- return __u._M_ptr;<br>
+ return (__u._M_ptr = ... = __value);<br>
#ifdef __SIZEOF_INT128__<br>
else if constexpr (is_same_v<_Tp, __int128>)<br>
- return __u._M_i128;<br>
+ return (__u._M_i128 = ... = __value);<br>
else if constexpr (is_same_v<_Tp, unsigned __int128>)<br>
- return __u._M_u128;<br>
+ return (__u._M_u128 = ... = __value);<br>
#endif<br>
#ifdef __BFLT16_DIG__<br>
else if constexpr (is_same_v<_Tp, __bflt16_t>)<br>
- return __u._M_bf16;<br>
+ return (__u._M_bf16 = ... = __value);<br>
#endif<br>
#ifdef __FLT16_DIG__<br>
else if constexpr (is_same_v<_Tp, _Float16>)<br>
- return __u._M_f16;<br>
+ return (__u._M_f16 = ... = __value);<br>
#endif<br>
#ifdef __FLT32_DIG__<br>
else if constexpr (is_same_v<_Tp, _Float32>)<br>
- return __u._M_f32;<br>
+ return (__u._M_f32 = ... = __value);<br>
#endif<br>
#ifdef __FLT64_DIG__<br>
else if constexpr (is_same_v<_Tp, _Float64>)<br>
- return __u._M_f64;<br>
+ return (__u._M_f64 = ... = __value);<br>
#endif<br>
else if constexpr (is_same_v<_Tp, handle>)<br>
return __u._M_handle;<br>
@@ -4283,23 +4286,28 @@ namespace __format<br>
[[__gnu__::__always_inline__]]<br>
auto&<br>
_M_get() noexcept<br>
- { return _S_get<_Tp>(*this); }<br>
+ { return _S_access<_Tp>(*this); }<br>
<br>
template<typename _Tp><br>
[[__gnu__::__always_inline__]]<br>
const auto&<br>
_M_get() const noexcept<br>
- { return _S_get<_Tp>(*this); }<br>
+ { return _S_access<_Tp>(*this); }<br>
<br>
template<typename _Tp><br>
[[__gnu__::__always_inline__]]<br>
void<br>
_M_set(_Tp __v) noexcept<br>
{<br>
- if constexpr (is_same_v<_Tp, handle>)<br>
+ // Explicitly construct types without trivial default constructor.<br>
+ if constexpr (is_same_v<_Tp, basic_string_view<_CharT>>)<br>
+ std::construct_at(&_M_sv, __v);<br>
+ else if constexpr (is_same_v<_Tp, handle>)<br>
std::construct_at(&_M_handle, __v);<br>
else<br>
- _S_get<_Tp>(*this) = __v;<br>
+ // Builtin types, has trivial default constructor and assignment<br></blockquote><div>Will change that to: "Builtin types are trivially default constructible, and assignment..."</div><div>They lack a constructor, so that was technically incorrect.</div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
+ // changes active member per N5032 [class.union.general] p5.<br>
+ _S_access<_Tp>(*this, __v);<br>
}<br>
};<br>
<br>
-- <br>
2.53.0<br>
<br>
</blockquote></div></div>