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
>>
>>

Reply via email to