aboutsummaryrefslogtreecommitdiff
path: root/libstdc++-v3/testsuite/std
diff options
context:
space:
mode:
authorTomasz Kamiński <tkaminsk@redhat.com>2025-04-30 10:37:48 +0200
committerTomasz Kamiński <tkaminsk@redhat.com>2025-05-14 12:50:50 +0200
commit9c9a7316adb99693e237164908893a78b86ba000 (patch)
tree8fc7cabc00806ceb1fad1ef487a865be2b75b75f /libstdc++-v3/testsuite/std
parentfb4952dddb5860acd8460568a51981b9261362f5 (diff)
downloadgcc-9c9a7316adb99693e237164908893a78b86ba000.zip
gcc-9c9a7316adb99693e237164908893a78b86ba000.tar.gz
gcc-9c9a7316adb99693e237164908893a78b86ba000.tar.bz2
libstdc++: Preserve the argument type in basic_format_args [PR119246]
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 fixed width floating-point types 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, if present, is same type as __ieee128. 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) 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 __format::__flt128_t is always provided, when type is available. It is still correct when __format::__flt128_t is _Float128. 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): Handle _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. Reviewed-by: Jonathan Wakely <jwakely@redhat.com> Signed-off-by: Tomasz Kamiński <tkaminsk@redhat.com>
Diffstat (limited to 'libstdc++-v3/testsuite/std')
-rw-r--r--libstdc++-v3/testsuite/std/format/arguments/args.cc45
-rw-r--r--libstdc++-v3/testsuite/std/format/parse_ctx.cc72
2 files changed, 81 insertions, 36 deletions
diff --git a/libstdc++-v3/testsuite/std/format/arguments/args.cc b/libstdc++-v3/testsuite/std/format/arguments/args.cc
index 4c50bc7..6029675 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 b5dd7cd..adafc58 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
}