[libcxx-commits] [libcxx] a1e13a8 - [libc++] Implements constexpr <charconv>.

Mark de Wever via libcxx-commits libcxx-commits at lists.llvm.org
Wed Oct 12 08:43:32 PDT 2022


Author: Mark de Wever
Date: 2022-10-12T17:43:23+02:00
New Revision: a1e13a80d06c2d1764ab573df06a3b903f134703

URL: https://github.com/llvm/llvm-project/commit/a1e13a80d06c2d1764ab573df06a3b903f134703
DIFF: https://github.com/llvm/llvm-project/commit/a1e13a80d06c2d1764ab573df06a3b903f134703.diff

LOG: [libc++] Implements constexpr <charconv>.

Implements:
- P2291R3 Add Constexpr Modifiers to Functions to_chars and from_chars for
  Integral Types in <charconv> Header

Reviewed By: #libc, ldionne

Differential Revision: https://reviews.llvm.org/D131317

Added: 
    

Modified: 
    libcxx/docs/FeatureTestMacroTable.rst
    libcxx/docs/ReleaseNotes.rst
    libcxx/docs/Status/Cxx2bPapers.csv
    libcxx/include/__charconv/tables.h
    libcxx/include/__charconv/to_chars_base_10.h
    libcxx/include/charconv
    libcxx/include/version
    libcxx/src/include/ryu/digit_table.h
    libcxx/test/std/language.support/support.limits/support.limits.general/charconv.version.compile.pass.cpp
    libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
    libcxx/test/std/utilities/charconv/charconv.from.chars/integral.pass.cpp
    libcxx/test/std/utilities/charconv/charconv.from.chars/integral.roundtrip.pass.cpp
    libcxx/test/std/utilities/charconv/charconv.to.chars/integral.pass.cpp
    libcxx/test/support/charconv_test_helpers.h
    libcxx/utils/generate_feature_test_macro_components.py

Removed: 
    


################################################################################
diff  --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst
index 32f7c0c001fb0..e4a180235604b 100644
--- a/libcxx/docs/FeatureTestMacroTable.rst
+++ b/libcxx/docs/FeatureTestMacroTable.rst
@@ -310,6 +310,8 @@ Status
     ------------------------------------------------- -----------------
     ``__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
index 6a8c61b9ec818..7befa46d199ee 100644
--- a/libcxx/docs/ReleaseNotes.rst
+++ b/libcxx/docs/ReleaseNotes.rst
@@ -42,6 +42,8 @@ Implemented Papers
 - 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 ``<charconv>`` Header
 
 Improvements and New Features
 -----------------------------

diff  --git a/libcxx/docs/Status/Cxx2bPapers.csv b/libcxx/docs/Status/Cxx2bPapers.csv
index d4d1176eb1b9d..7e8f0e3ca4dfd 100644
--- a/libcxx/docs/Status/Cxx2bPapers.csv
+++ b/libcxx/docs/Status/Cxx2bPapers.csv
@@ -63,7 +63,7 @@
 "`P2165R4 <https://wg21.link/P2165R4>`__","LWG","Compatibility between ``tuple``, ``pair`` and ``tuple-like`` objects","July 2022","",""
 "`P2278R4 <https://wg21.link/P2278R4>`__","LWG","``cbegin`` should always return a constant iterator","July 2022","",""
 "`P2286R8 <https://wg21.link/P2286R8>`__","LWG","Formatting Ranges","July 2022","",""
-"`P2291R3 <https://wg21.link/P2291R3>`__","LWG","Add Constexpr Modifiers to Functions ``to_chars`` and ``from_chars`` for Integral Types in ``<charconv>`` Header","July 2022","|In Progress|",""
+"`P2291R3 <https://wg21.link/P2291R3>`__","LWG","Add Constexpr Modifiers to Functions ``to_chars`` and ``from_chars`` for Integral Types in ``<charconv>`` Header","July 2022","|Complete|","16.0"
 "`P2302R4 <https://wg21.link/P2302R4>`__","LWG","``std::ranges::contains``","July 2022","",""
 "`P2322R6 <https://wg21.link/P2322R6>`__","LWG","``ranges::fold``","July 2022","",""
 "`P2374R4 <https://wg21.link/P2374R4>`__","LWG","``views::cartesian_product``","July 2022","",""

diff  --git a/libcxx/include/__charconv/tables.h b/libcxx/include/__charconv/tables.h
index 08664d710cc95..9b82440348bee 100644
--- a/libcxx/include/__charconv/tables.h
+++ b/libcxx/include/__charconv/tables.h
@@ -23,34 +23,12 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 
 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 <class = void>
-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 <class _Tp>
-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 <class _Tp>
-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 @@ const char __table<_Tp>::__base_8_lut[128] = {
     '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 <class _Tp>
-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 @@ const char __table<_Tp>::__base_16_lut[512] = {
     '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 <class _Tp>
-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 <class _Tp>
-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 @@ const uint64_t __table<_Tp>::__pow10_64[20] = {UINT64_C(0),
                                                UINT64_C(10000000000000000000)};
 
 #  ifndef _LIBCPP_HAS_NO_INT128
-template <class _Tp>
-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 @@ const __uint128_t __table<_Tp>::__pow10_128[40] = {
     (__uint128_t(UINT64_C(10000000000000000000)) * UINT64_C(10000000000000000000)) * 10};
 #  endif
 
-template <class _Tp>
-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
index 0c47597c15e29..fc7fb76e3e987 100644
--- a/libcxx/include/__charconv/to_chars_base_10.h
+++ b/libcxx/include/__charconv/to_chars_base_10.h
@@ -29,50 +29,50 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 
 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<char>(__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 <class _Tp>
-_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<uint32_t>(__value / 100000000)),
                            static_cast<uint32_t>(__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 @@ _LIBCPP_HIDE_FROM_ABI inline char* __base_10_u32(char* __first, uint32_t __value
   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<uint32_t>(__value));
 
@@ -129,12 +129,12 @@ _LIBCPP_HIDE_FROM_ABI inline char* __base_10_u64(char* __buffer, uint64_t __valu
 /// \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<uint64_t>::max(), "The optimizations for this algorithm fail when this isn't true.");
 

diff  --git a/libcxx/include/charconv b/libcxx/include/charconv
index 8e58d96af93f1..b8664a456b6b9 100644
--- a/libcxx/include/charconv
+++ b/libcxx/include/charconv
@@ -30,8 +30,8 @@ namespace std {
     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 @@ namespace std {
     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 @@ namespace std {
 
 */
 
+#include <__algorithm/copy_n.h>
 #include <__assert> // all public C++ headers provide the assertion handler
 #include <__availability>
 #include <__bits>
@@ -88,6 +89,7 @@ namespace std {
 #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 <cmath> // for log2f
@@ -130,18 +132,18 @@ struct _LIBCPP_HIDDEN __traits_base<_Tp, __enable_if_t<sizeof(_Tp) <= sizeof(uin
     /// 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<type>(__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 <typename _Tp>
@@ -157,14 +159,14 @@ struct _LIBCPP_HIDDEN
   /// 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<type>(__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 @@ struct _LIBCPP_HIDDEN
   /// 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<uint64_t>::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<uint64_t>(__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 <typename _Tp>
-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 @@ __mul_overflowed(unsigned char __a, _Tp __b, unsigned char& __r)
 }
 
 template <typename _Tp>
-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 @@ __mul_overflowed(unsigned short __a, _Tp __b, unsigned short& __r)
 }
 
 template <typename _Tp>
-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 @@ __mul_overflowed(_Tp __a, _Tp __b, _Tp& __r)
 
 template <typename _Tp, typename _Up>
 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 @@ struct _LIBCPP_HIDDEN __traits : __traits_base<_Tp>
     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 @@ struct _LIBCPP_HIDDEN __traits : __traits_base<_Tp>
     }
 
     template <typename _It1, typename _It2, class _Up>
-    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 @@ struct _LIBCPP_HIDDEN __traits : __traits_base<_Tp>
 }  // namespace __itoa
 
 template <typename _Tp>
-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 @@ __complement(_Tp __x)
 }
 
 template <typename _Tp>
-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 @@ __to_chars_itoa(char* __first, char* __last, _Tp __value, true_type)
 }
 
 template <typename _Tp>
-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 @@ __to_chars_itoa(char* __first, char* __last, _Tp __value, false_type)
 
 #  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 @@ __to_chars_itoa(char* __first, char* __last, __uint128_t __value, false_type)
 #endif
 
 template <typename _Tp>
-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 @@ struct _LIBCPP_HIDDEN __integral<2> {
   }
 
   template <typename _Tp>
-  _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) {
     ptr
diff _t __cap = __last - __first;
     int __n = __width(__value);
     if (__n > __cap)
@@ -373,7 +375,7 @@ struct _LIBCPP_HIDDEN __integral<2> {
       unsigned __c = __value % __divisor;
       __value /= __divisor;
       __p -= 4;
-      std::memcpy(__p, &__table<>::__base_2_lut[4 * __c], 4);
+      std::copy_n(&__base_2_lut[4 * __c], 4, __p);
     }
     do {
       unsigned __c = __value % 2;
@@ -395,7 +397,7 @@ struct _LIBCPP_HIDDEN __integral<8> {
   }
 
   template <typename _Tp>
-  _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) {
     ptr
diff _t __cap = __last - __first;
     int __n = __width(__value);
     if (__n > __cap)
@@ -408,7 +410,7 @@ struct _LIBCPP_HIDDEN __integral<8> {
       unsigned __c = __value % __divisor;
       __value /= __divisor;
       __p -= 2;
-      std::memcpy(__p, &__table<>::__base_8_lut[2 * __c], 2);
+      std::copy_n(&__base_8_lut[2 * __c], 2, __p);
     }
     do {
       unsigned __c = __value % 8;
@@ -431,7 +433,7 @@ struct _LIBCPP_HIDDEN __integral<16> {
   }
 
   template <typename _Tp>
-  _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) {
     ptr
diff _t __cap = __last - __first;
     int __n = __width(__value);
     if (__n > __cap)
@@ -444,7 +446,7 @@ struct _LIBCPP_HIDDEN __integral<16> {
       unsigned __c = __value % __divisor;
       __value /= __divisor;
       __p -= 2;
-      std::memcpy(__p, &__table<>::__base_16_lut[2 * __c], 2);
+      std::copy_n(&__base_16_lut[2 * __c], 2, __p);
     }
     if (__first != __last)
       do {
@@ -460,34 +462,34 @@ struct _LIBCPP_HIDDEN __integral<16> {
 
 template <unsigned _Base, typename _Tp,
           typename enable_if<(sizeof(_Tp) >= 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 <unsigned _Base, typename _Tp,
           typename enable_if<(sizeof(_Tp) < 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 std::__to_chars_integral_width<_Base>(static_cast<unsigned>(__value));
 }
 
 template <unsigned _Base, typename _Tp,
           typename enable_if<(sizeof(_Tp) >= 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 <unsigned _Base, typename _Tp,
           typename enable_if<(sizeof(_Tp) < 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 std::__to_chars_integral<_Base>(__first, __last, static_cast<unsigned>(__value));
 }
 
 template <typename _Tp>
-_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 @@ __to_chars_integral_width(_Tp __value, unsigned __base) {
 }
 
 template <typename _Tp>
-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 @@ __to_chars_integral(char* __first, char* __last, _Tp __value, int __base,
 }
 
 template <typename _Tp, typename enable_if<is_integral<_Tp>::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 @@ to_chars(char* __first, char* __last, _Tp __value)
 }
 
 template <typename _Tp, typename enable_if<is_integral<_Tp>::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 @@ to_chars(char* __first, char* __last, _Tp __value, int __base)
 }
 
 template <typename _It, typename _Tp, typename _Fn, typename... _Ts>
-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 @@ __sign_combinator(_It __first, _It __last, _Tp& __value, _Fn __f, _Ts... __args)
         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 @@ __sign_combinator(_It __first, _It __last, _Tp& __value, _Fn __f, _Ts... __args)
 }
 
 template <typename _Tp>
-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 @@ struct _LIBCPP_HIDDEN __in_pattern_result
     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 <typename _Tp>
-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 @@ __in_pattern(_Tp __c, int __base)
 }
 
 template <typename _It, typename _Tp, typename _Fn, typename... _Ts>
-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 @@ __subject_seq_combinator(_It __first, _It __last, _Tp& __value, _Fn __f,
 }
 
 template <typename _Tp, typename enable_if<is_unsigned<_Tp>::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,34 @@ __from_chars_atoi(const char* __first, const char* __last, _Tp& __value)
 }
 
 template <typename _Tp, typename enable_if<is_signed<_Tp>::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>);
 }
 
+
+/*
+// Code used to generate __from_chars_log2f_lut.
+#include <cmath>
+#include <iostream>
+#include <format>
+
+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};
+
 template <typename _Tp, typename enable_if<is_unsigned<_Tp>::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 +738,8 @@ __from_chars_integral(const char* __first, const char* __last, _Tp& __value,
         [](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));
+            // __base is always between 2 and 36 inclusive.
+            auto __digits = __tl::digits / __from_chars_log2f_lut[__b - 2];
             _Tp __x = __in_pattern(*__p++, __b).__val, __y = 0;
 
             for (int __i = 1; __p != __lastp; ++__i, ++__p)
@@ -752,7 +774,7 @@ __from_chars_integral(const char* __first, const char* __last, _Tp& __value,
 }
 
 template <typename _Tp, typename enable_if<is_signed<_Tp>::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 +784,14 @@ __from_chars_integral(const char* __first, const char* __last, _Tp& __value,
 }
 
 template <typename _Tp, typename enable_if<is_integral<_Tp>::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 <typename _Tp, typename enable_if<is_integral<_Tp>::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
index 646ea84da3525..3e1b6344f5f65 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -57,6 +57,7 @@ __cpp_lib_complex_udls                                  201309L <complex>
 __cpp_lib_concepts                                      202002L <concepts>
 __cpp_lib_constexpr_algorithms                          201806L <algorithm>
 __cpp_lib_constexpr_bitset                              202207L <bitset>
+__cpp_lib_constexpr_charconv                            202207L <charconv>
 __cpp_lib_constexpr_cmath                               202202L <cmath> <cstdlib>
 __cpp_lib_constexpr_complex                             201711L <complex>
 __cpp_lib_constexpr_dynamic_alloc                       201907L <memory>
@@ -384,6 +385,7 @@ __cpp_lib_void_t                                        201411L <type_traits>
 // # 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
index c57a0966ef2c9..bf660b0be7853 100644
--- a/libcxx/src/include/ryu/digit_table.h
+++ b/libcxx/src/include/ryu/digit_table.h
@@ -50,7 +50,7 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 // 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
index 41d042ca53dbe..8042ff683441a 100644
--- 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 <charconv>
 
-/*  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 <charconv>
@@ -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
index b766f3df131b0..464ec2e2047af 100644
--- 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
@@ -2671,6 +2684,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
@@ -3889,6 +3906,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
index 9cd006d0ac501..c88bf11d61e0e 100644
--- 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 @@
 
 // <charconv>
 
-// 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 <charconv>
 #include "test_macros.h"
@@ -20,7 +20,7 @@
 template <typename T>
 struct test_basics
 {
-    void operator()()
+    TEST_CONSTEXPR_CXX23 void operator()()
     {
         std::from_chars_result r;
         T x;
@@ -84,7 +84,7 @@ struct test_basics
 template <typename T>
 struct test_signed
 {
-    void operator()()
+    TEST_CONSTEXPR_CXX23 void operator()()
     {
         std::from_chars_result r;
         T x = 42;
@@ -135,10 +135,19 @@ struct test_signed
     }
 };
 
-int main(int, char**)
+TEST_CONSTEXPR_CXX23 bool test()
 {
     run<test_basics>(integrals);
     run<test_signed>(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
index 70cefa4eee098..5b5ea7c91e8af 100644
--- 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
@@ -8,10 +8,12 @@
 
 // UNSUPPORTED: c++03, c++11, c++14
 
+// ADDITIONAL_COMPILE_FLAGS(has-fconstexpr-steps): -fconstexpr-steps=12712420
+
 // <charconv>
 
-// 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 <charconv>
 #include "test_macros.h"
@@ -22,7 +24,7 @@ struct test_basics : roundtrip_test_base<T>
 {
     using roundtrip_test_base<T>::test;
 
-    void operator()()
+    TEST_CONSTEXPR_CXX23 void operator()()
     {
         test(0);
         test(42);
@@ -52,7 +54,7 @@ struct test_signed : roundtrip_test_base<T>
 {
     using roundtrip_test_base<T>::test;
 
-    void operator()()
+    TEST_CONSTEXPR_CXX23 void operator()()
     {
         test(-1);
         test(-12);
@@ -73,10 +75,19 @@ struct test_signed : roundtrip_test_base<T>
     }
 };
 
-int main(int, char**)
+TEST_CONSTEXPR_CXX23 bool test()
 {
     run<test_basics>(integrals);
     run<test_signed>(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
index fa6c21431708a..98781e53263d4 100644
--- 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=50000000
 
 // <charconv>
 
-// 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 <charconv>
 #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 @@ struct test_basics : to_chars_test_base<T>
     using to_chars_test_base<T>::test;
     using to_chars_test_base<T>::test_value;
 
-    void operator()()
+    TEST_CONSTEXPR_CXX23 void operator()()
     {
         test(0, "0");
         test(42, "42");
@@ -175,7 +175,7 @@ struct test_signed : to_chars_test_base<T>
     using to_chars_test_base<T>::test;
     using to_chars_test_base<T>::test_value;
 
-    void operator()()
+    TEST_CONSTEXPR_CXX23 void operator()()
     {
         test(-1, "-1");
         test(-12, "-12");
@@ -289,10 +289,20 @@ struct test_signed : to_chars_test_base<T>
     }
 };
 
-int main(int, char**)
+TEST_CONSTEXPR_CXX23 bool test()
 {
     run<test_basics>(integrals);
     run<test_signed>(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
index c8d104510f26f..1ba95e79a66c8 100644
--- a/libcxx/test/support/charconv_test_helpers.h
+++ b/libcxx/test/support/charconv_test_helpers.h
@@ -9,6 +9,7 @@
 #ifndef SUPPORT_CHARCONV_TEST_HELPERS_H
 #define SUPPORT_CHARCONV_TEST_HELPERS_H
 
+#include <algorithm>
 #include <charconv>
 #include <cassert>
 #include <limits>
@@ -78,9 +79,8 @@ template <typename X>
 struct to_chars_test_base
 {
     template <typename T, size_t N, typename... Ts>
-    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;
 
         constexpr size_t len = N - 1;
@@ -89,29 +89,29 @@ struct to_chars_test_base
         if (!fits_in<X>(v))
             return;
 
-        r = to_chars(buf, buf + len - 1, X(v), args...);
+        r = std::to_chars(buf, buf + len - 1, X(v), args...);
         assert(r.ptr == buf + len - 1);
         assert(r.ec == std::errc::value_too_large);
 
-        r = to_chars(buf, buf + sizeof(buf), X(v), args...);
+        r = std::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(std::equal(buf, buf + len, expect));
     }
 
     template <typename... Ts>
-    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));
-        r = to_chars(buf, buf + sizeof(buf), v, args...);
+        // 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<unsigned char>(1));
+        r = std::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<char>(i + 1));
+            assert(static_cast<unsigned char>(buf[i]) == i + 1);
         *r.ptr = '\0';
 
 #ifndef TEST_HAS_NO_INT128
@@ -126,42 +126,53 @@ struct to_chars_test_base
         }
 
         auto ep = r.ptr - 1;
-        r = to_chars(buf, ep, v, args...);
+        r = std::to_chars(buf, ep, v, args...);
         assert(r.ptr == ep);
         assert(r.ec == std::errc::value_too_large);
     }
 
 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<char*>(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<char*>(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 +180,18 @@ struct to_chars_test_base
         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 +199,7 @@ struct to_chars_test_base
         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<X>()))
     {
         return fromchars128_impl(p, ep, base, std::is_signed<X>());
@@ -193,7 +207,7 @@ struct to_chars_test_base
 
 #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<X>()))
     {
         return fromchars_impl(p, ep, base, std::is_signed<X>());
@@ -206,29 +220,27 @@ template <typename X>
 struct roundtrip_test_base
 {
     template <typename T, typename... Ts>
-    void test(T v, Ts... args)
+    TEST_CONSTEXPR_CXX23 void test(T v, Ts... args)
     {
-        using std::from_chars;
-        using std::to_chars;
         std::from_chars_result r2;
         std::to_chars_result r;
         X x = 0xc;
 
         if (fits_in<X>(v))
         {
-            r = to_chars(buf, buf + sizeof(buf), v, args...);
+            r = std::to_chars(buf, buf + sizeof(buf), v, args...);
             assert(r.ec == std::errc{});
 
-            r2 = from_chars(buf, r.ptr, x, args...);
+            r2 = std::from_chars(buf, r.ptr, x, args...);
             assert(r2.ptr == r.ptr);
             assert(x == X(v));
         }
         else
         {
-            r = to_chars(buf, buf + sizeof(buf), v, args...);
+            r = std::to_chars(buf, buf + sizeof(buf), v, args...);
             assert(r.ec == std::errc{});
 
-            r2 = from_chars(buf, r.ptr, x, args...);
+            r2 = std::from_chars(buf, r.ptr, x, args...);
 
             TEST_DIAGNOSTIC_PUSH
             TEST_MSVC_DIAGNOSTIC_IGNORED(4127) // conditional expression is constant
@@ -303,7 +315,7 @@ auto all_unsigned = type_list<
 auto integrals = concat(all_signed, all_unsigned);
 
 template <template <typename> class Fn, typename... Ts>
-void
+TEST_CONSTEXPR_CXX23 void
 run(type_list<Ts...>)
 {
     int ls[sizeof...(Ts)] = {(Fn<Ts>{}(), 0)...};

diff  --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index 62f703b60c2f1..e4324fccc9ab2 100755
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -218,6 +218,10 @@ def add_version_header(tc):
     "name": "__cpp_lib_constexpr_bitset",
     "values": { "c++2b": 202207 },
     "headers": ["bitset"],
+  }, {
+    "name": "__cpp_lib_constexpr_charconv",
+    "values": { "c++2b": 202207 },
+    "headers": ["charconv"],
   }, {
     "name": "__cpp_lib_constexpr_cmath",
     "values": { "c++2b": 202202 },


        


More information about the libcxx-commits mailing list