On Wed, Apr 30, 2025 at 3:06 PM Jonathan Wakely <jwak...@redhat.com> wrote:
> On Wed, 30 Apr 2025 at 13:54, 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}. > > > > The -mabi=ibmlongdouble and -mabi=ieeelongdouble passed. > > The -mno-float128 seem to be not handled on trunk due use of __float128 > instead of __ieee128 here: > > > https://gcc.gnu.org/git/?p=gcc.git;a=blob;f=libstdc%2B%2B-v3/include/std/charconv;h=dda49ce72d0b53c7a6e86c2e3fb510d0218fd5a6;hb=HEAD#l878 > > Nobody should be using that, it's ABI-changing and so you'd need to > rebuild the entire toolchain and libc. > I don't think it does. The only thing it seems to do, is to remove __float128 name. The __ieee128 is still available, and 128bit IEEE floats are supported. > > > Interestingly from chars use __ieee128: > > > https://gcc.gnu.org/git/?p=gcc.git;a=blob;f=libstdc%2B%2B-v3/include/std/charconv;h=dda49ce72d0b53c7a6e86c2e3fb510d0218fd5a6;hb=HEAD#l742 > >> > >> 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> > >> + { > >> + 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 > >> > >