[libcxx-commits] [libcxx] [libcxxabi] [libc++] Add __checked_int and replace uses of the overflow builtins with it (PR #234585)

Nikolas Klauser via libcxx-commits libcxx-commits at lists.llvm.org
Thu Oct 22 04:22:15 PDT 2026


https://github.com/philnik777 created https://github.com/llvm/llvm-project/pull/234585

None

>From 6faa93a2ffd7d851934abcada1d4fe5d120e1e01 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Thu, 22 Oct 2026 13:21:56 +0200
Subject: [PATCH] [libc++] Add __checked_int and replace uses of the overflow
 builtins with it

---
 libcxx/include/CMakeLists.txt                 |   1 +
 .../include/__charconv/from_chars_integral.h  |   5 +-
 libcxx/include/__charconv/traits.h            |  12 +-
 libcxx/include/__locale_dir/num.h             |  26 ++--
 libcxx/include/__mdspan/layout_left.h         |   8 +-
 libcxx/include/__mdspan/layout_right.h        |   8 +-
 libcxx/include/__mdspan/layout_stride.h       |  21 ++--
 libcxx/include/__mdspan/mdspan.h              |   8 +-
 libcxx/include/__memory/shared_ptr.h          |  17 +--
 libcxx/include/__numeric/gcd_lcm.h            |   8 +-
 .../include/__numeric/saturation_arithmetic.h |  14 +--
 libcxx/include/__utility/checked_int.h        | 113 ++++++++++++++++++
 libcxx/include/fstream                        |   5 +-
 libcxx/include/module.modulemap.in            |   1 +
 libcxxabi/src/cxa_vector.cpp                  |  36 +-----
 15 files changed, 184 insertions(+), 99 deletions(-)
 create mode 100644 libcxx/include/__utility/checked_int.h

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index fcb66d5fe2f48a..b5d560fe4f3823 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -969,6 +969,7 @@ set(files
   __utility/as_lvalue.h
   __utility/assume.h
   __utility/auto_cast.h
+  __utility/checked_int.h
   __utility/cmp.h
   __utility/constant_wrapper.h
   __utility/convert_to_integral.h
diff --git a/libcxx/include/__charconv/from_chars_integral.h b/libcxx/include/__charconv/from_chars_integral.h
index 8fd0c0cb4011ec..5509499e0e8645 100644
--- a/libcxx/include/__charconv/from_chars_integral.h
+++ b/libcxx/include/__charconv/from_chars_integral.h
@@ -20,6 +20,7 @@
 #include <__type_traits/is_signed.h>
 #include <__type_traits/is_unsigned.h>
 #include <__type_traits/make_unsigned.h>
+#include <__utility/checked_int.h>
 #include <limits>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
@@ -189,7 +190,9 @@ __from_chars_integral(const char* __first, const char* __last, _Tp& __value, int
             if (__i < __digits - 1)
               __x = __x * __b + __c.__val;
             else {
-              if (!__itoa::__mul_overflowed(__x, __b, __x))
+              auto __res = __checked_int<_Tp>(__x) * static_cast<unsigned char>(__b);
+              __x        = __res.__value();
+              if (!__res.__overflowed())
                 ++__p;
               __y = __c.__val;
               break;
diff --git a/libcxx/include/__charconv/traits.h b/libcxx/include/__charconv/traits.h
index ab7faacc0947d8..fa6776f5777011 100644
--- a/libcxx/include/__charconv/traits.h
+++ b/libcxx/include/__charconv/traits.h
@@ -15,9 +15,9 @@
 #include <__charconv/tables.h>
 #include <__charconv/to_chars_base_10.h>
 #include <__config>
-#include <__memory/addressof.h>
 #include <__type_traits/enable_if.h>
 #include <__type_traits/is_unsigned.h>
+#include <__utility/checked_int.h>
 #include <cstdint>
 #include <limits>
 
@@ -101,12 +101,6 @@ struct _LIBCPP_HIDDEN __traits_base<_Tp, __enable_if_t<sizeof(_Tp) == sizeof(__u
 };
 #endif
 
-template <typename _Tp, typename _Up>
-bool _LIBCPP_CONSTEXPR_SINCE_CXX23 __mul_overflowed(_Tp __a, _Up __b, _Tp& __r) {
-  static_assert(is_unsigned<_Tp>::value);
-  return __builtin_mul_overflow(__a, static_cast<_Tp>(__b), std::addressof(__r));
-}
-
 template <typename _Tp>
 struct _LIBCPP_HIDDEN __traits : __traits_base<_Tp> {
   static _LIBCPP_CONSTEXPR const int digits = numeric_limits<_Tp>::digits10 + 1;
@@ -125,7 +119,9 @@ struct _LIBCPP_HIDDEN __traits : __traits_base<_Tp> {
     } while (__p != __ep && __i != 0);
 
     __a = __inner_product(__cprod + __i + 1, __cprod + __j, __pow() + 1, __cprod[__i]);
-    if (__itoa::__mul_overflowed(__cprod[__j], __pow()[__j - __i], __b))
+    auto __res = __checked_int<type>(__cprod[__j]) * __pow()[__j - __i];
+    __b        = __res.__value();
+    if (__res.__overflowed())
       --__p;
     return __p;
   }
diff --git a/libcxx/include/__locale_dir/num.h b/libcxx/include/__locale_dir/num.h
index 6774903aa00693..923cf074ea290f 100644
--- a/libcxx/include/__locale_dir/num.h
+++ b/libcxx/include/__locale_dir/num.h
@@ -26,6 +26,7 @@
 #include <__memory/unique_ptr.h>
 #include <__system_error/errc.h>
 #include <__type_traits/is_signed.h>
+#include <__utility/checked_int.h>
 #include <cerrno>
 #include <ios>
 #include <streambuf>
@@ -465,7 +466,7 @@ class num_get : public locale::facet, private __num_get<_CharT> {
     using _Unsigned = __make_unsigned_t<_MaybeSigned>;
 
     // Stage 1
-    int __base = this->__get_base(__iob);
+    unsigned char __base = this->__get_base(__iob);
 
     // Stages 2 & 3
     // These are combined into a single step where we parse the characters and calculate the value in one go instead of
@@ -555,8 +556,7 @@ class num_get : public locale::facet, private __num_get<_CharT> {
     }
 
     // Calculate the actual number
-    _Unsigned __val   = 0;
-    bool __overflowed = false;
+    __checked_int<_Unsigned> __val(0);
     for (; __first != __last; ++__first) {
       auto __c = *__first;
       if (!__grouping.empty() && __c == __thousands_sep) {
@@ -574,9 +574,7 @@ class num_get : public locale::facet, private __num_get<_CharT> {
         __offset -= 6;
       if (__offset >= __base)
         break;
-      // __val = (__val * __base) + __offset
-      __overflowed |= __builtin_mul_overflow(__val, __base, std::addressof(__val)) ||
-                      __builtin_add_overflow(__val, __offset, std::addressof(__val));
+      __val = (__val * __base) + static_cast<unsigned short>(__offset);
       __parsed_num = true;
       ++__dc;
     }
@@ -584,29 +582,29 @@ class num_get : public locale::facet, private __num_get<_CharT> {
     if (!__parsed_num) {
       __err |= ios_base::failbit;
       __v = 0;
-    } else if (__overflowed) {
+    } else if (__val.__overflowed()) {
       __err |= ios_base::failbit;
       __v = is_signed<_MaybeSigned>::value && __negate
               ? numeric_limits<_MaybeSigned>::min()
               : numeric_limits<_MaybeSigned>::max();
-    } else if (!__negate) {
-      if (__val > static_cast<_Unsigned>(numeric_limits<_MaybeSigned>::max())) {
+    } else if (auto __value = __val.__value(); !__negate) {
+      if (__value > static_cast<_Unsigned>(numeric_limits<_MaybeSigned>::max())) {
         __err |= ios_base::failbit;
         __v = numeric_limits<_MaybeSigned>::max();
       } else {
-        __v = __val;
+        __v = __value;
       }
     } else if (is_signed<_MaybeSigned>::value) {
-      if (__val > static_cast<_Unsigned>(numeric_limits<_MaybeSigned>::max()) + 1) {
+      if (__value > static_cast<_Unsigned>(numeric_limits<_MaybeSigned>::max()) + 1) {
         __err |= ios_base::failbit;
         __v = numeric_limits<_MaybeSigned>::min();
-      } else if (__val == static_cast<_Unsigned>(numeric_limits<_MaybeSigned>::max()) + 1) {
+      } else if (__value == static_cast<_Unsigned>(numeric_limits<_MaybeSigned>::max()) + 1) {
         __v = numeric_limits<_MaybeSigned>::min();
       } else {
-        __v = -__val;
+        __v = -__value;
       }
     } else {
-      __v = -__val;
+      __v = -__value;
     }
 
     if (__grouping.size() != 0 && __g_end - __g < __num_get_base::__num_get_buf_sz)
diff --git a/libcxx/include/__mdspan/layout_left.h b/libcxx/include/__mdspan/layout_left.h
index fb1b59555b254b..d8af7de392df80 100644
--- a/libcxx/include/__mdspan/layout_left.h
+++ b/libcxx/include/__mdspan/layout_left.h
@@ -21,11 +21,11 @@
 #include <__config>
 #include <__fwd/mdspan.h>
 #include <__mdspan/extents.h>
-#include <__memory/addressof.h>
 #include <__type_traits/common_type.h>
 #include <__type_traits/is_constructible.h>
 #include <__type_traits/is_convertible.h>
 #include <__type_traits/is_nothrow_constructible.h>
+#include <__utility/checked_int.h>
 #include <__utility/integer_sequence.h>
 #include <array>
 
@@ -57,10 +57,10 @@ class layout_left::mapping {
     if constexpr (extents_type::rank() == 0)
       return true;
 
-    index_type __prod = __ext.extent(0);
+    __checked_int<index_type> __prod(__ext.extent(0));
     for (rank_type __r = 1; __r < extents_type::rank(); __r++) {
-      bool __overflowed = __builtin_mul_overflow(__prod, __ext.extent(__r), std::addressof(__prod));
-      if (__overflowed)
+      __prod *= __ext.extent(__r);
+      if (__prod.__overflowed())
         return false;
     }
     return true;
diff --git a/libcxx/include/__mdspan/layout_right.h b/libcxx/include/__mdspan/layout_right.h
index 7cb828022ea8e0..d3b01f32f56576 100644
--- a/libcxx/include/__mdspan/layout_right.h
+++ b/libcxx/include/__mdspan/layout_right.h
@@ -22,11 +22,11 @@
 #include <__cstddef/size_t.h>
 #include <__fwd/mdspan.h>
 #include <__mdspan/extents.h>
-#include <__memory/addressof.h>
 #include <__type_traits/common_type.h>
 #include <__type_traits/is_constructible.h>
 #include <__type_traits/is_convertible.h>
 #include <__type_traits/is_nothrow_constructible.h>
+#include <__utility/checked_int.h>
 #include <__utility/integer_sequence.h>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
@@ -57,10 +57,10 @@ class layout_right::mapping {
     if constexpr (extents_type::rank() == 0)
       return true;
 
-    index_type __prod = __ext.extent(0);
+    __checked_int __prod(__ext.extent(0));
     for (rank_type __r = 1; __r < extents_type::rank(); __r++) {
-      bool __overflowed = __builtin_mul_overflow(__prod, __ext.extent(__r), std::addressof(__prod));
-      if (__overflowed)
+      __prod *= __ext.extent(__r);
+      if (__prod.__overflowed())
         return false;
     }
     return true;
diff --git a/libcxx/include/__mdspan/layout_stride.h b/libcxx/include/__mdspan/layout_stride.h
index 5ee4672e0d1cfb..d7d61634e2a353 100644
--- a/libcxx/include/__mdspan/layout_stride.h
+++ b/libcxx/include/__mdspan/layout_stride.h
@@ -22,7 +22,6 @@
 #include <__config>
 #include <__fwd/mdspan.h>
 #include <__mdspan/extents.h>
-#include <__memory/addressof.h>
 #include <__type_traits/common_type.h>
 #include <__type_traits/is_constructible.h>
 #include <__type_traits/is_convertible.h>
@@ -30,6 +29,7 @@
 #include <__type_traits/is_nothrow_constructible.h>
 #include <__type_traits/is_same.h>
 #include <__utility/as_const.h>
+#include <__utility/checked_int.h>
 #include <__utility/integer_sequence.h>
 #include <__utility/swap.h>
 #include <array>
@@ -85,10 +85,10 @@ class layout_stride::mapping {
     if constexpr (__rank_ == 0)
       return true;
 
-    index_type __prod = __ext.extent(0);
-    for (rank_type __r = 1; __r < __rank_; __r++) {
-      bool __overflowed = __builtin_mul_overflow(__prod, __ext.extent(__r), std::addressof(__prod));
-      if (__overflowed)
+    __checked_int __prod(__ext.extent(0));
+    for (rank_type __r = 1; __r < extents_type::rank(); __r++) {
+      __prod *= __ext.extent(__r);
+      if (__prod.__overflowed())
         return false;
     }
     return true;
@@ -110,14 +110,11 @@ class layout_stride::mapping {
       }
       if (__ext.extent(__r) == static_cast<index_type>(0))
         return true;
-      index_type __prod = (__ext.extent(__r) - 1);
-      bool __overflowed_mul =
-          __builtin_mul_overflow(__prod, static_cast<index_type>(__strides[__r]), std::addressof(__prod));
-      if (__overflowed_mul)
-        return false;
-      bool __overflowed_add = __builtin_add_overflow(__size, __prod, std::addressof(__size));
-      if (__overflowed_add)
+      __checked_int<index_type> __prod{__ext.extent(__r) - 1};
+      __prod = __prod * static_cast<index_type>(__strides[__r]) + __size;
+      if (__prod.__overflowed())
         return false;
+      __size = __prod.__value();
     }
     return true;
   }
diff --git a/libcxx/include/__mdspan/mdspan.h b/libcxx/include/__mdspan/mdspan.h
index 3b78f792770f06..eba908da6caa08 100644
--- a/libcxx/include/__mdspan/mdspan.h
+++ b/libcxx/include/__mdspan/mdspan.h
@@ -39,6 +39,7 @@
 #include <__type_traits/remove_pointer.h>
 #include <__type_traits/remove_reference.h>
 #include <__utility/as_const.h>
+#include <__utility/checked_int.h>
 #include <__utility/integer_sequence.h>
 #include <array>
 #include <span>
@@ -254,10 +255,9 @@ class mdspan {
     // Could leave this as only checked in debug mode: semantically size() is never
     // guaranteed to be related to any accessible range
     _LIBCPP_ASSERT_UNCATEGORIZED(
-        false == ([&]<size_t... _Idxs>(index_sequence<_Idxs...>) {
-          size_type __prod = 1;
-          return (__builtin_mul_overflow(__prod, extent(_Idxs), std::addressof(__prod)) || ... || false);
-        }(make_index_sequence<rank()>())),
+        [&]<size_t... _Idxs>(index_sequence<_Idxs...>) {
+          return !(__checked_int(extent(_Idxs)) * ...).__overflowed();
+        }(make_index_sequence<rank()>()),
         "mdspan: size() is not representable as size_type");
     return [&]<size_t... _Idxs>(index_sequence<_Idxs...>) {
       return ((static_cast<size_type>(__map_.extents().extent(_Idxs))) * ... * size_type(1));
diff --git a/libcxx/include/__memory/shared_ptr.h b/libcxx/include/__memory/shared_ptr.h
index 0db58b37a6288b..549b9df9390daf 100644
--- a/libcxx/include/__memory/shared_ptr.h
+++ b/libcxx/include/__memory/shared_ptr.h
@@ -54,6 +54,7 @@
 #include <__type_traits/remove_reference.h>
 #include <__type_traits/type_identity.h>
 #include <__type_traits/void_t.h>
+#include <__utility/checked_int.h>
 #include <__utility/declval.h>
 #include <__utility/exception_guard.h>
 #include <__utility/forward.h>
@@ -748,20 +749,20 @@ struct __unbounded_array_control_block<_Tp[], _Alloc> : __shared_weak_count {
     // When there's 0 elements, the control block alone is enough since it holds one element.
     // Otherwise, we allocate one fewer element than requested because the control block already
     // holds one.
-    size_t __bytes = sizeof(__unbounded_array_control_block);
-    if (__elements != 0) {
-      if (__builtin_mul_overflow(__elements - 1, sizeof(_Tp), &__bytes) ||
-          __builtin_add_overflow(__bytes, sizeof(__unbounded_array_control_block), &__bytes))
-        std::__throw_bad_array_new_length();
-    }
+    __checked_int<size_t> __bytes(sizeof(__unbounded_array_control_block));
+    if (__elements != 0)
+      __bytes = __checked_int<size_t>(__elements - 1) * sizeof(__unbounded_array_control_block) +
+                sizeof(__unbounded_array_control_block);
 
     // We use the bitwise formula below to ensure that we allocate enough bytes for the whole
     // allocation to be a multiple of _Tp's alignment. That formula is taken from [1].
     //
     // [1]: https://en.wikipedia.org/wiki/Data_structure_alignment#Computing_padding
-    if (__builtin_add_overflow(__bytes, __align - 1, &__bytes))
+    __bytes += __align - 1;
+    if (__bytes.__overflowed())
       std::__throw_bad_array_new_length();
-    return __bytes & ~(__align - 1);
+
+    return __bytes.__value() & ~(__align - 1);
   }
 
   _LIBCPP_HIDE_FROM_ABI_VIRTUAL
diff --git a/libcxx/include/__numeric/gcd_lcm.h b/libcxx/include/__numeric/gcd_lcm.h
index 753b42621fcc1f..7f9a1595d852f8 100644
--- a/libcxx/include/__numeric/gcd_lcm.h
+++ b/libcxx/include/__numeric/gcd_lcm.h
@@ -20,6 +20,7 @@
 #include <__type_traits/is_signed.h>
 #include <__type_traits/make_unsigned.h>
 #include <__type_traits/remove_cv.h>
+#include <__utility/checked_int.h>
 #include <limits>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
@@ -105,10 +106,9 @@ template <class _Tp, class _Up>
   using _Rp  = common_type_t<_Tp, _Up>;
   _Rp __val1 = std::__abs_in_type<_Rp>(__m) / std::gcd(__m, __n);
   _Rp __val2 = std::__abs_in_type<_Rp>(__n);
-  _Rp __res;
-  [[maybe_unused]] bool __overflow = __builtin_mul_overflow(__val1, __val2, std::addressof(__res));
-  _LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN(!__overflow, "Overflow in lcm");
-  return __res;
+  auto __res = __checked_int(__val1) * __val2;
+  _LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN(!__res.__overflowed(), "Overflow in lcm");
+  return __res.__value();
 }
 
 #endif // _LIBCPP_STD_VER >= 17
diff --git a/libcxx/include/__numeric/saturation_arithmetic.h b/libcxx/include/__numeric/saturation_arithmetic.h
index f113370c271d01..2426e96f622fc8 100644
--- a/libcxx/include/__numeric/saturation_arithmetic.h
+++ b/libcxx/include/__numeric/saturation_arithmetic.h
@@ -12,8 +12,8 @@
 
 #include <__assert>
 #include <__config>
-#include <__memory/addressof.h>
 #include <__type_traits/integer_traits.h>
+#include <__utility/checked_int.h>
 #include <__utility/cmp.h>
 #include <limits>
 
@@ -32,8 +32,8 @@ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Tp __saturating_add(_Tp __x, _Tp __y) _
 #  if defined(_LIBCPP_CLANG_VER) && _LIBCPP_CLANG_VER >= 2101
   return __builtin_elementwise_add_sat(__x, __y);
 #  else
-  if (_Tp __sum; !__builtin_add_overflow(__x, __y, std::addressof(__sum)))
-    return __sum;
+  if (auto __sum = __checked_int<_Tp>(__x) + __y; !__sum.__overflowed())
+    return __sum.__value();
   // Handle overflow
   if _LIBCPP_CONSTEXPR (__is_unsigned_integer_v<_Tp>) {
     return std::numeric_limits<_Tp>::max();
@@ -56,8 +56,8 @@ _LIBCPP_HIDE_FROM_ABI constexpr _Tp __saturating_sub(_Tp __x, _Tp __y) noexcept
 #  if defined(_LIBCPP_CLANG_VER) && _LIBCPP_CLANG_VER >= 2101
   return __builtin_elementwise_sub_sat(__x, __y);
 #  else
-  if (_Tp __sub; !__builtin_sub_overflow(__x, __y, std::addressof(__sub)))
-    return __sub;
+  if (auto __sub = __checked_int(__x) - __y; !__sub.__overflowed())
+    return __sub.__value();
   // Handle overflow
   if constexpr (__unsigned_integer<_Tp>) {
     // Overflows if (x < y)
@@ -76,8 +76,8 @@ _LIBCPP_HIDE_FROM_ABI constexpr _Tp __saturating_sub(_Tp __x, _Tp __y) noexcept
 
 template <__signed_or_unsigned_integer _Tp>
 _LIBCPP_HIDE_FROM_ABI constexpr _Tp __saturating_mul(_Tp __x, _Tp __y) noexcept {
-  if (_Tp __mul; !__builtin_mul_overflow(__x, __y, std::addressof(__mul)))
-    return __mul;
+  if (auto __mul = __checked_int(__x) * __y; !__mul.__overflowed())
+    return __mul.__value();
   // Handle overflow
   if constexpr (__unsigned_integer<_Tp>) {
     return std::numeric_limits<_Tp>::max();
diff --git a/libcxx/include/__utility/checked_int.h b/libcxx/include/__utility/checked_int.h
new file mode 100644
index 00000000000000..724585bc7da02e
--- /dev/null
+++ b/libcxx/include/__utility/checked_int.h
@@ -0,0 +1,113 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP___UTILITY_CHECKED_INT_H
+#define _LIBCPP___UTILITY_CHECKED_INT_H
+
+#include <__config>
+#include <__memory/addressof.h>
+#include <__type_traits/enable_if.h>
+#include <__type_traits/is_signed.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+template <class _From, class _To>
+inline const bool __is_lossless_conversion_v =
+    is_signed<_From>::value == is_signed<_To>::value
+        ? sizeof(_From) <= sizeof(_To)
+        : is_signed<_To>::value && sizeof(_From) < sizeof(_To);
+
+template <class _IntT>
+class __checked_int {
+  _IntT __value_     = 0;
+  bool __overflowed_ = false;
+
+  __checked_int() = default;
+
+public:
+  _LIBCPP_CONSTEXPR explicit __checked_int(_IntT __value) : __value_(__value) {}
+
+  _LIBCPP_CONSTEXPR _IntT __value() const { return __value_; }
+  _LIBCPP_CONSTEXPR bool __overflowed() const { return __overflowed_; }
+
+  _LIBCPP_CONSTEXPR_SINCE_CXX14 friend __checked_int& operator+=(__checked_int& __lhs, __checked_int __rhs) {
+    __lhs.__overflowed_ |=
+        __rhs.__overflowed_ | __builtin_add_overflow(__lhs.__value_, __rhs.__value_, std::addressof(__lhs.__value_));
+    return __lhs;
+  }
+
+  _LIBCPP_CONSTEXPR_SINCE_CXX14 friend __checked_int& operator-=(__checked_int& __lhs, __checked_int __rhs) {
+    __lhs.__overflowed_ |=
+        __rhs.__overflowed_ | __builtin_sub_overflow(__lhs.__value_, __rhs.__value_, std::addressof(__lhs.__value_));
+    return __lhs;
+  }
+
+  _LIBCPP_CONSTEXPR_SINCE_CXX14 friend __checked_int& operator*=(__checked_int& __lhs, __checked_int __rhs) {
+    __lhs.__overflowed_ |=
+        __rhs.__overflowed_ | __builtin_mul_overflow(__lhs.__value_, __rhs.__value_, std::addressof(__lhs.__value_));
+    return __lhs;
+  }
+
+  template <class _OtherInt, __enable_if_t<__is_lossless_conversion_v<_OtherInt, _IntT>, int> = 0>
+  _LIBCPP_CONSTEXPR_SINCE_CXX14 friend __checked_int& operator+=(__checked_int& __lhs, _OtherInt __rhs) {
+    return __lhs += __checked_int(__rhs);
+  }
+
+  template <class _OtherInt, __enable_if_t<__is_lossless_conversion_v<_OtherInt, _IntT>, int> = 0>
+  _LIBCPP_CONSTEXPR_SINCE_CXX14 friend __checked_int& operator-=(__checked_int& __lhs, _OtherInt __rhs) {
+    return __lhs -= __checked_int(__rhs);
+  }
+
+  template <class _OtherInt, __enable_if_t<__is_lossless_conversion_v<_OtherInt, _IntT>, int> = 0>
+  _LIBCPP_CONSTEXPR_SINCE_CXX14 friend __checked_int& operator*=(__checked_int& __lhs, _OtherInt __rhs) {
+    return __lhs *= __checked_int(__rhs);
+  }
+
+  _LIBCPP_CONSTEXPR_SINCE_CXX14 friend __checked_int operator+(__checked_int __lhs, __checked_int __rhs) {
+    auto __copy = __lhs;
+    __copy += __rhs;
+    return __copy;
+  }
+
+  _LIBCPP_CONSTEXPR_SINCE_CXX14 friend __checked_int operator-(__checked_int __lhs, __checked_int __rhs) {
+    auto __copy = __lhs;
+    __copy -= __rhs;
+    return __copy;
+  }
+
+  _LIBCPP_CONSTEXPR_SINCE_CXX14 friend __checked_int operator*(__checked_int __lhs, __checked_int __rhs) {
+    auto __copy = __lhs;
+    __copy *= __rhs;
+    return __copy;
+  }
+
+  template <class _OtherInt, __enable_if_t<__is_lossless_conversion_v<_OtherInt, _IntT>, int> = 0>
+  _LIBCPP_CONSTEXPR_SINCE_CXX14 friend __checked_int operator+(__checked_int __lhs, _OtherInt __rhs) {
+    return __lhs + __checked_int(__rhs);
+  }
+
+  template <class _OtherInt, __enable_if_t<__is_lossless_conversion_v<_OtherInt, _IntT>, int> = 0>
+  _LIBCPP_CONSTEXPR_SINCE_CXX14 friend __checked_int operator-(__checked_int __lhs, _OtherInt __rhs) {
+    return __lhs - __checked_int(__rhs);
+  }
+
+  template <class _OtherInt, __enable_if_t<__is_lossless_conversion_v<_OtherInt, _IntT>, int> = 0>
+  _LIBCPP_CONSTEXPR_SINCE_CXX14 friend __checked_int operator*(__checked_int __lhs, _OtherInt __rhs) {
+    return __lhs * __checked_int(__rhs);
+  }
+};
+
+_LIBCPP_CTAD_SUPPORTED_FOR_TYPE(__checked_int);
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___UTILITY_CHECKED_INT_H
diff --git a/libcxx/include/fstream b/libcxx/include/fstream
index 749abe971594a9..ca922f2e8094b7 100644
--- a/libcxx/include/fstream
+++ b/libcxx/include/fstream
@@ -204,6 +204,7 @@ typedef basic_fstream<wchar_t> wfstream;
 #    include <__ostream/basic_ostream.h>
 #    include <__type_traits/enable_if.h>
 #    include <__type_traits/is_same.h>
+#    include <__utility/checked_int.h>
 #    include <__utility/move.h>
 #    include <__utility/swap.h>
 #    include <__utility/unreachable.h>
@@ -546,8 +547,8 @@ private:
     } else {
       // Otherwise seek to the requested position in the underlying C stream and drop the buffer. This is the expensive
       // path, since it performs a syscall.
-      if (off_type __offset; __builtin_sub_overflow(__byte_off, this->egptr() - this->gptr(), &__offset) ||
-                             __fseek(__file_, __offset, SEEK_CUR))
+      if (auto __offset = __checked_int<off_type>(__byte_off) - (this->egptr() - this->gptr());
+          __offset.__overflowed() || __fseek(__file_, __offset.__value(), SEEK_CUR))
         return pos_type(off_type(-1));
       this->setg(nullptr, nullptr, nullptr);
       __cm_          = 0;
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 6ca65ba556fa1c..2f6fb0177820d9 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -2263,6 +2263,7 @@ module std {
       export std_core.type_traits.decay // the macro expansion uses that trait
     }
     module cmp                             { header "__utility/cmp.h" }
+    module checked_int                     { header "__utility/checked_int.h" }
     module constant_wrapper                { header "__utility/constant_wrapper.h" }
     module convert_to_integral             { header "__utility/convert_to_integral.h" }
     module default_three_way_comparator    { header "__utility/default_three_way_comparator.h" }
diff --git a/libcxxabi/src/cxa_vector.cpp b/libcxxabi/src/cxa_vector.cpp
index 5eac7a8dc97294..156c8654bffe2c 100644
--- a/libcxxabi/src/cxa_vector.cpp
+++ b/libcxxabi/src/cxa_vector.cpp
@@ -15,6 +15,7 @@
 
 #include <exception>        // for std::terminate
 #include <new>              // for std::bad_array_new_length
+#include <__utility/checked_int.h>
 
 #include "abort_message.h"
 
@@ -124,38 +125,11 @@ namespace {
 #endif
 }
 
-bool mul_overflow(size_t x, size_t y, size_t *res) {
-#if (defined(_LIBCXXABI_COMPILER_CLANG) && __has_builtin(__builtin_mul_overflow)) \
-    || defined(_LIBCXXABI_COMPILER_GCC)
-    return __builtin_mul_overflow(x, y, res);
-#else
-    *res = x * y;
-    return x && ((*res / x) != y);
-#endif
-}
-
-bool add_overflow(size_t x, size_t y, size_t *res) {
-#if (defined(_LIBCXXABI_COMPILER_CLANG) && __has_builtin(__builtin_add_overflow)) \
-    || defined(_LIBCXXABI_COMPILER_GCC)
-  return __builtin_add_overflow(x, y, res);
-#else
-  *res = x + y;
-  return *res < y;
-#endif
-}
-
-size_t calculate_allocation_size_or_throw(size_t element_count,
-                                          size_t element_size,
-                                          size_t padding_size) {
-  size_t element_heap_size;
-  if (mul_overflow(element_count, element_size, &element_heap_size))
+size_t calculate_allocation_size_or_throw(size_t element_count, size_t element_size, size_t padding_size) {
+  auto size = std::__checked_int<size_t>(element_count) * element_size + padding_size;
+  if (size.__overflowed())
     throw_bad_array_new_length();
-
-  size_t allocation_size;
-  if (add_overflow(element_heap_size, padding_size, &allocation_size))
-    throw_bad_array_new_length();
-
-  return allocation_size;
+  return size.__value();
 }
 
 } // namespace



More information about the libcxx-commits mailing list