On Wed, Apr 30, 2025 at 3:09 PM Tomasz Kaminski <tkami...@redhat.com> wrote:
> > > On Wed, Apr 30, 2025 at 1:26 PM Tomasz Kamiński <tkami...@redhat.com> > wrote: > >> This commits adjust the way how the arguments are stored in the _Arg_value >> (and thus basic_format_args), by preserving the types of fixed width >> floating-point types, that were previously converted to float, double, >> long double. >> >> The _Arg_value union now contains alternatives with std::bfloat16_t, >> std::float16_t, std::float32_t, std::float64_t that use pre-existing >> _Arg_bf16, _Arg_f16, _Arg_f32, _Arg_f32 argument types. >> >> This does not affect formatting, as specialization of formatters for >> formats them by casting to the corresponding standard floating point >> type. >> >> For the 128bit floating we need to handle the ppc64 architecture, >> (_GLIBCXX_LONG_DOUBLE_ALT128_COMPAT) for which the long double may (per TU >> basis) designate either __ibm128 and __ieee128 type, we need to store both >> types in the _Arg_value and have two _Arg_types (_Arg_ibm128, >> _Arg_ieee128). >> On other architectures we use extra enumerator value to store __float128, >> that is different from long double and _Float128. This is consistent with >> ppc64, >> for which __float128 is same type as __ieee128 if present. We use >> _Arg_float128 >> _M_float128 names that deviate from _Arg_fN naming scheme, to emphasize >> that >> this flag is not used for std::float128_t (_Float128_t) type, that is >> consistenly >> formatted via handle. >> >> The __format::_float128_t type is renamed to __format::__flt128_t, to >> mitigate >> visual confusion between this type and __float128. We also introduce >> __bflt16_t >> typedef instead of using of decltype. >> >> We add new alternative for the _Arg_value and allow them to be accessed >> via _S_get, >> when the types are available. However, we produce and handle >> corresponding _Arg_type, >> only when we can format them. See also r14-3329-g27d0cfcb2b33de. >> >> The formatter<_Float128, _CharT> that formats via __flt128_t is always >> provided, when type is available. It is still correct __flt128_t is >> _Float128_t. >> >> We also provide formatter<__float128, _CharT> that formats via __flt128_t. >> As this type may be disabled (-mno-float128), extra care needs to be >> taken, >> for situation when __float128 is same as long double. If the formatter >> would be >> defined in such case, the formatter<long double, charT> would be >> generated from >> different specializations, and have different mangling: >> * formatter<__float128, _CharT> if __float128 is present, >> * formatter<_format::__formattable_float, _CharT> otherwise. >> To best of my knowledge this happens only on ppc64 for __ieee128 and >> __float128, >> so the formatter is not defined in this case. static_assert is added to >> detect >> other configurations like that. In such case we should replace it with >> constraint. >> >> PR libstdc++/119246 >> >> libstdc++-v3/ChangeLog: >> >> * include/std/format (__format::__bflt16_t): Define. >> (_GLIBCXX_FORMAT_F128): Separate value for cases where _Float128 >> is used. >> (__format::__float128_t): Renamed to __format::_flt128_t. >> (std::formatter<_Float128, _CharT>): Define always if there is >> formattable >> 128bit float. >> (std::formatter<__float128, _CharT>): Define. >> (_Arg_type::_Arg_f128): Rename to _Arg_float128 and adjust value. >> (_Arg_type::_Arg_ibm128): Change value to _Arg_ldbl. >> (_Arg_type::_Arg_ieee128): Define as alias to _Arg_float128. >> (_Arg_value::_M_f128): Replaced with _M_ieee128 and _M_float128. >> (_Arg_value::_M_ieee128, _Arg_value::_M_float128, >> _Arg_value::_M_bf16) >> (_Arg_value::_M_f16, _Arg_value::_M_f32, _Arg_value::_M_f64): >> Define. >> (_Arg_value::_S_get, basic_format_arg::_S_to_enum): Handle >> __bflt16, >> _Float16, _Float32, _Float64, and __float128 types. >> (basic_format_arg::_S_to_arg_type): Preserve _bflt16, _Float16, >> _Float32, _Float64 and __float128 types. >> (basic_format_arg::_M_visit): Hadndle _Arg_float128, _Arg_ieee128, >> _Arg_b16, _Arg_f16, _Arg_f32, _Arg_f64. >> * testsuite/std/format/arguments/args.cc: Updated to illustrate >> that >> extended floating point types use handles now. Added test for >> __float128. >> * testsuite/std/format/parse_ctx.cc: Extended test to cover class >> to >> check_dynamic_spec with floating point types and handles. >> --- >> Tested on x86_64-linux and powerpc64le-unknown-linux-gnu. >> Running additional test on powerpc64le with >> unix\{-mabi=ibmlongdouble,-mabi=ieeelongdouble,-mno-float128}. >> >> OK for trunk? >> >> libstdc++-v3/include/std/format | 217 ++++++++++++------ >> .../testsuite/std/format/arguments/args.cc | 45 ++-- >> .../testsuite/std/format/parse_ctx.cc | 72 +++++- >> 3 files changed, 227 insertions(+), 107 deletions(-) >> >> diff --git a/libstdc++-v3/include/std/format >> b/libstdc++-v3/include/std/format >> index 054ce350440..73819f52f50 100644 >> --- a/libstdc++-v3/include/std/format >> +++ b/libstdc++-v3/include/std/format >> @@ -1863,20 +1863,24 @@ namespace __format >> _Spec<_CharT> _M_spec{}; >> }; >> >> +#ifdef __BFLT16_DIG__ >> + using __bflt16_t = decltype(0.0bf16); >> +#endif >> + >> // Decide how 128-bit floating-point types should be formatted (or >> not). >> - // When supported, the typedef __format::__float128_t is the type that >> - // format arguments should be converted to for storage in >> basic_format_arg. >> + // When supported, the typedef __format::__flt128_t is the type that >> + // format arguments should be converted before formatting code. >> // Define the macro _GLIBCXX_FORMAT_F128 to say they're supported. >> - // _GLIBCXX_FORMAT_F128=1 means __float128, _Float128 etc. will be >> formatted >> - // by converting them to long double (or __ieee128 for powerpc64le). >> - // _GLIBCXX_FORMAT_F128=2 means basic_format_arg needs to enable >> explicit >> - // support for _Float128, rather than formatting it as another type. >> + // The __float128, _Float128 will be formatted by converting them to: >> + // __ieee128 (same as __float128) _GLIBCXX_FORMAT_F128=1, >> + // long double if _GLIBCXX_FORMAT_F128=2, >> + // _Float128 if _GLIBCXX_FORMAT_F128=3. >> #undef _GLIBCXX_FORMAT_F128 >> >> #ifdef _GLIBCXX_LONG_DOUBLE_ALT128_COMPAT >> >> // Format 128-bit floating-point types using __ieee128. >> - using __float128_t = __ieee128; >> + using __flt128_t = __ieee128; >> # define _GLIBCXX_FORMAT_F128 1 >> >> #ifdef __LONG_DOUBLE_IEEE128__ >> @@ -1910,14 +1914,14 @@ namespace __format >> #elif defined _GLIBCXX_LDOUBLE_IS_IEEE_BINARY128 >> >> // Format 128-bit floating-point types using long double. >> - using __float128_t = long double; >> -# define _GLIBCXX_FORMAT_F128 1 >> + using __flt128_t = long double; >> +# define _GLIBCXX_FORMAT_F128 2 >> >> #elif __FLT128_DIG__ && defined(_GLIBCXX_HAVE_FLOAT128_MATH) >> >> // Format 128-bit floating-point types using _Float128. >> - using __float128_t = _Float128; >> -# define _GLIBCXX_FORMAT_F128 2 >> + using __flt128_t = _Float128; >> +# define _GLIBCXX_FORMAT_F128 3 >> >> # if __cplusplus == 202002L >> // These overloads exist in the library, but are not declared for >> C++20. >> @@ -2947,8 +2951,8 @@ namespace __format >> }; >> #endif >> >> -#if defined(__FLT128_DIG__) && _GLIBCXX_FORMAT_F128 == 1 >> - // Reuse __formatter_fp<C>::format<__float128_t, Out> for _Float128. >> +#if defined(__FLT128_DIG__) && _GLIBCXX_FORMAT_F128 >> + // Use __formatter_fp<C>::format<__format::flt128_t, Out> for >> _Float128. >> template<__format::__char _CharT> >> struct formatter<_Float128, _CharT> >> { >> @@ -2962,17 +2966,45 @@ namespace __format >> template<typename _Out> >> typename basic_format_context<_Out, _CharT>::iterator >> format(_Float128 __u, basic_format_context<_Out, _CharT>& __fc) >> const >> - { return _M_f.format((__format::__float128_t)__u, __fc); } >> + { return _M_f.format((__format::__flt128_t)__u, __fc); } >> + >> + private: >> + __format::__formatter_fp<_CharT> _M_f; >> + }; >> +#endif >> + >> +#if defined(__SIZEOF_FLOAT128__) && _GLIBCXX_FORMAT_F128 != 1 >> + // Reuse __formatter_fp<C>::format<__format::_flt128_t, Out> for >> __float128. >> + // This formatter is not declared if >> _GLIBCXX_LONG_DOUBLE_ALT128_COMPAT is true, >> + // as __float128 when present is same type as __ieee128, which may be >> same as >> + // long double. >> + template<__format::__char _CharT> >> + struct formatter<__float128, _CharT> >> > Am I am too aggressive here? Could __float128 not be IEEE, despite having > _Float128 or IEE 128bit long double? > As discussed internally, __float128 is always IEEE 128bit floating point, if it exists. > + { >> + formatter() = default; >> + >> + [[__gnu__::__always_inline__]] >> + constexpr typename basic_format_parse_context<_CharT>::iterator >> + parse(basic_format_parse_context<_CharT>& __pc) >> + { return _M_f.parse(__pc); } >> + >> + template<typename _Out> >> + typename basic_format_context<_Out, _CharT>::iterator >> + format(__float128 __u, basic_format_context<_Out, _CharT>& __fc) >> const >> + { return _M_f.format((__format::__flt128_t)__u, __fc); } >> >> private: >> __format::__formatter_fp<_CharT> _M_f; >> + >> + static_assert( !is_same_v<__float128, long double>, >> + "This specialization should not be used for long >> double" ); >> }; >> #endif >> >> #if defined(__STDCPP_BFLOAT16_T__) && >> defined(_GLIBCXX_FLOAT_IS_IEEE_BINARY32) >> // Reuse __formatter_fp<C>::format<float, Out> for bfloat16_t. >> template<__format::__char _CharT> >> - struct formatter<__gnu_cxx::__bfloat16_t, _CharT> >> + struct formatter<__format::__bflt16_t, _CharT> >> { >> formatter() = default; >> >> @@ -3795,16 +3827,14 @@ namespace __format >> enum _Arg_t : unsigned char { >> _Arg_none, _Arg_bool, _Arg_c, _Arg_i, _Arg_u, _Arg_ll, _Arg_ull, >> _Arg_flt, _Arg_dbl, _Arg_ldbl, _Arg_str, _Arg_sv, _Arg_ptr, >> _Arg_handle, >> - _Arg_i128, _Arg_u128, >> - _Arg_bf16, _Arg_f16, _Arg_f32, _Arg_f64, // These are unused. >> + _Arg_i128, _Arg_u128, _Arg_float128, >> + _Arg_bf16, _Arg_f16, _Arg_f32, _Arg_f64, >> + _Arg_max_, >> + >> #ifdef _GLIBCXX_LONG_DOUBLE_ALT128_COMPAT >> - _Arg_next_value_, >> - _Arg_f128 = _Arg_ldbl, >> - _Arg_ibm128 = _Arg_next_value_, >> -#else >> - _Arg_f128, >> + _Arg_ibm128 = _Arg_ldbl, >> + _Arg_ieee128 = _Arg_float128, >> #endif >> - _Arg_max_ >> }; >> >> template<typename _Context> >> @@ -3831,6 +3861,12 @@ namespace __format >> double _M_dbl; >> #ifndef _GLIBCXX_LONG_DOUBLE_ALT128_COMPAT // No long double if it's >> ambiguous. >> long double _M_ldbl; >> +#else >> + __ibm128 _M_ibm128; >> + __ieee128 _M_ieee128; >> +#endif >> +#ifdef __SIZEOF_FLOAT128__ >> + __float128 _M_float128; >> #endif >> const _CharT* _M_str; >> basic_string_view<_CharT> _M_sv; >> @@ -3840,11 +3876,17 @@ namespace __format >> __int128 _M_i128; >> unsigned __int128 _M_u128; >> #endif >> -#ifdef _GLIBCXX_LONG_DOUBLE_ALT128_COMPAT >> - __ieee128 _M_f128; >> - __ibm128 _M_ibm128; >> -#elif _GLIBCXX_FORMAT_F128 == 2 >> - __float128_t _M_f128; >> +#ifdef __BFLT16_DIG__ >> + __bflt16_t _M_bf16; >> +#endif >> +#ifdef __FLT16_DIG__ >> + _Float16 _M_f16; >> +#endif >> +#ifdef __FLT32_DIG__ >> + _Float32 _M_f32; >> +#endif >> +#ifdef __FLT64_DIG__ >> + _Float64 _M_f64; >> #endif >> }; >> >> @@ -3882,10 +3924,14 @@ namespace __format >> else if constexpr (is_same_v<_Tp, long double>) >> return __u._M_ldbl; >> #else >> - else if constexpr (is_same_v<_Tp, __ieee128>) >> - return __u._M_f128; >> else if constexpr (is_same_v<_Tp, __ibm128>) >> return __u._M_ibm128; >> + else if constexpr (is_same_v<_Tp, __ieee128>) >> + return __u._M_ieee128; >> +#endif >> +#ifdef __SIZEOF_FLOAT128__ >> + else if constexpr (is_same_v<_Tp, __float128>) >> + return __u._M_float128; >> #endif >> else if constexpr (is_same_v<_Tp, const _CharT*>) >> return __u._M_str; >> @@ -3899,9 +3945,21 @@ namespace __format >> else if constexpr (is_same_v<_Tp, unsigned __int128>) >> return __u._M_u128; >> #endif >> -#if _GLIBCXX_FORMAT_F128 == 2 >> - else if constexpr (is_same_v<_Tp, __float128_t>) >> - return __u._M_f128; >> +#ifdef __BFLT16_DIG__ >> + else if constexpr (is_same_v<_Tp, __bflt16_t>) >> + return __u._M_bf16; >> +#endif >> +#ifdef __FLT16_DIG__ >> + else if constexpr (is_same_v<_Tp, _Float16>) >> + return __u._M_f16; >> +#endif >> +#ifdef __FLT32_DIG__ >> + else if constexpr (is_same_v<_Tp, _Float32>) >> + return __u._M_f32; >> +#endif >> +#ifdef __FLT64_DIG__ >> + else if constexpr (is_same_v<_Tp, _Float64>) >> + return __u._M_f64; >> #endif >> else if constexpr (derived_from<_Tp, _HandleBase>) >> return static_cast<_Tp&>(__u._M_handle); >> @@ -4080,36 +4138,25 @@ namespace __format >> else if constexpr (is_same_v<_Td, __ieee128>) >> return type_identity<__ieee128>(); >> #endif >> - >> -#if defined(__FLT16_DIG__) && defined(_GLIBCXX_FLOAT_IS_IEEE_BINARY32) >> - else if constexpr (is_same_v<_Td, _Float16>) >> - return type_identity<float>(); >> +#if defined(__SIZEOF_FLOAT128__) && _GLIBCXX_FORMAT_F128 >> + else if constexpr (is_same_v<_Td, __float128>) >> + return type_identity<__float128>(); >> #endif >> - >> -#if defined(__BFLT16_DIG__) && defined(_GLIBCXX_FLOAT_IS_IEEE_BINARY32) >> - else if constexpr (is_same_v<_Td, decltype(0.0bf16)>) >> - return type_identity<float>(); >> +#if defined(__STDCPP_BFLOAT16_T__) && >> defined(_GLIBCXX_FLOAT_IS_IEEE_BINARY32) >> + else if constexpr (is_same_v<_Td, __format::__bflt16_t>) >> + return type_identity<__format::__bflt16_t>(); >> +#endif >> +#if defined(__STDCPP_FLOAT16_T__) && >> defined(_GLIBCXX_FLOAT_IS_IEEE_BINARY32) >> + else if constexpr (is_same_v<_Td, _Float16>) >> + return type_identity<_Float16>(); >> #endif >> - >> #if defined(__FLT32_DIG__) && defined(_GLIBCXX_FLOAT_IS_IEEE_BINARY32) >> else if constexpr (is_same_v<_Td, _Float32>) >> - return type_identity<float>(); >> + return type_identity<_Float32>(); >> #endif >> - >> #if defined(__FLT64_DIG__) && defined(_GLIBCXX_DOUBLE_IS_IEEE_BINARY64) >> else if constexpr (is_same_v<_Td, _Float64>) >> - return type_identity<double>(); >> -#endif >> - >> -#if _GLIBCXX_FORMAT_F128 >> -# if __FLT128_DIG__ >> - else if constexpr (is_same_v<_Td, _Float128>) >> - return type_identity<__format::__float128_t>(); >> -# endif >> -# if __SIZEOF_FLOAT128__ >> - else if constexpr (is_same_v<_Td, __float128>) >> - return type_identity<__format::__float128_t>(); >> -# endif >> + return type_identity<_Float64>(); >> #endif >> else if constexpr (__is_specialization_of<_Td, >> basic_string_view> >> || __is_specialization_of<_Td, basic_string>) >> @@ -4165,7 +4212,27 @@ namespace __format >> else if constexpr (is_same_v<_Tp, __ibm128>) >> return _Arg_ibm128; >> else if constexpr (is_same_v<_Tp, __ieee128>) >> - return _Arg_f128; >> + return _Arg_ieee128; >> +#endif >> +#if defined(__SIZEOF_FLOAT128__) && _GLIBCXX_FORMAT_F128 >> + else if constexpr (is_same_v<_Tp, __float128>) >> + return _Arg_float128; >> +#endif >> +#if defined(__STDCPP_BFLOAT16_T__) && >> defined(_GLIBCXX_FLOAT_IS_IEEE_BINARY32) >> + else if constexpr (is_same_v<_Tp, __format::__bflt16_t>) >> + return _Arg_bf16; >> +#endif >> +#if defined(__STDCPP_FLOAT16_T__) && >> defined(_GLIBCXX_FLOAT_IS_IEEE_BINARY32) >> + else if constexpr (is_same_v<_Tp, _Float16>) >> + return _Arg_f16; >> +#endif >> +#if defined(__FLT32_DIG__) && defined(_GLIBCXX_FLOAT_IS_IEEE_BINARY32) >> + else if constexpr (is_same_v<_Tp, _Float32>) >> + return _Arg_f32; >> +#endif >> +#if defined(__FLT64_DIG__) && defined(_GLIBCXX_DOUBLE_IS_IEEE_BINARY64) >> + else if constexpr (is_same_v<_Tp, _Float64>) >> + return _Arg_f64; >> #endif >> else if constexpr (is_same_v<_Tp, const _CharT*>) >> return _Arg_str; >> @@ -4179,11 +4246,6 @@ namespace __format >> else if constexpr (is_same_v<_Tp, unsigned __int128>) >> return _Arg_u128; >> #endif >> - >> -#if _GLIBCXX_FORMAT_F128 == 2 >> - else if constexpr (is_same_v<_Tp, __format::__float128_t>) >> - return _Arg_f128; >> -#endif >> else if constexpr (is_same_v<_Tp, handle>) >> return _Arg_handle; >> } >> @@ -4256,13 +4318,33 @@ namespace __format >> #ifndef _GLIBCXX_LONG_DOUBLE_ALT128_COMPAT >> case _Arg_ldbl: >> return std::forward<_Visitor>(__vis)(_M_val._M_ldbl); >> +#if defined(__SIZEOF_FLOAT128__) && _GLIBCXX_FORMAT_F128 >> + case _Arg_float128: >> + return std::forward<_Visitor>(__vis)(_M_val._M_float128); >> +#endif >> #else >> - case _Arg_f128: >> - return std::forward<_Visitor>(__vis)(_M_val._M_f128); >> case _Arg_ibm128: >> return std::forward<_Visitor>(__vis)(_M_val._M_ibm128); >> + case _Arg_ieee128: >> + return std::forward<_Visitor>(__vis)(_M_val._M_ieee128); >> #endif >> +#if defined(__STDCPP_BFLOAT16_T__) && >> defined(_GLIBCXX_FLOAT_IS_IEEE_BINARY32) >> + case _Arg_bf16: >> + return std::forward<_Visitor>(__vis)(_M_val._M_bf16); >> +#endif >> +#if defined(__STDCPP_FLOAT16_T__) && >> defined(_GLIBCXX_FLOAT_IS_IEEE_BINARY32) >> + case _Arg_f16: >> + return std::forward<_Visitor>(__vis)(_M_val._M_f16); >> +#endif >> +#if defined(__FLT32_DIG__) && defined(_GLIBCXX_FLOAT_IS_IEEE_BINARY32) >> + case _Arg_f32: >> + return std::forward<_Visitor>(__vis)(_M_val._M_f32); >> #endif >> +#if defined(__FLT64_DIG__) && defined(_GLIBCXX_DOUBLE_IS_IEEE_BINARY64) >> + case _Arg_f64: >> + return std::forward<_Visitor>(__vis)(_M_val._M_f64); >> +#endif >> +#endif // __glibcxx_to_chars >> case _Arg_str: >> return std::forward<_Visitor>(__vis)(_M_val._M_str); >> case _Arg_sv: >> @@ -4280,14 +4362,7 @@ namespace __format >> case _Arg_u128: >> return std::forward<_Visitor>(__vis)(_M_val._M_u128); >> #endif >> - >> -#if _GLIBCXX_FORMAT_F128 == 2 >> - case _Arg_f128: >> - return std::forward<_Visitor>(__vis)(_M_val._M_f128); >> -#endif >> - >> default: >> - // _Arg_f16 etc. >> __builtin_unreachable(); >> } >> } >> diff --git a/libstdc++-v3/testsuite/std/format/arguments/args.cc >> b/libstdc++-v3/testsuite/std/format/arguments/args.cc >> index 4c50bc74319..60296753919 100644 >> --- a/libstdc++-v3/testsuite/std/format/arguments/args.cc >> +++ b/libstdc++-v3/testsuite/std/format/arguments/args.cc >> @@ -164,24 +164,6 @@ void test_visited_as_handle() >> #endif >> } >> >> -template<typename E, typename S> >> -void test_visited_as() >> -{ >> - auto v = static_cast<S>(1.0); >> - auto store = std::make_format_args(v); >> - std::format_args args = store; >> - >> - auto is_expected_val = [v](auto arg) { >> - if constexpr (std::is_same_v<decltype(arg), E>) >> - return arg == static_cast<E>(v); >> - return false; >> - }; >> - VERIFY( std::visit_format_arg(is_expected_val, args.get(0)) ); >> -#if __cpp_lib_format >= 202306L // C++26 adds >> std::basic_format_arg::visit >> - VERIFY( args.get(0).visit(is_expected_val) ); >> -#endif >> -} >> - >> template<typename T> >> concept can_format = std::is_default_constructible_v<std::formatter<T, >> char>>; >> >> @@ -195,30 +177,31 @@ int main() >> test_visited_as_handle<__int128>(); >> test_visited_as_handle<unsigned __int128>(); >> #endif >> -// TODO: This should be visited as handle. >> -#ifdef __STDCPP_FLOAT16_T__ >> - if constexpr (can_format<_Float16>) >> - test_visited_as<float, _Float16>(); >> -#endif >> -#ifdef __STDCPP_BFLOAT16_T__ >> +#ifdef __BFLT16_DIG__ >> if constexpr (can_format<__gnu_cxx::__bfloat16_t>) >> - test_visited_as<float, __gnu_cxx::__bfloat16_t>(); >> + test_visited_as_handle<__gnu_cxx::__bfloat16_t>(); >> +#endif >> +#ifdef __FLT16_DIG__ >> + if constexpr (can_format<_Float16>) >> + test_visited_as_handle<_Float16>(); >> #endif >> #ifdef __FLT32_DIG__ >> if constexpr (can_format<_Float32>) >> - test_visited_as<float, _Float32>(); >> + test_visited_as_handle<_Float32>(); >> #endif >> #ifdef __FLT64_DIG__ >> if constexpr (can_format<_Float64>) >> - test_visited_as<double, _Float64>(); >> + test_visited_as_handle<_Float64>(); >> #endif >> #ifdef __FLT128_DIG__ >> if constexpr (can_format<_Float128>) >> -# ifdef _GLIBCXX_LDOUBLE_IS_IEEE_BINARY128 >> - test_visited_as<long double, _Float128>(); >> -# else >> test_visited_as_handle<_Float128>(); >> -# endif >> +#endif >> +#ifdef __SIZEOF_FLOAT128__ >> + // __ieee128 is same type as __float128, and may be long double >> + if constexpr (!std::is_same_v<__float128, long double>) >> + if constexpr (can_format<__float128>) >> + test_visited_as_handle<__float128>(); >> #endif >> #ifdef _GLIBCXX_LONG_DOUBLE_ALT128_COMPAT >> if constexpr (!std::is_same_v<__ieee128, long double>) >> diff --git a/libstdc++-v3/testsuite/std/format/parse_ctx.cc >> b/libstdc++-v3/testsuite/std/format/parse_ctx.cc >> index b5dd7cdba78..adafc58c183 100644 >> --- a/libstdc++-v3/testsuite/std/format/parse_ctx.cc >> +++ b/libstdc++-v3/testsuite/std/format/parse_ctx.cc >> @@ -443,6 +443,8 @@ test_custom() >> } >> >> #if __cpp_lib_format >= 202305 >> +#include <stdfloat> >> + >> struct X { }; >> >> template<> >> @@ -458,13 +460,20 @@ struct std::formatter<X, char> >> if (spec == "int") >> { >> pc.check_dynamic_spec_integral(pc.next_arg_id()); >> - integer = true; >> + type = Type::integral; >> } >> else if (spec == "str") >> { >> pc.check_dynamic_spec_string(pc.next_arg_id()); >> - integer = false; >> + type = Type::string; >> + } >> + else if (spec == "float") >> + { >> + pc.check_dynamic_spec<float, double, long >> double>(pc.next_arg_id()); >> + type = Type::floating; >> } >> + else if (spec == "other") >> + type = Type::other; >> else >> throw std::format_error("invalid format-spec"); >> return pc.begin() + spec.size(); >> @@ -474,13 +483,44 @@ struct std::formatter<X, char> >> format(X, std::format_context& c) const >> { >> std::visit_format_arg([this]<typename T>(T) { // { dg-warning >> "deprecated" "" { target c++26 } } >> - if (is_integral_v<T> != this->integer) >> - throw std::format_error("invalid argument type"); >> + constexpr bool is_handle >> + = >> std::is_same_v<std::basic_format_arg<std::format_context>::handle, T>; >> + constexpr bool is_integral >> + = std::is_same_v<int, T> || std::is_same_v<unsigned int, T> >> + || is_same_v<long long, T> || std::is_same_v<unsigned long >> long, T>; >> + constexpr bool is_string >> + = std::is_same_v<const char*, T> || >> std::is_same_v<std::string_view, T>; >> + constexpr bool is_floating >> + = std::is_same_v<float, T> || std::is_same_v<double, T> >> + || std::is_same_v<long double, T>; >> + switch (this->type) >> + { >> + case Type::other: >> + if (is_handle) return; >> + break; >> + case Type::integral: >> + if (is_integral) return; >> + break; >> + case Type::string: >> + if (is_string) return; >> + break; >> + case Type::floating: >> + if (is_floating) return; >> + break; >> + } >> + throw std::format_error("invalid argument type"); >> }, c.arg(1)); >> return c.out(); >> } >> private: >> - bool integer = false; >> + enum class Type >> + { >> + other, >> + integral, >> + string, >> + floating, >> + }; >> + Type type = Type::other; >> }; >> #endif >> >> @@ -497,6 +537,28 @@ test_dynamic_type_check() >> >> (void) std::format("{:int}", X{}, 42L); >> (void) std::format("{:str}", X{}, "H2G2"); >> + (void) std::format("{:float}", X{}, 10.0); >> + >> +#ifdef __STDCPP_FLOAT16_T__ >> + if constexpr (std::formattable<std::bfloat16_t, char>) >> + (void) std::format("{:other}", X{}, 10.0bf16); >> +#endif >> +#ifdef __STDCPP_FLOAT16_T__ >> + if constexpr (std::formattable<std::float16_t, char>) >> + (void) std::format("{:other}", X{}, 10.0f16); >> +#endif >> +#ifdef __STDCPP_FLOAT32_T__ >> + if constexpr (std::formattable<std::float32_t, char>) >> + (void) std::format("{:other}", X{}, 10.0f32); >> +#endif >> +#ifdef __STDCPP_FLOAT64_T__ >> + if constexpr (std::formattable<std::float64_t, char>) >> + (void) std::format("{:other}", X{}, 10.0f64); >> +#endif >> +#ifdef __STDCPP_FLOAT128_T__ >> + if constexpr (std::formattable<std::float128_t, char>) >> + (void) std::format("{:other}", X{}, 10.0f128); >> +#endif >> #endif >> } >> >> -- >> 2.49.0 >> >>