On Tue, May 13, 2025 at 10:04 PM Jonathan Wakely <jwak...@redhat.com> wrote:
> On 30/04/25 13:23 +0200, Tomasz Kamiński 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 > > This sentence seems to be missing one or more words? > Was it supposed to be "as specialization of formatters for floating > point types formats them by ..." ? > > >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 > > The internal name for the type is _Float128 not _Float128_t (which > reinforces that it would be useful to clearly distinguish __float128_t > and __float128 :-) > > >formatted via handle. > > > >The __format::_float128_t type is renamed to __format::__flt128_t, to > mitigate > > This should be a double underscore for __float128_t not _float128_t. > > >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. > > s/_Float128_t/_Float128/ here as well > > >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. > > s/_format/__format/ > > >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. > > s/_flt128_t/__flt128_t/ > > > (std::formatter<_Float128, _CharT>): Define always if there is > formattable > > This line looks too long for the Changelog format, which should be > kept below 80 columns (and ideally below 75). Please break the line > before "formattable" and check the other long lines in this ChangeLog > part. > > > 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, > > s/Hadndle/Handle/ > > > _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? > > The code and tests look good, just a few spelling and grammar > clarifications needed, as noted above and two more below ... > > > 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. > > I'm not sure what "before formatting code" refers to here. > > Maybe "should be converted to before being processed by the formatting > code"? > > Or just "should be converted to before formatting"? > We do this conversion inside the formatter for _Float128, so we are already in the middle of formatting. I will go for "should be converted to before passing to __formatter_fp::format". > > // 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, > > Should there be "if" before _GLIBCXX_FORMAT_F128=1 ? > > I think it would be slightly better to say "when" rather than "if" > here, i.e. > > // The __float128, _Float128 will be formatted by converting them to: > // __ieee128 (same as __float128) when _GLIBCXX_FORMAT_F128=1, > // long double when _GLIBCXX_FORMAT_F128=2, > // _Float128 when _GLIBCXX_FORMAT_F128=3. > > >+ // 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. > > s/_flt128_t/__flt128_t/ > > >+ // 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 > > > > > >