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

via libcxx-commits libcxx-commits at lists.llvm.org
Tue Sep 29 02:55:29 PDT 2026


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

>From 993963d0435eae62af4185e3ed36c8ccdd17b490 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] [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    | 11 +++++++++++
 libcxx/test/support/charconv_test_helpers.h           |  4 +++-
 3 files changed, 16 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/__charconv/traits.h b/libcxx/include/__charconv/traits.h
index b8c840d1ebe32..2f08dac4c2afe 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 4708da4c38b9b..6c1d7e0cc5497 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,8 @@
 
 // UNSUPPORTED: c++03, c++11, c++14
 
+// ADDITIONAL_COMPILE_FLAGS(ubsan): -fsanitize=unsigned-integer-overflow
+
 // <charconv>
 
 // constexpr from_chars_result from_chars(const char* first, const char* last,
@@ -134,6 +136,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 in __complement(0)
+            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);
+        }
     }
 };
 
diff --git a/libcxx/test/support/charconv_test_helpers.h b/libcxx/test/support/charconv_test_helpers.h
index fcae09478457b..fce7db24cc286 100644
--- a/libcxx/test/support/charconv_test_helpers.h
+++ b/libcxx/test/support/charconv_test_helpers.h
@@ -323,7 +323,9 @@ template <template <typename> class Fn, typename... Ts>
 TEST_CONSTEXPR_CXX23 void
 run(type_list<Ts...>)
 {
-    int ls[sizeof...(Ts)] = {(Fn<Ts>{}(), 0)...};
+    // Avoid constant-folding the array initializer at compile time when Fn<Ts>{}() is constexpr.
+    int dummy = 0;
+    int ls[sizeof...(Ts)] = {(Fn<Ts>{}(), dummy)...};
     (void)ls;
 }
 



More information about the libcxx-commits mailing list