diff --git a/stl/inc/charconv b/stl/inc/charconv index 75021352e64..85a83419bee 100644 --- a/stl/inc/charconv +++ b/stl/inc/charconv @@ -35,7 +35,7 @@ inline constexpr char _Charconv_digits[] = {'0', '1', '2', '3', '4', '5', '6', ' _STL_INTERNAL_STATIC_ASSERT(_STD size(_Charconv_digits) == 36); template -_NODISCARD to_chars_result _Integer_to_chars( +_NODISCARD _CONSTEXPR23 to_chars_result _Integer_to_chars( char* _First, char* const _Last, const _RawTy _Raw_value, const int _Base) noexcept { _Adl_verify_range(_First, _Last); _STL_ASSERT(_Base >= 2 && _Base <= 36, "invalid base in to_chars()"); @@ -152,52 +152,52 @@ _NODISCARD to_chars_result _Integer_to_chars( return {_Last, errc::value_too_large}; } - _CSTD memcpy(_First, _RNext, static_cast(_Digits_written)); + _Copy_n_unchecked4(_RNext, _Digits_written, _First); return {_First + _Digits_written, errc{}}; } -inline to_chars_result to_chars(char* const _First, char* const _Last, const char _Value, const int _Base = 10) noexcept -/* strengthened */ { +_CONSTEXPR23 to_chars_result to_chars( + char* const _First, char* const _Last, const char _Value, const int _Base = 10) noexcept /* strengthened */ { return _Integer_to_chars(_First, _Last, _Value, _Base); } -inline to_chars_result to_chars( +_CONSTEXPR23 to_chars_result to_chars( char* const _First, char* const _Last, const signed char _Value, const int _Base = 10) noexcept /* strengthened */ { return _Integer_to_chars(_First, _Last, _Value, _Base); } -inline to_chars_result to_chars(char* const _First, char* const _Last, const unsigned char _Value, +_CONSTEXPR23 to_chars_result to_chars(char* const _First, char* const _Last, const unsigned char _Value, const int _Base = 10) noexcept /* strengthened */ { return _Integer_to_chars(_First, _Last, _Value, _Base); } -inline to_chars_result to_chars( +_CONSTEXPR23 to_chars_result to_chars( char* const _First, char* const _Last, const short _Value, const int _Base = 10) noexcept /* strengthened */ { return _Integer_to_chars(_First, _Last, _Value, _Base); } -inline to_chars_result to_chars(char* const _First, char* const _Last, const unsigned short _Value, +_CONSTEXPR23 to_chars_result to_chars(char* const _First, char* const _Last, const unsigned short _Value, const int _Base = 10) noexcept /* strengthened */ { return _Integer_to_chars(_First, _Last, _Value, _Base); } -inline to_chars_result to_chars(char* const _First, char* const _Last, const int _Value, const int _Base = 10) noexcept -/* strengthened */ { +_CONSTEXPR23 to_chars_result to_chars( + char* const _First, char* const _Last, const int _Value, const int _Base = 10) noexcept /* strengthened */ { return _Integer_to_chars(_First, _Last, _Value, _Base); } -inline to_chars_result to_chars(char* const _First, char* const _Last, const unsigned int _Value, +_CONSTEXPR23 to_chars_result to_chars(char* const _First, char* const _Last, const unsigned int _Value, const int _Base = 10) noexcept /* strengthened */ { return _Integer_to_chars(_First, _Last, _Value, _Base); } -inline to_chars_result to_chars(char* const _First, char* const _Last, const long _Value, const int _Base = 10) noexcept -/* strengthened */ { +_CONSTEXPR23 to_chars_result to_chars( + char* const _First, char* const _Last, const long _Value, const int _Base = 10) noexcept /* strengthened */ { return _Integer_to_chars(_First, _Last, _Value, _Base); } -inline to_chars_result to_chars(char* const _First, char* const _Last, const unsigned long _Value, +_CONSTEXPR23 to_chars_result to_chars(char* const _First, char* const _Last, const unsigned long _Value, const int _Base = 10) noexcept /* strengthened */ { return _Integer_to_chars(_First, _Last, _Value, _Base); } -inline to_chars_result to_chars( +_CONSTEXPR23 to_chars_result to_chars( char* const _First, char* const _Last, const long long _Value, const int _Base = 10) noexcept /* strengthened */ { return _Integer_to_chars(_First, _Last, _Value, _Base); } -inline to_chars_result to_chars(char* const _First, char* const _Last, const unsigned long long _Value, +_CONSTEXPR23 to_chars_result to_chars(char* const _First, char* const _Last, const unsigned long long _Value, const int _Base = 10) noexcept /* strengthened */ { return _Integer_to_chars(_First, _Last, _Value, _Base); } @@ -213,27 +213,27 @@ struct from_chars_result { #endif // _HAS_CXX20 }; -_NODISCARD inline unsigned char _Digit_from_char(const char _Ch) noexcept { +// convert ['0', '9'] ['A', 'Z'] ['a', 'z'] to [0, 35], everything else to 255 +inline constexpr unsigned char _Digit_from_byte[] = {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 255, 255, 255, 255, 255, + 255, 255, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, + 255, 255, 255, 255, 255, 255, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, + 31, 32, 33, 34, 35, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}; +_STL_INTERNAL_STATIC_ASSERT(_STD size(_Digit_from_byte) == 256); + +_NODISCARD _CONSTEXPR23 unsigned char _Digit_from_char(const char _Ch) noexcept { // convert ['0', '9'] ['A', 'Z'] ['a', 'z'] to [0, 35], everything else to 255 - static constexpr unsigned char _Digit_from_byte[] = {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 255, 255, - 255, 255, 255, 255, 255, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, - 32, 33, 34, 35, 255, 255, 255, 255, 255, 255, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, - 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255}; - _STL_INTERNAL_STATIC_ASSERT(_STD size(_Digit_from_byte) == 256); - return _Digit_from_byte[static_cast(_Ch)]; } template -_NODISCARD from_chars_result _Integer_from_chars( +_NODISCARD _CONSTEXPR23 from_chars_result _Integer_from_chars( const char* const _First, const char* const _Last, _RawTy& _Raw_value, const int _Base) noexcept { _Adl_verify_range(_First, _Last); _STL_ASSERT(_Base >= 2 && _Base <= 36, "invalid base in from_chars()"); @@ -309,47 +309,47 @@ _NODISCARD from_chars_result _Integer_from_chars( return {_Next, errc{}}; } -inline from_chars_result from_chars( +_CONSTEXPR23 from_chars_result from_chars( const char* const _First, const char* const _Last, char& _Value, const int _Base = 10) noexcept /* strengthened */ { return _Integer_from_chars(_First, _Last, _Value, _Base); } -inline from_chars_result from_chars(const char* const _First, const char* const _Last, signed char& _Value, +_CONSTEXPR23 from_chars_result from_chars(const char* const _First, const char* const _Last, signed char& _Value, const int _Base = 10) noexcept /* strengthened */ { return _Integer_from_chars(_First, _Last, _Value, _Base); } -inline from_chars_result from_chars(const char* const _First, const char* const _Last, unsigned char& _Value, +_CONSTEXPR23 from_chars_result from_chars(const char* const _First, const char* const _Last, unsigned char& _Value, const int _Base = 10) noexcept /* strengthened */ { return _Integer_from_chars(_First, _Last, _Value, _Base); } -inline from_chars_result from_chars(const char* const _First, const char* const _Last, short& _Value, +_CONSTEXPR23 from_chars_result from_chars(const char* const _First, const char* const _Last, short& _Value, const int _Base = 10) noexcept /* strengthened */ { return _Integer_from_chars(_First, _Last, _Value, _Base); } -inline from_chars_result from_chars(const char* const _First, const char* const _Last, unsigned short& _Value, +_CONSTEXPR23 from_chars_result from_chars(const char* const _First, const char* const _Last, unsigned short& _Value, const int _Base = 10) noexcept /* strengthened */ { return _Integer_from_chars(_First, _Last, _Value, _Base); } -inline from_chars_result from_chars( +_CONSTEXPR23 from_chars_result from_chars( const char* const _First, const char* const _Last, int& _Value, const int _Base = 10) noexcept /* strengthened */ { return _Integer_from_chars(_First, _Last, _Value, _Base); } -inline from_chars_result from_chars(const char* const _First, const char* const _Last, unsigned int& _Value, +_CONSTEXPR23 from_chars_result from_chars(const char* const _First, const char* const _Last, unsigned int& _Value, const int _Base = 10) noexcept /* strengthened */ { return _Integer_from_chars(_First, _Last, _Value, _Base); } -inline from_chars_result from_chars( +_CONSTEXPR23 from_chars_result from_chars( const char* const _First, const char* const _Last, long& _Value, const int _Base = 10) noexcept /* strengthened */ { return _Integer_from_chars(_First, _Last, _Value, _Base); } -inline from_chars_result from_chars(const char* const _First, const char* const _Last, unsigned long& _Value, +_CONSTEXPR23 from_chars_result from_chars(const char* const _First, const char* const _Last, unsigned long& _Value, const int _Base = 10) noexcept /* strengthened */ { return _Integer_from_chars(_First, _Last, _Value, _Base); } -inline from_chars_result from_chars(const char* const _First, const char* const _Last, long long& _Value, +_CONSTEXPR23 from_chars_result from_chars(const char* const _First, const char* const _Last, long long& _Value, const int _Base = 10) noexcept /* strengthened */ { return _Integer_from_chars(_First, _Last, _Value, _Base); } -inline from_chars_result from_chars(const char* const _First, const char* const _Last, unsigned long long& _Value, +_CONSTEXPR23 from_chars_result from_chars(const char* const _First, const char* const _Last, unsigned long long& _Value, const int _Base = 10) noexcept /* strengthened */ { return _Integer_from_chars(_First, _Last, _Value, _Base); } diff --git a/stl/inc/yvals_core.h b/stl/inc/yvals_core.h index 68ad8fa7c49..25e51c30bed 100644 --- a/stl/inc/yvals_core.h +++ b/stl/inc/yvals_core.h @@ -321,6 +321,7 @@ // P2166R1 Prohibiting basic_string And basic_string_view Construction From nullptr // P2186R2 Removing Garbage Collection Support // P2273R3 constexpr unique_ptr +// P2291R3 constexpr Integral // P2302R4 ranges::contains, ranges::contains_subrange // P2321R2 zip // (changes to pair, tuple, and vector::reference only) @@ -1496,6 +1497,7 @@ _EMIT_STL_ERROR(STL1004, "C++98 unexpected() is incompatible with C++23 unexpect #define __cpp_lib_bind_back 202202L #define __cpp_lib_byteswap 202110L #define __cpp_lib_constexpr_bitset 202207L +#define __cpp_lib_constexpr_charconv 202207L #define __cpp_lib_constexpr_typeinfo 202106L #ifdef __cpp_lib_concepts diff --git a/tests/std/tests/P0067R5_charconv/test.cpp b/tests/std/tests/P0067R5_charconv/test.cpp index 5b0da2c4d24..d2d1ba0f378 100644 --- a/tests/std/tests/P0067R5_charconv/test.cpp +++ b/tests/std/tests/P0067R5_charconv/test.cpp @@ -197,9 +197,11 @@ void test_common_to_chars( } template -void test_integer_to_chars(const T value, const optional opt_base, const string_view correct) { - - test_common_to_chars(value, opt_base, nullopt, correct); +_CONSTEXPR23 void test_integer_to_chars(const T value, const optional opt_base, const string_view correct) { + // This reaches the constexpr step limit too quickly + if (!_Is_constant_evaluated()) { + test_common_to_chars(value, opt_base, nullopt, correct); + } { // Also test successful from_chars() scenarios. const char* const correct_first = correct.data(); @@ -387,7 +389,7 @@ constexpr pair> output_negative[] = { }; template -void test_integer_to_chars() { +_CONSTEXPR23 bool test_integer_to_chars() { for (int base = 2; base <= 36; ++base) { test_integer_to_chars(static_cast(0), base, "0"); test_integer_to_chars(static_cast(1), base, "1"); @@ -413,12 +415,14 @@ void test_integer_to_chars() { } test_integer_to_chars(static_cast(42), nullopt, "42"); + + return true; } enum class TestFromCharsMode { Normal, SignalingNaN }; template -void test_from_chars(const string_view input, const BaseOrFmt base_or_fmt, const size_t correct_idx, +_CONSTEXPR23 void test_from_chars(const string_view input, const BaseOrFmt base_or_fmt, const size_t correct_idx, const errc correct_ec, const optional opt_correct = nullopt, const TestFromCharsMode mode = TestFromCharsMode::Normal) { @@ -460,7 +464,17 @@ constexpr errc inv_arg = errc::invalid_argument; constexpr errc out_ran = errc::result_out_of_range; template -void test_integer_from_chars() { +_CONSTEXPR23 bool test_integer_from_chars() { + const string hundred_zeroes(100, '0'); + const string hundred_zeroes_and_11 = hundred_zeroes + "11"s; + const string minus_hundred_zeroes = "-"s + hundred_zeroes; + const string minus_hundred_zeroes_and_11 = "-"s + hundred_zeroes_and_11; + + const string hundred_ones(100, '1'); + const string hundred_ones_and_atatat = hundred_ones + "@@@"s; + const string minus_hundred_ones = "-"s + hundred_ones; + const string minus_hundred_ones_and_atatat = "-"s + hundred_ones_and_atatat; + for (int base = 2; base <= 36; ++base) { test_from_chars("", base, 0, inv_arg); // no characters test_from_chars("@1", base, 0, inv_arg); // '@' is bogus @@ -497,14 +511,14 @@ void test_integer_from_chars() { } // Test leading zeroes. - test_from_chars(string(100, '0'), base, 100, errc{}, static_cast(0)); - test_from_chars(string(100, '0') + "11"s, base, 102, errc{}, static_cast(base + 1)); + test_from_chars(hundred_zeroes, base, 100, errc{}, static_cast(0)); + test_from_chars(hundred_zeroes_and_11, base, 102, errc{}, static_cast(base + 1)); // Test negative zero and negative leading zeroes. if constexpr (is_signed_v) { test_from_chars("-0", base, 2, errc{}, static_cast(0)); - test_from_chars("-"s + string(100, '0'), base, 101, errc{}, static_cast(0)); - test_from_chars("-"s + string(100, '0') + "11"s, base, 103, errc{}, static_cast(-base - 1)); + test_from_chars(minus_hundred_zeroes, base, 101, errc{}, static_cast(0)); + test_from_chars(minus_hundred_zeroes_and_11, base, 103, errc{}, static_cast(-base - 1)); } // N4713 23.20.3 [charconv.from.chars]/1 "The member ptr of the return value points to the @@ -513,12 +527,12 @@ void test_integer_from_chars() { test_from_chars("11@@@", base, 2, errc{}, static_cast(base + 1)); // When overflowing, we need to keep consuming valid digits, in order to return ptr correctly. - test_from_chars(string(100, '1'), base, 100, out_ran); - test_from_chars(string(100, '1') + "@@@"s, base, 100, out_ran); + test_from_chars(hundred_ones, base, 100, out_ran); + test_from_chars(hundred_ones_and_atatat, base, 100, out_ran); if constexpr (is_signed_v) { - test_from_chars("-"s + string(100, '1'), base, 101, out_ran); - test_from_chars("-"s + string(100, '1') + "@@@"s, base, 101, out_ran); + test_from_chars(minus_hundred_ones, base, 101, out_ran); + test_from_chars(minus_hundred_ones_and_atatat, base, 101, out_ran); } } @@ -538,27 +552,22 @@ void test_integer_from_chars() { test_from_chars("-0x1729", 16, 2, errc{}, static_cast(0)); // reads "-0", stops at 'x' test_from_chars("-0X1729", 16, 2, errc{}, static_cast(0)); // reads "-0", stops at 'X' } + + return true; } template void test_integer() { test_integer_to_chars(); test_integer_from_chars(); -} -void all_integer_tests() { - test_integer(); - test_integer(); - test_integer(); - test_integer(); - test_integer(); - test_integer(); - test_integer(); - test_integer(); - test_integer(); - test_integer(); - test_integer(); +#if _HAS_CXX23 + static_assert(test_integer_to_chars()); + static_assert(test_integer_from_chars()); +#endif // _HAS_CXX23 +} +_CONSTEXPR23 bool test_integer_overflow_scenarios() { // Test overflow scenarios. test_from_chars("4294967289", 10, 10, errc{}, 4294967289U); // not risky test_from_chars("4294967294", 10, 10, errc{}, 4294967294U); // risky with good digit @@ -577,6 +586,27 @@ void all_integer_tests() { test_from_chars("-2147483648", 10, 11, errc{}, -2147483647 - 1); // risky with max digit test_from_chars("-2147483649", 10, 11, out_ran); // risky with bad digit test_from_chars("-2147483650", 10, 11, out_ran); // beyond risky + + return true; +} + +void all_integer_tests() { + test_integer(); + test_integer(); + test_integer(); + test_integer(); + test_integer(); + test_integer(); + test_integer(); + test_integer(); + test_integer(); + test_integer(); + test_integer(); + + test_integer_overflow_scenarios(); +#if _HAS_CXX23 + static_assert(test_integer_overflow_scenarios()); +#endif // _HAS_CXX23 } void assert_message_bits(const bool b, const char* const msg, const uint32_t bits) { diff --git a/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp b/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp index 8d20d002e40..d47fa0d042c 100644 --- a/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp +++ b/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp @@ -500,6 +500,20 @@ STATIC_ASSERT(__cpp_lib_constexpr_bitset == 202207L); #endif #endif +#if _HAS_CXX23 +#ifndef __cpp_lib_constexpr_charconv +#error __cpp_lib_constexpr_charconv is not defined +#elif __cpp_lib_constexpr_charconv != 202207L +#error __cpp_lib_constexpr_charconv is not 202207L +#else +STATIC_ASSERT(__cpp_lib_constexpr_charconv == 202207L); +#endif +#else +#ifdef __cpp_lib_constexpr_charconv +#error __cpp_lib_constexpr_charconv is defined +#endif +#endif + #if _HAS_CXX20 #ifndef __cpp_lib_constexpr_complex #error __cpp_lib_constexpr_complex is not defined