On Wed, Feb 25, 2026 at 2:13 PM Tomasz Kamiński <[email protected]> wrote:
> The _Arg_value::_M_set method, initialized the union member, by > assining to reference to that member produced by _M_get(*this). > However, per language rules, such assigment has undefined behavior, > if alternative was not already active. > > To address above, we modify _M_set to use placement new for the class > types, and invoke _S_access with two arguments for all other types. > The _S_access (rename of _S_get) is modified to assign the value of > the second parameter (if provided) to the union memnber. Such direct > assigments are treated specially in the language, and will start > lifetime of union alternative of implicit-lifetime type. > > libstdc++-v3/ChangeLog: > > * include/std/format (_Arg_value::_M_get): Rename to... > (_Arg_value::_M_access): Modified to accept optional > second parameter that is assigned to value. > (_Arg_value::_M_get): Handle rename. > (_Arg_value::_M_set): Use construct_at for basic_string_view, > and two-argument _S_access for other types. > > Signed-off-by: Tomasz Kamiński <[email protected]> > Signed-off-by: Ivan Lazaric <[email protected]> > Co-authored-by: Ivan Lazaric <[email protected]> > --- > This was detected by constant evaluator, and while it seem to not > be harmfull at runtime, we will need to address it anyway. > > Testing on x86_64-linux. OK for trunk when all test passes? > All test passed. > > libstdc++-v3/include/std/format | 61 ++++++++++++++++++--------------- > 1 file changed, 33 insertions(+), 28 deletions(-) > > diff --git a/libstdc++-v3/include/std/format > b/libstdc++-v3/include/std/format > index 245ea964675..4f0b0f377c6 100644 > --- a/libstdc++-v3/include/std/format > +++ b/libstdc++-v3/include/std/format > @@ -4234,70 +4234,73 @@ namespace __format > { _S_get<_Tp>() = __val; } > #endif > > - template<typename _Tp, typename _Self> > + // Returns reference to the _Arg_value member with the type _Tp. > + // Value of second argument (if provided), is assigned to that > member. > + template<typename _Tp, typename _Self, typename... _Value> > [[__gnu__::__always_inline__]] > static auto& > - _S_get(_Self& __u) noexcept > + _S_access(_Self& __u, _Value... __value) noexcept > { > + static_assert(sizeof...(_Value) <= 1); > if constexpr (is_same_v<_Tp, bool>) > - return __u._M_bool; > + return (__u._M_bool = ... = __value); > else if constexpr (is_same_v<_Tp, _CharT>) > - return __u._M_c; > + return (__u._M_c = ... = __value); > else if constexpr (is_same_v<_Tp, int>) > - return __u._M_i; > + return (__u._M_i = ... = __value); > else if constexpr (is_same_v<_Tp, unsigned>) > - return __u._M_u; > + return (__u._M_u = ... = __value); > else if constexpr (is_same_v<_Tp, long long>) > - return __u._M_ll; > + return (__u._M_ll = ... = __value); > else if constexpr (is_same_v<_Tp, unsigned long long>) > - return __u._M_ull; > + return (__u._M_ull = ... = __value); > else if constexpr (is_same_v<_Tp, float>) > - return __u._M_flt; > + return (__u._M_flt = ... = __value); > else if constexpr (is_same_v<_Tp, double>) > - return __u._M_dbl; > + return (__u._M_dbl = ... = __value); > #ifndef _GLIBCXX_LONG_DOUBLE_ALT128_COMPAT > else if constexpr (is_same_v<_Tp, long double>) > - return __u._M_ldbl; > + return (__u._M_ldbl = ... = __value); > #else > else if constexpr (is_same_v<_Tp, __ibm128>) > - return __u._M_ibm128; > + return (__u._M_ibm128 = ... = __value); > else if constexpr (is_same_v<_Tp, __ieee128>) > - return __u._M_ieee128; > + return (__u._M_ieee128 = ... = __value); > #endif > #ifdef __SIZEOF_FLOAT128__ > else if constexpr (is_same_v<_Tp, __float128>) > - return __u._M_float128; > + return (__u._M_float128 = ... = __value); > #endif > else if constexpr (is_same_v<_Tp, const _CharT*>) > - return __u._M_str; > + return (__u._M_str = ... = __value); > else if constexpr (is_same_v<_Tp, basic_string_view<_CharT>>) > - return __u._M_sv; > + return (__u._M_sv = ... = __value); > else if constexpr (is_same_v<_Tp, const void*>) > - return __u._M_ptr; > + return (__u._M_ptr = ... = __value); > #ifdef __SIZEOF_INT128__ > else if constexpr (is_same_v<_Tp, __int128>) > - return __u._M_i128; > + return (__u._M_i128 = ... = __value); > else if constexpr (is_same_v<_Tp, unsigned __int128>) > - return __u._M_u128; > + return (__u._M_u128 = ... = __value); > #endif > #ifdef __BFLT16_DIG__ > else if constexpr (is_same_v<_Tp, __bflt16_t>) > - return __u._M_bf16; > + return (__u._M_bf16 = ... = __value); > #endif > #ifdef __FLT16_DIG__ > else if constexpr (is_same_v<_Tp, _Float16>) > - return __u._M_f16; > + return (__u._M_f16 = ... = __value); > #endif > #ifdef __FLT32_DIG__ > else if constexpr (is_same_v<_Tp, _Float32>) > - return __u._M_f32; > + return (__u._M_f32 = ... = __value); > #endif > #ifdef __FLT64_DIG__ > else if constexpr (is_same_v<_Tp, _Float64>) > - return __u._M_f64; > + return (__u._M_f64 = ... = __value); > #endif > else if constexpr (is_same_v<_Tp, _Handle<_Context>>) > - return __u._M_handle; > + return (__u._M_handle = ... = __value); > // Otherwise, ill-formed. > } > > @@ -4305,23 +4308,25 @@ namespace __format > [[__gnu__::__always_inline__]] > auto& > _M_get() noexcept > - { return _S_get<_Tp>(*this); } > + { return _S_access<_Tp>(*this); } > > template<typename _Tp> > [[__gnu__::__always_inline__]] > const auto& > _M_get() const noexcept > - { return _S_get<_Tp>(*this); } > + { return _S_access<_Tp>(*this); } > > template<typename _Tp> > [[__gnu__::__always_inline__]] > void > _M_set(_Tp __v) noexcept > { > - if constexpr (is_same_v<_Tp, _Handle<_Context>>) > + if constexpr (is_same_v<_Tp, basic_string_view<_CharT>>) > + std::construct_at(&_M_sv, __v); > + else if constexpr (is_same_v<_Tp, _Handle<_Context>>) > std::construct_at(&_M_handle, __v); > else > - _S_get<_Tp>(*this) = __v; > + _S_access<_Tp>(*this, __v); > } > }; > > -- > 2.53.0 > >
