diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst --- a/libcxx/docs/FeatureTestMacroTable.rst +++ b/libcxx/docs/FeatureTestMacroTable.rst @@ -310,6 +310,8 @@ ------------------------------------------------- ----------------- ``__cpp_lib_constexpr_bitset`` ``202207L`` ------------------------------------------------- ----------------- + ``__cpp_lib_constexpr_charconv`` ``202207L`` + ------------------------------------------------- ----------------- ``__cpp_lib_constexpr_cmath`` *unimplemented* ------------------------------------------------- ----------------- ``__cpp_lib_constexpr_memory`` ``202202L`` diff --git a/libcxx/docs/ReleaseNotes.rst b/libcxx/docs/ReleaseNotes.rst --- a/libcxx/docs/ReleaseNotes.rst +++ b/libcxx/docs/ReleaseNotes.rst @@ -42,6 +42,8 @@ - P2445R1 - ``std::forward_like`` - P2273R3 - Making ``std::unique_ptr`` constexpr - P0591R4 - Utility functions to implement uses-allocator construction +- P2291R3 - Add Constexpr Modifiers to Functions ``to_chars`` and + ``from_chars`` for Integral Types in ```` Header Improvements and New Features ----------------------------- diff --git a/libcxx/docs/Status/Cxx2bPapers.csv b/libcxx/docs/Status/Cxx2bPapers.csv --- a/libcxx/docs/Status/Cxx2bPapers.csv +++ b/libcxx/docs/Status/Cxx2bPapers.csv @@ -63,7 +63,7 @@ "`P2165R4 `__","LWG","Compatibility between ``tuple``, ``pair`` and ``tuple-like`` objects","July 2022","","" "`P2278R4 `__","LWG","``cbegin`` should always return a constant iterator","July 2022","","" "`P2286R8 `__","LWG","Formatting Ranges","July 2022","","" -"`P2291R3 `__","LWG","Add Constexpr Modifiers to Functions ``to_chars`` and ``from_chars`` for Integral Types in ```` Header","July 2022","|In Progress|","" +"`P2291R3 `__","LWG","Add Constexpr Modifiers to Functions ``to_chars`` and ``from_chars`` for Integral Types in ```` Header","July 2022","|Complete|","16.0" "`P2302R4 `__","LWG","``std::ranges::contains``","July 2022","","" "`P2322R6 `__","LWG","``ranges::fold``","July 2022","","" "`P2374R4 `__","LWG","``views::cartesian_product``","July 2022","","" diff --git a/libcxx/include/__charconv/tables.h b/libcxx/include/__charconv/tables.h --- a/libcxx/include/__charconv/tables.h +++ b/libcxx/include/__charconv/tables.h @@ -23,34 +23,12 @@ namespace __itoa { -/// Contains the charconv helper tables. -/// -/// In C++17 these could be inline constexpr variable, but libc++ supports -/// charconv for integrals in C++11 mode. -template -struct __table { - static const char __base_2_lut[64]; - static const char __base_8_lut[128]; - static const char __base_16_lut[512]; - - static const uint32_t __pow10_32[10]; - static const uint64_t __pow10_64[20]; -# ifndef _LIBCPP_HAS_NO_INT128 - // TODO FMT Reduce the number of entries in this table. - static const __uint128_t __pow10_128[40]; - static const int __pow10_128_offset = 0; -# endif - static const char __digits_base_10[200]; -}; - -template -const char __table<_Tp>::__base_2_lut[64] = { +inline constexpr char __base_2_lut[64] = { '0', '0', '0', '0', '0', '0', '0', '1', '0', '0', '1', '0', '0', '0', '1', '1', '0', '1', '0', '0', '0', '1', '0', '1', '0', '1', '1', '0', '0', '1', '1', '1', '1', '0', '0', '0', '1', '0', '0', '1', '1', '0', '1', '0', '1', '0', '1', '1', '1', '1', '0', '0', '1', '1', '0', '1', '1', '1', '1', '0', '1', '1', '1', '1'}; -template -const char __table<_Tp>::__base_8_lut[128] = { +inline constexpr char __base_8_lut[128] = { '0', '0', '0', '1', '0', '2', '0', '3', '0', '4', '0', '5', '0', '6', '0', '7', '1', '0', '1', '1', '1', '2', '1', '3', '1', '4', '1', '5', '1', '6', '1', '7', '2', '0', '2', '1', '2', '2', '2', '3', '2', '4', '2', '5', '2', '6', '2', '7', '3', '0', '3', '1', '3', '2', '3', '3', '3', '4', '3', '5', '3', '6', '3', '7', '4', '0', @@ -58,8 +36,7 @@ '5', '4', '5', '5', '5', '6', '5', '7', '6', '0', '6', '1', '6', '2', '6', '3', '6', '4', '6', '5', '6', '6', '6', '7', '7', '0', '7', '1', '7', '2', '7', '3', '7', '4', '7', '5', '7', '6', '7', '7'}; -template -const char __table<_Tp>::__base_16_lut[512] = { +inline constexpr char __base_16_lut[512] = { '0', '0', '0', '1', '0', '2', '0', '3', '0', '4', '0', '5', '0', '6', '0', '7', '0', '8', '0', '9', '0', 'a', '0', 'b', '0', 'c', '0', 'd', '0', 'e', '0', 'f', '1', '0', '1', '1', '1', '2', '1', '3', '1', '4', '1', '5', '1', '6', '1', '7', '1', '8', '1', '9', '1', 'a', '1', 'b', '1', 'c', '1', 'd', '1', 'e', '1', 'f', '2', '0', '2', '1', '2', @@ -84,13 +61,11 @@ '1', 'f', '2', 'f', '3', 'f', '4', 'f', '5', 'f', '6', 'f', '7', 'f', '8', 'f', '9', 'f', 'a', 'f', 'b', 'f', 'c', 'f', 'd', 'f', 'e', 'f', 'f'}; -template -const uint32_t __table<_Tp>::__pow10_32[10] = { +inline constexpr uint32_t __pow10_32[10] = { UINT32_C(0), UINT32_C(10), UINT32_C(100), UINT32_C(1000), UINT32_C(10000), UINT32_C(100000), UINT32_C(1000000), UINT32_C(10000000), UINT32_C(100000000), UINT32_C(1000000000)}; -template -const uint64_t __table<_Tp>::__pow10_64[20] = {UINT64_C(0), +inline constexpr uint64_t __pow10_64[20] = {UINT64_C(0), UINT64_C(10), UINT64_C(100), UINT64_C(1000), @@ -112,8 +87,8 @@ UINT64_C(10000000000000000000)}; # ifndef _LIBCPP_HAS_NO_INT128 -template -const __uint128_t __table<_Tp>::__pow10_128[40] = { +inline constexpr int __pow10_128_offset = 0; +inline constexpr __uint128_t __pow10_128[40] = { UINT64_C(0), UINT64_C(10), UINT64_C(100), @@ -156,8 +131,7 @@ (__uint128_t(UINT64_C(10000000000000000000)) * UINT64_C(10000000000000000000)) * 10}; # endif -template -const char __table<_Tp>::__digits_base_10[200] = { +inline constexpr char __digits_base_10[200] = { // clang-format off '0', '0', '0', '1', '0', '2', '0', '3', '0', '4', '0', '5', '0', '6', '0', '7', '0', '8', '0', '9', '1', '0', '1', '1', '1', '2', '1', '3', '1', '4', '1', '5', '1', '6', '1', '7', '1', '8', '1', '9', diff --git a/libcxx/include/__charconv/to_chars_base_10.h b/libcxx/include/__charconv/to_chars_base_10.h --- a/libcxx/include/__charconv/to_chars_base_10.h +++ b/libcxx/include/__charconv/to_chars_base_10.h @@ -29,50 +29,50 @@ namespace __itoa { -_LIBCPP_HIDE_FROM_ABI inline char* __append1(char* __first, uint32_t __value) noexcept { +_LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI inline char* __append1(char* __first, uint32_t __value) noexcept { *__first = '0' + static_cast(__value); return __first + 1; } -_LIBCPP_HIDE_FROM_ABI inline char* __append2(char* __first, uint32_t __value) noexcept { - return std::copy_n(&__table<>::__digits_base_10[__value * 2], 2, __first); +_LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI inline char* __append2(char* __first, uint32_t __value) noexcept { + return std::copy_n(&__digits_base_10[__value * 2], 2, __first); } -_LIBCPP_HIDE_FROM_ABI inline char* __append3(char* __first, uint32_t __value) noexcept { +_LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI inline char* __append3(char* __first, uint32_t __value) noexcept { return __itoa::__append2(__itoa::__append1(__first, __value / 100), __value % 100); } -_LIBCPP_HIDE_FROM_ABI inline char* __append4(char* __first, uint32_t __value) noexcept { +_LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI inline char* __append4(char* __first, uint32_t __value) noexcept { return __itoa::__append2(__itoa::__append2(__first, __value / 100), __value % 100); } -_LIBCPP_HIDE_FROM_ABI inline char* __append5(char* __first, uint32_t __value) noexcept { +_LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI inline char* __append5(char* __first, uint32_t __value) noexcept { return __itoa::__append4(__itoa::__append1(__first, __value / 10000), __value % 10000); } -_LIBCPP_HIDE_FROM_ABI inline char* __append6(char* __first, uint32_t __value) noexcept { +_LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI inline char* __append6(char* __first, uint32_t __value) noexcept { return __itoa::__append4(__itoa::__append2(__first, __value / 10000), __value % 10000); } -_LIBCPP_HIDE_FROM_ABI inline char* __append7(char* __first, uint32_t __value) noexcept { +_LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI inline char* __append7(char* __first, uint32_t __value) noexcept { return __itoa::__append6(__itoa::__append1(__first, __value / 1000000), __value % 1000000); } -_LIBCPP_HIDE_FROM_ABI inline char* __append8(char* __first, uint32_t __value) noexcept { +_LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI inline char* __append8(char* __first, uint32_t __value) noexcept { return __itoa::__append6(__itoa::__append2(__first, __value / 1000000), __value % 1000000); } -_LIBCPP_HIDE_FROM_ABI inline char* __append9(char* __first, uint32_t __value) noexcept { +_LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI inline char* __append9(char* __first, uint32_t __value) noexcept { return __itoa::__append8(__itoa::__append1(__first, __value / 100000000), __value % 100000000); } template -_LIBCPP_HIDE_FROM_ABI char* __append10(char* __first, _Tp __value) noexcept { +_LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI char* __append10(char* __first, _Tp __value) noexcept { return __itoa::__append8(__itoa::__append2(__first, static_cast(__value / 100000000)), static_cast(__value % 100000000)); } -_LIBCPP_HIDE_FROM_ABI inline char* __base_10_u32(char* __first, uint32_t __value) noexcept { +_LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI inline char* __base_10_u32(char* __first, uint32_t __value) noexcept { if (__value < 1000000) { if (__value < 10000) { if (__value < 100) { @@ -107,7 +107,7 @@ return __itoa::__append10(__first, __value); } -_LIBCPP_HIDE_FROM_ABI inline char* __base_10_u64(char* __buffer, uint64_t __value) noexcept { +_LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI inline char* __base_10_u64(char* __buffer, uint64_t __value) noexcept { if (__value <= UINT32_MAX) return __itoa::__base_10_u32(__buffer, static_cast(__value)); @@ -129,12 +129,12 @@ /// \note The lookup table contains a partial set of exponents limiting the /// range that can be used. However the range is sufficient for /// \ref __base_10_u128. -_LIBCPP_HIDE_FROM_ABI inline __uint128_t __pow_10(int __exp) noexcept { - _LIBCPP_ASSERT(__exp >= __table<>::__pow10_128_offset, "Index out of bounds"); - return __table<>::__pow10_128[__exp - __table<>::__pow10_128_offset]; +_LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI inline __uint128_t __pow_10(int __exp) noexcept { + _LIBCPP_ASSERT(__exp >= __pow10_128_offset, "Index out of bounds"); + return __pow10_128[__exp - __pow10_128_offset]; } -_LIBCPP_HIDE_FROM_ABI inline char* __base_10_u128(char* __buffer, __uint128_t __value) noexcept { +_LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI inline char* __base_10_u128(char* __buffer, __uint128_t __value) noexcept { _LIBCPP_ASSERT( __value > numeric_limits::max(), "The optimizations for this algorithm fail when this isn't true."); diff --git a/libcxx/include/charconv b/libcxx/include/charconv --- a/libcxx/include/charconv +++ b/libcxx/include/charconv @@ -30,8 +30,8 @@ friend bool operator==(const to_chars_result&, const to_chars_result&) = default; // since C++20 }; - to_chars_result to_chars(char* first, char* last, see below value, - int base = 10); + constexpr to_chars_result to_chars(char* first, char* last, see below value, + int base = 10); // constexpr since C++23 to_chars_result to_chars(char* first, char* last, bool value, int base = 10) = delete; @@ -60,8 +60,8 @@ friend bool operator==(const from_chars_result&, const from_chars_result&) = default; // since C++20 }; - from_chars_result from_chars(const char* first, const char* last, - see below& value, int base = 10); + constexpr from_chars_result from_chars(const char* first, const char* last, + see below& value, int base = 10); // constexpr since C++23 from_chars_result from_chars(const char* first, const char* last, float& value, @@ -77,6 +77,7 @@ */ +#include <__algorithm/copy_n.h> #include <__assert> // all public C++ headers provide the assertion handler #include <__availability> #include <__bits> @@ -88,6 +89,7 @@ #include <__config> #include <__debug> #include <__errc> +#include <__memory/addressof.h> #include <__type_traits/make_32_64_or_128_bit.h> #include <__utility/unreachable.h> #include // for log2f @@ -130,18 +132,18 @@ /// function requires its input to have at least one bit set the value of /// zero is set to one. This means the first element of the lookup table is /// zero. - static _LIBCPP_HIDE_FROM_ABI int __width(_Tp __v) + static _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI int __width(_Tp __v) { auto __t = (32 - std::__libcpp_clz(static_cast(__v | 1))) * 1233 >> 12; - return __t - (__v < __table<>::__pow10_32[__t]) + 1; + return __t - (__v < __itoa::__pow10_32[__t]) + 1; } - static _LIBCPP_HIDE_FROM_ABI char* __convert(char* __p, _Tp __v) + static _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI char* __convert(char* __p, _Tp __v) { return __itoa::__base_10_u32(__p, __v); } - static _LIBCPP_HIDE_FROM_ABI decltype(__table<>::__pow10_32)& __pow() { return __table<>::__pow10_32; } + static _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI decltype(__pow10_32)& __pow() { return __itoa::__pow10_32; } }; template @@ -157,14 +159,14 @@ /// function requires its input to have at least one bit set the value of /// zero is set to one. This means the first element of the lookup table is /// zero. - static _LIBCPP_HIDE_FROM_ABI int __width(_Tp __v) { + static _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI int __width(_Tp __v) { auto __t = (64 - std::__libcpp_clz(static_cast(__v | 1))) * 1233 >> 12; - return __t - (__v < __table<>::__pow10_64[__t]) + 1; + return __t - (__v < __itoa::__pow10_64[__t]) + 1; } - static _LIBCPP_HIDE_FROM_ABI char* __convert(char* __p, _Tp __v) { return __itoa::__base_10_u64(__p, __v); } + static _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI char* __convert(char* __p, _Tp __v) { return __itoa::__base_10_u64(__p, __v); } - static _LIBCPP_HIDE_FROM_ABI decltype(__table<>::__pow10_64)& __pow() { return __table<>::__pow10_64; } + static _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI decltype(__pow10_64)& __pow() { return __itoa::__pow10_64; } }; @@ -182,25 +184,25 @@ /// function requires its input to have at least one bit set the value of /// zero is set to one. This means the first element of the lookup table is /// zero. - static _LIBCPP_HIDE_FROM_ABI int __width(_Tp __v) { + static _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI int __width(_Tp __v) { _LIBCPP_ASSERT(__v > numeric_limits::max(), "The optimizations for this algorithm fail when this isn't true."); // There's always a bit set in the upper 64-bits. auto __t = (128 - std::__libcpp_clz(static_cast(__v >> 64))) * 1233 >> 12; - _LIBCPP_ASSERT(__t >= __table<>::__pow10_128_offset, "Index out of bounds"); + _LIBCPP_ASSERT(__t >= __itoa::__pow10_128_offset, "Index out of bounds"); // __t is adjusted since the lookup table misses the lower entries. - return __t - (__v < __table<>::__pow10_128[__t - __table<>::__pow10_128_offset]) + 1; + return __t - (__v < __itoa::__pow10_128[__t - __itoa::__pow10_128_offset]) + 1; } - static _LIBCPP_HIDE_FROM_ABI char* __convert(char* __p, _Tp __v) { return __itoa::__base_10_u128(__p, __v); } + static _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI char* __convert(char* __p, _Tp __v) { return __itoa::__base_10_u128(__p, __v); } // TODO FMT This pow function should get an index. // By moving this to its own header it can be reused by the pow function in to_chars_base_10. - static _LIBCPP_HIDE_FROM_ABI decltype(__table<>::__pow10_128)& __pow() { return __table<>::__pow10_128; } + static _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI decltype(__pow10_128)& __pow() { return __itoa::__pow10_128; } }; #endif template -inline _LIBCPP_HIDE_FROM_ABI bool +inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool __mul_overflowed(unsigned char __a, _Tp __b, unsigned char& __r) { auto __c = __a * __b; @@ -209,7 +211,7 @@ } template -inline _LIBCPP_HIDE_FROM_ABI bool +inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool __mul_overflowed(unsigned short __a, _Tp __b, unsigned short& __r) { auto __c = __a * __b; @@ -218,7 +220,7 @@ } template -inline _LIBCPP_HIDE_FROM_ABI bool +inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool __mul_overflowed(_Tp __a, _Tp __b, _Tp& __r) { static_assert(is_unsigned<_Tp>::value, ""); @@ -227,7 +229,7 @@ template inline _LIBCPP_HIDE_FROM_ABI bool -__mul_overflowed(_Tp __a, _Up __b, _Tp& __r) +_LIBCPP_CONSTEXPR_SINCE_CXX23 __mul_overflowed(_Tp __a, _Up __b, _Tp& __r) { return __mul_overflowed(__a, static_cast<_Tp>(__b), __r); } @@ -240,7 +242,7 @@ using typename __traits_base<_Tp>::type; // precondition: at least one non-zero character available - static _LIBCPP_HIDE_FROM_ABI char const* + static _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI char const* __read(char const* __p, char const* __ep, type& __a, type& __b) { type __cprod[digits]; @@ -261,7 +263,7 @@ } template - static _LIBCPP_HIDE_FROM_ABI _Up + static _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI _Up __inner_product(_It1 __first1, _It1 __last1, _It2 __first2, _Up __init) { for (; __first1 < __last1; ++__first1, ++__first2) @@ -273,7 +275,7 @@ } // namespace __itoa template -inline _LIBCPP_HIDE_FROM_ABI _Tp +inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI _Tp __complement(_Tp __x) { static_assert(is_unsigned<_Tp>::value, "cast to unsigned first"); @@ -281,7 +283,7 @@ } template -inline _LIBCPP_HIDE_FROM_ABI to_chars_result +inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI to_chars_result __to_chars_itoa(char* __first, char* __last, _Tp __value, true_type) { auto __x = __to_unsigned_like(__value); @@ -295,7 +297,7 @@ } template -inline _LIBCPP_HIDE_FROM_ABI to_chars_result +inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI to_chars_result __to_chars_itoa(char* __first, char* __last, _Tp __value, false_type) { using __tx = __itoa::__traits<_Tp>; @@ -309,7 +311,7 @@ # ifndef _LIBCPP_HAS_NO_INT128 template <> -inline _LIBCPP_HIDE_FROM_ABI to_chars_result +inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI to_chars_result __to_chars_itoa(char* __first, char* __last, __uint128_t __value, false_type) { // When the value fits in 64-bits use the 64-bit code path. This reduces @@ -330,7 +332,7 @@ #endif template -inline _LIBCPP_HIDE_FROM_ABI to_chars_result +inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI to_chars_result __to_chars_integral(char* __first, char* __last, _Tp __value, int __base, true_type) { @@ -360,7 +362,7 @@ } template - _LIBCPP_HIDE_FROM_ABI static to_chars_result __to_chars(char* __first, char* __last, _Tp __value) { + _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI static to_chars_result __to_chars(char* __first, char* __last, _Tp __value) { ptrdiff_t __cap = __last - __first; int __n = __width(__value); if (__n > __cap) @@ -373,7 +375,7 @@ unsigned __c = __value % __divisor; __value /= __divisor; __p -= 4; - std::memcpy(__p, &__table<>::__base_2_lut[4 * __c], 4); + std::copy_n(std::addressof(__base_2_lut[4 * __c]), 4, __p); } do { unsigned __c = __value % 2; @@ -395,7 +397,7 @@ } template - _LIBCPP_HIDE_FROM_ABI static to_chars_result __to_chars(char* __first, char* __last, _Tp __value) { + _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI static to_chars_result __to_chars(char* __first, char* __last, _Tp __value) { ptrdiff_t __cap = __last - __first; int __n = __width(__value); if (__n > __cap) @@ -408,7 +410,7 @@ unsigned __c = __value % __divisor; __value /= __divisor; __p -= 2; - std::memcpy(__p, &__table<>::__base_8_lut[2 * __c], 2); + std::copy_n(std::addressof(__base_8_lut[2 * __c]), 2, __p); } do { unsigned __c = __value % 8; @@ -431,7 +433,7 @@ } template - _LIBCPP_HIDE_FROM_ABI static to_chars_result __to_chars(char* __first, char* __last, _Tp __value) { + _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI static to_chars_result __to_chars(char* __first, char* __last, _Tp __value) { ptrdiff_t __cap = __last - __first; int __n = __width(__value); if (__n > __cap) @@ -444,7 +446,7 @@ unsigned __c = __value % __divisor; __value /= __divisor; __p -= 2; - std::memcpy(__p, &__table<>::__base_16_lut[2 * __c], 2); + std::copy_n(std::addressof(__base_16_lut[2 * __c]), 2, __p); } if (__first != __last) do { @@ -460,34 +462,34 @@ template = sizeof(unsigned)), int>::type = 0> -_LIBCPP_HIDE_FROM_ABI int +_LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI int __to_chars_integral_width(_Tp __value) { return __itoa::__integral<_Base>::__width(__value); } template ::type = 0> -_LIBCPP_HIDE_FROM_ABI int +_LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI int __to_chars_integral_width(_Tp __value) { return std::__to_chars_integral_width<_Base>(static_cast(__value)); } template = sizeof(unsigned)), int>::type = 0> -_LIBCPP_HIDE_FROM_ABI to_chars_result +_LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI to_chars_result __to_chars_integral(char* __first, char* __last, _Tp __value) { return __itoa::__integral<_Base>::__to_chars(__first, __last, __value); } template ::type = 0> -_LIBCPP_HIDE_FROM_ABI to_chars_result +_LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI to_chars_result __to_chars_integral(char* __first, char* __last, _Tp __value) { return std::__to_chars_integral<_Base>(__first, __last, static_cast(__value)); } template -_LIBCPP_HIDE_FROM_ABI int +_LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI int __to_chars_integral_width(_Tp __value, unsigned __base) { _LIBCPP_ASSERT(__value >= 0, "The function requires a non-negative value."); @@ -514,7 +516,7 @@ } template -inline _LIBCPP_HIDE_FROM_ABI to_chars_result +inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI to_chars_result __to_chars_integral(char* __first, char* __last, _Tp __value, int __base, false_type) { @@ -546,7 +548,7 @@ } template ::value, int>::type = 0> -inline _LIBCPP_HIDE_FROM_ABI to_chars_result +inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI to_chars_result to_chars(char* __first, char* __last, _Tp __value) { using _Type = __make_32_64_or_128_bit_t<_Tp>; @@ -555,7 +557,7 @@ } template ::value, int>::type = 0> -inline _LIBCPP_HIDE_FROM_ABI to_chars_result +inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI to_chars_result to_chars(char* __first, char* __last, _Tp __value, int __base) { _LIBCPP_ASSERT(2 <= __base && __base <= 36, "base not in [2, 36]"); @@ -565,7 +567,7 @@ } template -inline _LIBCPP_HIDE_FROM_ABI from_chars_result +inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI from_chars_result __sign_combinator(_It __first, _It __last, _Tp& __value, _Fn __f, _Ts... __args) { using __tl = numeric_limits<_Tp>; @@ -588,7 +590,7 @@ if (__x <= __complement(__to_unsigned_like(__tl::min()))) { __x = __complement(__x); - std::memcpy(&__value, &__x, sizeof(__x)); + std::copy_n(std::addressof(__x), 1, std::addressof(__value)); return __r; } } @@ -605,7 +607,7 @@ } template -inline _LIBCPP_HIDE_FROM_ABI bool +inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool __in_pattern(_Tp __c) { return '0' <= __c && __c <= '9'; @@ -616,11 +618,11 @@ bool __ok; int __val; - explicit _LIBCPP_HIDE_FROM_ABI operator bool() const { return __ok; } + explicit _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI operator bool() const { return __ok; } }; template -inline _LIBCPP_HIDE_FROM_ABI __in_pattern_result +inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI __in_pattern_result __in_pattern(_Tp __c, int __base) { if (__base <= 10) @@ -634,7 +636,7 @@ } template -inline _LIBCPP_HIDE_FROM_ABI from_chars_result +inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI from_chars_result __subject_seq_combinator(_It __first, _It __last, _Tp& __value, _Fn __f, _Ts... __args) { @@ -671,7 +673,7 @@ } template ::value, int>::type = 0> -inline _LIBCPP_HIDE_FROM_ABI from_chars_result +inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI from_chars_result __from_chars_atoi(const char* __first, const char* __last, _Tp& __value) { using __tx = __itoa::__traits<_Tp>; @@ -697,15 +699,36 @@ } template ::value, int>::type = 0> -inline _LIBCPP_HIDE_FROM_ABI from_chars_result +inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI from_chars_result __from_chars_atoi(const char* __first, const char* __last, _Tp& __value) { using __t = decltype(__to_unsigned_like(__value)); return __sign_combinator(__first, __last, __value, __from_chars_atoi<__t>); } + +#if _LIBCPP_STD_VER > 14 +/* +// Code used to generate __from_chars_log2f_lut. +#include +#include +#include + +int main() { + for (int i = 2; i <= 36; ++i) + std::cout << std::format("{},\n", log2f(i)); +} +*/ +/// log2f table for bases [2, 36]. +inline constexpr float __from_chars_log2f_lut[35] = { + 1, 1.5849625, 2, 2.321928, 2.5849626, 2.807355, 3, 3.169925, 3.321928, + 3.4594316, 3.5849626, 3.7004397, 3.807355, 3.9068906, 4, 4.087463, 4.169925, 4.2479277, + 4.321928, 4.3923173, 4.4594316, 4.523562, 4.5849624, 4.643856, 4.70044, 4.7548876, 4.807355, + 4.857981, 4.9068904, 4.9541965, 5, 5.044394, 5.087463, 5.129283, 5.169925}; +# endif + template ::value, int>::type = 0> -inline _LIBCPP_HIDE_FROM_ABI from_chars_result +inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI from_chars_result __from_chars_integral(const char* __first, const char* __last, _Tp& __value, int __base) { @@ -717,7 +740,11 @@ [](const char* __p, const char* __lastp, _Tp& __val, int __b) -> from_chars_result { using __tl = numeric_limits<_Tp>; - auto __digits = __tl::digits / log2f(float(__b)); +#if _LIBCPP_STD_VER > 14 + auto __digits = __tl::digits / __from_chars_log2f_lut[__b - 2]; +#else + auto __digits = __tl::digits / log2f(float(__b)); +#endif _Tp __x = __in_pattern(*__p++, __b).__val, __y = 0; for (int __i = 1; __p != __lastp; ++__i, ++__p) @@ -752,7 +779,7 @@ } template ::value, int>::type = 0> -inline _LIBCPP_HIDE_FROM_ABI from_chars_result +inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI from_chars_result __from_chars_integral(const char* __first, const char* __last, _Tp& __value, int __base) { @@ -762,14 +789,14 @@ } template ::value, int>::type = 0> -inline _LIBCPP_HIDE_FROM_ABI from_chars_result +inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI from_chars_result from_chars(const char* __first, const char* __last, _Tp& __value) { return __from_chars_atoi(__first, __last, __value); } template ::value, int>::type = 0> -inline _LIBCPP_HIDE_FROM_ABI from_chars_result +inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI from_chars_result from_chars(const char* __first, const char* __last, _Tp& __value, int __base) { _LIBCPP_ASSERT(2 <= __base && __base <= 36, "base not in [2, 36]"); diff --git a/libcxx/include/version b/libcxx/include/version --- a/libcxx/include/version +++ b/libcxx/include/version @@ -57,6 +57,7 @@ __cpp_lib_concepts 202002L __cpp_lib_constexpr_algorithms 201806L __cpp_lib_constexpr_bitset 202207L +__cpp_lib_constexpr_charconv 202207L __cpp_lib_constexpr_cmath 202202L __cpp_lib_constexpr_complex 201711L __cpp_lib_constexpr_dynamic_alloc 201907L @@ -384,6 +385,7 @@ // # 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_cmath 202202L # undef __cpp_lib_constexpr_memory # define __cpp_lib_constexpr_memory 202202L diff --git a/libcxx/src/include/ryu/digit_table.h b/libcxx/src/include/ryu/digit_table.h --- a/libcxx/src/include/ryu/digit_table.h +++ b/libcxx/src/include/ryu/digit_table.h @@ -50,7 +50,7 @@ // In order to minimize the diff in the Ryu code between MSVC STL and libc++ // the code uses the name __DIGIT_TABLE. In order to avoid code duplication it // reuses the table already available in libc++. -inline constexpr auto& __DIGIT_TABLE = __itoa::__table<>::__digits_base_10; +inline constexpr auto& __DIGIT_TABLE = __itoa::__digits_base_10; _LIBCPP_END_NAMESPACE_STD diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/charconv.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/charconv.version.compile.pass.cpp --- a/libcxx/test/std/language.support/support.limits/support.limits.general/charconv.version.compile.pass.cpp +++ b/libcxx/test/std/language.support/support.limits/support.limits.general/charconv.version.compile.pass.cpp @@ -15,8 +15,9 @@ // Test the feature test macros defined by -/* Constant Value - __cpp_lib_to_chars 201611L [C++17] +/* Constant Value + __cpp_lib_constexpr_charconv 202207L [C++2b] + __cpp_lib_to_chars 201611L [C++17] */ #include @@ -24,18 +25,30 @@ #if TEST_STD_VER < 14 +# ifdef __cpp_lib_constexpr_charconv +# error "__cpp_lib_constexpr_charconv should not be defined before c++2b" +# endif + # ifdef __cpp_lib_to_chars # error "__cpp_lib_to_chars should not be defined before c++17" # endif #elif TEST_STD_VER == 14 +# ifdef __cpp_lib_constexpr_charconv +# error "__cpp_lib_constexpr_charconv should not be defined before c++2b" +# endif + # ifdef __cpp_lib_to_chars # error "__cpp_lib_to_chars should not be defined before c++17" # endif #elif TEST_STD_VER == 17 +# ifdef __cpp_lib_constexpr_charconv +# error "__cpp_lib_constexpr_charconv should not be defined before c++2b" +# endif + # if !defined(_LIBCPP_VERSION) # ifndef __cpp_lib_to_chars # error "__cpp_lib_to_chars should be defined in c++17" @@ -51,6 +64,10 @@ #elif TEST_STD_VER == 20 +# ifdef __cpp_lib_constexpr_charconv +# error "__cpp_lib_constexpr_charconv should not be defined before c++2b" +# endif + # if !defined(_LIBCPP_VERSION) # ifndef __cpp_lib_to_chars # error "__cpp_lib_to_chars should be defined in c++20" @@ -66,6 +83,13 @@ #elif TEST_STD_VER > 20 +# ifndef __cpp_lib_constexpr_charconv +# error "__cpp_lib_constexpr_charconv should be defined in c++2b" +# endif +# if __cpp_lib_constexpr_charconv != 202207L +# error "__cpp_lib_constexpr_charconv should have the value 202207L in c++2b" +# endif + # if !defined(_LIBCPP_VERSION) # ifndef __cpp_lib_to_chars # error "__cpp_lib_to_chars should be defined in c++2b" diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp --- a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp +++ b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp @@ -53,6 +53,7 @@ __cpp_lib_concepts 202002L [C++20] __cpp_lib_constexpr_algorithms 201806L [C++20] __cpp_lib_constexpr_bitset 202207L [C++2b] + __cpp_lib_constexpr_charconv 202207L [C++2b] __cpp_lib_constexpr_cmath 202202L [C++2b] __cpp_lib_constexpr_complex 201711L [C++20] __cpp_lib_constexpr_dynamic_alloc 201907L [C++20] @@ -331,6 +332,10 @@ # error "__cpp_lib_constexpr_bitset should not be defined before c++2b" # endif +# ifdef __cpp_lib_constexpr_charconv +# error "__cpp_lib_constexpr_charconv should not be defined before c++2b" +# endif + # ifdef __cpp_lib_constexpr_cmath # error "__cpp_lib_constexpr_cmath should not be defined before c++2b" # endif @@ -967,6 +972,10 @@ # error "__cpp_lib_constexpr_bitset should not be defined before c++2b" # endif +# ifdef __cpp_lib_constexpr_charconv +# error "__cpp_lib_constexpr_charconv should not be defined before c++2b" +# endif + # ifdef __cpp_lib_constexpr_cmath # error "__cpp_lib_constexpr_cmath should not be defined before c++2b" # endif @@ -1699,6 +1708,10 @@ # error "__cpp_lib_constexpr_bitset should not be defined before c++2b" # endif +# ifdef __cpp_lib_constexpr_charconv +# error "__cpp_lib_constexpr_charconv should not be defined before c++2b" +# endif + # ifdef __cpp_lib_constexpr_cmath # error "__cpp_lib_constexpr_cmath should not be defined before c++2b" # endif @@ -2677,6 +2690,10 @@ # error "__cpp_lib_constexpr_bitset should not be defined before c++2b" # endif +# ifdef __cpp_lib_constexpr_charconv +# error "__cpp_lib_constexpr_charconv should not be defined before c++2b" +# endif + # ifdef __cpp_lib_constexpr_cmath # error "__cpp_lib_constexpr_cmath should not be defined before c++2b" # endif @@ -3901,6 +3918,13 @@ # error "__cpp_lib_constexpr_bitset should have the value 202207L in c++2b" # endif +# ifndef __cpp_lib_constexpr_charconv +# error "__cpp_lib_constexpr_charconv should be defined in c++2b" +# endif +# if __cpp_lib_constexpr_charconv != 202207L +# error "__cpp_lib_constexpr_charconv should have the value 202207L in c++2b" +# endif + # if !defined(_LIBCPP_VERSION) # ifndef __cpp_lib_constexpr_cmath # error "__cpp_lib_constexpr_cmath should be defined in c++2b" diff --git a/libcxx/test/std/utilities/charconv/charconv.from.chars/integral.pass.cpp b/libcxx/test/std/utilities/charconv/charconv.from.chars/integral.pass.cpp --- a/libcxx/test/std/utilities/charconv/charconv.from.chars/integral.pass.cpp +++ b/libcxx/test/std/utilities/charconv/charconv.from.chars/integral.pass.cpp @@ -10,8 +10,8 @@ // -// from_chars_result from_chars(const char* first, const char* last, -// Integral& value, int base = 10) +// constexpr from_chars_result from_chars(const char* first, const char* last, +// Integral& value, int base = 10) #include #include "test_macros.h" @@ -20,7 +20,7 @@ template struct test_basics { - void operator()() + TEST_CONSTEXPR_CXX23 void operator()() { std::from_chars_result r; T x; @@ -84,7 +84,7 @@ template struct test_signed { - void operator()() + TEST_CONSTEXPR_CXX23 void operator()() { std::from_chars_result r; T x = 42; @@ -135,10 +135,19 @@ } }; -int main(int, char**) +TEST_CONSTEXPR_CXX23 bool test() { run(integrals); run(all_signed); + return true; +} + +int main(int, char**) { + test(); +#if TEST_STD_VER > 20 + static_assert(test()); +#endif + return 0; } diff --git a/libcxx/test/std/utilities/charconv/charconv.from.chars/integral.roundtrip.pass.cpp b/libcxx/test/std/utilities/charconv/charconv.from.chars/integral.roundtrip.pass.cpp --- a/libcxx/test/std/utilities/charconv/charconv.from.chars/integral.roundtrip.pass.cpp +++ b/libcxx/test/std/utilities/charconv/charconv.from.chars/integral.roundtrip.pass.cpp @@ -10,8 +10,8 @@ // -// from_chars_result from_chars(const char* first, const char* last, -// Integral& value, int base = 10) +// constexpr from_chars_result from_chars(const char* first, const char* last, +// Integral& value, int base = 10) #include #include "test_macros.h" @@ -22,7 +22,7 @@ { using roundtrip_test_base::test; - void operator()() + TEST_CONSTEXPR_CXX23 void operator()() { test(0); test(42); @@ -52,7 +52,7 @@ { using roundtrip_test_base::test; - void operator()() + TEST_CONSTEXPR_CXX23 void operator()() { test(-1); test(-12); @@ -73,10 +73,19 @@ } }; -int main(int, char**) +TEST_CONSTEXPR_CXX23 bool test() { run(integrals); run(all_signed); + return true; +} + +int main(int, char**) { + test(); +#if TEST_STD_VER > 20 + static_assert(test()); +#endif + return 0; } diff --git a/libcxx/test/std/utilities/charconv/charconv.to.chars/integral.pass.cpp b/libcxx/test/std/utilities/charconv/charconv.to.chars/integral.pass.cpp --- a/libcxx/test/std/utilities/charconv/charconv.to.chars/integral.pass.cpp +++ b/libcxx/test/std/utilities/charconv/charconv.to.chars/integral.pass.cpp @@ -9,38 +9,38 @@ // UNSUPPORTED: c++03, c++11, c++14 // ADDITIONAL_COMPILE_FLAGS(has-fconstexpr-steps): -fconstexpr-steps=12712420 -// ADDITIONAL_COMPILE_FLAGS(has-fconstexpr-ops-limit): -fconstexpr-ops-limit=12712420 +// ADDITIONAL_COMPILE_FLAGS(has-fconstexpr-ops-limit): -fconstexpr-ops-limit=13200000 // -// to_chars_result to_chars(char* first, char* last, Integral value, -// int base = 10) +// constexpr to_chars_result to_chars(char* first, char* last, Integral value, +// int base = 10) #include #include "test_macros.h" #include "charconv_test_helpers.h" #ifndef TEST_HAS_NO_INT128 -__uint128_t make_u128(__uint128_t a, uint64_t b) { +TEST_CONSTEXPR_CXX23 __uint128_t make_u128(__uint128_t a, uint64_t b) { a *= 1000000000000000000UL; a *= 10; return a + b; } -__uint128_t make_u128(__uint128_t a, uint64_t b, uint64_t c) { +TEST_CONSTEXPR_CXX23 __uint128_t make_u128(__uint128_t a, uint64_t b, uint64_t c) { a *= 10000000000000ULL; a += b; a *= 10000000000000ULL; return a + c; } -__int128_t make_i128(__int128_t a, int64_t b) { +TEST_CONSTEXPR_CXX23 __int128_t make_i128(__int128_t a, int64_t b) { if (a < 0) return -make_u128(-a, b); return make_u128(a, b); } -__int128_t make_i128(__int128_t a, __int128_t b, int64_t c) { +TEST_CONSTEXPR_CXX23 __int128_t make_i128(__int128_t a, __int128_t b, int64_t c) { if (a < 0) return -make_u128(-a, b, c); return make_u128(a, b, c); @@ -53,7 +53,7 @@ using to_chars_test_base::test; using to_chars_test_base::test_value; - void operator()() + TEST_CONSTEXPR_CXX23 void operator()() { test(0, "0"); test(42, "42"); @@ -175,7 +175,7 @@ using to_chars_test_base::test; using to_chars_test_base::test_value; - void operator()() + TEST_CONSTEXPR_CXX23 void operator()() { test(-1, "-1"); test(-12, "-12"); @@ -289,10 +289,20 @@ } }; -int main(int, char**) +TEST_CONSTEXPR_CXX23 bool test() { run(integrals); run(all_signed); + return true; +} + +int main(int, char**) +{ + test(); +#if TEST_STD_VER > 20 + static_assert(test()); +#endif + return 0; } diff --git a/libcxx/test/support/charconv_test_helpers.h b/libcxx/test/support/charconv_test_helpers.h --- a/libcxx/test/support/charconv_test_helpers.h +++ b/libcxx/test/support/charconv_test_helpers.h @@ -78,7 +78,7 @@ struct to_chars_test_base { template - void test(T v, char const (&expect)[N], Ts... args) + TEST_CONSTEXPR_CXX23 void test(T v, char const (&expect)[N], Ts... args) { using std::to_chars; std::to_chars_result r; @@ -96,22 +96,23 @@ r = to_chars(buf, buf + sizeof(buf), X(v), args...); assert(r.ptr == buf + len); assert(r.ec == std::errc{}); - assert(memcmp(buf, expect, len) == 0); + assert(__builtin_memcmp(buf, expect, len) == 0); } template - void test_value(X v, Ts... args) + TEST_CONSTEXPR_CXX23 void test_value(X v, Ts... args) { using std::to_chars; std::to_chars_result r; // Poison the buffer for testing whether a successful std::to_chars - // doesn't modify data beyond r.ptr. - std::iota(buf, buf + sizeof(buf), char(1)); + // doesn't modify data beyond r.ptr. Use unsigned values to avoid + // overflowing char when it's signed. + std::iota(buf, buf + sizeof(buf), static_cast(1)); r = to_chars(buf, buf + sizeof(buf), v, args...); assert(r.ec == std::errc{}); for (size_t i = r.ptr - buf; i < sizeof(buf); ++i) - assert(buf[i] == static_cast(i + 1)); + assert(static_cast(buf[i]) == i + 1); *r.ptr = '\0'; #ifndef TEST_HAS_NO_INT128 @@ -132,36 +133,47 @@ } private: - static long long fromchars_impl(char const* p, char const* ep, int base, true_type) + static TEST_CONSTEXPR_CXX23 long long fromchars_impl(char const* p, char const* ep, int base, true_type) { char* last; - auto r = strtoll(p, &last, base); + long long r; + if (TEST_IS_CONSTANT_EVALUATED) + last = const_cast(std::from_chars(p, ep, r, base).ptr); + else + r = strtoll(p, &last, base); assert(last == ep); return r; } - static unsigned long long fromchars_impl(char const* p, char const* ep, int base, false_type) + static TEST_CONSTEXPR_CXX23 unsigned long long fromchars_impl(char const* p, char const* ep, int base, false_type) { char* last; - auto r = strtoull(p, &last, base); + unsigned long long r; + if (TEST_IS_CONSTANT_EVALUATED) + last = const_cast(std::from_chars(p, ep, r, base).ptr); + else + r = strtoull(p, &last, base); assert(last == ep); return r; } #ifndef TEST_HAS_NO_INT128 - static __int128_t fromchars128_impl(char const* p, char const* ep, int base, true_type) + static TEST_CONSTEXPR_CXX23 __int128_t fromchars128_impl(char const* p, char const* ep, int base, true_type) { - char* last; - __int128_t r = strtoll(p, &last, base); - if(errno != ERANGE) { - assert(last == ep); - return r; + if (!TEST_IS_CONSTANT_EVALUATED) { + char* last; + __int128_t r = strtoll(p, &last, base); + if(errno != ERANGE) { + assert(last == ep); + return r; + } } // When the value doesn't fit in a long long use from_chars. This is // not ideal since it does a round-trip test instead if using an // external source. + __int128_t r; std::from_chars_result s = std::from_chars(p, ep, r, base); assert(s.ec == std::errc{}); assert(s.ptr == ep); @@ -169,15 +181,18 @@ return r; } - static __uint128_t fromchars128_impl(char const* p, char const* ep, int base, false_type) + static TEST_CONSTEXPR_CXX23 __uint128_t fromchars128_impl(char const* p, char const* ep, int base, false_type) { - char* last; - __uint128_t r = strtoull(p, &last, base); - if(errno != ERANGE) { - assert(last == ep); - return r; + if (!TEST_IS_CONSTANT_EVALUATED) { + char* last; + __uint128_t r = strtoull(p, &last, base); + if(errno != ERANGE) { + assert(last == ep); + return r; + } } + __uint128_t r; std::from_chars_result s = std::from_chars(p, ep, r, base); assert(s.ec == std::errc{}); assert(s.ptr == ep); @@ -185,7 +200,7 @@ return r; } - static auto fromchars128_impl(char const* p, char const* ep, int base = 10) + static TEST_CONSTEXPR_CXX23 auto fromchars128_impl(char const* p, char const* ep, int base = 10) -> decltype(fromchars128_impl(p, ep, base, std::is_signed())) { return fromchars128_impl(p, ep, base, std::is_signed()); @@ -193,7 +208,7 @@ #endif - static auto fromchars_impl(char const* p, char const* ep, int base = 10) + static TEST_CONSTEXPR_CXX23 auto fromchars_impl(char const* p, char const* ep, int base = 10) -> decltype(fromchars_impl(p, ep, base, std::is_signed())) { return fromchars_impl(p, ep, base, std::is_signed()); @@ -206,7 +221,7 @@ struct roundtrip_test_base { template - void test(T v, Ts... args) + TEST_CONSTEXPR_CXX23 void test(T v, Ts... args) { using std::from_chars; using std::to_chars; @@ -303,7 +318,7 @@ auto integrals = concat(all_signed, all_unsigned); template