[libc++][format] Implements 128-bit support.

With to_chars supporting 128-bit it's possible to support the full
128-bit range in format. This only removes the previous restrictions
and updates the tests to validate proper support.

Depends on D128929.

Reviewed By: #libc, ldionne

Differential Revision: https://reviews.llvm.org/D129007
This commit is contained in:
Mark de Wever 2022-07-01 19:35:38 +02:00
parent d955185b94
commit 0857a02ef0
4 changed files with 59 additions and 83 deletions

View File

@ -13,23 +13,18 @@
#include <__availability>
#include <__concepts/arithmetic.h>
#include <__config>
#include <__format/format_error.h> // TODO FMT Remove after adding 128-bit support
#include <__format/format_fwd.h>
#include <__format/format_parse_context.h>
#include <__format/formatter.h>
#include <__format/formatter_integral.h>
#include <__format/formatter_output.h>
#include <__format/parser_std_format_spec.h>
#include <limits> // TODO FMT Remove after adding 128-bit support
#include <type_traits>
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
# pragma GCC system_header
#endif
_LIBCPP_PUSH_MACROS // TODO FMT Remove after adding 128-bit support
#include <__undef_macros>
_LIBCPP_BEGIN_NAMESPACE_STD
#if _LIBCPP_STD_VER > 17
@ -79,18 +74,7 @@ struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT formatter<long long, _Ch
# ifndef _LIBCPP_HAS_NO_INT128
template <__formatter::__char_type _CharT>
struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT formatter<__int128_t, _CharT>
: public __formatter_integer<_CharT> {
using _Base = __formatter_integer<_CharT>;
_LIBCPP_HIDE_FROM_ABI auto format(__int128_t __value, auto& __ctx) const -> decltype(__ctx.out()) {
// TODO FMT Implement full 128 bit support.
using _To = long long;
if (__value < numeric_limits<_To>::min() || __value > numeric_limits<_To>::max())
std::__throw_format_error("128-bit value is outside of implemented range");
return _Base::format(static_cast<_To>(__value), __ctx);
}
};
: public __formatter_integer<_CharT> {};
# endif
// Unsigned integral types.
@ -112,24 +96,11 @@ struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT formatter<unsigned long
# ifndef _LIBCPP_HAS_NO_INT128
template <__formatter::__char_type _CharT>
struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT formatter<__uint128_t, _CharT>
: public __formatter_integer<_CharT> {
using _Base = __formatter_integer<_CharT>;
_LIBCPP_HIDE_FROM_ABI auto format(__uint128_t __value, auto& __ctx) const -> decltype(__ctx.out()) {
// TODO FMT Implement full 128 bit support.
using _To = unsigned long long;
if (__value < numeric_limits<_To>::min() || __value > numeric_limits<_To>::max())
std::__throw_format_error("128-bit value is outside of implemented range");
return _Base::format(static_cast<_To>(__value), __ctx);
}
};
: public __formatter_integer<_CharT> {};
# endif
#endif //_LIBCPP_STD_VER > 17
_LIBCPP_END_NAMESPACE_STD
_LIBCPP_POP_MACROS
#endif // _LIBCPP___FORMAT_FORMATTER_INTEGER_H

View File

@ -86,18 +86,17 @@ void test_signed_integral_type() {
test_termination_condition(STR("2147483647"), STR("}"), A(2147483647));
}
if (sizeof(A) > 4) {
// -9223372036854775808 can't be used directly, it gives the following
// diagnostic:
// integer literal is too large to be represented in a signed integer type,
// interpreting as unsigned [-Werror,-Wimplicitly-unsigned-literal]
test_termination_condition(STR("-9223372036854775808"), STR("}"),
A(-9223372036854775807 - 1));
test_termination_condition(STR("9223372036854775807"), STR("}"),
A(9223372036854775807));
test_termination_condition(STR("-9223372036854775808"), STR("}"), A(std::numeric_limits<int64_t>::min()));
test_termination_condition(STR("9223372036854775807"), STR("}"), A(std::numeric_limits<int64_t>::max()));
}
// TODO FMT Implement the __int128_t minimum and maximum once the formatter
// can handle these values.
#ifndef TEST_HAS_NO_INT128
if (sizeof(A) > 8) {
test_termination_condition(
STR("-170141183460469231731687303715884105728"), STR("}"), A(std::numeric_limits<__int128_t>::min()));
test_termination_condition(
STR("170141183460469231731687303715884105727"), STR("}"), A(std::numeric_limits<__int128_t>::max()));
}
#endif
}
template <class CharT>

View File

@ -83,9 +83,11 @@ void test_unsigned_integral_type() {
if (sizeof(A) > 4)
test_termination_condition(STR("8446744073709551615"), STR("}"),
A(8446744073709551615));
// TODO FMT Implement the __uint128_t maximum once the formatter can handle
// these values.
#ifndef TEST_HAS_NO_INT128
if (sizeof(A) > 8)
test_termination_condition(
STR("340282366920938463463374607431768211455"), STR("}"), A(std::numeric_limits<__uint128_t>::max()));
#endif
}
template <class CharT>

View File

@ -702,19 +702,16 @@ void format_test_integer_as_char(TestFunction check, ExceptionTest check_excepti
check_exception("The format-spec type has a type not supported for an integer argument", fmt, I(42));
// *** Validate range ***
// TODO FMT Update test after adding 128-bit support.
if constexpr (sizeof(I) <= sizeof(long long)) {
// The code has some duplications to keep the if statement readable.
if constexpr (std::signed_integral<CharT>) {
if constexpr (std::signed_integral<I> && sizeof(I) > sizeof(CharT)) {
check_exception("Integral value outside the range of the char type", SV("{:c}"), std::numeric_limits<I>::min());
check_exception("Integral value outside the range of the char type", SV("{:c}"), std::numeric_limits<I>::max());
} else if constexpr (std::unsigned_integral<I> && sizeof(I) >= sizeof(CharT)) {
check_exception("Integral value outside the range of the char type", SV("{:c}"), std::numeric_limits<I>::max());
}
} else if constexpr (sizeof(I) > sizeof(CharT)) {
// The code has some duplications to keep the if statement readable.
if constexpr (std::signed_integral<CharT>) {
if constexpr (std::signed_integral<I> && sizeof(I) > sizeof(CharT)) {
check_exception("Integral value outside the range of the char type", SV("{:c}"), std::numeric_limits<I>::min());
check_exception("Integral value outside the range of the char type", SV("{:c}"), std::numeric_limits<I>::max());
} else if constexpr (std::unsigned_integral<I> && sizeof(I) >= sizeof(CharT)) {
check_exception("Integral value outside the range of the char type", SV("{:c}"), std::numeric_limits<I>::max());
}
} else if constexpr (sizeof(I) > sizeof(CharT)) {
check_exception("Integral value outside the range of the char type", SV("{:c}"), std::numeric_limits<I>::max());
}
}
@ -756,6 +753,18 @@ void format_test_signed_integer(TestFunction check, ExceptionTest check_exceptio
check.template operator()<"{:#}">(SV("-9223372036854775808"), std::numeric_limits<int64_t>::min());
check.template operator()<"{:#x}">(SV("-0x8000000000000000"), std::numeric_limits<int64_t>::min());
#ifndef TEST_HAS_NO_INT128
check.template operator()<"{:#b}">(
SV("-0b1000000000000000000000000000000000000000000000000000000000000000"
"0000000000000000000000000000000000000000000000000000000000000000"),
std::numeric_limits<__int128_t>::min());
check.template
operator()<"{:#o}">(SV("-02000000000000000000000000000000000000000000"), std::numeric_limits<__int128_t>::min());
check.template
operator()<"{:#}">(SV("-170141183460469231731687303715884105728"), std::numeric_limits<__int128_t>::min());
check.template operator()<"{:#x}">(SV("-0x80000000000000000000000000000000"), std::numeric_limits<__int128_t>::min());
#endif
check.template operator()<"{:#b}">(SV("0b1111111"), std::numeric_limits<int8_t>::max());
check.template operator()<"{:#o}">(SV("0177"), std::numeric_limits<int8_t>::max());
check.template operator()<"{:#}">(SV("127"), std::numeric_limits<int8_t>::max());
@ -777,7 +786,17 @@ void format_test_signed_integer(TestFunction check, ExceptionTest check_exceptio
check.template operator()<"{:#}">(SV("9223372036854775807"), std::numeric_limits<int64_t>::max());
check.template operator()<"{:#x}">(SV("0x7fffffffffffffff"), std::numeric_limits<int64_t>::max());
// TODO FMT Add __int128_t test after implementing full range.
#ifndef TEST_HAS_NO_INT128
check.template operator()<"{:#b}">(
SV("0b111111111111111111111111111111111111111111111111111111111111111"
"1111111111111111111111111111111111111111111111111111111111111111"),
std::numeric_limits<__int128_t>::max());
check.template
operator()<"{:#o}">(SV("01777777777777777777777777777777777777777777"), std::numeric_limits<__int128_t>::max());
check.template
operator()<"{:#}">(SV("170141183460469231731687303715884105727"), std::numeric_limits<__int128_t>::max());
check.template operator()<"{:#x}">(SV("0x7fffffffffffffffffffffffffffffff"), std::numeric_limits<__int128_t>::max());
#endif
}
template <class CharT, class TestFunction, class ExceptionTest>
@ -812,7 +831,17 @@ void format_test_unsigned_integer(TestFunction check, ExceptionTest check_except
check.template operator()<"{:#}">(SV("18446744073709551615"), std::numeric_limits<uint64_t>::max());
check.template operator()<"{:#x}">(SV("0xffffffffffffffff"), std::numeric_limits<uint64_t>::max());
// TODO FMT Add __uint128_t test after implementing full range.
#ifndef TEST_HAS_NO_INT128
check.template operator()<"{:#b}">(
SV("0b1111111111111111111111111111111111111111111111111111111111111111"
"1111111111111111111111111111111111111111111111111111111111111111"),
std::numeric_limits<__uint128_t>::max());
check.template
operator()<"{:#o}">(SV("03777777777777777777777777777777777777777777"), std::numeric_limits<__uint128_t>::max());
check.template
operator()<"{:#}">(SV("340282366920938463463374607431768211455"), std::numeric_limits<__uint128_t>::max());
check.template operator()<"{:#x}">(SV("0xffffffffffffffffffffffffffffffff"), std::numeric_limits<__uint128_t>::max());
#endif
}
template <class CharT, class TestFunction, class ExceptionTest>
@ -2588,21 +2617,6 @@ void format_tests(TestFunction check, ExceptionTest check_exception) {
check.template operator()<"hello {}">(SV("hello 42"), static_cast<long long>(42));
#ifndef TEST_HAS_NO_INT128
check.template operator()<"hello {}">(SV("hello 42"), static_cast<__int128_t>(42));
{
// Note 128-bit support is only partly implemented test the range
// conditions here.
static constexpr auto fmt = string_literal("{}");
std::basic_string<CharT> min = std::format(fmt.template sv<CharT>(), std::numeric_limits<long long>::min());
check.template operator()<"{}">(std::basic_string_view<CharT>(min),
static_cast<__int128_t>(std::numeric_limits<long long>::min()));
std::basic_string<CharT> max = std::format(fmt.template sv<CharT>(), std::numeric_limits<long long>::max());
check.template operator()<"{}">(std::basic_string_view<CharT>(max),
static_cast<__int128_t>(std::numeric_limits<long long>::max()));
check_exception("128-bit value is outside of implemented range", SV("{}"),
static_cast<__int128_t>(std::numeric_limits<long long>::min()) - 1);
check_exception("128-bit value is outside of implemented range", SV("{}"),
static_cast<__int128_t>(std::numeric_limits<long long>::max()) + 1);
}
#endif
format_test_signed_integer<CharT>(check, check_exception);
@ -2614,16 +2628,6 @@ void format_tests(TestFunction check, ExceptionTest check_exception) {
check.template operator()<"hello {}">(SV("hello 42"), static_cast<unsigned long long>(42));
#ifndef TEST_HAS_NO_INT128
check.template operator()<"hello {}">(SV("hello 42"), static_cast<__uint128_t>(42));
{
// Note 128-bit support is only partly implemented test the range
// conditions here.
static constexpr auto fmt = string_literal("{}");
std::basic_string<CharT> max = std::format(fmt.template sv<CharT>(), std::numeric_limits<unsigned long long>::max());
check.template operator()<"{}">(std::basic_string_view<CharT>(max),
static_cast<__uint128_t>(std::numeric_limits<unsigned long long>::max()));
check_exception("128-bit value is outside of implemented range", SV("{}"),
static_cast<__uint128_t>(std::numeric_limits<unsigned long long>::max()) + 1);
}
#endif
format_test_unsigned_integer<CharT>(check, check_exception);