[libcxx-commits] [libcxx] [libc++] Suppress unsigned-integer-overflow warning in <charconv> (PR #225801)

via libcxx-commits libcxx-commits at lists.llvm.org
Fri Oct 2 01:14:47 PDT 2026


https://github.com/hchataing updated https://github.com/llvm/llvm-project/pull/225801

>From c21d7876e49900ea78524e2617a0f9a9e987f9e6 Mon Sep 17 00:00:00 2001
From: Henri Chataing <henrichataing at google.com>
Date: Wed, 30 Sep 2026 12:17:39 +0000
Subject: [PATCH 1/2] [libc++] Update charconv tests to use type_algorithms.h

---
 .../floating_point.pass.cpp                   |  24 +-
 .../charconv.from.chars/integral.pass.cpp     | 213 +++++----
 .../integral.roundtrip.pass.cpp               |  96 ++---
 .../charconv.to.chars/integral.pass.cpp       | 408 +++++++++---------
 libcxx/test/support/charconv_test_helpers.h   | 227 ++++------
 5 files changed, 440 insertions(+), 528 deletions(-)

diff --git a/libcxx/test/std/utilities/charconv/charconv.from.chars/floating_point.pass.cpp b/libcxx/test/std/utilities/charconv/charconv.from.chars/floating_point.pass.cpp
index a02c7ac466f04..512bda55c7966 100644
--- a/libcxx/test/std/utilities/charconv/charconv.from.chars/floating_point.pass.cpp
+++ b/libcxx/test/std/utilities/charconv/charconv.from.chars/floating_point.pass.cpp
@@ -17,6 +17,7 @@
 //                              double& value, chars_format fmt = chars_format::general)
 
 #include <array>
+#include <cassert>
 #include <charconv>
 #include <cmath>
 #include <cstring>
@@ -24,8 +25,8 @@
 #include <stdexcept>
 #include <system_error>
 
-#include "charconv_test_helpers.h"
 #include "test_macros.h"
+#include "type_algorithms.h"
 
 template <class F>
 void test_infinity(std::chars_format fmt) {
@@ -325,8 +326,8 @@ void test_fmt_independent(std::chars_format fmt) {
   }
 }
 
-template <class F>
 struct test_basics {
+  template <class F>
   void operator()() {
     for (auto fmt : {std::chars_format::scientific,
                      std::chars_format::fixed,
@@ -335,8 +336,8 @@ struct test_basics {
   }
 };
 
-template <class F>
 struct test_fixed {
+  template <class F>
   void operator()() {
     std::from_chars_result r;
     F x = 0.25;
@@ -600,8 +601,8 @@ struct test_fixed {
   }
 };
 
-template <class F>
 struct test_scientific {
+  template <class F>
   void operator()() {
     std::from_chars_result r;
     F x = 0.25;
@@ -874,8 +875,8 @@ struct test_scientific {
   }
 };
 
-template <class F>
 struct test_general {
+  template <class F>
   void operator()() {
     std::from_chars_result r;
     F x = 0.25;
@@ -1194,8 +1195,8 @@ struct test_general {
   }
 };
 
-template <class F>
 struct test_hex {
+  template <class F>
   void operator()() {
     std::from_chars_result r;
     F x = 0.25;
@@ -1547,12 +1548,13 @@ void test_random_errors() {
 }
 
 int main(int, char**) {
-  run<test_basics>(all_floats);
-  run<test_scientific>(all_floats);
-  run<test_fixed>(all_floats);
-  run<test_general>(all_floats);
+  using all_floats = types::type_list<float, double>; // TODO: Add long double
+  types::for_each(all_floats(), test_basics());
+  types::for_each(all_floats(), test_scientific());
+  types::for_each(all_floats(), test_fixed());
+  types::for_each(all_floats(), test_general());
 
-  run<test_hex>(all_floats);
+  types::for_each(all_floats(), test_hex());
 
   test_random_errors();
 
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 4708da4c38b9b..f1fe8af9cc424 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
@@ -19,130 +19,125 @@
 #include "test_macros.h"
 #include "charconv_test_helpers.h"
 
-template <typename T>
 struct test_basics
 {
-    TEST_CONSTEXPR_CXX23 void operator()()
+  template <typename T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    std::from_chars_result r;
+    T x;
+
+    {
+      char s[] = "001x";
+
+      // the expected form of the subject sequence is a sequence of
+      // letters and digits representing an integer with the radix
+      // specified by base (C11 7.22.1.4/3)
+      r = std::from_chars(s, s + sizeof(s), x);
+      assert(r.ec == std::errc{});
+      assert(r.ptr == s + 3);
+      assert(x == 1);
+    }
+
     {
-        std::from_chars_result r;
-        T x;
-
-        {
-            char s[] = "001x";
-
-            // the expected form of the subject sequence is a sequence of
-            // letters and digits representing an integer with the radix
-            // specified by base (C11 7.22.1.4/3)
-            r = std::from_chars(s, s + sizeof(s), x);
-            assert(r.ec == std::errc{});
-            assert(r.ptr == s + 3);
-            assert(x == 1);
-        }
-
-        {
-            // The string has more characters than valid in an 128-bit value.
-            char s[] = "0X7BAtSGHDkEIXZgQRfYChLpOzRnM ";
-
-            // The letters from a (or A) through z (or Z) are ascribed the
-            // values 10 through 35; (C11 7.22.1.4/3)
-            r = std::from_chars(s, s + sizeof(s), x, 36);
-            assert(r.ec == std::errc::result_out_of_range);
-            // The member ptr of the return value points to the first character
-            // not matching the pattern
-            assert(r.ptr == s + sizeof(s) - 2);
-            assert(x == 1);
-
-            // no "0x" or "0X" prefix shall appear if the value of base is 16
-            r = std::from_chars(s, s + sizeof(s), x, 16);
-            assert(r.ec == std::errc{});
-            assert(r.ptr == s + 1);
-            assert(x == 0);
-
-            // only letters and digits whose ascribed values are less than that
-            // of base are permitted. (C11 7.22.1.4/3)
-            r = std::from_chars(s + 2, s + sizeof(s), x, 12);
-            // If the parsed value is not in the range representable by the type
-            // of value,
-            if (!fits_in<T>(1150))
-            {
-                // value is unmodified and
-                assert(x == 0);
-                // the member ec of the return value is equal to
-                // errc::result_out_of_range
-                assert(r.ec == std::errc::result_out_of_range);
-            }
-            else
-            {
-                // Otherwise, value is set to the parsed value,
-                assert(x == 1150);
-                // and the member ec is value-initialized.
-                assert(r.ec == std::errc{});
-            }
-            assert(r.ptr == s + 5);
-        }
+      // The string has more characters than valid in an 128-bit value.
+      char s[] = "0X7BAtSGHDkEIXZgQRfYChLpOzRnM ";
+
+      // The letters from a (or A) through z (or Z) are ascribed the
+      // values 10 through 35; (C11 7.22.1.4/3)
+      r = std::from_chars(s, s + sizeof(s), x, 36);
+      assert(r.ec == std::errc::result_out_of_range);
+      // The member ptr of the return value points to the first character
+      // not matching the pattern
+      assert(r.ptr == s + sizeof(s) - 2);
+      assert(x == 1);
+
+      // no "0x" or "0X" prefix shall appear if the value of base is 16
+      r = std::from_chars(s, s + sizeof(s), x, 16);
+      assert(r.ec == std::errc{});
+      assert(r.ptr == s + 1);
+      assert(x == 0);
+
+      // only letters and digits whose ascribed values are less than that
+      // of base are permitted. (C11 7.22.1.4/3)
+      r = std::from_chars(s + 2, s + sizeof(s), x, 12);
+      // If the parsed value is not in the range representable by the type
+      // of value,
+      if (!fits_in<T>(1150)) {
+        // value is unmodified and
+        assert(x == 0);
+        // the member ec of the return value is equal to
+        // errc::result_out_of_range
+        assert(r.ec == std::errc::result_out_of_range);
+      } else {
+        // Otherwise, value is set to the parsed value,
+        assert(x == 1150);
+        // and the member ec is value-initialized.
+        assert(r.ec == std::errc{});
+      }
+      assert(r.ptr == s + 5);
     }
+  }
 };
 
-template <typename T>
 struct test_signed
 {
-    TEST_CONSTEXPR_CXX23 void operator()()
+  template <typename T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    std::from_chars_result r;
+    T x = 42;
+
+    {
+      // If the pattern allows for an optional sign,
+      // but the string has no digit characters following the sign,
+      char s[] = "- 9+12";
+      r        = std::from_chars(s, s + sizeof(s), x);
+      // value is unmodified,
+      assert(x == 42);
+      // no characters match the pattern.
+      assert(r.ptr == s);
+      assert(r.ec == std::errc::invalid_argument);
+    }
+
+    {
+      char s[] = "9+12";
+      r        = std::from_chars(s, s + sizeof(s), x);
+      assert(r.ec == std::errc{});
+      // The member ptr of the return value points to the first character
+      // not matching the pattern,
+      assert(r.ptr == s + 1);
+      assert(x == 9);
+    }
+
+    {
+      char s[] = "12";
+      r        = std::from_chars(s, s + 2, x);
+      assert(r.ec == std::errc{});
+      // or has the value last if all characters match.
+      assert(r.ptr == s + 2);
+      assert(x == 12);
+    }
+
     {
-        std::from_chars_result r;
-        T x = 42;
-
-        {
-            // If the pattern allows for an optional sign,
-            // but the string has no digit characters following the sign,
-            char s[] = "- 9+12";
-            r = std::from_chars(s, s + sizeof(s), x);
-            // value is unmodified,
-            assert(x == 42);
-            // no characters match the pattern.
-            assert(r.ptr == s);
-            assert(r.ec == std::errc::invalid_argument);
-        }
-
-        {
-            char s[] = "9+12";
-            r = std::from_chars(s, s + sizeof(s), x);
-            assert(r.ec == std::errc{});
-            // The member ptr of the return value points to the first character
-            // not matching the pattern,
-            assert(r.ptr == s + 1);
-            assert(x == 9);
-        }
-
-        {
-            char s[] = "12";
-            r = std::from_chars(s, s + 2, x);
-            assert(r.ec == std::errc{});
-            // or has the value last if all characters match.
-            assert(r.ptr == s + 2);
-            assert(x == 12);
-        }
-
-        {
-            // '-' is the only sign that may appear
-            char s[] = "+30";
-            // If no characters match the pattern,
-            r = std::from_chars(s, s + sizeof(s), x);
-            // value is unmodified,
-            assert(x == 12);
-            // the member ptr of the return value is first and
-            assert(r.ptr == s);
-            // the member ec is equal to errc::invalid_argument.
-            assert(r.ec == std::errc::invalid_argument);
-        }
+      // '-' is the only sign that may appear
+      char s[] = "+30";
+      // If no characters match the pattern,
+      r = std::from_chars(s, s + sizeof(s), x);
+      // value is unmodified,
+      assert(x == 12);
+      // the member ptr of the return value is first and
+      assert(r.ptr == s);
+      // the member ec is equal to errc::invalid_argument.
+      assert(r.ec == std::errc::invalid_argument);
     }
+  }
 };
 
 TEST_CONSTEXPR_CXX23 bool test()
 {
-    run<test_basics>(integrals);
-    run<test_signed>(all_signed);
+  types::for_each(integrals(), test_basics());
+  types::for_each(types::signed_integer_types(), test_signed());
 
-    return true;
+  return true;
 }
 
 int main(int, char**) {
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 b1f2c77b34f8a..a4f1a467f55ba 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
@@ -21,68 +21,58 @@
 #include "test_macros.h"
 #include "charconv_test_helpers.h"
 
-template <typename T>
-struct test_basics : roundtrip_test_base<T>
-{
-    using roundtrip_test_base<T>::test;
-
-    TEST_CONSTEXPR_CXX23 void operator()()
-    {
-        test(0);
-        test(42);
-        test(32768);
-        test(0, 10);
-        test(42, 10);
-        test(32768, 10);
-        test(0xf, 16);
-        test(0xdeadbeaf, 16);
-        test(0755, 8);
-
-        for (int b = 2; b < 37; ++b)
-        {
-            using xl = std::numeric_limits<T>;
-
-            test(1, b);
-            test(-1, b);
-            test(xl::lowest(), b);
-            test((xl::max)(), b);
-            test((xl::max)() / 2, b);
-        }
+struct test_basics : roundtrip_test_base {
+  template <typename T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>(0);
+    test<T>(42);
+    test<T>(32768);
+    test<T>(0, 10);
+    test<T>(42, 10);
+    test<T>(32768, 10);
+    test<T>(0xf, 16);
+    test<T>(0xdeadbeaf, 16);
+    test<T>(0755, 8);
+
+    for (int b = 2; b < 37; ++b) {
+      using xl = std::numeric_limits<T>;
+
+      test<T>(1, b);
+      test<T>(-1, b);
+      test<T>(xl::lowest(), b);
+      test<T>((xl::max)(), b);
+      test<T>((xl::max)() / 2, b);
     }
+  }
 };
 
-template <typename T>
-struct test_signed : roundtrip_test_base<T>
-{
-    using roundtrip_test_base<T>::test;
-
-    TEST_CONSTEXPR_CXX23 void operator()()
-    {
-        test(-1);
-        test(-12);
-        test(-1, 10);
-        test(-12, 10);
-        test(-21734634, 10);
-        test(-2647, 2);
-        test(-0xcc1, 16);
-
-        for (int b = 2; b < 37; ++b)
-        {
-            using xl = std::numeric_limits<T>;
-
-            test(0, b);
-            test(xl::lowest(), b);
-            test((xl::max)(), b);
-        }
+struct test_signed : roundtrip_test_base {
+  template <typename T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>(-1);
+    test<T>(-12);
+    test<T>(-1, 10);
+    test<T>(-12, 10);
+    test<T>(-21734634, 10);
+    test<T>(-2647, 2);
+    test<T>(-0xcc1, 16);
+
+    for (int b = 2; b < 37; ++b) {
+      using xl = std::numeric_limits<T>;
+
+      test<T>(0, b);
+      test<T>(xl::lowest(), b);
+      test<T>((xl::max)(), b);
     }
+  }
 };
 
 TEST_CONSTEXPR_CXX23 bool test()
 {
-    run<test_basics>(integrals);
-    run<test_signed>(all_signed);
+  types::for_each(integrals(), test_basics());
+  types::for_each(types::signed_integer_types(), test_signed());
 
-    return true;
+  return true;
 }
 
 int main(int, char**) {
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 e89b340bdfcbe..d9291140586c7 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
@@ -51,254 +51,244 @@ TEST_CONSTEXPR_CXX23 __int128_t make_i128(__int128_t a, __int128_t b, std::int64
 }
 #endif
 
-template <typename T>
-struct test_basics : to_chars_test_base<T>
-{
-    using to_chars_test_base<T>::test;
-    using to_chars_test_base<T>::test_value;
-
-    TEST_CONSTEXPR_CXX23 void operator()()
-    {
-        test(0, "0");
-        test(42, "42");
-        test(32768, "32768");
-        test(0, "0", 10);
-        test(42, "42", 10);
-        test(32768, "32768", 10);
-        test(0xf, "f", 16);
-        test(0xdeadbeaf, "deadbeaf", 16);
-        test(0755, "755", 8);
+struct test_basics : to_chars_test_base {
+  template <typename T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>(0, "0");
+    test<T>(42, "42");
+    test<T>(32768, "32768");
+    test<T>(0, "0", 10);
+    test<T>(42, "42", 10);
+    test<T>(32768, "32768", 10);
+    test<T>(0xf, "f", 16);
+    test<T>(0xdeadbeaf, "deadbeaf", 16);
+    test<T>(0755, "755", 8);
 
-        // Test each len till len of UINT64_MAX = 20 because to_chars algorithm
-        // makes branches based on decimal digits count in the value string
-        // representation.
-        // Test driver automatically skips values not fitting into source type.
-        test(1UL, "1");
-        test(12UL, "12");
-        test(123UL, "123");
-        test(1234UL, "1234");
-        test(12345UL, "12345");
-        test(123456UL, "123456");
-        test(1234567UL, "1234567");
-        test(12345678UL, "12345678");
-        test(123456789UL, "123456789");
-        test(1234567890UL, "1234567890");
-        test(12345678901UL, "12345678901");
-        test(123456789012UL, "123456789012");
-        test(1234567890123UL, "1234567890123");
-        test(12345678901234UL, "12345678901234");
-        test(123456789012345UL, "123456789012345");
-        test(1234567890123456UL, "1234567890123456");
-        test(12345678901234567UL, "12345678901234567");
-        test(123456789012345678UL, "123456789012345678");
-        test(1234567890123456789UL, "1234567890123456789");
-        test(12345678901234567890UL, "12345678901234567890");
+    // Test each len till len of UINT64_MAX = 20 because to_chars algorithm
+    // makes branches based on decimal digits count in the value string
+    // representation.
+    // Test driver automatically skips values not fitting into source type.
+    test<T>(1UL, "1");
+    test<T>(12UL, "12");
+    test<T>(123UL, "123");
+    test<T>(1234UL, "1234");
+    test<T>(12345UL, "12345");
+    test<T>(123456UL, "123456");
+    test<T>(1234567UL, "1234567");
+    test<T>(12345678UL, "12345678");
+    test<T>(123456789UL, "123456789");
+    test<T>(1234567890UL, "1234567890");
+    test<T>(12345678901UL, "12345678901");
+    test<T>(123456789012UL, "123456789012");
+    test<T>(1234567890123UL, "1234567890123");
+    test<T>(12345678901234UL, "12345678901234");
+    test<T>(123456789012345UL, "123456789012345");
+    test<T>(1234567890123456UL, "1234567890123456");
+    test<T>(12345678901234567UL, "12345678901234567");
+    test<T>(123456789012345678UL, "123456789012345678");
+    test<T>(1234567890123456789UL, "1234567890123456789");
+    test<T>(12345678901234567890UL, "12345678901234567890");
 #ifndef TEST_HAS_NO_INT128
-        test(make_u128(12UL, 3456789012345678901UL), "123456789012345678901");
-        test(make_u128(123UL, 4567890123456789012UL), "1234567890123456789012");
-        test(make_u128(1234UL, 5678901234567890123UL), "12345678901234567890123");
-        test(make_u128(12345UL, 6789012345678901234UL), "123456789012345678901234");
-        test(make_u128(123456UL, 7890123456789012345UL), "1234567890123456789012345");
-        test(make_u128(1234567UL, 8901234567890123456UL), "12345678901234567890123456");
-        test(make_u128(12345678UL, 9012345678901234567UL), "123456789012345678901234567");
-        test(make_u128(123456789UL, 123456789012345678UL), "1234567890123456789012345678");
-        test(make_u128(123UL, 4567890123456UL, 7890123456789UL), "12345678901234567890123456789");
-        test(make_u128(1234UL, 5678901234567UL, 8901234567890UL), "123456789012345678901234567890");
-        test(make_u128(12345UL, 6789012345678UL, 9012345678901UL), "1234567890123456789012345678901");
-        test(make_u128(123456UL, 7890123456789UL, 123456789012UL), "12345678901234567890123456789012");
-        test(make_u128(1234567UL, 8901234567890UL, 1234567890123UL), "123456789012345678901234567890123");
-        test(make_u128(12345678UL, 9012345678901UL, 2345678901234UL), "1234567890123456789012345678901234");
-        test(make_u128(123456789UL, 123456789012UL, 3456789012345UL), "12345678901234567890123456789012345");
-        test(make_u128(1234567890UL, 1234567890123UL, 4567890123456UL), "123456789012345678901234567890123456");
-        test(make_u128(12345678901UL, 2345678901234UL, 5678901234567UL), "1234567890123456789012345678901234567");
-        test(make_u128(123456789012UL, 3456789012345UL, 6789012345678UL), "12345678901234567890123456789012345678");
-        test(make_u128(1234567890123UL, 4567890123456UL, 7890123456789UL), "123456789012345678901234567890123456789");
+    test<T>(make_u128(12UL, 3456789012345678901UL), "123456789012345678901");
+    test<T>(make_u128(123UL, 4567890123456789012UL), "1234567890123456789012");
+    test<T>(make_u128(1234UL, 5678901234567890123UL), "12345678901234567890123");
+    test<T>(make_u128(12345UL, 6789012345678901234UL), "123456789012345678901234");
+    test<T>(make_u128(123456UL, 7890123456789012345UL), "1234567890123456789012345");
+    test<T>(make_u128(1234567UL, 8901234567890123456UL), "12345678901234567890123456");
+    test<T>(make_u128(12345678UL, 9012345678901234567UL), "123456789012345678901234567");
+    test<T>(make_u128(123456789UL, 123456789012345678UL), "1234567890123456789012345678");
+    test<T>(make_u128(123UL, 4567890123456UL, 7890123456789UL), "12345678901234567890123456789");
+    test<T>(make_u128(1234UL, 5678901234567UL, 8901234567890UL), "123456789012345678901234567890");
+    test<T>(make_u128(12345UL, 6789012345678UL, 9012345678901UL), "1234567890123456789012345678901");
+    test<T>(make_u128(123456UL, 7890123456789UL, 123456789012UL), "12345678901234567890123456789012");
+    test<T>(make_u128(1234567UL, 8901234567890UL, 1234567890123UL), "123456789012345678901234567890123");
+    test<T>(make_u128(12345678UL, 9012345678901UL, 2345678901234UL), "1234567890123456789012345678901234");
+    test<T>(make_u128(123456789UL, 123456789012UL, 3456789012345UL), "12345678901234567890123456789012345");
+    test<T>(make_u128(1234567890UL, 1234567890123UL, 4567890123456UL), "123456789012345678901234567890123456");
+    test<T>(make_u128(12345678901UL, 2345678901234UL, 5678901234567UL), "1234567890123456789012345678901234567");
+    test<T>(make_u128(123456789012UL, 3456789012345UL, 6789012345678UL), "12345678901234567890123456789012345678");
+    test<T>(make_u128(1234567890123UL, 4567890123456UL, 7890123456789UL), "123456789012345678901234567890123456789");
 #endif
 
         // Test special cases with zeros inside a value string representation,
         // to_chars algorithm processes them in a special way and should not
         // skip trailing zeros
         // Test driver automatically skips values not fitting into source type.
-        test(0UL, "0");
-        test(10UL, "10");
-        test(100UL, "100");
-        test(1000UL, "1000");
-        test(10000UL, "10000");
-        test(100000UL, "100000");
-        test(1000000UL, "1000000");
-        test(10000000UL, "10000000");
-        test(100000000UL, "100000000");
-        test(1000000000UL, "1000000000");
-        test(10000000000UL, "10000000000");
-        test(100000000000UL, "100000000000");
-        test(1000000000000UL, "1000000000000");
-        test(10000000000000UL, "10000000000000");
-        test(100000000000000UL, "100000000000000");
-        test(1000000000000000UL, "1000000000000000");
-        test(10000000000000000UL, "10000000000000000");
-        test(100000000000000000UL, "100000000000000000");
-        test(1000000000000000000UL, "1000000000000000000");
-        test(10000000000000000000UL, "10000000000000000000");
+        test<T>(0UL, "0");
+        test<T>(10UL, "10");
+        test<T>(100UL, "100");
+        test<T>(1000UL, "1000");
+        test<T>(10000UL, "10000");
+        test<T>(100000UL, "100000");
+        test<T>(1000000UL, "1000000");
+        test<T>(10000000UL, "10000000");
+        test<T>(100000000UL, "100000000");
+        test<T>(1000000000UL, "1000000000");
+        test<T>(10000000000UL, "10000000000");
+        test<T>(100000000000UL, "100000000000");
+        test<T>(1000000000000UL, "1000000000000");
+        test<T>(10000000000000UL, "10000000000000");
+        test<T>(100000000000000UL, "100000000000000");
+        test<T>(1000000000000000UL, "1000000000000000");
+        test<T>(10000000000000000UL, "10000000000000000");
+        test<T>(100000000000000000UL, "100000000000000000");
+        test<T>(1000000000000000000UL, "1000000000000000000");
+        test<T>(10000000000000000000UL, "10000000000000000000");
 #ifndef TEST_HAS_NO_INT128
-        test(make_u128(10UL, 0), "100000000000000000000");
-        test(make_u128(100UL, 0), "1000000000000000000000");
-        test(make_u128(1000UL, 0), "10000000000000000000000");
-        test(make_u128(10000UL, 0), "100000000000000000000000");
-        test(make_u128(100000UL, 0), "1000000000000000000000000");
-        test(make_u128(1000000UL, 0), "10000000000000000000000000");
-        test(make_u128(10000000UL, 0), "100000000000000000000000000");
-        test(make_u128(100000000UL, 0), "1000000000000000000000000000");
-        test(make_u128(100UL, 0, 0), "10000000000000000000000000000");
-        test(make_u128(1000UL, 0, 0), "100000000000000000000000000000");
-        test(make_u128(10000UL, 0, 0), "1000000000000000000000000000000");
-        test(make_u128(100000UL, 0, 0), "10000000000000000000000000000000");
-        test(make_u128(1000000UL, 0, 0), "100000000000000000000000000000000");
-        test(make_u128(10000000UL, 0, 0), "1000000000000000000000000000000000");
-        test(make_u128(100000000UL, 0, 0), "10000000000000000000000000000000000");
-        test(make_u128(1000000000UL, 0, 0), "100000000000000000000000000000000000");
-        test(make_u128(10000000000UL, 0, 0), "1000000000000000000000000000000000000");
-        test(make_u128(100000000000UL, 0, 0), "10000000000000000000000000000000000000");
-        test(make_u128(1000000000000UL, 0, 0), "100000000000000000000000000000000000000");
+        test<T>(make_u128(10UL, 0), "100000000000000000000");
+        test<T>(make_u128(100UL, 0), "1000000000000000000000");
+        test<T>(make_u128(1000UL, 0), "10000000000000000000000");
+        test<T>(make_u128(10000UL, 0), "100000000000000000000000");
+        test<T>(make_u128(100000UL, 0), "1000000000000000000000000");
+        test<T>(make_u128(1000000UL, 0), "10000000000000000000000000");
+        test<T>(make_u128(10000000UL, 0), "100000000000000000000000000");
+        test<T>(make_u128(100000000UL, 0), "1000000000000000000000000000");
+        test<T>(make_u128(100UL, 0, 0), "10000000000000000000000000000");
+        test<T>(make_u128(1000UL, 0, 0), "100000000000000000000000000000");
+        test<T>(make_u128(10000UL, 0, 0), "1000000000000000000000000000000");
+        test<T>(make_u128(100000UL, 0, 0), "10000000000000000000000000000000");
+        test<T>(make_u128(1000000UL, 0, 0), "100000000000000000000000000000000");
+        test<T>(make_u128(10000000UL, 0, 0), "1000000000000000000000000000000000");
+        test<T>(make_u128(100000000UL, 0, 0), "10000000000000000000000000000000000");
+        test<T>(make_u128(1000000000UL, 0, 0), "100000000000000000000000000000000000");
+        test<T>(make_u128(10000000000UL, 0, 0), "1000000000000000000000000000000000000");
+        test<T>(make_u128(100000000000UL, 0, 0), "10000000000000000000000000000000000000");
+        test<T>(make_u128(1000000000000UL, 0, 0), "100000000000000000000000000000000000000");
 #endif
 
         for (int b = 2; b < 37; ++b)
         {
             using xl = std::numeric_limits<T>;
 
-            test_value(1, b);
-            test_value(xl::lowest(), b);
-            test_value((xl::max)(), b);
-            test_value((xl::max)() / 2, b);
+            test_value<T>(1, b);
+            test_value<T>(xl::lowest(), b);
+            test_value<T>((xl::max)(), b);
+            test_value<T>((xl::max)() / 2, b);
         }
-    }
+  }
 };
 
-template <typename T>
-struct test_signed : to_chars_test_base<T>
-{
-    using to_chars_test_base<T>::test;
-    using to_chars_test_base<T>::test_value;
-
-    TEST_CONSTEXPR_CXX23 void operator()()
-    {
-        test(-1, "-1");
-        test(-12, "-12");
-        test(-1, "-1", 10);
-        test(-12, "-12", 10);
-        test(-21734634, "-21734634", 10);
-        test(-2647, "-101001010111", 2);
-        test(-0xcc1, "-cc1", 16);
+struct test_signed : to_chars_test_base {
+  template <typename T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>(-1, "-1");
+    test<T>(-12, "-12");
+    test<T>(-1, "-1", 10);
+    test<T>(-12, "-12", 10);
+    test<T>(-21734634, "-21734634", 10);
+    test<T>(-2647, "-101001010111", 2);
+    test<T>(-0xcc1, "-cc1", 16);
 
-        // Test each len till len of INT64_MAX = 19 because to_chars algorithm
-        // makes branches based on decimal digits count in the value string
-        // representation.
-        // Test driver automatically skips values not fitting into source type.
-        test(-1L, "-1");
-        test(-12L, "-12");
-        test(-123L, "-123");
-        test(-1234L, "-1234");
-        test(-12345L, "-12345");
-        test(-123456L, "-123456");
-        test(-1234567L, "-1234567");
-        test(-12345678L, "-12345678");
-        test(-123456789L, "-123456789");
-        test(-1234567890L, "-1234567890");
-        test(-12345678901L, "-12345678901");
-        test(-123456789012L, "-123456789012");
-        test(-1234567890123L, "-1234567890123");
-        test(-12345678901234L, "-12345678901234");
-        test(-123456789012345L, "-123456789012345");
-        test(-1234567890123456L, "-1234567890123456");
-        test(-12345678901234567L, "-12345678901234567");
-        test(-123456789012345678L, "-123456789012345678");
-        test(-1234567890123456789L, "-1234567890123456789");
+    // Test each len till len of INT64_MAX = 19 because to_chars algorithm
+    // makes branches based on decimal digits count in the value string
+    // representation.
+    // Test driver automatically skips values not fitting into source type.
+    test<T>(-1L, "-1");
+    test<T>(-12L, "-12");
+    test<T>(-123L, "-123");
+    test<T>(-1234L, "-1234");
+    test<T>(-12345L, "-12345");
+    test<T>(-123456L, "-123456");
+    test<T>(-1234567L, "-1234567");
+    test<T>(-12345678L, "-12345678");
+    test<T>(-123456789L, "-123456789");
+    test<T>(-1234567890L, "-1234567890");
+    test<T>(-12345678901L, "-12345678901");
+    test<T>(-123456789012L, "-123456789012");
+    test<T>(-1234567890123L, "-1234567890123");
+    test<T>(-12345678901234L, "-12345678901234");
+    test<T>(-123456789012345L, "-123456789012345");
+    test<T>(-1234567890123456L, "-1234567890123456");
+    test<T>(-12345678901234567L, "-12345678901234567");
+    test<T>(-123456789012345678L, "-123456789012345678");
+    test<T>(-1234567890123456789L, "-1234567890123456789");
 #ifndef TEST_HAS_NO_INT128
-        test(make_i128(-1L, 2345678901234567890L), "-12345678901234567890");
-        test(make_i128(-12L, 3456789012345678901L), "-123456789012345678901");
-        test(make_i128(-123L, 4567890123456789012L), "-1234567890123456789012");
-        test(make_i128(-1234L, 5678901234567890123L), "-12345678901234567890123");
-        test(make_i128(-12345L, 6789012345678901234L), "-123456789012345678901234");
-        test(make_i128(-123456L, 7890123456789012345L), "-1234567890123456789012345");
-        test(make_i128(-1234567L, 8901234567890123456L), "-12345678901234567890123456");
-        test(make_i128(-12345678L, 9012345678901234567L), "-123456789012345678901234567");
-        test(make_i128(-123456789L, 123456789012345678L), "-1234567890123456789012345678");
-        test(make_i128(-1234567890L, 1234567890123456789L), "-12345678901234567890123456789");
-        test(make_i128(-123L, 4567890123456L, 7890123456789L), "-12345678901234567890123456789");
-        test(make_i128(-1234L, 5678901234567L, 8901234567890L), "-123456789012345678901234567890");
-        test(make_i128(-12345L, 6789012345678L, 9012345678901L), "-1234567890123456789012345678901");
-        test(make_i128(-123456L, 7890123456789L, 123456789012L), "-12345678901234567890123456789012");
-        test(make_i128(-1234567L, 8901234567890L, 1234567890123L), "-123456789012345678901234567890123");
-        test(make_i128(-12345678L, 9012345678901L, 2345678901234L), "-1234567890123456789012345678901234");
-        test(make_i128(-123456789L, 123456789012L, 3456789012345L), "-12345678901234567890123456789012345");
-        test(make_i128(-1234567890L, 1234567890123L, 4567890123456L), "-123456789012345678901234567890123456");
-        test(make_i128(-12345678901L, 2345678901234L, 5678901234567L), "-1234567890123456789012345678901234567");
-        test(make_i128(-123456789012L, 3456789012345L, 6789012345678L), "-12345678901234567890123456789012345678");
-        test(make_i128(-1234567890123L, 4567890123456L, 7890123456789L), "-123456789012345678901234567890123456789");
+    test<T>(make_i128(-1L, 2345678901234567890L), "-12345678901234567890");
+    test<T>(make_i128(-12L, 3456789012345678901L), "-123456789012345678901");
+    test<T>(make_i128(-123L, 4567890123456789012L), "-1234567890123456789012");
+    test<T>(make_i128(-1234L, 5678901234567890123L), "-12345678901234567890123");
+    test<T>(make_i128(-12345L, 6789012345678901234L), "-123456789012345678901234");
+    test<T>(make_i128(-123456L, 7890123456789012345L), "-1234567890123456789012345");
+    test<T>(make_i128(-1234567L, 8901234567890123456L), "-12345678901234567890123456");
+    test<T>(make_i128(-12345678L, 9012345678901234567L), "-123456789012345678901234567");
+    test<T>(make_i128(-123456789L, 123456789012345678L), "-1234567890123456789012345678");
+    test<T>(make_i128(-1234567890L, 1234567890123456789L), "-12345678901234567890123456789");
+    test<T>(make_i128(-123L, 4567890123456L, 7890123456789L), "-12345678901234567890123456789");
+    test<T>(make_i128(-1234L, 5678901234567L, 8901234567890L), "-123456789012345678901234567890");
+    test<T>(make_i128(-12345L, 6789012345678L, 9012345678901L), "-1234567890123456789012345678901");
+    test<T>(make_i128(-123456L, 7890123456789L, 123456789012L), "-12345678901234567890123456789012");
+    test<T>(make_i128(-1234567L, 8901234567890L, 1234567890123L), "-123456789012345678901234567890123");
+    test<T>(make_i128(-12345678L, 9012345678901L, 2345678901234L), "-1234567890123456789012345678901234");
+    test<T>(make_i128(-123456789L, 123456789012L, 3456789012345L), "-12345678901234567890123456789012345");
+    test<T>(make_i128(-1234567890L, 1234567890123L, 4567890123456L), "-123456789012345678901234567890123456");
+    test<T>(make_i128(-12345678901L, 2345678901234L, 5678901234567L), "-1234567890123456789012345678901234567");
+    test<T>(make_i128(-123456789012L, 3456789012345L, 6789012345678L), "-12345678901234567890123456789012345678");
+    test<T>(make_i128(-1234567890123L, 4567890123456L, 7890123456789L), "-123456789012345678901234567890123456789");
 #endif
 
         // Test special cases with zeros inside a value string representation,
         // to_chars algorithm processes them in a special way and should not
         // skip trailing zeros
         // Test driver automatically skips values not fitting into source type.
-        test(-10L, "-10");
-        test(-100L, "-100");
-        test(-1000L, "-1000");
-        test(-10000L, "-10000");
-        test(-100000L, "-100000");
-        test(-1000000L, "-1000000");
-        test(-10000000L, "-10000000");
-        test(-100000000L, "-100000000");
-        test(-1000000000L, "-1000000000");
-        test(-10000000000L, "-10000000000");
-        test(-100000000000L, "-100000000000");
-        test(-1000000000000L, "-1000000000000");
-        test(-10000000000000L, "-10000000000000");
-        test(-100000000000000L, "-100000000000000");
-        test(-1000000000000000L, "-1000000000000000");
-        test(-10000000000000000L, "-10000000000000000");
-        test(-100000000000000000L, "-100000000000000000");
-        test(-1000000000000000000L, "-1000000000000000000");
+        test<T>(-10L, "-10");
+        test<T>(-100L, "-100");
+        test<T>(-1000L, "-1000");
+        test<T>(-10000L, "-10000");
+        test<T>(-100000L, "-100000");
+        test<T>(-1000000L, "-1000000");
+        test<T>(-10000000L, "-10000000");
+        test<T>(-100000000L, "-100000000");
+        test<T>(-1000000000L, "-1000000000");
+        test<T>(-10000000000L, "-10000000000");
+        test<T>(-100000000000L, "-100000000000");
+        test<T>(-1000000000000L, "-1000000000000");
+        test<T>(-10000000000000L, "-10000000000000");
+        test<T>(-100000000000000L, "-100000000000000");
+        test<T>(-1000000000000000L, "-1000000000000000");
+        test<T>(-10000000000000000L, "-10000000000000000");
+        test<T>(-100000000000000000L, "-100000000000000000");
+        test<T>(-1000000000000000000L, "-1000000000000000000");
 #ifndef TEST_HAS_NO_INT128
-        test(make_i128(-1L, 0L), "-10000000000000000000");
-        test(make_i128(-10L, 0L), "-100000000000000000000");
-        test(make_i128(-100L, 0L), "-1000000000000000000000");
-        test(make_i128(-1000L, 0L), "-10000000000000000000000");
-        test(make_i128(-10000L, 0L), "-100000000000000000000000");
-        test(make_i128(-100000L, 0L), "-1000000000000000000000000");
-        test(make_i128(-1000000L, 0L), "-10000000000000000000000000");
-        test(make_i128(-10000000L, 0L), "-100000000000000000000000000");
-        test(make_i128(-100000000L, 0L), "-1000000000000000000000000000");
-        test(make_i128(-1000000000L, 0L), "-10000000000000000000000000000");
-        test(make_i128(-100L, 0L, 0L), "-10000000000000000000000000000");
-        test(make_i128(-1000L, 0L, 0L), "-100000000000000000000000000000");
-        test(make_i128(-10000L, 0L, 0L), "-1000000000000000000000000000000");
-        test(make_i128(-100000L, 0L, 0L), "-10000000000000000000000000000000");
-        test(make_i128(-1000000L, 0L, 0L), "-100000000000000000000000000000000");
-        test(make_i128(-10000000L, 0L, 0L), "-1000000000000000000000000000000000");
-        test(make_i128(-100000000L, 0L, 0L), "-10000000000000000000000000000000000");
-        test(make_i128(-1000000000L, 0L, 0L), "-100000000000000000000000000000000000");
-        test(make_i128(-10000000000L, 0L, 0L), "-1000000000000000000000000000000000000");
-        test(make_i128(-100000000000L, 0L, 0L), "-10000000000000000000000000000000000000");
-        test(make_i128(-1000000000000L, 0L, 0L), "-100000000000000000000000000000000000000");
+        test<T>(make_i128(-1L, 0L), "-10000000000000000000");
+        test<T>(make_i128(-10L, 0L), "-100000000000000000000");
+        test<T>(make_i128(-100L, 0L), "-1000000000000000000000");
+        test<T>(make_i128(-1000L, 0L), "-10000000000000000000000");
+        test<T>(make_i128(-10000L, 0L), "-100000000000000000000000");
+        test<T>(make_i128(-100000L, 0L), "-1000000000000000000000000");
+        test<T>(make_i128(-1000000L, 0L), "-10000000000000000000000000");
+        test<T>(make_i128(-10000000L, 0L), "-100000000000000000000000000");
+        test<T>(make_i128(-100000000L, 0L), "-1000000000000000000000000000");
+        test<T>(make_i128(-1000000000L, 0L), "-10000000000000000000000000000");
+        test<T>(make_i128(-100L, 0L, 0L), "-10000000000000000000000000000");
+        test<T>(make_i128(-1000L, 0L, 0L), "-100000000000000000000000000000");
+        test<T>(make_i128(-10000L, 0L, 0L), "-1000000000000000000000000000000");
+        test<T>(make_i128(-100000L, 0L, 0L), "-10000000000000000000000000000000");
+        test<T>(make_i128(-1000000L, 0L, 0L), "-100000000000000000000000000000000");
+        test<T>(make_i128(-10000000L, 0L, 0L), "-1000000000000000000000000000000000");
+        test<T>(make_i128(-100000000L, 0L, 0L), "-10000000000000000000000000000000000");
+        test<T>(make_i128(-1000000000L, 0L, 0L), "-100000000000000000000000000000000000");
+        test<T>(make_i128(-10000000000L, 0L, 0L), "-1000000000000000000000000000000000000");
+        test<T>(make_i128(-100000000000L, 0L, 0L), "-10000000000000000000000000000000000000");
+        test<T>(make_i128(-1000000000000L, 0L, 0L), "-100000000000000000000000000000000000000");
 #endif
 
         for (int b = 2; b < 37; ++b)
         {
             using xl = std::numeric_limits<T>;
 
-            test_value(0, b);
-            test_value(xl::lowest(), b);
-            test_value((xl::max)(), b);
+            test_value<T>(0, b);
+            test_value<T>(xl::lowest(), b);
+            test_value<T>((xl::max)(), b);
         }
-    }
+  }
 };
 
 TEST_CONSTEXPR_CXX23 bool test()
 {
-    run<test_basics>(integrals);
-    run<test_signed>(all_signed);
+  types::for_each(integrals(), test_basics());
+  types::for_each(types::signed_integer_types(), test_signed());
 
-    return true;
+  return true;
 }
 
 int main(int, char**)
diff --git a/libcxx/test/support/charconv_test_helpers.h b/libcxx/test/support/charconv_test_helpers.h
index fcae09478457b..7cd14204ddb00 100644
--- a/libcxx/test/support/charconv_test_helpers.h
+++ b/libcxx/test/support/charconv_test_helpers.h
@@ -21,6 +21,7 @@
 #include <type_traits>
 
 #include "test_macros.h"
+#include "type_algorithms.h"
 
 #if TEST_STD_VER < 11
 #error This file requires C++11
@@ -78,61 +79,58 @@ fits_in(T v)
   return _fits_in<X>(v, is_non_narrowing<X>(v), std::is_signed<T>(), std::is_signed<X>());
 }
 
-template <typename X>
 struct to_chars_test_base
 {
-    template <typename T, std::size_t N, typename... Ts>
-    TEST_CONSTEXPR_CXX23 void test(T v, char const (&expect)[N], Ts... args)
-    {
-        std::to_chars_result r;
-
-        constexpr std::size_t len = N - 1;
-        static_assert(len > 0, "expected output won't be empty");
-
-        if (!fits_in<X>(v))
-            return;
-
-        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 = std::to_chars(buf, buf + sizeof(buf), X(v), args...);
-        assert(r.ptr == buf + len);
-        assert(r.ec == std::errc{});
-        assert(std::equal(buf, buf + len, expect));
-    }
-
-    template <typename... Ts>
-    TEST_CONSTEXPR_CXX23 void test_value(X v, Ts... args)
-    {
-        std::to_chars_result r;
-
-        // Poison the buffer for testing whether a successful std::to_chars
-        // 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 (std::size_t i = r.ptr - buf; i < sizeof(buf); ++i)
-            assert(static_cast<unsigned char>(buf[i]) == i + 1);
-        *r.ptr = '\0';
+  template <typename X, typename T, std::size_t N, typename... Ts>
+  TEST_CONSTEXPR_CXX23 void test(T v, char const (&expect)[N], Ts... args) {
+    std::to_chars_result r;
+
+    constexpr std::size_t len = N - 1;
+    static_assert(len > 0, "expected output won't be empty");
+
+    if (!fits_in<X>(v))
+      return;
+
+    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 = std::to_chars(buf, buf + sizeof(buf), X(v), args...);
+    assert(r.ptr == buf + len);
+    assert(r.ec == std::errc{});
+    assert(std::equal(buf, buf + len, expect));
+  }
+
+  template <typename X, typename... Ts>
+  TEST_CONSTEXPR_CXX23 void test_value(X v, Ts... args) {
+    std::to_chars_result r;
+
+    // Poison the buffer for testing whether a successful std::to_chars
+    // 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 (std::size_t i = r.ptr - buf; i < sizeof(buf); ++i)
+      assert(static_cast<unsigned char>(buf[i]) == i + 1);
+    *r.ptr = '\0';
 
 #ifndef TEST_HAS_NO_INT128
         if (sizeof(X) == sizeof(__int128_t)) {
-            auto a = fromchars128_impl(buf, r.ptr, args...);
-            assert(v == a);
+          auto a = fromchars128_impl<X>(buf, r.ptr, args...);
+          assert(v == a);
         } else
 #endif
         {
-            auto a = fromchars_impl(buf, r.ptr, args...);
-            assert(v == a);
+          auto a = fromchars_impl<X>(buf, r.ptr, args...);
+          assert(v == a);
         }
 
         auto ep = r.ptr - 1;
         r = std::to_chars(buf, ep, v, args...);
         assert(r.ptr == ep);
         assert(r.ec == std::errc::value_too_large);
-    }
+  }
 
 private:
     static TEST_CONSTEXPR_CXX23 long long fromchars_impl(char const* p, char const* ep, int base, true_type)
@@ -202,129 +200,66 @@ struct to_chars_test_base
         return r;
     }
 
+    template <typename X>
     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>());
+        -> decltype(fromchars128_impl(p, ep, base, std::is_signed<X>())) {
+      return fromchars128_impl(p, ep, base, std::is_signed<X>());
     }
 
 #endif
 
+    template <typename X>
     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>());
+        -> decltype(fromchars_impl(p, ep, base, std::is_signed<X>())) {
+      return fromchars_impl(p, ep, base, std::is_signed<X>());
     }
 
     char buf[150];
 };
 
-template <typename X>
 struct roundtrip_test_base
 {
-    template <typename T, typename... Ts>
-    TEST_CONSTEXPR_CXX23 void test(T v, Ts... args)
-    {
-        std::from_chars_result r2;
-        std::to_chars_result r;
-        X x = 0xc;
-
-        if (fits_in<X>(v))
-        {
-            r = std::to_chars(buf, buf + sizeof(buf), v, args...);
-            assert(r.ec == std::errc{});
-
-            r2 = std::from_chars(buf, r.ptr, x, args...);
-            assert(r2.ptr == r.ptr);
-            assert(x == X(v));
-        }
-        else
-        {
-            r = std::to_chars(buf, buf + sizeof(buf), v, args...);
-            assert(r.ec == std::errc{});
-
-            r2 = std::from_chars(buf, r.ptr, x, args...);
-
-            TEST_DIAGNOSTIC_PUSH
-            TEST_MSVC_DIAGNOSTIC_IGNORED(4127) // conditional expression is constant
-
-            if (std::is_signed<T>::value && v < 0 && std::is_unsigned<X>::value)
-            {
-                assert(x == 0xc);
-                assert(r2.ptr == buf);
-                assert(r2.ec == std::errc::invalid_argument);
-            }
-            else
-            {
-                assert(x == 0xc);
-                assert(r2.ptr == r.ptr);
-                assert(r2.ec == std::errc::result_out_of_range);
-            }
-
-            TEST_DIAGNOSTIC_POP
-        }
+  template <typename X, typename T, typename... Ts>
+  TEST_CONSTEXPR_CXX23 void test(T v, Ts... args) {
+    std::from_chars_result r2;
+    std::to_chars_result r;
+    X x = 0xc;
+
+    if (fits_in<X>(v)) {
+      r = std::to_chars(buf, buf + sizeof(buf), v, args...);
+      assert(r.ec == std::errc{});
+
+      r2 = std::from_chars(buf, r.ptr, x, args...);
+      assert(r2.ptr == r.ptr);
+      assert(x == X(v));
+    } else {
+      r = std::to_chars(buf, buf + sizeof(buf), v, args...);
+      assert(r.ec == std::errc{});
+
+      r2 = std::from_chars(buf, r.ptr, x, args...);
+
+      TEST_DIAGNOSTIC_PUSH
+      TEST_MSVC_DIAGNOSTIC_IGNORED(4127) // conditional expression is constant
+
+      if (std::is_signed<T>::value && v < 0 && std::is_unsigned<X>::value) {
+        assert(x == 0xc);
+        assert(r2.ptr == buf);
+        assert(r2.ec == std::errc::invalid_argument);
+      } else {
+        assert(x == 0xc);
+        assert(r2.ptr == r.ptr);
+        assert(r2.ec == std::errc::result_out_of_range);
+      }
+
+      TEST_DIAGNOSTIC_POP
     }
+  }
 
 private:
     char buf[150];
 };
 
-template <typename... T>
-struct type_list
-{
-};
-
-template <typename L1, typename L2>
-struct type_concat;
-
-template <typename... Xs, typename... Ys>
-struct type_concat<type_list<Xs...>, type_list<Ys...>>
-{
-    using type = type_list<Xs..., Ys...>;
-};
-
-template <typename L1, typename L2>
-using concat_t = typename type_concat<L1, L2>::type;
-
-template <typename L1, typename L2>
-constexpr auto concat(L1, L2) -> concat_t<L1, L2>
-{
-    return {};
-}
-
-auto all_signed = type_list<
-    char,
-    signed char,
-    short,
-    int,
-    long,
-    long long
-#ifndef TEST_HAS_NO_INT128
-    ,
-    __int128_t
-#endif
-    >();
-auto all_unsigned = type_list<
-    unsigned char,
-    unsigned short,
-    unsigned int,
-    unsigned long,
-    unsigned long long
-#ifndef TEST_HAS_NO_INT128
-    ,
-    __uint128_t
-#endif
-    >();
-auto integrals = concat(all_signed, all_unsigned);
-
-auto all_floats = type_list< float, double >(); //TODO: Add long double
-
-template <template <typename> class Fn, typename... Ts>
-TEST_CONSTEXPR_CXX23 void
-run(type_list<Ts...>)
-{
-    int ls[sizeof...(Ts)] = {(Fn<Ts>{}(), 0)...};
-    (void)ls;
-}
+using integrals =
+    types::concatenate_t<types::type_list<char>, types::signed_integer_types, types::unsigned_integer_types>;
 
 #endif // SUPPORT_CHARCONV_TEST_HELPERS_H

>From 76b9f3db97e175c75c87b923d8ebee0f0b4cd75d Mon Sep 17 00:00:00 2001
From: Henri Chataing <henrichataing at google.com>
Date: Wed, 23 Sep 2026 14:51:23 +0000
Subject: [PATCH 2/2] [libc++] Suppress unsigned-integer-overflow warning in
 <charconv>

Invoking __complement with 0 as parameter triggers this runtime error:

> include/c++/v1/__charconv/traits.h:181:19: runtime error: unsigned integer overflow: 18446744073709551615 + 1 cannot be represented in type 'unsigned long'
> SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior include/c++/v1/__charconv/traits.h:181:19
---
 libcxx/include/__charconv/traits.h                   |  3 ++-
 .../charconv/charconv.from.chars/integral.pass.cpp   | 12 ++++++++++++
 2 files changed, 14 insertions(+), 1 deletion(-)

diff --git a/libcxx/include/__charconv/traits.h b/libcxx/include/__charconv/traits.h
index b8c840d1ebe32..a13c30a19c23d 100644
--- a/libcxx/include/__charconv/traits.h
+++ b/libcxx/include/__charconv/traits.h
@@ -155,7 +155,8 @@ struct _LIBCPP_HIDDEN __traits : __traits_base<_Tp> {
 } // namespace __itoa
 
 template <typename _Tp>
-inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI _Tp __complement(_Tp __x) {
+inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI _LIBCPP_DISABLE_UBSAN_UNSIGNED_INTEGER_CHECK _Tp
+__complement(_Tp __x) {
   static_assert(is_unsigned<_Tp>::value, "cast to unsigned first");
   return _Tp(~__x + 1);
 }
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 f1fe8af9cc424..0ece8bdc8bbfa 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
@@ -8,6 +8,9 @@
 
 // UNSUPPORTED: c++03, c++11, c++14
 
+// FIXME: Enable this for all tests once possible
+// ADDITIONAL_COMPILE_FLAGS(ubsan): -fsanitize=unsigned-integer-overflow
+
 // <charconv>
 
 // constexpr from_chars_result from_chars(const char* first, const char* last,
@@ -129,6 +132,15 @@ struct test_signed
       // the member ec is equal to errc::invalid_argument.
       assert(r.ec == std::errc::invalid_argument);
     }
+
+    {
+      // Ensure "-0" does not trigger unsigned integer overflow.
+      char s[] = "-0";
+      r        = std::from_chars(s, s + sizeof(s), x);
+      assert(r.ec == std::errc{});
+      assert(r.ptr == s + 2);
+      assert(x == 0);
+    }
   }
 };
 



More information about the libcxx-commits mailing list