Update core/fxcrt/numerics

Synchronize with Chromium base/numerics at https://crrev.com/1444872.
Then let `git cl format` do its thing.

Change-Id: Iad62904aaa0d0f05500da8f2473b46c3be9225a7
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/130990
Commit-Queue: Lei Zhang <thestig@chromium.org>
Reviewed-by: Tom Sepez <tsepez@chromium.org>
diff --git a/core/fxcrt/BUILD.gn b/core/fxcrt/BUILD.gn
index 21060a4..37ad003 100644
--- a/core/fxcrt/BUILD.gn
+++ b/core/fxcrt/BUILD.gn
@@ -93,6 +93,7 @@
     "numerics/checked_math_impl.h",
     "numerics/clamped_math.h",
     "numerics/clamped_math_impl.h",
+    "numerics/integral_constant_like.h",
     "numerics/safe_conversions.h",
     "numerics/safe_conversions_arm_impl.h",
     "numerics/safe_conversions_impl.h",
diff --git a/core/fxcrt/numerics/checked_math.h b/core/fxcrt/numerics/checked_math.h
index 6c5973b..09c1c39 100644
--- a/core/fxcrt/numerics/checked_math.h
+++ b/core/fxcrt/numerics/checked_math.h
@@ -5,25 +5,22 @@
 #ifndef CORE_FXCRT_NUMERICS_CHECKED_MATH_H_
 #define CORE_FXCRT_NUMERICS_CHECKED_MATH_H_
 
-#include <stddef.h>
+#include <stdint.h>
 
 #include <limits>
 #include <type_traits>
 
-#include "core/fxcrt/numerics/checked_math_impl.h"
+#include "core/fxcrt/numerics/checked_math_impl.h"  // IWYU pragma: export
+#include "core/fxcrt/numerics/safe_conversions.h"
+#include "core/fxcrt/numerics/safe_math_shared_impl.h"  // IWYU pragma: export
 
 namespace pdfium {
 namespace internal {
 
 template <typename T>
+  requires std::is_arithmetic_v<T>
 class CheckedNumeric {
-  static_assert(std::is_arithmetic<T>::value,
-                "CheckedNumeric<T>: T must be a numeric type.");
-
  public:
-  template <typename Src>
-  friend class CheckedNumeric;
-
   using type = T;
 
   constexpr CheckedNumeric() = default;
@@ -33,16 +30,10 @@
   constexpr CheckedNumeric(const CheckedNumeric<Src>& rhs)
       : state_(rhs.state_.value(), rhs.IsValid()) {}
 
-  // Strictly speaking, this is not necessary, but declaring this allows class
-  // template argument deduction to be used so that it is possible to simply
-  // write `CheckedNumeric(777)` instead of `CheckedNumeric<int>(777)`.
-  // NOLINTNEXTLINE(google-explicit-constructor)
-  constexpr CheckedNumeric(T value) : state_(value) {}
-
   // This is not an explicit constructor because we implicitly upgrade regular
   // numerics to CheckedNumerics to make them easier to use.
-  template <typename Src,
-            typename = std::enable_if_t<std::is_arithmetic<Src>::value>>
+  template <typename Src>
+    requires(std::is_arithmetic_v<Src>)
   // NOLINTNEXTLINE(google-explicit-constructor)
   constexpr CheckedNumeric(Src value) : state_(value) {}
 
@@ -73,9 +64,11 @@
 #endif
   constexpr bool
   AssignIfValid(Dst* result) const {
-    return BASE_NUMERICS_LIKELY(IsValid<Dst>())
-               ? ((*result = static_cast<Dst>(state_.value())), true)
-               : false;
+    if (IsValid<Dst>()) [[likely]] {
+      *result = static_cast<Dst>(state_.value());
+      return true;
+    }
+    return false;
   }
 
   // ValueOrDie() - The primary accessor for the underlying value. If the
@@ -88,9 +81,10 @@
   // the underlying value, and it is not available through other means.
   template <typename Dst = T, class CheckHandler = CheckOnFailure>
   constexpr StrictNumeric<Dst> ValueOrDie() const {
-    return BASE_NUMERICS_LIKELY(IsValid<Dst>())
-               ? static_cast<Dst>(state_.value())
-               : CheckHandler::template HandleFailure<Dst>();
+    if (IsValid<Dst>()) [[likely]] {
+      return static_cast<Dst>(state_.value());
+    }
+    return CheckHandler::template HandleFailure<Dst>();
   }
 
   // ValueOrDefault(T default_value) - A convenience method that returns the
@@ -100,17 +94,18 @@
   // parameter. WARNING: This function may fail to compile or CHECK at runtime
   // if the supplied default_value is not within range of the destination type.
   template <typename Dst = T, typename Src>
-  constexpr StrictNumeric<Dst> ValueOrDefault(const Src default_value) const {
-    return BASE_NUMERICS_LIKELY(IsValid<Dst>())
-               ? static_cast<Dst>(state_.value())
-               : checked_cast<Dst>(default_value);
+  constexpr StrictNumeric<Dst> ValueOrDefault(Src default_value) const {
+    if (IsValid<Dst>()) [[likely]] {
+      return static_cast<Dst>(state_.value());
+    }
+    return checked_cast<Dst>(default_value);
   }
 
   // Returns a checked numeric of the specified type, cast from the current
   // CheckedNumeric. If the current state is invalid or the destination cannot
   // represent the result then the returned CheckedNumeric will be invalid.
   template <typename Dst>
-  constexpr CheckedNumeric<typename UnderlyingType<Dst>::type> Cast() const {
+  constexpr CheckedNumeric<UnderlyingType<Dst>> Cast() const {
     return *this;
   }
 
@@ -144,14 +139,14 @@
 
   constexpr CheckedNumeric operator-() const {
     // Use an optimized code path for a known run-time variable.
-    if (!IsConstantEvaluated() && std::is_signed<T>::value &&
-        std::is_floating_point<T>::value) {
+    if (!std::is_constant_evaluated() && std::is_signed_v<T> &&
+        std::is_floating_point_v<T>) {
       return FastRuntimeNegate();
     }
     // The negation of two's complement int min is int min.
     const bool is_valid =
         IsValid() &&
-        (!std::is_signed<T>::value || std::is_floating_point<T>::value ||
+        (!std::is_signed_v<T> || std::is_floating_point_v<T> ||
          NegateWrapper(state_.value()) != std::numeric_limits<T>::lowest());
     return CheckedNumeric<T>(NegateWrapper(state_.value()), is_valid);
   }
@@ -167,13 +162,13 @@
 
   template <typename U>
   constexpr CheckedNumeric<typename MathWrapper<CheckedMaxOp, T, U>::type> Max(
-      const U rhs) const {
+      U rhs) const {
     return CheckMax(*this, rhs);
   }
 
   template <typename U>
   constexpr CheckedNumeric<typename MathWrapper<CheckedMinOp, T, U>::type> Min(
-      const U rhs) const {
+      U rhs) const {
     return CheckMin(*this, rhs);
   }
 
@@ -192,8 +187,8 @@
   }
 
   constexpr CheckedNumeric operator++(int) {
-    CheckedNumeric value = *this;
-    *this += 1;
+    const CheckedNumeric value = *this;
+    ++*this;
     return value;
   }
 
@@ -203,18 +198,15 @@
   }
 
   constexpr CheckedNumeric operator--(int) {
-    // TODO(pkasting): Consider std::exchange() once it's constexpr in C++20.
     const CheckedNumeric value = *this;
-    *this -= 1;
+    --*this;
     return value;
   }
 
   // These perform the actual math operations on the CheckedNumerics.
   // Binary arithmetic operations.
-  template <template <typename, typename, typename> class M,
-            typename L,
-            typename R>
-  static constexpr CheckedNumeric MathOp(const L lhs, const R rhs) {
+  template <template <typename, typename> class M, typename L, typename R>
+  static constexpr CheckedNumeric MathOp(L lhs, R rhs) {
     using Math = typename MathWrapper<M, L, R>::math;
     T result = 0;
     const bool is_valid =
@@ -224,8 +216,8 @@
   }
 
   // Assignment arithmetic operations.
-  template <template <typename, typename, typename> class M, typename R>
-  constexpr CheckedNumeric& MathOp(const R rhs) {
+  template <template <typename, typename> class M, typename R>
+  constexpr CheckedNumeric& MathOp(R rhs) {
     using Math = typename MathWrapper<M, T, R>::math;
     T result = 0;  // Using T as the destination saves a range check.
     const bool is_valid =
@@ -236,6 +228,10 @@
   }
 
  private:
+  template <typename U>
+    requires std::is_arithmetic_v<U>
+  friend class CheckedNumeric;
+
   CheckedNumericState<T> state_;
 
   CheckedNumeric FastRuntimeNegate() const {
@@ -258,23 +254,26 @@
 
   template <typename Src>
   struct Wrapper<CheckedNumeric<Src>> {
-    static constexpr bool is_valid(const CheckedNumeric<Src> v) {
+    static constexpr bool is_valid(CheckedNumeric<Src> v) {
       return v.IsValid();
     }
-    static constexpr Src value(const CheckedNumeric<Src> v) {
+    static constexpr Src value(CheckedNumeric<Src> v) {
       return v.state_.value();
     }
   };
 
   template <typename Src>
   struct Wrapper<StrictNumeric<Src>> {
-    static constexpr bool is_valid(const StrictNumeric<Src>) { return true; }
-    static constexpr Src value(const StrictNumeric<Src> v) {
+    static constexpr bool is_valid(StrictNumeric<Src>) { return true; }
+    static constexpr Src value(StrictNumeric<Src> v) {
       return static_cast<Src>(v);
     }
   };
 };
 
+template <typename T>
+CheckedNumeric(T) -> CheckedNumeric<T>;
+
 // Convenience functions to avoid the ugly template disambiguator syntax.
 template <typename Dst, typename Src>
 constexpr bool IsValidForType(const CheckedNumeric<Src> value) {
@@ -288,38 +287,34 @@
 }
 
 template <typename Dst, typename Src, typename Default>
-constexpr StrictNumeric<Dst> ValueOrDefaultForType(
-    const CheckedNumeric<Src> value,
-    const Default default_value) {
+constexpr StrictNumeric<Dst> ValueOrDefaultForType(CheckedNumeric<Src> value,
+                                                   Default default_value) {
   return value.template ValueOrDefault<Dst>(default_value);
 }
 
 // Convenience wrapper to return a new CheckedNumeric from the provided
 // arithmetic or CheckedNumericType.
 template <typename T>
-constexpr CheckedNumeric<typename UnderlyingType<T>::type> MakeCheckedNum(
-    const T value) {
+constexpr CheckedNumeric<UnderlyingType<T>> MakeCheckedNum(T value) {
   return value;
 }
 
 // These implement the variadic wrapper for the math operations.
-template <template <typename, typename, typename> class M,
-          typename L,
-          typename R>
+template <template <typename, typename> class M, typename L, typename R>
 constexpr CheckedNumeric<typename MathWrapper<M, L, R>::type> CheckMathOp(
-    const L lhs,
-    const R rhs) {
+    L lhs,
+    R rhs) {
   using Math = typename MathWrapper<M, L, R>::math;
   return CheckedNumeric<typename Math::result_type>::template MathOp<M>(lhs,
                                                                         rhs);
 }
 
 // General purpose wrapper template for arithmetic operations.
-template <template <typename, typename, typename> class M,
+template <template <typename, typename> class M,
           typename L,
           typename R,
           typename... Args>
-constexpr auto CheckMathOp(const L lhs, const R rhs, const Args... args) {
+constexpr auto CheckMathOp(L lhs, R rhs, Args... args) {
   return CheckMathOp<M>(CheckMathOp<M>(lhs, rhs), args...);
 }
 
@@ -340,7 +335,7 @@
 // arithmetic with our result types. Since wrapping on a pointer is always
 // bad, we trigger the CHECK condition here.
 template <typename L, typename R>
-L* operator+(L* lhs, const StrictNumeric<R> rhs) {
+L* operator+(L* lhs, StrictNumeric<R> rhs) {
   const uintptr_t result = CheckAdd(reinterpret_cast<uintptr_t>(lhs),
                                     CheckMul(sizeof(L), static_cast<R>(rhs)))
                                .template ValueOrDie<uintptr_t>();
@@ -348,7 +343,7 @@
 }
 
 template <typename L, typename R>
-L* operator-(L* lhs, const StrictNumeric<R> rhs) {
+L* operator-(L* lhs, StrictNumeric<R> rhs) {
   const uintptr_t result = CheckSub(reinterpret_cast<uintptr_t>(lhs),
                                     CheckMul(sizeof(L), static_cast<R>(rhs)))
                                .template ValueOrDie<uintptr_t>();
diff --git a/core/fxcrt/numerics/checked_math_impl.h b/core/fxcrt/numerics/checked_math_impl.h
index 7ee488d..0e2071b 100644
--- a/core/fxcrt/numerics/checked_math_impl.h
+++ b/core/fxcrt/numerics/checked_math_impl.h
@@ -5,24 +5,24 @@
 #ifndef CORE_FXCRT_NUMERICS_CHECKED_MATH_IMPL_H_
 #define CORE_FXCRT_NUMERICS_CHECKED_MATH_IMPL_H_
 
-#include <stddef.h>
+// IWYU pragma: private, include "core/fxcrt/numerics/checked_math.h"
+
 #include <stdint.h>
 
-#include <climits>
 #include <cmath>
-#include <cstdlib>
+#include <concepts>
 #include <limits>
 #include <type_traits>
 
 #include "core/fxcrt/numerics/safe_conversions.h"
-#include "core/fxcrt/numerics/safe_math_shared_impl.h"
+#include "core/fxcrt/numerics/safe_math_shared_impl.h"  // IWYU pragma: export
 
 namespace pdfium {
 namespace internal {
 
 template <typename T>
 constexpr bool CheckedAddImpl(T x, T y, T* result) {
-  static_assert(std::is_integral<T>::value, "Type must be integral");
+  static_assert(std::integral<T>, "Type must be integral");
   // Since the value of x+y is undefined if we have a signed type, we compute
   // it using the unsigned type of the same size.
   using UnsignedDst = typename std::make_unsigned<T>::type;
@@ -32,7 +32,7 @@
   const UnsignedDst uresult = static_cast<UnsignedDst>(ux + uy);
   // Addition is valid if the sign of (x + y) is equal to either that of x or
   // that of y.
-  if (std::is_signed<T>::value
+  if (std::is_signed_v<T>
           ? static_cast<SignedDst>((uresult ^ ux) & (uresult ^ uy)) < 0
           : uresult < uy) {  // Unsigned is either valid or underflow.
     return false;
@@ -41,15 +41,13 @@
   return true;
 }
 
-template <typename T, typename U, class Enable = void>
+template <typename T, typename U>
 struct CheckedAddOp {};
 
 template <typename T, typename U>
-struct CheckedAddOp<T,
-                    U,
-                    typename std::enable_if<std::is_integral<T>::value &&
-                                            std::is_integral<U>::value>::type> {
-  using result_type = typename MaxExponentPromotion<T, U>::type;
+  requires(std::integral<T> && std::integral<U>)
+struct CheckedAddOp<T, U> {
+  using result_type = MaxExponentPromotion<T, U>;
   template <typename V>
   static constexpr bool Do(T x, U y, V* result) {
     if constexpr (CheckedAddFastOp<T, U>::is_supported) {
@@ -57,22 +55,21 @@
     }
 
     // Double the underlying type up to a full machine word.
-    using FastPromotion = typename FastIntegerArithmeticPromotion<T, U>::type;
+    using FastPromotion = FastIntegerArithmeticPromotion<T, U>;
     using Promotion =
-        typename std::conditional<(IntegerBitsPlusSign<FastPromotion>::value >
-                                   IntegerBitsPlusSign<intptr_t>::value),
-                                  typename BigEnoughPromotion<T, U>::type,
-                                  FastPromotion>::type;
+        std::conditional_t<(kIntegerBitsPlusSign<FastPromotion> >
+                            kIntegerBitsPlusSign<intptr_t>),
+                           BigEnoughPromotion<T, U>, FastPromotion>;
     // Fail if either operand is out of range for the promoted type.
     // TODO(jschuh): This could be made to work for a broader range of values.
-    if (BASE_NUMERICS_UNLIKELY(!IsValueInRangeForNumericType<Promotion>(x) ||
-                               !IsValueInRangeForNumericType<Promotion>(y))) {
+    if (!IsValueInRangeForNumericType<Promotion>(x) ||
+        !IsValueInRangeForNumericType<Promotion>(y)) [[unlikely]] {
       return false;
     }
 
     Promotion presult = {};
     bool is_valid = true;
-    if (IsIntegerArithmeticSafe<Promotion, T, U>::value) {
+    if constexpr (kIsIntegerArithmeticSafe<Promotion, T, U>) {
       presult = static_cast<Promotion>(x) + static_cast<Promotion>(y);
     } else {
       is_valid = CheckedAddImpl(static_cast<Promotion>(x),
@@ -88,7 +85,7 @@
 
 template <typename T>
 constexpr bool CheckedSubImpl(T x, T y, T* result) {
-  static_assert(std::is_integral<T>::value, "Type must be integral");
+  static_assert(std::integral<T>, "Type must be integral");
   // Since the value of x+y is undefined if we have a signed type, we compute
   // it using the unsigned type of the same size.
   using UnsignedDst = typename std::make_unsigned<T>::type;
@@ -98,7 +95,7 @@
   const UnsignedDst uresult = static_cast<UnsignedDst>(ux - uy);
   // Subtraction is valid if either x and y have same sign, or (x-y) and x have
   // the same sign.
-  if (std::is_signed<T>::value
+  if (std::is_signed_v<T>
           ? static_cast<SignedDst>((uresult ^ ux) & (ux ^ uy)) < 0
           : x < y) {
     return false;
@@ -107,15 +104,13 @@
   return true;
 }
 
-template <typename T, typename U, class Enable = void>
+template <typename T, typename U>
 struct CheckedSubOp {};
 
 template <typename T, typename U>
-struct CheckedSubOp<T,
-                    U,
-                    typename std::enable_if<std::is_integral<T>::value &&
-                                            std::is_integral<U>::value>::type> {
-  using result_type = typename MaxExponentPromotion<T, U>::type;
+  requires(std::integral<T> && std::integral<U>)
+struct CheckedSubOp<T, U> {
+  using result_type = MaxExponentPromotion<T, U>;
   template <typename V>
   static constexpr bool Do(T x, U y, V* result) {
     if constexpr (CheckedSubFastOp<T, U>::is_supported) {
@@ -123,22 +118,21 @@
     }
 
     // Double the underlying type up to a full machine word.
-    using FastPromotion = typename FastIntegerArithmeticPromotion<T, U>::type;
+    using FastPromotion = FastIntegerArithmeticPromotion<T, U>;
     using Promotion =
-        typename std::conditional<(IntegerBitsPlusSign<FastPromotion>::value >
-                                   IntegerBitsPlusSign<intptr_t>::value),
-                                  typename BigEnoughPromotion<T, U>::type,
-                                  FastPromotion>::type;
+        std::conditional_t<(kIntegerBitsPlusSign<FastPromotion> >
+                            kIntegerBitsPlusSign<intptr_t>),
+                           BigEnoughPromotion<T, U>, FastPromotion>;
     // Fail if either operand is out of range for the promoted type.
     // TODO(jschuh): This could be made to work for a broader range of values.
-    if (BASE_NUMERICS_UNLIKELY(!IsValueInRangeForNumericType<Promotion>(x) ||
-                               !IsValueInRangeForNumericType<Promotion>(y))) {
+    if (!IsValueInRangeForNumericType<Promotion>(x) ||
+        !IsValueInRangeForNumericType<Promotion>(y)) [[unlikely]] {
       return false;
     }
 
     Promotion presult = {};
     bool is_valid = true;
-    if (IsIntegerArithmeticSafe<Promotion, T, U>::value) {
+    if constexpr (kIsIntegerArithmeticSafe<Promotion, T, U>) {
       presult = static_cast<Promotion>(x) - static_cast<Promotion>(y);
     } else {
       is_valid = CheckedSubImpl(static_cast<Promotion>(x),
@@ -154,7 +148,7 @@
 
 template <typename T>
 constexpr bool CheckedMulImpl(T x, T y, T* result) {
-  static_assert(std::is_integral<T>::value, "Type must be integral");
+  static_assert(std::integral<T>, "Type must be integral");
   // Since the value of x*y is potentially undefined if we have a signed type,
   // we compute it using the unsigned type of the same size.
   using UnsignedDst = typename std::make_unsigned<T>::type;
@@ -163,11 +157,11 @@
   const UnsignedDst uy = SafeUnsignedAbs(y);
   const UnsignedDst uresult = static_cast<UnsignedDst>(ux * uy);
   const bool is_negative =
-      std::is_signed<T>::value && static_cast<SignedDst>(x ^ y) < 0;
+      std::is_signed_v<T> && static_cast<SignedDst>(x ^ y) < 0;
   // We have a fast out for unsigned identity or zero on the second operand.
   // After that it's an unsigned overflow check on the absolute value, with
   // a +1 bound for a negative result.
-  if (uy > UnsignedDst(!std::is_signed<T>::value || is_negative) &&
+  if (uy > UnsignedDst(!std::is_signed_v<T> || is_negative) &&
       ux > (std::numeric_limits<T>::max() + UnsignedDst(is_negative)) / uy) {
     return false;
   }
@@ -175,38 +169,36 @@
   return true;
 }
 
-template <typename T, typename U, class Enable = void>
+template <typename T, typename U>
 struct CheckedMulOp {};
 
 template <typename T, typename U>
-struct CheckedMulOp<T,
-                    U,
-                    typename std::enable_if<std::is_integral<T>::value &&
-                                            std::is_integral<U>::value>::type> {
-  using result_type = typename MaxExponentPromotion<T, U>::type;
+  requires(std::integral<T> && std::integral<U>)
+struct CheckedMulOp<T, U> {
+  using result_type = MaxExponentPromotion<T, U>;
   template <typename V>
   static constexpr bool Do(T x, U y, V* result) {
     if constexpr (CheckedMulFastOp<T, U>::is_supported) {
       return CheckedMulFastOp<T, U>::Do(x, y, result);
     }
 
-    using Promotion = typename FastIntegerArithmeticPromotion<T, U>::type;
+    using Promotion = FastIntegerArithmeticPromotion<T, U>;
     // Verify the destination type can hold the result (always true for 0).
-    if (BASE_NUMERICS_UNLIKELY((!IsValueInRangeForNumericType<Promotion>(x) ||
-                                !IsValueInRangeForNumericType<Promotion>(y)) &&
-                               x && y)) {
+    if ((!IsValueInRangeForNumericType<Promotion>(x) ||
+         !IsValueInRangeForNumericType<Promotion>(y)) &&
+        x && y) [[unlikely]] {
       return false;
     }
 
     Promotion presult = {};
     bool is_valid = true;
-    if (CheckedMulFastOp<Promotion, Promotion>::is_supported) {
+    if constexpr (CheckedMulFastOp<Promotion, Promotion>::is_supported) {
       // The fast op may be available with the promoted type.
       // The casts here are safe because of the "value in range" conditional
       // above.
       is_valid = CheckedMulFastOp<Promotion, Promotion>::Do(
           static_cast<Promotion>(x), static_cast<Promotion>(y), &presult);
-    } else if (IsIntegerArithmeticSafe<Promotion, T, U>::value) {
+    } else if constexpr (kIsIntegerArithmeticSafe<Promotion, T, U>) {
       presult = static_cast<Promotion>(x) * static_cast<Promotion>(y);
     } else {
       is_valid = CheckedMulImpl(static_cast<Promotion>(x),
@@ -222,37 +214,33 @@
 
 // Division just requires a check for a zero denominator or an invalid negation
 // on signed min/-1.
-template <typename T, typename U, class Enable = void>
+template <typename T, typename U>
 struct CheckedDivOp {};
 
 template <typename T, typename U>
-struct CheckedDivOp<T,
-                    U,
-                    typename std::enable_if<std::is_integral<T>::value &&
-                                            std::is_integral<U>::value>::type> {
-  using result_type = typename MaxExponentPromotion<T, U>::type;
+  requires(std::integral<T> && std::integral<U>)
+struct CheckedDivOp<T, U> {
+  using result_type = MaxExponentPromotion<T, U>;
   template <typename V>
   static constexpr bool Do(T x, U y, V* result) {
-    if (BASE_NUMERICS_UNLIKELY(!y)) {
+    if (!y) [[unlikely]] {
       return false;
     }
 
     // The overflow check can be compiled away if we don't have the exact
     // combination of types needed to trigger this case.
-    using Promotion = typename BigEnoughPromotion<T, U>::type;
-    if (BASE_NUMERICS_UNLIKELY(
-            (std::is_signed<T>::value && std::is_signed<U>::value &&
-             IsTypeInRangeForNumericType<T, Promotion>::value &&
-             static_cast<Promotion>(x) ==
-                 std::numeric_limits<Promotion>::lowest() &&
-             y == static_cast<U>(-1)))) {
+    using Promotion = BigEnoughPromotion<T, U>;
+    if (std::is_signed_v<T> && std::is_signed_v<U> &&
+        kIsTypeInRangeForNumericType<T, Promotion> &&
+        static_cast<Promotion>(x) == std::numeric_limits<Promotion>::lowest() &&
+        y == static_cast<U>(-1)) [[unlikely]] {
       return false;
     }
 
     // This branch always compiles away if the above branch wasn't removed.
-    if (BASE_NUMERICS_UNLIKELY((!IsValueInRangeForNumericType<Promotion>(x) ||
-                                !IsValueInRangeForNumericType<Promotion>(y)) &&
-                               x)) {
+    if ((!IsValueInRangeForNumericType<Promotion>(x) ||
+         !IsValueInRangeForNumericType<Promotion>(y)) &&
+        x) [[unlikely]] {
       return false;
     }
 
@@ -265,28 +253,24 @@
   }
 };
 
-template <typename T, typename U, class Enable = void>
+template <typename T, typename U>
 struct CheckedModOp {};
 
 template <typename T, typename U>
-struct CheckedModOp<T,
-                    U,
-                    typename std::enable_if<std::is_integral<T>::value &&
-                                            std::is_integral<U>::value>::type> {
-  using result_type = typename MaxExponentPromotion<T, U>::type;
+  requires(std::integral<T> && std::integral<U>)
+struct CheckedModOp<T, U> {
+  using result_type = MaxExponentPromotion<T, U>;
   template <typename V>
   static constexpr bool Do(T x, U y, V* result) {
-    if (BASE_NUMERICS_UNLIKELY(!y)) {
+    if (!y) [[unlikely]] {
       return false;
     }
 
-    using Promotion = typename BigEnoughPromotion<T, U>::type;
-    if (BASE_NUMERICS_UNLIKELY(
-            (std::is_signed<T>::value && std::is_signed<U>::value &&
-             IsTypeInRangeForNumericType<T, Promotion>::value &&
-             static_cast<Promotion>(x) ==
-                 std::numeric_limits<Promotion>::lowest() &&
-             y == static_cast<U>(-1)))) {
+    using Promotion = BigEnoughPromotion<T, U>;
+    if (std::is_signed_v<T> && std::is_signed_v<U> &&
+        kIsTypeInRangeForNumericType<T, Promotion> &&
+        static_cast<Promotion>(x) == std::numeric_limits<Promotion>::lowest() &&
+        y == static_cast<U>(-1)) [[unlikely]] {
       *result = 0;
       return true;
     }
@@ -301,24 +285,22 @@
   }
 };
 
-template <typename T, typename U, class Enable = void>
+template <typename T, typename U>
 struct CheckedLshOp {};
 
 // Left shift. Shifts less than 0 or greater than or equal to the number
 // of bits in the promoted type are undefined. Shifts of negative values
 // are undefined. Otherwise it is defined when the result fits.
 template <typename T, typename U>
-struct CheckedLshOp<T,
-                    U,
-                    typename std::enable_if<std::is_integral<T>::value &&
-                                            std::is_integral<U>::value>::type> {
+  requires(std::integral<T> && std::integral<U>)
+struct CheckedLshOp<T, U> {
   using result_type = T;
   template <typename V>
   static constexpr bool Do(T x, U shift, V* result) {
     // Disallow negative numbers and verify the shift is in bounds.
-    if (BASE_NUMERICS_LIKELY(!IsValueNegative(x) &&
-                             as_unsigned(shift) <
-                                 as_unsigned(std::numeric_limits<T>::digits))) {
+    if (!IsValueNegative(x) &&
+        as_unsigned(shift) < as_unsigned(std::numeric_limits<T>::digits))
+        [[likely]] {
       // Shift as unsigned to avoid undefined behavior.
       *result = static_cast<V>(as_unsigned(x) << shift);
       // If the shift can be reversed, we know it was valid.
@@ -326,7 +308,7 @@
     }
 
     // Handle the legal corner-case of a full-width signed shift of zero.
-    if (!std::is_signed<T>::value || x ||
+    if (!std::is_signed_v<T> || x ||
         as_unsigned(shift) != as_unsigned(std::numeric_limits<T>::digits)) {
       return false;
     }
@@ -335,23 +317,20 @@
   }
 };
 
-template <typename T, typename U, class Enable = void>
+template <typename T, typename U>
 struct CheckedRshOp {};
 
 // Right shift. Shifts less than 0 or greater than or equal to the number
 // of bits in the promoted type are undefined. Otherwise, it is always defined,
 // but a right shift of a negative value is implementation-dependent.
 template <typename T, typename U>
-struct CheckedRshOp<T,
-                    U,
-                    typename std::enable_if<std::is_integral<T>::value &&
-                                            std::is_integral<U>::value>::type> {
+  requires(std::integral<T> && std::integral<U>)
+struct CheckedRshOp<T, U> {
   using result_type = T;
   template <typename V>
   static constexpr bool Do(T x, U shift, V* result) {
     // Use sign conversion to push negative values out of range.
-    if (BASE_NUMERICS_UNLIKELY(as_unsigned(shift) >=
-                               IntegerBitsPlusSign<T>::value)) {
+    if (as_unsigned(shift) >= kIntegerBitsPlusSign<T>) [[unlikely]] {
       return false;
     }
 
@@ -364,17 +343,14 @@
   }
 };
 
-template <typename T, typename U, class Enable = void>
+template <typename T, typename U>
 struct CheckedAndOp {};
 
 // For simplicity we support only unsigned integer results.
 template <typename T, typename U>
-struct CheckedAndOp<T,
-                    U,
-                    typename std::enable_if<std::is_integral<T>::value &&
-                                            std::is_integral<U>::value>::type> {
-  using result_type = typename std::make_unsigned<
-      typename MaxExponentPromotion<T, U>::type>::type;
+  requires(std::integral<T> && std::integral<U>)
+struct CheckedAndOp<T, U> {
+  using result_type = std::make_unsigned_t<MaxExponentPromotion<T, U>>;
   template <typename V>
   static constexpr bool Do(T x, U y, V* result) {
     const result_type tmp =
@@ -387,17 +363,14 @@
   }
 };
 
-template <typename T, typename U, class Enable = void>
+template <typename T, typename U>
 struct CheckedOrOp {};
 
 // For simplicity we support only unsigned integers.
 template <typename T, typename U>
-struct CheckedOrOp<T,
-                   U,
-                   typename std::enable_if<std::is_integral<T>::value &&
-                                           std::is_integral<U>::value>::type> {
-  using result_type = typename std::make_unsigned<
-      typename MaxExponentPromotion<T, U>::type>::type;
+  requires(std::integral<T> && std::integral<U>)
+struct CheckedOrOp<T, U> {
+  using result_type = std::make_unsigned_t<MaxExponentPromotion<T, U>>;
   template <typename V>
   static constexpr bool Do(T x, U y, V* result) {
     const result_type tmp =
@@ -410,17 +383,14 @@
   }
 };
 
-template <typename T, typename U, class Enable = void>
+template <typename T, typename U>
 struct CheckedXorOp {};
 
 // For simplicity we support only unsigned integers.
 template <typename T, typename U>
-struct CheckedXorOp<T,
-                    U,
-                    typename std::enable_if<std::is_integral<T>::value &&
-                                            std::is_integral<U>::value>::type> {
-  using result_type = typename std::make_unsigned<
-      typename MaxExponentPromotion<T, U>::type>::type;
+  requires(std::integral<T> && std::integral<U>)
+struct CheckedXorOp<T, U> {
+  using result_type = std::make_unsigned_t<MaxExponentPromotion<T, U>>;
   template <typename V>
   static constexpr bool Do(T x, U y, V* result) {
     const result_type tmp =
@@ -435,16 +405,13 @@
 
 // Max doesn't really need to be implemented this way because it can't fail,
 // but it makes the code much cleaner to use the MathOp wrappers.
-template <typename T, typename U, class Enable = void>
+template <typename T, typename U>
 struct CheckedMaxOp {};
 
 template <typename T, typename U>
-struct CheckedMaxOp<
-    T,
-    U,
-    typename std::enable_if<std::is_arithmetic<T>::value &&
-                            std::is_arithmetic<U>::value>::type> {
-  using result_type = typename MaxExponentPromotion<T, U>::type;
+  requires(std::is_arithmetic_v<T> && std::is_arithmetic_v<U>)
+struct CheckedMaxOp<T, U> {
+  using result_type = MaxExponentPromotion<T, U>;
   template <typename V>
   static constexpr bool Do(T x, U y, V* result) {
     const result_type tmp = IsGreater<T, U>::Test(x, y)
@@ -460,16 +427,13 @@
 
 // Min doesn't really need to be implemented this way because it can't fail,
 // but it makes the code much cleaner to use the MathOp wrappers.
-template <typename T, typename U, class Enable = void>
+template <typename T, typename U>
 struct CheckedMinOp {};
 
 template <typename T, typename U>
-struct CheckedMinOp<
-    T,
-    U,
-    typename std::enable_if<std::is_arithmetic<T>::value &&
-                            std::is_arithmetic<U>::value>::type> {
-  using result_type = typename LowestValuePromotion<T, U>::type;
+  requires(std::is_arithmetic_v<T> && std::is_arithmetic_v<U>)
+struct CheckedMinOp<T, U> {
+  using result_type = LowestValuePromotion<T, U>;
   template <typename V>
   static constexpr bool Do(T x, U y, V* result) {
     const result_type tmp = IsLess<T, U>::Test(x, y)
@@ -485,22 +449,19 @@
 
 // This is just boilerplate that wraps the standard floating point arithmetic.
 // A macro isn't the nicest solution, but it beats rewriting these repeatedly.
-#define BASE_FLOAT_ARITHMETIC_OPS(NAME, OP)                              \
-  template <typename T, typename U>                                      \
-  struct Checked##NAME##Op<                                              \
-      T, U,                                                              \
-      typename std::enable_if<std::is_floating_point<T>::value ||        \
-                              std::is_floating_point<U>::value>::type> { \
-    using result_type = typename MaxExponentPromotion<T, U>::type;       \
-    template <typename V>                                                \
-    static constexpr bool Do(T x, U y, V* result) {                      \
-      using Promotion = typename MaxExponentPromotion<T, U>::type;       \
-      const Promotion presult = x OP y;                                  \
-      if (!IsValueInRangeForNumericType<V>(presult))                     \
-        return false;                                                    \
-      *result = static_cast<V>(presult);                                 \
-      return true;                                                       \
-    }                                                                    \
+#define BASE_FLOAT_ARITHMETIC_OPS(NAME, OP)                    \
+  template <typename T, typename U>                            \
+    requires(std::floating_point<T> || std::floating_point<U>) \
+  struct Checked##NAME##Op<T, U> {                             \
+    using result_type = MaxExponentPromotion<T, U>;            \
+    template <typename V>                                      \
+    static constexpr bool Do(T x, U y, V* result) {            \
+      const result_type presult = x OP y;                      \
+      if (!IsValueInRangeForNumericType<V>(presult))           \
+        return false;                                          \
+      *result = static_cast<V>(presult);                       \
+      return true;                                             \
+    }                                                          \
   };
 
 BASE_FLOAT_ARITHMETIC_OPS(Add, +)
@@ -522,10 +483,10 @@
 template <typename NumericType>
 struct GetNumericRepresentation {
   static const NumericRepresentation value =
-      std::is_integral<NumericType>::value
+      std::integral<NumericType>
           ? NUMERIC_INTEGER
-          : (std::is_floating_point<NumericType>::value ? NUMERIC_FLOATING
-                                                        : NUMERIC_UNKNOWN);
+          : (std::floating_point<NumericType> ? NUMERIC_FLOATING
+                                              : NUMERIC_UNKNOWN);
 };
 
 template <typename T,
@@ -540,7 +501,7 @@
   constexpr explicit CheckedNumericState(Src value = 0, bool is_valid = true)
       : is_valid_(is_valid && IsValueInRangeForNumericType<T>(value)),
         value_(WellDefinedConversionOrZero(value, is_valid_)) {
-    static_assert(std::is_arithmetic<Src>::value, "Argument must be numeric.");
+    static_assert(std::is_arithmetic_v<Src>, "Argument must be numeric.");
   }
 
   template <typename Src>
@@ -555,8 +516,7 @@
   // Ensures that a type conversion does not trigger undefined behavior.
   template <typename Src>
   static constexpr T WellDefinedConversionOrZero(Src value, bool is_valid) {
-    using SrcType = typename internal::UnderlyingType<Src>::type;
-    return (std::is_integral<SrcType>::value || is_valid)
+    return (std::integral<UnderlyingType<Src>> || is_valid)
                ? static_cast<T>(value)
                : 0;
   }
@@ -583,8 +543,9 @@
       : CheckedNumericState(rhs.value(), rhs.is_valid()) {}
 
   constexpr bool is_valid() const {
-    // Written this way because std::isfinite is not reliably constexpr.
-    return IsConstantEvaluated()
+    // Written this way because std::isfinite is not constexpr before C++23.
+    // TODO(C++23): Use `std::isfinite()` unconditionally.
+    return std::is_constant_evaluated()
                ? value_ <= std::numeric_limits<T>::max() &&
                      value_ >= std::numeric_limits<T>::lowest()
                : std::isfinite(value_);
@@ -596,9 +557,8 @@
   // Ensures that a type conversion does not trigger undefined behavior.
   template <typename Src>
   static constexpr T WellDefinedConversionOrNaN(Src value, bool is_valid) {
-    using SrcType = typename internal::UnderlyingType<Src>::type;
-    return (StaticDstRangeRelationToSrcRange<T, SrcType>::value ==
-                NUMERIC_RANGE_CONTAINED ||
+    return (kStaticDstRangeRelationToSrcRange<T, UnderlyingType<Src>> ==
+                NumericRangeRepresentation::kContained ||
             is_valid)
                ? static_cast<T>(value)
                : std::numeric_limits<T>::quiet_NaN();
diff --git a/core/fxcrt/numerics/clamped_math.h b/core/fxcrt/numerics/clamped_math.h
index 4e98785..597a76c 100644
--- a/core/fxcrt/numerics/clamped_math.h
+++ b/core/fxcrt/numerics/clamped_math.h
@@ -5,47 +5,34 @@
 #ifndef CORE_FXCRT_NUMERICS_CLAMPED_MATH_H_
 #define CORE_FXCRT_NUMERICS_CLAMPED_MATH_H_
 
-#include <stddef.h>
-
-#include <limits>
 #include <type_traits>
 
-#include "core/fxcrt/numerics/clamped_math_impl.h"
+#include "core/fxcrt/numerics/clamped_math_impl.h"  // IWYU pragma: export
+#include "core/fxcrt/numerics/safe_conversions.h"
+#include "core/fxcrt/numerics/safe_math_shared_impl.h"  // IWYU pragma: export
 
 namespace pdfium {
 namespace internal {
 
 template <typename T>
+  requires std::is_arithmetic_v<T>
 class ClampedNumeric {
-  static_assert(std::is_arithmetic<T>::value,
-                "ClampedNumeric<T>: T must be a numeric type.");
-
  public:
   using type = T;
 
-  constexpr ClampedNumeric() : value_(0) {}
+  constexpr ClampedNumeric() = default;
 
   // Copy constructor.
   template <typename Src>
   constexpr ClampedNumeric(const ClampedNumeric<Src>& rhs)
       : value_(saturated_cast<T>(rhs.value_)) {}
 
-  template <typename Src>
-  friend class ClampedNumeric;
-
-  // Strictly speaking, this is not necessary, but declaring this allows class
-  // template argument deduction to be used so that it is possible to simply
-  // write `ClampedNumeric(777)` instead of `ClampedNumeric<int>(777)`.
-  // NOLINTNEXTLINE(google-explicit-constructor)
-  constexpr ClampedNumeric(T value) : value_(value) {}
-
   // This is not an explicit constructor because we implicitly upgrade regular
   // numerics to ClampedNumerics to make them easier to use.
   template <typename Src>
+    requires(IsNumeric<Src>)
   // NOLINTNEXTLINE(google-explicit-constructor)
-  constexpr ClampedNumeric(Src value) : value_(saturated_cast<T>(value)) {
-    static_assert(UnderlyingType<Src>::is_numeric, "Argument must be numeric.");
-  }
+  constexpr ClampedNumeric(Src value) : value_(saturated_cast<T>(value)) {}
 
   // This is not an explicit constructor because we want a seamless conversion
   // from StrictNumeric types.
@@ -57,7 +44,7 @@
   // Returns a ClampedNumeric of the specified type, cast from the current
   // ClampedNumeric, and saturated to the destination type.
   template <typename Dst>
-  constexpr ClampedNumeric<typename UnderlyingType<Dst>::type> Cast() const {
+  constexpr ClampedNumeric<UnderlyingType<Dst>> Cast() const {
     return *this;
   }
 
@@ -101,7 +88,7 @@
 
   template <typename U>
   constexpr ClampedNumeric<typename MathWrapper<ClampedMaxOp, T, U>::type> Max(
-      const U rhs) const {
+      U rhs) const {
     using result_type = typename MathWrapper<ClampedMaxOp, T, U>::type;
     return ClampedNumeric<result_type>(
         ClampedMaxOp<T, U>::Do(value_, Wrapper<U>::value(rhs)));
@@ -109,7 +96,7 @@
 
   template <typename U>
   constexpr ClampedNumeric<typename MathWrapper<ClampedMinOp, T, U>::type> Min(
-      const U rhs) const {
+      U rhs) const {
     using result_type = typename MathWrapper<ClampedMinOp, T, U>::type;
     return ClampedNumeric<result_type>(
         ClampedMinOp<T, U>::Do(value_, Wrapper<U>::value(rhs)));
@@ -148,18 +135,16 @@
 
   // These perform the actual math operations on the ClampedNumerics.
   // Binary arithmetic operations.
-  template <template <typename, typename, typename> class M,
-            typename L,
-            typename R>
-  static constexpr ClampedNumeric MathOp(const L lhs, const R rhs) {
+  template <template <typename, typename> class M, typename L, typename R>
+  static constexpr ClampedNumeric MathOp(L lhs, R rhs) {
     using Math = typename MathWrapper<M, L, R>::math;
     return ClampedNumeric<T>(
         Math::template Do<T>(Wrapper<L>::value(lhs), Wrapper<R>::value(rhs)));
   }
 
   // Assignment arithmetic operations.
-  template <template <typename, typename, typename> class M, typename R>
-  constexpr ClampedNumeric& MathOp(const R rhs) {
+  template <template <typename, typename> class M, typename R>
+  constexpr ClampedNumeric& MathOp(R rhs) {
     using Math = typename MathWrapper<M, T, R>::math;
     *this =
         ClampedNumeric<T>(Math::template Do<T>(value_, Wrapper<R>::value(rhs)));
@@ -167,9 +152,9 @@
   }
 
   template <typename Dst>
-  constexpr operator Dst() const {
-    return saturated_cast<typename ArithmeticOrUnderlyingEnum<Dst>::type>(
-        value_);
+    requires std::is_arithmetic_v<ArithmeticOrUnderlyingEnum<Dst>>
+  constexpr operator Dst() const {  // NOLINT(google-explicit-constructor)
+    return saturated_cast<ArithmeticOrUnderlyingEnum<Dst>>(value_);
   }
 
   // This method extracts the raw integer value without saturating it to the
@@ -178,44 +163,46 @@
   constexpr T RawValue() const { return value_; }
 
  private:
-  T value_;
+  template <typename U>
+    requires std::is_arithmetic_v<U>
+  friend class ClampedNumeric;
+
+  T value_ = 0;
 
   // These wrappers allow us to handle state the same way for both
   // ClampedNumeric and POD arithmetic types.
   template <typename Src>
   struct Wrapper {
-    static constexpr typename UnderlyingType<Src>::type value(Src value) {
-      return value;
-    }
+    static constexpr UnderlyingType<Src> value(Src value) { return value; }
   };
 };
 
+template <typename T>
+ClampedNumeric(T) -> ClampedNumeric<T>;
+
 // Convenience wrapper to return a new ClampedNumeric from the provided
 // arithmetic or ClampedNumericType.
 template <typename T>
-constexpr ClampedNumeric<typename UnderlyingType<T>::type> MakeClampedNum(
-    const T value) {
+constexpr ClampedNumeric<UnderlyingType<T>> MakeClampedNum(T value) {
   return value;
 }
 
 // These implement the variadic wrapper for the math operations.
-template <template <typename, typename, typename> class M,
-          typename L,
-          typename R>
+template <template <typename, typename> class M, typename L, typename R>
 constexpr ClampedNumeric<typename MathWrapper<M, L, R>::type> ClampMathOp(
-    const L lhs,
-    const R rhs) {
+    L lhs,
+    R rhs) {
   using Math = typename MathWrapper<M, L, R>::math;
   return ClampedNumeric<typename Math::result_type>::template MathOp<M>(lhs,
                                                                         rhs);
 }
 
 // General purpose wrapper template for arithmetic operations.
-template <template <typename, typename, typename> class M,
+template <template <typename, typename> class M,
           typename L,
           typename R,
           typename... Args>
-constexpr auto ClampMathOp(const L lhs, const R rhs, const Args... args) {
+constexpr auto ClampMathOp(L lhs, R rhs, Args... args) {
   return ClampMathOp<M>(ClampMathOp<M>(lhs, rhs), args...);
 }
 
diff --git a/core/fxcrt/numerics/clamped_math_impl.h b/core/fxcrt/numerics/clamped_math_impl.h
index 75ca8c6..7d262ea 100644
--- a/core/fxcrt/numerics/clamped_math_impl.h
+++ b/core/fxcrt/numerics/clamped_math_impl.h
@@ -5,49 +5,43 @@
 #ifndef CORE_FXCRT_NUMERICS_CLAMPED_MATH_IMPL_H_
 #define CORE_FXCRT_NUMERICS_CLAMPED_MATH_IMPL_H_
 
-#include <stddef.h>
-#include <stdint.h>
+// IWYU pragma: private, include "core/fxcrt/numerics/clamped_math.h"
 
-#include <climits>
-#include <cmath>
-#include <cstdlib>
+#include <concepts>
 #include <limits>
 #include <type_traits>
 
 #include "core/fxcrt/numerics/checked_math.h"
 #include "core/fxcrt/numerics/safe_conversions.h"
-#include "core/fxcrt/numerics/safe_math_shared_impl.h"
+#include "core/fxcrt/numerics/safe_math_shared_impl.h"  // IWYU pragma: export
 
 namespace pdfium {
 namespace internal {
 
-template <typename T,
-          typename std::enable_if<std::is_integral<T>::value &&
-                                  std::is_signed<T>::value>::type* = nullptr>
+template <typename T>
+  requires(std::signed_integral<T>)
 constexpr T SaturatedNegWrapper(T value) {
-  return IsConstantEvaluated() || !ClampedNegFastOp<T>::is_supported
+  return std::is_constant_evaluated() || !ClampedNegFastOp<T>::is_supported
              ? (NegateWrapper(value) != std::numeric_limits<T>::lowest()
                     ? NegateWrapper(value)
                     : std::numeric_limits<T>::max())
              : ClampedNegFastOp<T>::Do(value);
 }
 
-template <typename T,
-          typename std::enable_if<std::is_integral<T>::value &&
-                                  !std::is_signed<T>::value>::type* = nullptr>
+template <typename T>
+  requires(std::unsigned_integral<T>)
 constexpr T SaturatedNegWrapper(T value) {
   return T(0);
 }
 
-template <
-    typename T,
-    typename std::enable_if<std::is_floating_point<T>::value>::type* = nullptr>
+template <typename T>
+  requires(std::floating_point<T>)
 constexpr T SaturatedNegWrapper(T value) {
   return -value;
 }
 
-template <typename T,
-          typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
+template <typename T>
+  requires(std::integral<T>)
 constexpr T SaturatedAbsWrapper(T value) {
   // The calculation below is a static identity for unsigned types, but for
   // signed integer types it provides a non-branching, saturated absolute value.
@@ -62,104 +56,89 @@
       IsValueNegative<T>(static_cast<T>(SafeUnsignedAbs(value))));
 }
 
-template <
-    typename T,
-    typename std::enable_if<std::is_floating_point<T>::value>::type* = nullptr>
+template <typename T>
+  requires(std::floating_point<T>)
 constexpr T SaturatedAbsWrapper(T value) {
   return value < 0 ? -value : value;
 }
 
-template <typename T, typename U, class Enable = void>
+template <typename T, typename U>
 struct ClampedAddOp {};
 
 template <typename T, typename U>
-struct ClampedAddOp<T,
-                    U,
-                    typename std::enable_if<std::is_integral<T>::value &&
-                                            std::is_integral<U>::value>::type> {
-  using result_type = typename MaxExponentPromotion<T, U>::type;
+  requires(std::integral<T> && std::integral<U>)
+struct ClampedAddOp<T, U> {
+  using result_type = MaxExponentPromotion<T, U>;
   template <typename V = result_type>
+    requires(std::same_as<V, result_type> || kIsTypeInRangeForNumericType<U, V>)
   static constexpr V Do(T x, U y) {
-    if (!IsConstantEvaluated() && ClampedAddFastOp<T, U>::is_supported) {
+    if (!std::is_constant_evaluated() && ClampedAddFastOp<T, U>::is_supported) {
       return ClampedAddFastOp<T, U>::template Do<V>(x, y);
     }
-
-    static_assert(std::is_same<V, result_type>::value ||
-                      IsTypeInRangeForNumericType<U, V>::value,
-                  "The saturation result cannot be determined from the "
-                  "provided types.");
     const V saturated = CommonMaxOrMin<V>(IsValueNegative(y));
     V result = {};
-    return BASE_NUMERICS_LIKELY((CheckedAddOp<T, U>::Do(x, y, &result)))
-               ? result
-               : saturated;
+    if (CheckedAddOp<T, U>::Do(x, y, &result)) [[likely]] {
+      return result;
+    }
+    return saturated;
   }
 };
 
-template <typename T, typename U, class Enable = void>
+template <typename T, typename U>
 struct ClampedSubOp {};
 
 template <typename T, typename U>
-struct ClampedSubOp<T,
-                    U,
-                    typename std::enable_if<std::is_integral<T>::value &&
-                                            std::is_integral<U>::value>::type> {
-  using result_type = typename MaxExponentPromotion<T, U>::type;
+  requires(std::integral<T> && std::integral<U>)
+struct ClampedSubOp<T, U> {
+  using result_type = MaxExponentPromotion<T, U>;
   template <typename V = result_type>
+    requires(std::same_as<V, result_type> || kIsTypeInRangeForNumericType<U, V>)
   static constexpr V Do(T x, U y) {
-    if (!IsConstantEvaluated() && ClampedSubFastOp<T, U>::is_supported) {
+    if (!std::is_constant_evaluated() && ClampedSubFastOp<T, U>::is_supported) {
       return ClampedSubFastOp<T, U>::template Do<V>(x, y);
     }
-
-    static_assert(std::is_same<V, result_type>::value ||
-                      IsTypeInRangeForNumericType<U, V>::value,
-                  "The saturation result cannot be determined from the "
-                  "provided types.");
     const V saturated = CommonMaxOrMin<V>(!IsValueNegative(y));
     V result = {};
-    return BASE_NUMERICS_LIKELY((CheckedSubOp<T, U>::Do(x, y, &result)))
-               ? result
-               : saturated;
+    if (CheckedSubOp<T, U>::Do(x, y, &result)) [[likely]] {
+      return result;
+    }
+    return saturated;
   }
 };
 
-template <typename T, typename U, class Enable = void>
+template <typename T, typename U>
 struct ClampedMulOp {};
 
 template <typename T, typename U>
-struct ClampedMulOp<T,
-                    U,
-                    typename std::enable_if<std::is_integral<T>::value &&
-                                            std::is_integral<U>::value>::type> {
-  using result_type = typename MaxExponentPromotion<T, U>::type;
+  requires(std::integral<T> && std::integral<U>)
+struct ClampedMulOp<T, U> {
+  using result_type = MaxExponentPromotion<T, U>;
   template <typename V = result_type>
   static constexpr V Do(T x, U y) {
-    if (!IsConstantEvaluated() && ClampedMulFastOp<T, U>::is_supported) {
+    if (!std::is_constant_evaluated() && ClampedMulFastOp<T, U>::is_supported) {
       return ClampedMulFastOp<T, U>::template Do<V>(x, y);
     }
-
     V result = {};
     const V saturated =
         CommonMaxOrMin<V>(IsValueNegative(x) ^ IsValueNegative(y));
-    return BASE_NUMERICS_LIKELY((CheckedMulOp<T, U>::Do(x, y, &result)))
-               ? result
-               : saturated;
+    if (CheckedMulOp<T, U>::Do(x, y, &result)) [[likely]] {
+      return result;
+    }
+    return saturated;
   }
 };
 
-template <typename T, typename U, class Enable = void>
+template <typename T, typename U>
 struct ClampedDivOp {};
 
 template <typename T, typename U>
-struct ClampedDivOp<T,
-                    U,
-                    typename std::enable_if<std::is_integral<T>::value &&
-                                            std::is_integral<U>::value>::type> {
-  using result_type = typename MaxExponentPromotion<T, U>::type;
+  requires(std::integral<T> && std::integral<U>)
+struct ClampedDivOp<T, U> {
+  using result_type = MaxExponentPromotion<T, U>;
   template <typename V = result_type>
   static constexpr V Do(T x, U y) {
     V result = {};
-    if (BASE_NUMERICS_LIKELY((CheckedDivOp<T, U>::Do(x, y, &result)))) {
+    if ((CheckedDivOp<T, U>::Do(x, y, &result))) [[likely]] {
       return result;
     }
     // Saturation goes to max, min, or NaN (if x is zero).
@@ -168,43 +147,39 @@
   }
 };
 
-template <typename T, typename U, class Enable = void>
+template <typename T, typename U>
 struct ClampedModOp {};
 
 template <typename T, typename U>
-struct ClampedModOp<T,
-                    U,
-                    typename std::enable_if<std::is_integral<T>::value &&
-                                            std::is_integral<U>::value>::type> {
-  using result_type = typename MaxExponentPromotion<T, U>::type;
+  requires(std::integral<T> && std::integral<U>)
+struct ClampedModOp<T, U> {
+  using result_type = MaxExponentPromotion<T, U>;
   template <typename V = result_type>
   static constexpr V Do(T x, U y) {
     V result = {};
-    return BASE_NUMERICS_LIKELY((CheckedModOp<T, U>::Do(x, y, &result)))
-               ? result
-               : x;
+    if (CheckedModOp<T, U>::Do(x, y, &result)) [[likely]] {
+      return result;
+    }
+    return x;
   }
 };
 
-template <typename T, typename U, class Enable = void>
+template <typename T, typename U>
 struct ClampedLshOp {};
 
 // Left shift. Non-zero values saturate in the direction of the sign. A zero
 // shifted by any value always results in zero.
 template <typename T, typename U>
-struct ClampedLshOp<T,
-                    U,
-                    typename std::enable_if<std::is_integral<T>::value &&
-                                            std::is_integral<U>::value>::type> {
+  requires(std::integral<T> && std::unsigned_integral<U>)
+struct ClampedLshOp<T, U> {
   using result_type = T;
   template <typename V = result_type>
   static constexpr V Do(T x, U shift) {
-    static_assert(!std::is_signed<U>::value, "Shift value must be unsigned.");
-    if (BASE_NUMERICS_LIKELY(shift < std::numeric_limits<T>::digits)) {
+    if (shift < std::numeric_limits<T>::digits) [[likely]] {
       // Shift as unsigned to avoid undefined behavior.
       V result = static_cast<V>(as_unsigned(x) << shift);
       // If the shift can be reversed, we know it was valid.
-      if (BASE_NUMERICS_LIKELY(result >> shift == x)) {
+      if (result >> shift == x) [[likely]] {
         return result;
       }
     }
@@ -212,87 +187,73 @@
   }
 };
 
-template <typename T, typename U, class Enable = void>
+template <typename T, typename U>
 struct ClampedRshOp {};
 
 // Right shift. Negative values saturate to -1. Positive or 0 saturates to 0.
 template <typename T, typename U>
-struct ClampedRshOp<T,
-                    U,
-                    typename std::enable_if<std::is_integral<T>::value &&
-                                            std::is_integral<U>::value>::type> {
+  requires(std::integral<T> && std::unsigned_integral<U>)
+struct ClampedRshOp<T, U> {
   using result_type = T;
   template <typename V = result_type>
   static constexpr V Do(T x, U shift) {
-    static_assert(!std::is_signed<U>::value, "Shift value must be unsigned.");
     // Signed right shift is odd, because it saturates to -1 or 0.
     const V saturated = as_unsigned(V(0)) - IsValueNegative(x);
-    return BASE_NUMERICS_LIKELY(shift < IntegerBitsPlusSign<T>::value)
-               ? saturated_cast<V>(x >> shift)
-               : saturated;
+    if (shift < kIntegerBitsPlusSign<T>) [[likely]] {
+      return saturated_cast<V>(x >> shift);
+    }
+    return saturated;
   }
 };
 
-template <typename T, typename U, class Enable = void>
+template <typename T, typename U>
 struct ClampedAndOp {};
 
 template <typename T, typename U>
-struct ClampedAndOp<T,
-                    U,
-                    typename std::enable_if<std::is_integral<T>::value &&
-                                            std::is_integral<U>::value>::type> {
-  using result_type = typename std::make_unsigned<
-      typename MaxExponentPromotion<T, U>::type>::type;
+  requires(std::integral<T> && std::integral<U>)
+struct ClampedAndOp<T, U> {
+  using result_type = std::make_unsigned_t<MaxExponentPromotion<T, U>>;
   template <typename V>
   static constexpr V Do(T x, U y) {
     return static_cast<result_type>(x) & static_cast<result_type>(y);
   }
 };
 
-template <typename T, typename U, class Enable = void>
+template <typename T, typename U>
 struct ClampedOrOp {};
 
 // For simplicity we promote to unsigned integers.
 template <typename T, typename U>
-struct ClampedOrOp<T,
-                   U,
-                   typename std::enable_if<std::is_integral<T>::value &&
-                                           std::is_integral<U>::value>::type> {
-  using result_type = typename std::make_unsigned<
-      typename MaxExponentPromotion<T, U>::type>::type;
+  requires(std::integral<T> && std::integral<U>)
+struct ClampedOrOp<T, U> {
+  using result_type = std::make_unsigned_t<MaxExponentPromotion<T, U>>;
   template <typename V>
   static constexpr V Do(T x, U y) {
     return static_cast<result_type>(x) | static_cast<result_type>(y);
   }
 };
 
-template <typename T, typename U, class Enable = void>
+template <typename T, typename U>
 struct ClampedXorOp {};
 
 // For simplicity we support only unsigned integers.
 template <typename T, typename U>
-struct ClampedXorOp<T,
-                    U,
-                    typename std::enable_if<std::is_integral<T>::value &&
-                                            std::is_integral<U>::value>::type> {
-  using result_type = typename std::make_unsigned<
-      typename MaxExponentPromotion<T, U>::type>::type;
+  requires(std::integral<T> && std::integral<U>)
+struct ClampedXorOp<T, U> {
+  using result_type = std::make_unsigned_t<MaxExponentPromotion<T, U>>;
   template <typename V>
   static constexpr V Do(T x, U y) {
     return static_cast<result_type>(x) ^ static_cast<result_type>(y);
   }
 };
 
-template <typename T, typename U, class Enable = void>
+template <typename T, typename U>
 struct ClampedMaxOp {};
 
 template <typename T, typename U>
-struct ClampedMaxOp<
-    T,
-    U,
-    typename std::enable_if<std::is_arithmetic<T>::value &&
-                            std::is_arithmetic<U>::value>::type> {
-  using result_type = typename MaxExponentPromotion<T, U>::type;
+  requires(std::is_arithmetic_v<T> && std::is_arithmetic_v<U>)
+struct ClampedMaxOp<T, U> {
+  using result_type = MaxExponentPromotion<T, U>;
   template <typename V = result_type>
   static constexpr V Do(T x, U y) {
     return IsGreater<T, U>::Test(x, y) ? saturated_cast<V>(x)
@@ -300,16 +261,13 @@
   }
 };
 
-template <typename T, typename U, class Enable = void>
+template <typename T, typename U>
 struct ClampedMinOp {};
 
 template <typename T, typename U>
-struct ClampedMinOp<
-    T,
-    U,
-    typename std::enable_if<std::is_arithmetic<T>::value &&
-                            std::is_arithmetic<U>::value>::type> {
-  using result_type = typename LowestValuePromotion<T, U>::type;
+  requires(std::is_arithmetic_v<T> && std::is_arithmetic_v<U>)
+struct ClampedMinOp<T, U> {
+  using result_type = LowestValuePromotion<T, U>;
   template <typename V = result_type>
   static constexpr V Do(T x, U y) {
     return IsLess<T, U>::Test(x, y) ? saturated_cast<V>(x)
@@ -319,17 +277,15 @@
 
 // This is just boilerplate that wraps the standard floating point arithmetic.
 // A macro isn't the nicest solution, but it beats rewriting these repeatedly.
-#define BASE_FLOAT_ARITHMETIC_OPS(NAME, OP)                              \
-  template <typename T, typename U>                                      \
-  struct Clamped##NAME##Op<                                              \
-      T, U,                                                              \
-      typename std::enable_if<std::is_floating_point<T>::value ||        \
-                              std::is_floating_point<U>::value>::type> { \
-    using result_type = typename MaxExponentPromotion<T, U>::type;       \
-    template <typename V = result_type>                                  \
-    static constexpr V Do(T x, U y) {                                    \
-      return saturated_cast<V>(x OP y);                                  \
-    }                                                                    \
+#define BASE_FLOAT_ARITHMETIC_OPS(NAME, OP)                    \
+  template <typename T, typename U>                            \
+    requires(std::floating_point<T> || std::floating_point<U>) \
+  struct Clamped##NAME##Op<T, U> {                             \
+    using result_type = MaxExponentPromotion<T, U>;            \
+    template <typename V = result_type>                        \
+    static constexpr V Do(T x, U y) {                          \
+      return saturated_cast<V>(x OP y);                        \
+    }                                                          \
   };
 
 BASE_FLOAT_ARITHMETIC_OPS(Add, +)
diff --git a/core/fxcrt/numerics/integral_constant_like.h b/core/fxcrt/numerics/integral_constant_like.h
new file mode 100644
index 0000000..d3ab575
--- /dev/null
+++ b/core/fxcrt/numerics/integral_constant_like.h
@@ -0,0 +1,25 @@
+// Copyright 2025 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CORE_FXCRT_NUMERICS_INTEGRAL_CONSTANT_LIKE_H_
+#define CORE_FXCRT_NUMERICS_INTEGRAL_CONSTANT_LIKE_H_
+
+#include <concepts>
+#include <type_traits>
+
+namespace pdfium {
+
+// Exposition-only concept from [span.syn]
+template <typename T>
+concept IntegralConstantLike =
+    std::is_integral_v<decltype(T::value)> &&
+    !std::is_same_v<bool, std::remove_const_t<decltype(T::value)>> &&
+    std::convertible_to<T, decltype(T::value)> &&
+    std::equality_comparable_with<T, decltype(T::value)> &&
+    std::bool_constant<T() == T::value>::value &&
+    std::bool_constant<static_cast<decltype(T::value)>(T()) == T::value>::value;
+
+}  // namespace pdfium
+
+#endif  // CORE_FXCRT_NUMERICS_INTEGRAL_CONSTANT_LIKE_H_
diff --git a/core/fxcrt/numerics/safe_conversions.h b/core/fxcrt/numerics/safe_conversions.h
index 70a82fc..7041c81 100644
--- a/core/fxcrt/numerics/safe_conversions.h
+++ b/core/fxcrt/numerics/safe_conversions.h
@@ -8,13 +8,14 @@
 #include <stddef.h>
 
 #include <cmath>
+#include <concepts>
 #include <limits>
 #include <type_traits>
 
-#include "core/fxcrt/numerics/safe_conversions_impl.h"
+#include "core/fxcrt/numerics/safe_conversions_impl.h"  // IWYU pragma: export
 
 #if defined(__ARMEL__) && !defined(__native_client__)
-#include "core/fxcrt/numerics/safe_conversions_arm_impl.h"
+#include "core/fxcrt/numerics/safe_conversions_arm_impl.h"  // IWYU pragma: export
 #define BASE_HAS_OPTIMIZED_SAFE_CONVERSIONS (1)
 #else
 #define BASE_HAS_OPTIMIZED_SAFE_CONVERSIONS (0)
@@ -37,10 +38,10 @@
 
 // The following special case a few specific integer conversions where we can
 // eke out better performance than range checking.
-template <typename Dst, typename Src, typename Enable = void>
+template <typename Dst, typename Src>
 struct IsValueInRangeFastOp {
   static constexpr bool is_supported = false;
-  static constexpr bool Do(Src value) {
+  static constexpr bool Do(Src) {
     // Force a compile failure if instantiated.
     return CheckOnFailure::template HandleFailure<bool>();
   }
@@ -48,13 +49,9 @@
 
 // Signed to signed range comparison.
 template <typename Dst, typename Src>
-struct IsValueInRangeFastOp<
-    Dst,
-    Src,
-    typename std::enable_if<
-        std::is_integral<Dst>::value && std::is_integral<Src>::value &&
-        std::is_signed<Dst>::value && std::is_signed<Src>::value &&
-        !IsTypeInRangeForNumericType<Dst, Src>::value>::type> {
+  requires(std::signed_integral<Dst> && std::signed_integral<Src> &&
+           !kIsTypeInRangeForNumericType<Dst, Src>)
+struct IsValueInRangeFastOp<Dst, Src> {
   static constexpr bool is_supported = true;
 
   static constexpr bool Do(Src value) {
@@ -66,32 +63,30 @@
 
 // Signed to unsigned range comparison.
 template <typename Dst, typename Src>
-struct IsValueInRangeFastOp<
-    Dst,
-    Src,
-    typename std::enable_if<
-        std::is_integral<Dst>::value && std::is_integral<Src>::value &&
-        !std::is_signed<Dst>::value && std::is_signed<Src>::value &&
-        !IsTypeInRangeForNumericType<Dst, Src>::value>::type> {
+  requires(std::unsigned_integral<Dst> && std::signed_integral<Src> &&
+           !kIsTypeInRangeForNumericType<Dst, Src>)
+struct IsValueInRangeFastOp<Dst, Src> {
   static constexpr bool is_supported = true;
 
   static constexpr bool Do(Src value) {
     // We cast a signed as unsigned to overflow negative values to the top,
     // then compare against whichever maximum is smaller, as our upper bound.
-    return as_unsigned(value) <= as_unsigned(CommonMax<Src, Dst>());
+    return as_unsigned(value) <= as_unsigned(kCommonMax<Src, Dst>);
   }
 };
 
 // Convenience function that returns true if the supplied value is in range
 // for the destination type.
 template <typename Dst, typename Src>
+  requires(IsNumeric<Src> && std::is_arithmetic_v<Dst> &&
+           std::numeric_limits<Dst>::lowest() < std::numeric_limits<Dst>::max())
 constexpr bool IsValueInRangeForNumericType(Src value) {
-  using SrcType = typename internal::UnderlyingType<Src>::type;
+  using SrcType = UnderlyingType<Src>;
+  const auto underlying_value = static_cast<SrcType>(value);
   return internal::IsValueInRangeFastOp<Dst, SrcType>::is_supported
              ? internal::IsValueInRangeFastOp<Dst, SrcType>::Do(
-                   static_cast<SrcType>(value))
-             : internal::DstRangeRelationToSrcRange<Dst>(
-                   static_cast<SrcType>(value))
+                   underlying_value)
+             : internal::DstRangeRelationToSrcRange<Dst>(underlying_value)
                    .IsValid();
 }
 
@@ -101,13 +96,15 @@
 template <typename Dst,
           class CheckHandler = internal::CheckOnFailure,
           typename Src>
+  requires(IsNumeric<Src> && std::is_arithmetic_v<Dst> &&
+           std::numeric_limits<Dst>::lowest() < std::numeric_limits<Dst>::max())
 constexpr Dst checked_cast(Src value) {
   // This throws a compile-time error on evaluating the constexpr if it can be
   // determined at compile-time as failing, otherwise it will CHECK at runtime.
-  using SrcType = typename internal::UnderlyingType<Src>::type;
-  return BASE_NUMERICS_LIKELY((IsValueInRangeForNumericType<Dst>(value)))
-             ? static_cast<Dst>(static_cast<SrcType>(value))
-             : CheckHandler::template HandleFailure<Dst>();
+  if (IsValueInRangeForNumericType<Dst>(value)) [[likely]] {
+    return static_cast<Dst>(static_cast<UnderlyingType<Src>>(value));
+  }
+  return CheckHandler::template HandleFailure<Dst>();
 }
 
 // Default boundaries for integral/float: max/infinity, lowest/-infinity, 0/NaN.
@@ -148,7 +145,7 @@
              ? (!constraint.IsUnderflowFlagSet() ? static_cast<Dst>(value)
                                                  : S<Dst>::Underflow())
              // Skip this check for integral Src, which cannot be NaN.
-             : (std::is_integral<Src>::value || !constraint.IsUnderflowFlagSet()
+             : (std::is_integral_v<Src> || !constraint.IsUnderflowFlagSet()
                     ? S<Dst>::Overflow()
                     : S<Dst>::NaN());
 }
@@ -156,22 +153,19 @@
 // We can reduce the number of conditions and get slightly better performance
 // for normal signed and unsigned integer ranges. And in the specific case of
 // Arm, we can use the optimized saturation instructions.
-template <typename Dst, typename Src, typename Enable = void>
+template <typename Dst, typename Src>
 struct SaturateFastOp {
   static constexpr bool is_supported = false;
-  static constexpr Dst Do(Src value) {
+  static constexpr Dst Do(Src) {
     // Force a compile failure if instantiated.
     return CheckOnFailure::template HandleFailure<Dst>();
   }
 };
 
 template <typename Dst, typename Src>
-struct SaturateFastOp<
-    Dst,
-    Src,
-    typename std::enable_if<std::is_integral<Src>::value &&
-                            std::is_integral<Dst>::value &&
-                            SaturateFastAsmOp<Dst, Src>::is_supported>::type> {
+  requires(std::integral<Src> && std::integral<Dst> &&
+           SaturateFastAsmOp<Dst, Src>::is_supported)
+struct SaturateFastOp<Dst, Src> {
   static constexpr bool is_supported = true;
   static constexpr Dst Do(Src value) {
     return SaturateFastAsmOp<Dst, Src>::Do(value);
@@ -179,23 +173,21 @@
 };
 
 template <typename Dst, typename Src>
-struct SaturateFastOp<
-    Dst,
-    Src,
-    typename std::enable_if<std::is_integral<Src>::value &&
-                            std::is_integral<Dst>::value &&
-                            !SaturateFastAsmOp<Dst, Src>::is_supported>::type> {
+  requires(std::integral<Src> && std::integral<Dst> &&
+           !SaturateFastAsmOp<Dst, Src>::is_supported)
+struct SaturateFastOp<Dst, Src> {
   static constexpr bool is_supported = true;
   static constexpr Dst Do(Src value) {
     // The exact order of the following is structured to hit the correct
     // optimization heuristics across compilers. Do not change without
     // checking the emitted code.
     const Dst saturated = CommonMaxOrMin<Dst, Src>(
-        IsMaxInRangeForNumericType<Dst, Src>() ||
-        (!IsMinInRangeForNumericType<Dst, Src>() && IsValueNegative(value)));
-    return BASE_NUMERICS_LIKELY(IsValueInRangeForNumericType<Dst>(value))
-               ? static_cast<Dst>(value)
-               : saturated;
+        kIsMaxInRangeForNumericType<Dst, Src> ||
+        (!kIsMinInRangeForNumericType<Dst, Src> && IsValueNegative(value)));
+    if (IsValueInRangeForNumericType<Dst>(value)) [[likely]] {
+      return static_cast<Dst>(value);
+    }
+    return saturated;
   }
 };
 
@@ -207,56 +199,47 @@
           template <typename> class SaturationHandler = SaturationDefaultLimits,
           typename Src>
 constexpr Dst saturated_cast(Src value) {
-  using SrcType = typename UnderlyingType<Src>::type;
-  return !IsConstantEvaluated() && SaturateFastOp<Dst, SrcType>::is_supported &&
-                 std::is_same<SaturationHandler<Dst>,
-                              SaturationDefaultLimits<Dst>>::value
-             ? SaturateFastOp<Dst, SrcType>::Do(static_cast<SrcType>(value))
+  using SrcType = UnderlyingType<Src>;
+  const auto underlying_value = static_cast<SrcType>(value);
+  return !std::is_constant_evaluated() &&
+                 SaturateFastOp<Dst, SrcType>::is_supported &&
+                 std::is_same_v<SaturationHandler<Dst>,
+                                SaturationDefaultLimits<Dst>>
+             ? SaturateFastOp<Dst, SrcType>::Do(underlying_value)
              : saturated_cast_impl<Dst, SaturationHandler, SrcType>(
-                   static_cast<SrcType>(value),
+                   underlying_value,
                    DstRangeRelationToSrcRange<Dst, SaturationHandler, SrcType>(
-                       static_cast<SrcType>(value)));
+                       underlying_value));
 }
 
 // strict_cast<> is analogous to static_cast<> for numeric types, except that
 // it will cause a compile failure if the destination type is not large enough
 // to contain any value in the source type. It performs no runtime checking.
-template <typename Dst, typename Src>
+template <typename Dst, typename Src, typename SrcType = UnderlyingType<Src>>
+  requires(
+      IsNumeric<Src> && std::is_arithmetic_v<Dst> &&
+      // If you got here from a compiler error, it's because you tried to assign
+      // from a source type to a destination type that has insufficient range.
+      // The solution may be to change the destination type you're assigning to,
+      // and use one large enough to represent the source.
+      // Alternatively, you may be better served with the checked_cast<> or
+      // saturated_cast<> template functions for your particular use case.
+      kStaticDstRangeRelationToSrcRange<Dst, SrcType> ==
+          NumericRangeRepresentation::kContained)
 constexpr Dst strict_cast(Src value) {
-  using SrcType = typename UnderlyingType<Src>::type;
-  static_assert(UnderlyingType<Src>::is_numeric, "Argument must be numeric.");
-  static_assert(std::is_arithmetic<Dst>::value, "Result must be numeric.");
-
-  // If you got here from a compiler error, it's because you tried to assign
-  // from a source type to a destination type that has insufficient range.
-  // The solution may be to change the destination type you're assigning to,
-  // and use one large enough to represent the source.
-  // Alternatively, you may be better served with the checked_cast<> or
-  // saturated_cast<> template functions for your particular use case.
-  static_assert(StaticDstRangeRelationToSrcRange<Dst, SrcType>::value ==
-                    NUMERIC_RANGE_CONTAINED,
-                "The source type is out of range for the destination type. "
-                "Please see strict_cast<> comments for more information.");
-
   return static_cast<Dst>(static_cast<SrcType>(value));
 }
 
 // Some wrappers to statically check that a type is in range.
-template <typename Dst, typename Src, class Enable = void>
-struct IsNumericRangeContained {
-  static constexpr bool value = false;
-};
+template <typename Dst, typename Src>
+inline constexpr bool kIsNumericRangeContained = false;
 
 template <typename Dst, typename Src>
-struct IsNumericRangeContained<
-    Dst,
-    Src,
-    typename std::enable_if<ArithmeticOrUnderlyingEnum<Dst>::value &&
-                            ArithmeticOrUnderlyingEnum<Src>::value>::type> {
-  static constexpr bool value =
-      StaticDstRangeRelationToSrcRange<Dst, Src>::value ==
-      NUMERIC_RANGE_CONTAINED;
-};
+  requires(std::is_arithmetic_v<ArithmeticOrUnderlyingEnum<Dst>> &&
+           std::is_arithmetic_v<ArithmeticOrUnderlyingEnum<Src>>)
+inline constexpr bool kIsNumericRangeContained<Dst, Src> =
+    kStaticDstRangeRelationToSrcRange<Dst, Src> ==
+    NumericRangeRepresentation::kContained;
 
 // StrictNumeric implements compile time range checking between numeric types by
 // wrapping assignment operations in a strict_cast. This class is intended to be
@@ -269,6 +252,7 @@
 // runtime checking of any of the associated mathematical operations. Use
 // CheckedNumeric for runtime range checks of the actual value being assigned.
 template <typename T>
+  requires std::is_arithmetic_v<T>
 class StrictNumeric {
  public:
   using type = T;
@@ -280,12 +264,6 @@
   constexpr StrictNumeric(const StrictNumeric<Src>& rhs)
       : value_(strict_cast<T>(rhs.value_)) {}
 
-  // Strictly speaking, this is not necessary, but declaring this allows class
-  // template argument deduction to be used so that it is possible to simply
-  // write `StrictNumeric(777)` instead of `StrictNumeric<int>(777)`.
-  // NOLINTNEXTLINE(google-explicit-constructor)
-  constexpr StrictNumeric(T value) : value_(value) {}
-
   // This is not an explicit constructor because we implicitly upgrade regular
   // numerics to StrictNumerics to make them easier to use.
   template <typename Src>
@@ -304,32 +282,38 @@
   // to explicitly cast the result to the destination type.
   // If none of that works, you may be better served with the checked_cast<> or
   // saturated_cast<> template functions for your particular use case.
-  template <typename Dst,
-            typename std::enable_if<
-                IsNumericRangeContained<Dst, T>::value>::type* = nullptr>
-  constexpr operator Dst() const {
-    return static_cast<typename ArithmeticOrUnderlyingEnum<Dst>::type>(value_);
+  template <typename Dst>
+    requires(kIsNumericRangeContained<Dst, T>)
+  constexpr operator Dst() const {  // NOLINT(google-explicit-constructor)
+    return static_cast<ArithmeticOrUnderlyingEnum<Dst>>(value_);
   }
 
+  // Unary negation does not require any conversions.
+  constexpr bool operator!() const { return !value_; }
+
  private:
-  const T value_;
+  template <typename U>
+    requires std::is_arithmetic_v<U>
+  friend class StrictNumeric;
+
+  T value_;
 };
 
+template <typename T>
+StrictNumeric(T) -> StrictNumeric<T>;
+
 // Convenience wrapper returns a StrictNumeric from the provided arithmetic
 // type.
 template <typename T>
-constexpr StrictNumeric<typename UnderlyingType<T>::type> MakeStrictNum(
-    const T value) {
+constexpr StrictNumeric<UnderlyingType<T>> MakeStrictNum(const T value) {
   return value;
 }
 
-#define BASE_NUMERIC_COMPARISON_OPERATORS(CLASS, NAME, OP)              \
-  template <typename L, typename R,                                     \
-            typename std::enable_if<                                    \
-                internal::Is##CLASS##Op<L, R>::value>::type* = nullptr> \
-  constexpr bool operator OP(const L lhs, const R rhs) {                \
-    return SafeCompare<NAME, typename UnderlyingType<L>::type,          \
-                       typename UnderlyingType<R>::type>(lhs, rhs);     \
+#define BASE_NUMERIC_COMPARISON_OPERATORS(CLASS, NAME, OP)                    \
+  template <typename L, typename R>                                           \
+    requires(internal::Is##CLASS##Op<L, R>)                                   \
+  constexpr bool operator OP(L lhs, R rhs) {                                  \
+    return SafeCompare<NAME, UnderlyingType<L>, UnderlyingType<R>>(lhs, rhs); \
   }
 
 BASE_NUMERIC_COMPARISON_OPERATORS(Strict, IsLess, <)
@@ -344,9 +328,9 @@
 using internal::as_signed;
 using internal::as_unsigned;
 using internal::checked_cast;
-using internal::IsTypeInRangeForNumericType;
 using internal::IsValueInRangeForNumericType;
 using internal::IsValueNegative;
+using internal::kIsTypeInRangeForNumericType;
 using internal::MakeStrictNum;
 using internal::SafeUnsignedAbs;
 using internal::saturated_cast;
@@ -357,29 +341,44 @@
 using SizeT = StrictNumeric<size_t>;
 
 // floating -> integral conversions that saturate and thus can actually return
-// an integral type.  In most cases, these should be preferred over the std::
-// versions.
-template <typename Dst = int,
-          typename Src,
-          typename = std::enable_if_t<std::is_integral<Dst>::value &&
-                                      std::is_floating_point<Src>::value>>
+// an integral type.
+//
+// Generally, what you want is saturated_cast<Dst>(std::nearbyint(x)), which
+// rounds correctly according to IEEE-754 (round to nearest, ties go to nearest
+// even number; this avoids bias). If your code is performance-critical
+// and you are sure that you will never overflow, you can use std::lrint()
+// or std::llrint(), which return a long or long long directly.
+//
+// Below are convenience functions around similar patterns, except that
+// they round in nonstandard directions and will generally be slower.
+
+// Rounds towards negative infinity (i.e., down).
+template <typename Dst = int, typename Src>
+  requires(std::integral<Dst> && std::floating_point<Src>)
 Dst ClampFloor(Src value) {
   return saturated_cast<Dst>(std::floor(value));
 }
-template <typename Dst = int,
-          typename Src,
-          typename = std::enable_if_t<std::is_integral<Dst>::value &&
-                                      std::is_floating_point<Src>::value>>
+
+// Rounds towards positive infinity (i.e., up).
+template <typename Dst = int, typename Src>
+  requires(std::integral<Dst> && std::floating_point<Src>)
 Dst ClampCeil(Src value) {
   return saturated_cast<Dst>(std::ceil(value));
 }
-template <typename Dst = int,
-          typename Src,
-          typename = std::enable_if_t<std::is_integral<Dst>::value &&
-                                      std::is_floating_point<Src>::value>>
+
+// Rounds towards nearest integer, with ties away from zero.
+// This means that 0.5 will be rounded to 1 and 1.5 will be rounded to 2.
+// Similarly, -0.5 will be rounded to -1 and -1.5 will be rounded to -2.
+//
+// This is normally not what you want accuracy-wise (it introduces a small bias
+// away from zero), and it is not the fastest option, but it is frequently what
+// existing code expects. Compare with saturated_cast<Dst>(std::nearbyint(x))
+// or std::lrint(x), which would round 0.5 and -0.5 to 0 but 1.5 to 2 and
+// -1.5 to -2.
+template <typename Dst = int, typename Src>
+  requires(std::integral<Dst> && std::floating_point<Src>)
 Dst ClampRound(Src value) {
-  const Src rounded =
-      (value >= 0.0f) ? std::floor(value + 0.5f) : std::ceil(value - 0.5f);
+  const Src rounded = std::round(value);
   return saturated_cast<Dst>(rounded);
 }
 
diff --git a/core/fxcrt/numerics/safe_conversions_arm_impl.h b/core/fxcrt/numerics/safe_conversions_arm_impl.h
index 2eaf7a8..c85c9a4 100644
--- a/core/fxcrt/numerics/safe_conversions_arm_impl.h
+++ b/core/fxcrt/numerics/safe_conversions_arm_impl.h
@@ -5,8 +5,12 @@
 #ifndef CORE_FXCRT_NUMERICS_SAFE_CONVERSIONS_ARM_IMPL_H_
 #define CORE_FXCRT_NUMERICS_SAFE_CONVERSIONS_ARM_IMPL_H_
 
-#include <cassert>
-#include <limits>
+// IWYU pragma: private, include "core/fxcrt/numerics/safe_conversions.h"
+
+#include <stdint.h>
+
+#include <algorithm>
+#include <concepts>
 #include <type_traits>
 
 #include "core/fxcrt/numerics/safe_conversions_impl.h"
@@ -18,30 +22,28 @@
 template <typename Dst, typename Src>
 struct SaturateFastAsmOp {
   static constexpr bool is_supported =
-      kEnableAsmCode && std::is_signed<Src>::value &&
-      std::is_integral<Dst>::value && std::is_integral<Src>::value &&
-      IntegerBitsPlusSign<Src>::value <= IntegerBitsPlusSign<int32_t>::value &&
-      IntegerBitsPlusSign<Dst>::value <= IntegerBitsPlusSign<int32_t>::value &&
-      !IsTypeInRangeForNumericType<Dst, Src>::value;
+      kEnableAsmCode && std::signed_integral<Src> && std::integral<Dst> &&
+      kIntegerBitsPlusSign<Src> <= kIntegerBitsPlusSign<int32_t> &&
+      kIntegerBitsPlusSign<Dst> <= kIntegerBitsPlusSign<int32_t> &&
+      !kIsTypeInRangeForNumericType<Dst, Src>;
 
   __attribute__((always_inline)) static Dst Do(Src value) {
     int32_t src = value;
-    typename std::conditional<std::is_signed<Dst>::value, int32_t,
-                              uint32_t>::type result;
-    if (std::is_signed<Dst>::value) {
+    if constexpr (std::is_signed_v<Dst>) {
+      int32_t result;
       asm("ssat %[dst], %[shift], %[src]"
           : [dst] "=r"(result)
-          : [src] "r"(src), [shift] "n"(IntegerBitsPlusSign<Dst>::value <= 32
-                                            ? IntegerBitsPlusSign<Dst>::value
-                                            : 32));
+          : [src] "r"(src), [shift] "n"(
+                                std::min(kIntegerBitsPlusSign<Dst>, 32)));
+      return static_cast<Dst>(result);
     } else {
+      uint32_t result;
       asm("usat %[dst], %[shift], %[src]"
           : [dst] "=r"(result)
-          : [src] "r"(src), [shift] "n"(IntegerBitsPlusSign<Dst>::value < 32
-                                            ? IntegerBitsPlusSign<Dst>::value
-                                            : 31));
+          : [src] "r"(src), [shift] "n"(
+                                std::min(kIntegerBitsPlusSign<Dst>, 31)));
+      return static_cast<Dst>(result);
     }
-    return static_cast<Dst>(result);
   }
 };
 
diff --git a/core/fxcrt/numerics/safe_conversions_impl.h b/core/fxcrt/numerics/safe_conversions_impl.h
index 4dae1ed..b510787 100644
--- a/core/fxcrt/numerics/safe_conversions_impl.h
+++ b/core/fxcrt/numerics/safe_conversions_impl.h
@@ -5,72 +5,54 @@
 #ifndef CORE_FXCRT_NUMERICS_SAFE_CONVERSIONS_IMPL_H_
 #define CORE_FXCRT_NUMERICS_SAFE_CONVERSIONS_IMPL_H_
 
+// IWYU pragma: private, include "core/fxcrt/numerics/safe_conversions.h"
+
+#include <stddef.h>
 #include <stdint.h>
 
+#include <concepts>
 #include <limits>
 #include <type_traits>
+#include <utility>
 
-#if defined(__GNUC__) || defined(__clang__)
-#define BASE_NUMERICS_LIKELY(x) __builtin_expect(!!(x), 1)
-#define BASE_NUMERICS_UNLIKELY(x) __builtin_expect(!!(x), 0)
-#else
-#define BASE_NUMERICS_LIKELY(x) (x)
-#define BASE_NUMERICS_UNLIKELY(x) (x)
-#endif
+#include "core/fxcrt/numerics/integral_constant_like.h"
 
-namespace pdfium {
-namespace internal {
+namespace pdfium::internal {
 
 // The std library doesn't provide a binary max_exponent for integers, however
 // we can compute an analog using std::numeric_limits<>::digits.
 template <typename NumericType>
-struct MaxExponent {
-  static const int value = std::is_floating_point<NumericType>::value
-                               ? std::numeric_limits<NumericType>::max_exponent
-                               : std::numeric_limits<NumericType>::digits + 1;
-};
+inline constexpr int kMaxExponent =
+    std::is_floating_point_v<NumericType>
+        ? std::numeric_limits<NumericType>::max_exponent
+        : std::numeric_limits<NumericType>::digits + 1;
 
 // The number of bits (including the sign) in an integer. Eliminates sizeof
 // hacks.
 template <typename NumericType>
-struct IntegerBitsPlusSign {
-  static const int value = std::numeric_limits<NumericType>::digits +
-                           std::is_signed<NumericType>::value;
-};
-
-// Helper templates for integer manipulations.
-
-template <typename Integer>
-struct PositionOfSignBit {
-  static const size_t value = IntegerBitsPlusSign<Integer>::value - 1;
-};
+inline constexpr int kIntegerBitsPlusSign =
+    std::numeric_limits<NumericType>::digits + std::is_signed_v<NumericType>;
 
 // Determines if a numeric value is negative without throwing compiler
 // warnings on: unsigned(value) < 0.
-template <typename T,
-          typename std::enable_if<std::is_signed<T>::value>::type* = nullptr>
+template <typename T>
+  requires(std::is_arithmetic_v<T>)
 constexpr bool IsValueNegative(T value) {
-  static_assert(std::is_arithmetic<T>::value, "Argument must be numeric.");
-  return value < 0;
-}
-
-template <typename T,
-          typename std::enable_if<!std::is_signed<T>::value>::type* = nullptr>
-constexpr bool IsValueNegative(T) {
-  static_assert(std::is_arithmetic<T>::value, "Argument must be numeric.");
-  return false;
+  if constexpr (std::is_signed_v<T>) {
+    return value < 0;
+  } else {
+    return false;
+  }
 }
 
 // This performs a fast negation, returning a signed value. It works on unsigned
 // arguments, but probably doesn't do what you want for any unsigned value
 // larger than max / 2 + 1 (i.e. signed min cast to unsigned).
 template <typename T>
-constexpr typename std::make_signed<T>::type ConditionalNegate(
-    T x,
-    bool is_negative) {
-  static_assert(std::is_integral<T>::value, "Type must be integral");
-  using SignedT = typename std::make_signed<T>::type;
-  using UnsignedT = typename std::make_unsigned<T>::type;
+  requires std::is_integral_v<T>
+constexpr auto ConditionalNegate(T x, bool is_negative) {
+  using SignedT = std::make_signed_t<T>;
+  using UnsignedT = std::make_unsigned_t<T>;
   return static_cast<SignedT>((static_cast<UnsignedT>(x) ^
                                static_cast<UnsignedT>(-SignedT(is_negative))) +
                               is_negative);
@@ -78,28 +60,24 @@
 
 // This performs a safe, absolute value via unsigned overflow.
 template <typename T>
-constexpr typename std::make_unsigned<T>::type SafeUnsignedAbs(T value) {
-  static_assert(std::is_integral<T>::value, "Type must be integral");
-  using UnsignedT = typename std::make_unsigned<T>::type;
+  requires std::is_integral_v<T>
+constexpr auto SafeUnsignedAbs(T value) {
+  using UnsignedT = std::make_unsigned_t<T>;
   return IsValueNegative(value)
              ? static_cast<UnsignedT>(0u - static_cast<UnsignedT>(value))
              : static_cast<UnsignedT>(value);
 }
 
-// TODO(jschuh): Switch to std::is_constant_evaluated() once C++20 is supported.
-// Alternately, the usage could be restructured for "consteval if" in C++23.
-#define IsConstantEvaluated() (__builtin_is_constant_evaluated())
-
 // TODO(jschuh): Debug builds don't reliably propagate constants, so we restrict
 // some accelerated runtime paths to release builds until this can be forced
 // with consteval support in C++20 or C++23.
 #if defined(NDEBUG)
-constexpr bool kEnableAsmCode = true;
+inline constexpr bool kEnableAsmCode = true;
 #else
-constexpr bool kEnableAsmCode = false;
+inline constexpr bool kEnableAsmCode = false;
 #endif
 
-// Forces a crash, like a CHECK(false). Used for numeric boundary errors.
+// Forces a crash, like a NOTREACHED(). Used for numeric boundary errors.
 // Also used in a constexpr template to trigger a compilation failure on
 // an error condition.
 struct CheckOnFailure {
@@ -116,92 +94,76 @@
   }
 };
 
-enum IntegerRepresentation {
-  INTEGER_REPRESENTATION_UNSIGNED,
-  INTEGER_REPRESENTATION_SIGNED
-};
+enum class IntegerRepresentation { kUnsigned, kSigned };
 
 // A range for a given nunmeric Src type is contained for a given numeric Dst
 // type if both numeric_limits<Src>::max() <= numeric_limits<Dst>::max() and
 // numeric_limits<Src>::lowest() >= numeric_limits<Dst>::lowest() are true.
 // We implement this as template specializations rather than simple static
 // comparisons to ensure type correctness in our comparisons.
-enum NumericRangeRepresentation {
-  NUMERIC_RANGE_NOT_CONTAINED,
-  NUMERIC_RANGE_CONTAINED
-};
+enum class NumericRangeRepresentation { kNotContained, kContained };
 
 // Helper templates to statically determine if our destination type can contain
 // maximum and minimum values represented by the source type.
 
+// Default case, used for same sign: Dst is guaranteed to contain Src only if
+// its range is equal or larger.
 template <typename Dst,
           typename Src,
-          IntegerRepresentation DstSign = std::is_signed<Dst>::value
-                                              ? INTEGER_REPRESENTATION_SIGNED
-                                              : INTEGER_REPRESENTATION_UNSIGNED,
-          IntegerRepresentation SrcSign = std::is_signed<Src>::value
-                                              ? INTEGER_REPRESENTATION_SIGNED
-                                              : INTEGER_REPRESENTATION_UNSIGNED>
-struct StaticDstRangeRelationToSrcRange;
-
-// Same sign: Dst is guaranteed to contain Src only if its range is equal or
-// larger.
-template <typename Dst, typename Src, IntegerRepresentation Sign>
-struct StaticDstRangeRelationToSrcRange<Dst, Src, Sign, Sign> {
-  static const NumericRangeRepresentation value =
-      MaxExponent<Dst>::value >= MaxExponent<Src>::value
-          ? NUMERIC_RANGE_CONTAINED
-          : NUMERIC_RANGE_NOT_CONTAINED;
-};
+          IntegerRepresentation DstSign =
+              std::is_signed_v<Dst> ? IntegerRepresentation::kSigned
+                                    : IntegerRepresentation::kUnsigned,
+          IntegerRepresentation SrcSign =
+              std::is_signed_v<Src> ? IntegerRepresentation::kSigned
+                                    : IntegerRepresentation::kUnsigned>
+inline constexpr auto kStaticDstRangeRelationToSrcRange =
+    kMaxExponent<Dst> >= kMaxExponent<Src>
+        ? NumericRangeRepresentation::kContained
+        : NumericRangeRepresentation::kNotContained;
 
 // Unsigned to signed: Dst is guaranteed to contain source only if its range is
 // larger.
 template <typename Dst, typename Src>
-struct StaticDstRangeRelationToSrcRange<Dst,
-                                        Src,
-                                        INTEGER_REPRESENTATION_SIGNED,
-                                        INTEGER_REPRESENTATION_UNSIGNED> {
-  static const NumericRangeRepresentation value =
-      MaxExponent<Dst>::value > MaxExponent<Src>::value
-          ? NUMERIC_RANGE_CONTAINED
-          : NUMERIC_RANGE_NOT_CONTAINED;
-};
+inline constexpr auto
+    kStaticDstRangeRelationToSrcRange<Dst,
+                                      Src,
+                                      IntegerRepresentation::kSigned,
+                                      IntegerRepresentation::kUnsigned> =
+        kMaxExponent<Dst> > kMaxExponent<Src>
+            ? NumericRangeRepresentation::kContained
+            : NumericRangeRepresentation::kNotContained;
 
 // Signed to unsigned: Dst cannot be statically determined to contain Src.
 template <typename Dst, typename Src>
-struct StaticDstRangeRelationToSrcRange<Dst,
-                                        Src,
-                                        INTEGER_REPRESENTATION_UNSIGNED,
-                                        INTEGER_REPRESENTATION_SIGNED> {
-  static const NumericRangeRepresentation value = NUMERIC_RANGE_NOT_CONTAINED;
-};
+inline constexpr auto
+    kStaticDstRangeRelationToSrcRange<Dst,
+                                      Src,
+                                      IntegerRepresentation::kUnsigned,
+                                      IntegerRepresentation::kSigned> =
+        NumericRangeRepresentation::kNotContained;
 
 // This class wraps the range constraints as separate booleans so the compiler
 // can identify constants and eliminate unused code paths.
 class RangeCheck {
  public:
+  constexpr RangeCheck() = default;
   constexpr RangeCheck(bool is_in_lower_bound, bool is_in_upper_bound)
       : is_underflow_(!is_in_lower_bound), is_overflow_(!is_in_upper_bound) {}
-  constexpr RangeCheck() : is_underflow_(false), is_overflow_(false) {}
+
+  constexpr bool operator==(const RangeCheck& rhs) const = default;
+
   constexpr bool IsValid() const { return !is_overflow_ && !is_underflow_; }
   constexpr bool IsInvalid() const { return is_overflow_ && is_underflow_; }
   constexpr bool IsOverflow() const { return is_overflow_ && !is_underflow_; }
   constexpr bool IsUnderflow() const { return !is_overflow_ && is_underflow_; }
   constexpr bool IsOverflowFlagSet() const { return is_overflow_; }
   constexpr bool IsUnderflowFlagSet() const { return is_underflow_; }
-  constexpr bool operator==(const RangeCheck rhs) const {
-    return is_underflow_ == rhs.is_underflow_ &&
-           is_overflow_ == rhs.is_overflow_;
-  }
-  constexpr bool operator!=(const RangeCheck rhs) const {
-    return !(*this == rhs);
-  }
 
  private:
   // Do not change the order of these member variables. The integral conversion
   // optimization depends on this exact order.
-  const bool is_underflow_;
-  const bool is_overflow_;
+  const bool is_underflow_ = false;
+  const bool is_overflow_ = false;
 };
 
 // The following helper template addresses a corner case in range checks for
@@ -228,71 +190,54 @@
 template <typename Dst, typename Src, template <typename> class Bounds>
 struct NarrowingRange {
   using SrcLimits = std::numeric_limits<Src>;
-  using DstLimits = typename std::numeric_limits<Dst>;
+  using DstLimits = std::numeric_limits<Dst>;
 
   // Computes the mask required to make an accurate comparison between types.
-  static const int kShift =
-      (MaxExponent<Src>::value > MaxExponent<Dst>::value &&
-       SrcLimits::digits < DstLimits::digits)
-          ? (DstLimits::digits - SrcLimits::digits)
-          : 0;
-  template <
-      typename T,
-      typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
+  static constexpr int kShift = (kMaxExponent<Src> > kMaxExponent<Dst> &&
+                                 SrcLimits::digits < DstLimits::digits)
+                                    ? (DstLimits::digits - SrcLimits::digits)
+                                    : 0;
 
+  template <typename T>
+    requires(std::same_as<T, Dst> &&
+             ((std::integral<T> && kShift < DstLimits::digits) ||
+              (std::floating_point<T> && kShift == 0)))
   // Masks out the integer bits that are beyond the precision of the
   // intermediate type used for comparison.
   static constexpr T Adjust(T value) {
-    static_assert(std::is_same<T, Dst>::value, "");
-    static_assert(kShift < DstLimits::digits, "");
-    using UnsignedDst = typename std::make_unsigned_t<T>;
-    return static_cast<T>(ConditionalNegate(
-        SafeUnsignedAbs(value) & ~((UnsignedDst{1} << kShift) - UnsignedDst{1}),
-        IsValueNegative(value)));
-  }
-
-  template <typename T,
-            typename std::enable_if<std::is_floating_point<T>::value>::type* =
-                nullptr>
-  static constexpr T Adjust(T value) {
-    static_assert(std::is_same<T, Dst>::value, "");
-    static_assert(kShift == 0, "");
-    return value;
+    if constexpr (std::integral<T>) {
+      using UnsignedDst = typename std::make_unsigned_t<T>;
+      return static_cast<T>(
+          ConditionalNegate(SafeUnsignedAbs(value) &
+                                ~((UnsignedDst{1} << kShift) - UnsignedDst{1}),
+                            IsValueNegative(value)));
+    } else {
+      return value;
+    }
   }
 
   static constexpr Dst max() { return Adjust(Bounds<Dst>::max()); }
   static constexpr Dst lowest() { return Adjust(Bounds<Dst>::lowest()); }
 };
 
-template <typename Dst,
-          typename Src,
-          template <typename> class Bounds,
-          IntegerRepresentation DstSign = std::is_signed<Dst>::value
-                                              ? INTEGER_REPRESENTATION_SIGNED
-                                              : INTEGER_REPRESENTATION_UNSIGNED,
-          IntegerRepresentation SrcSign = std::is_signed<Src>::value
-                                              ? INTEGER_REPRESENTATION_SIGNED
-                                              : INTEGER_REPRESENTATION_UNSIGNED,
-          NumericRangeRepresentation DstRange =
-              StaticDstRangeRelationToSrcRange<Dst, Src>::value>
-struct DstRangeRelationToSrcRangeImpl;
-
 // The following templates are for ranges that must be verified at runtime. We
 // split it into checks based on signedness to avoid confusing casts and
 // compiler warnings on signed an unsigned comparisons.
 
-// Same sign narrowing: The range is contained for normal limits.
+// Default case, used for same sign narrowing: The range is contained for normal
+// limits.
 template <typename Dst,
           typename Src,
           template <typename> class Bounds,
-          IntegerRepresentation DstSign,
-          IntegerRepresentation SrcSign>
-struct DstRangeRelationToSrcRangeImpl<Dst,
-                                      Src,
-                                      Bounds,
-                                      DstSign,
-                                      SrcSign,
-                                      NUMERIC_RANGE_CONTAINED> {
+          IntegerRepresentation DstSign =
+              std::is_signed_v<Dst> ? IntegerRepresentation::kSigned
+                                    : IntegerRepresentation::kUnsigned,
+          IntegerRepresentation SrcSign =
+              std::is_signed_v<Src> ? IntegerRepresentation::kSigned
+                                    : IntegerRepresentation::kUnsigned,
+          NumericRangeRepresentation DstRange =
+              kStaticDstRangeRelationToSrcRange<Dst, Src>>
+struct DstRangeRelationToSrcRangeImpl {
   static constexpr RangeCheck Check(Src value) {
     using SrcLimits = std::numeric_limits<Src>;
     using DstLimits = NarrowingRange<Dst, Src, Bounds>;
@@ -307,12 +252,13 @@
 // Signed to signed narrowing: Both the upper and lower boundaries may be
 // exceeded for standard limits.
 template <typename Dst, typename Src, template <typename> class Bounds>
-struct DstRangeRelationToSrcRangeImpl<Dst,
-                                      Src,
-                                      Bounds,
-                                      INTEGER_REPRESENTATION_SIGNED,
-                                      INTEGER_REPRESENTATION_SIGNED,
-                                      NUMERIC_RANGE_NOT_CONTAINED> {
+struct DstRangeRelationToSrcRangeImpl<
+    Dst,
+    Src,
+    Bounds,
+    IntegerRepresentation::kSigned,
+    IntegerRepresentation::kSigned,
+    NumericRangeRepresentation::kNotContained> {
   static constexpr RangeCheck Check(Src value) {
     using DstLimits = NarrowingRange<Dst, Src, Bounds>;
     return RangeCheck(value >= DstLimits::lowest(), value <= DstLimits::max());
@@ -322,32 +268,34 @@
 // Unsigned to unsigned narrowing: Only the upper bound can be exceeded for
 // standard limits.
 template <typename Dst, typename Src, template <typename> class Bounds>
-struct DstRangeRelationToSrcRangeImpl<Dst,
-                                      Src,
-                                      Bounds,
-                                      INTEGER_REPRESENTATION_UNSIGNED,
-                                      INTEGER_REPRESENTATION_UNSIGNED,
-                                      NUMERIC_RANGE_NOT_CONTAINED> {
+struct DstRangeRelationToSrcRangeImpl<
+    Dst,
+    Src,
+    Bounds,
+    IntegerRepresentation::kUnsigned,
+    IntegerRepresentation::kUnsigned,
+    NumericRangeRepresentation::kNotContained> {
   static constexpr RangeCheck Check(Src value) {
     using DstLimits = NarrowingRange<Dst, Src, Bounds>;
     return RangeCheck(
-        DstLimits::lowest() == Dst(0) || value >= DstLimits::lowest(),
+        DstLimits::lowest() == Dst{0} || value >= DstLimits::lowest(),
         value <= DstLimits::max());
   }
 };
 
 // Unsigned to signed: Only the upper bound can be exceeded for standard limits.
 template <typename Dst, typename Src, template <typename> class Bounds>
-struct DstRangeRelationToSrcRangeImpl<Dst,
-                                      Src,
-                                      Bounds,
-                                      INTEGER_REPRESENTATION_SIGNED,
-                                      INTEGER_REPRESENTATION_UNSIGNED,
-                                      NUMERIC_RANGE_NOT_CONTAINED> {
+struct DstRangeRelationToSrcRangeImpl<
+    Dst,
+    Src,
+    Bounds,
+    IntegerRepresentation::kSigned,
+    IntegerRepresentation::kUnsigned,
+    NumericRangeRepresentation::kNotContained> {
   static constexpr RangeCheck Check(Src value) {
     using DstLimits = NarrowingRange<Dst, Src, Bounds>;
     using Promotion = decltype(Src() + Dst());
-    return RangeCheck(DstLimits::lowest() <= Dst(0) ||
+    return RangeCheck(DstLimits::lowest() <= Dst{0} ||
                           static_cast<Promotion>(value) >=
                               static_cast<Promotion>(DstLimits::lowest()),
                       static_cast<Promotion>(value) <=
@@ -358,23 +306,24 @@
 // Signed to unsigned: The upper boundary may be exceeded for a narrower Dst,
 // and any negative value exceeds the lower boundary for standard limits.
 template <typename Dst, typename Src, template <typename> class Bounds>
-struct DstRangeRelationToSrcRangeImpl<Dst,
-                                      Src,
-                                      Bounds,
-                                      INTEGER_REPRESENTATION_UNSIGNED,
-                                      INTEGER_REPRESENTATION_SIGNED,
-                                      NUMERIC_RANGE_NOT_CONTAINED> {
+struct DstRangeRelationToSrcRangeImpl<
+    Dst,
+    Src,
+    Bounds,
+    IntegerRepresentation::kUnsigned,
+    IntegerRepresentation::kSigned,
+    NumericRangeRepresentation::kNotContained> {
   static constexpr RangeCheck Check(Src value) {
     using SrcLimits = std::numeric_limits<Src>;
     using DstLimits = NarrowingRange<Dst, Src, Bounds>;
     using Promotion = decltype(Src() + Dst());
-    bool ge_zero = false;
+    bool ge_zero;
     // Converting floating-point to integer will discard fractional part, so
     // values in (-1.0, -0.0) will truncate to 0 and fit in Dst.
-    if (std::is_floating_point<Src>::value) {
-      ge_zero = value > Src(-1);
+    if constexpr (std::is_floating_point_v<Src>) {
+      ge_zero = value > Src{-1};
     } else {
-      ge_zero = value >= Src(0);
+      ge_zero = value >= Src{0};
     }
     return RangeCheck(
         ge_zero && (DstLimits::lowest() == 0 ||
@@ -388,30 +337,28 @@
 
 // Simple wrapper for statically checking if a type's range is contained.
 template <typename Dst, typename Src>
-struct IsTypeInRangeForNumericType {
-  static const bool value = StaticDstRangeRelationToSrcRange<Dst, Src>::value ==
-                            NUMERIC_RANGE_CONTAINED;
-};
+inline constexpr bool kIsTypeInRangeForNumericType =
+    kStaticDstRangeRelationToSrcRange<Dst, Src> ==
+    NumericRangeRepresentation::kContained;
 
 template <typename Dst,
           template <typename> class Bounds = std::numeric_limits,
           typename Src>
+  requires(std::is_arithmetic_v<Src> && std::is_arithmetic_v<Dst> &&
+           Bounds<Dst>::lowest() < Bounds<Dst>::max())
 constexpr RangeCheck DstRangeRelationToSrcRange(Src value) {
-  static_assert(std::is_arithmetic<Src>::value, "Argument must be numeric.");
-  static_assert(std::is_arithmetic<Dst>::value, "Result must be numeric.");
-  static_assert(Bounds<Dst>::lowest() < Bounds<Dst>::max(), "");
   return DstRangeRelationToSrcRangeImpl<Dst, Src, Bounds>::Check(value);
 }
 
 // Integer promotion templates used by the portable checked integer arithmetic.
 template <size_t Size, bool IsSigned>
-struct IntegerForDigitsAndSign;
+struct IntegerForDigitsAndSignImpl;
 
-#define INTEGER_FOR_DIGITS_AND_SIGN(I)                          \
-  template <>                                                   \
-  struct IntegerForDigitsAndSign<IntegerBitsPlusSign<I>::value, \
-                                 std::is_signed<I>::value> {    \
-    using type = I;                                             \
+#define INTEGER_FOR_DIGITS_AND_SIGN(I)                        \
+  template <>                                                 \
+  struct IntegerForDigitsAndSignImpl<kIntegerBitsPlusSign<I>, \
+                                     std::is_signed_v<I>> {   \
+    using type = I;                                           \
   }
 
 INTEGER_FOR_DIGITS_AND_SIGN(int8_t);
@@ -424,422 +371,352 @@
 INTEGER_FOR_DIGITS_AND_SIGN(uint64_t);
 #undef INTEGER_FOR_DIGITS_AND_SIGN
 
+template <size_t Size, bool IsSigned>
+using IntegerForDigitsAndSign =
+    IntegerForDigitsAndSignImpl<Size, IsSigned>::type;
+
 // WARNING: We have no IntegerForSizeAndSign<16, *>. If we ever add one to
 // support 128-bit math, then the ArithmeticPromotion template below will need
 // to be updated (or more likely replaced with a decltype expression).
-static_assert(IntegerBitsPlusSign<intmax_t>::value == 64,
+static_assert(kIntegerBitsPlusSign<intmax_t> == 64,
               "Max integer size not supported for this toolchain.");
 
-template <typename Integer, bool IsSigned = std::is_signed<Integer>::value>
-struct TwiceWiderInteger {
-  using type =
-      typename IntegerForDigitsAndSign<IntegerBitsPlusSign<Integer>::value * 2,
-                                       IsSigned>::type;
-};
-
-enum ArithmeticPromotionCategory {
-  LEFT_PROMOTION,  // Use the type of the left-hand argument.
-  RIGHT_PROMOTION  // Use the type of the right-hand argument.
-};
+template <typename Integer, bool IsSigned = std::is_signed_v<Integer>>
+using TwiceWiderInteger =
+    IntegerForDigitsAndSign<kIntegerBitsPlusSign<Integer> * 2, IsSigned>;
 
 // Determines the type that can represent the largest positive value.
-template <typename Lhs,
-          typename Rhs,
-          ArithmeticPromotionCategory Promotion =
-              (MaxExponent<Lhs>::value > MaxExponent<Rhs>::value)
-                  ? LEFT_PROMOTION
-                  : RIGHT_PROMOTION>
-struct MaxExponentPromotion;
-
 template <typename Lhs, typename Rhs>
-struct MaxExponentPromotion<Lhs, Rhs, LEFT_PROMOTION> {
-  using type = Lhs;
-};
-
-template <typename Lhs, typename Rhs>
-struct MaxExponentPromotion<Lhs, Rhs, RIGHT_PROMOTION> {
-  using type = Rhs;
-};
+using MaxExponentPromotion =
+    std::conditional_t<(kMaxExponent<Lhs> > kMaxExponent<Rhs>), Lhs, Rhs>;
 
 // Determines the type that can represent the lowest arithmetic value.
-template <typename Lhs,
-          typename Rhs,
-          ArithmeticPromotionCategory Promotion =
-              std::is_signed<Lhs>::value
-                  ? (std::is_signed<Rhs>::value
-                         ? (MaxExponent<Lhs>::value > MaxExponent<Rhs>::value
-                                ? LEFT_PROMOTION
-                                : RIGHT_PROMOTION)
-                         : LEFT_PROMOTION)
-                  : (std::is_signed<Rhs>::value
-                         ? RIGHT_PROMOTION
-                         : (MaxExponent<Lhs>::value < MaxExponent<Rhs>::value
-                                ? LEFT_PROMOTION
-                                : RIGHT_PROMOTION))>
-struct LowestValuePromotion;
-
 template <typename Lhs, typename Rhs>
-struct LowestValuePromotion<Lhs, Rhs, LEFT_PROMOTION> {
-  using type = Lhs;
-};
-
-template <typename Lhs, typename Rhs>
-struct LowestValuePromotion<Lhs, Rhs, RIGHT_PROMOTION> {
-  using type = Rhs;
-};
+using LowestValuePromotion = std::conditional_t<
+    std::is_signed_v<Lhs>
+        ? (!std::is_signed_v<Rhs> || kMaxExponent<Lhs> > kMaxExponent<Rhs>)
+        : (!std::is_signed_v<Rhs> && kMaxExponent<Lhs> < kMaxExponent<Rhs>),
+    Lhs,
+    Rhs>;
 
 // Determines the type that is best able to represent an arithmetic result.
-template <
-    typename Lhs,
-    typename Rhs = Lhs,
-    bool is_intmax_type =
-        std::is_integral<
-            typename MaxExponentPromotion<Lhs, Rhs>::type>::value &&
-        IntegerBitsPlusSign<typename MaxExponentPromotion<Lhs, Rhs>::type>::
-                value == IntegerBitsPlusSign<intmax_t>::value,
-    bool is_max_exponent = StaticDstRangeRelationToSrcRange<
-                               typename MaxExponentPromotion<Lhs, Rhs>::type,
-                               Lhs>::value == NUMERIC_RANGE_CONTAINED &&
-                           StaticDstRangeRelationToSrcRange<
-                               typename MaxExponentPromotion<Lhs, Rhs>::type,
-                               Rhs>::value == NUMERIC_RANGE_CONTAINED>
-struct BigEnoughPromotion;
 
-// The side with the max exponent is big enough.
-template <typename Lhs, typename Rhs, bool is_intmax_type>
-struct BigEnoughPromotion<Lhs, Rhs, is_intmax_type, true> {
-  using type = typename MaxExponentPromotion<Lhs, Rhs>::type;
-  static const bool is_contained = true;
+// Default case, used when the side with the max exponent is big enough.
+template <typename Lhs,
+          typename Rhs = Lhs,
+          bool is_intmax_type =
+              std::is_integral_v<MaxExponentPromotion<Lhs, Rhs>> &&
+              kIntegerBitsPlusSign<MaxExponentPromotion<Lhs, Rhs>> ==
+                  kIntegerBitsPlusSign<intmax_t>,
+          bool is_max_exponent =
+              kStaticDstRangeRelationToSrcRange<MaxExponentPromotion<Lhs, Rhs>,
+                                                Lhs> ==
+                  NumericRangeRepresentation::kContained &&
+              kStaticDstRangeRelationToSrcRange<MaxExponentPromotion<Lhs, Rhs>,
+                                                Rhs> ==
+                  NumericRangeRepresentation::kContained>
+struct BigEnoughPromotionImpl {
+  using type = MaxExponentPromotion<Lhs, Rhs>;
+  static constexpr bool kContained = true;
 };
 
 // We can use a twice wider type to fit.
 template <typename Lhs, typename Rhs>
-struct BigEnoughPromotion<Lhs, Rhs, false, false> {
+struct BigEnoughPromotionImpl<Lhs, Rhs, false, false> {
   using type =
-      typename TwiceWiderInteger<typename MaxExponentPromotion<Lhs, Rhs>::type,
-                                 std::is_signed<Lhs>::value ||
-                                     std::is_signed<Rhs>::value>::type;
-  static const bool is_contained = true;
+      TwiceWiderInteger<MaxExponentPromotion<Lhs, Rhs>,
+                        std::is_signed_v<Lhs> || std::is_signed_v<Rhs>>;
+  static constexpr bool kContained = true;
 };
 
 // No type is large enough.
 template <typename Lhs, typename Rhs>
-struct BigEnoughPromotion<Lhs, Rhs, true, false> {
-  using type = typename MaxExponentPromotion<Lhs, Rhs>::type;
-  static const bool is_contained = false;
+struct BigEnoughPromotionImpl<Lhs, Rhs, true, false> {
+  using type = MaxExponentPromotion<Lhs, Rhs>;
+  static constexpr bool kContained = false;
 };
 
+template <typename Lhs, typename Rhs>
+using BigEnoughPromotion = BigEnoughPromotionImpl<Lhs, Rhs>::type;
+
+template <typename Lhs, typename Rhs>
+inline constexpr bool kIsBigEnoughPromotionContained =
+    BigEnoughPromotionImpl<Lhs, Rhs>::kContained;
+
 // We can statically check if operations on the provided types can wrap, so we
 // can skip the checked operations if they're not needed. So, for an integer we
 // care if the destination type preserves the sign and is twice the width of
 // the source.
 template <typename T, typename Lhs, typename Rhs = Lhs>
-struct IsIntegerArithmeticSafe {
-  static const bool value =
-      !std::is_floating_point<T>::value &&
-      !std::is_floating_point<Lhs>::value &&
-      !std::is_floating_point<Rhs>::value &&
-      std::is_signed<T>::value >= std::is_signed<Lhs>::value &&
-      IntegerBitsPlusSign<T>::value >= (2 * IntegerBitsPlusSign<Lhs>::value) &&
-      std::is_signed<T>::value >= std::is_signed<Rhs>::value &&
-      IntegerBitsPlusSign<T>::value >= (2 * IntegerBitsPlusSign<Rhs>::value);
-};
+inline constexpr bool kIsIntegerArithmeticSafe =
+    !std::is_floating_point_v<T> && !std::is_floating_point_v<Lhs> &&
+    !std::is_floating_point_v<Rhs> &&
+    std::is_signed_v<T> >= std::is_signed_v<Lhs> &&
+    kIntegerBitsPlusSign<T> >= (2 * kIntegerBitsPlusSign<Lhs>) &&
+    std::is_signed_v<T> >= std::is_signed_v<Rhs> &&
+    kIntegerBitsPlusSign<T> >= (2 * kIntegerBitsPlusSign<Rhs>);
 
 // Promotes to a type that can represent any possible result of a binary
 // arithmetic operation with the source types.
-template <typename Lhs,
-          typename Rhs,
-          bool is_promotion_possible = IsIntegerArithmeticSafe<
-              typename std::conditional<std::is_signed<Lhs>::value ||
-                                            std::is_signed<Rhs>::value,
-                                        intmax_t,
-                                        uintmax_t>::type,
-              typename MaxExponentPromotion<Lhs, Rhs>::type>::value>
-struct FastIntegerArithmeticPromotion;
-
 template <typename Lhs, typename Rhs>
-struct FastIntegerArithmeticPromotion<Lhs, Rhs, true> {
-  using type =
-      typename TwiceWiderInteger<typename MaxExponentPromotion<Lhs, Rhs>::type,
-                                 std::is_signed<Lhs>::value ||
-                                     std::is_signed<Rhs>::value>::type;
-  static_assert(IsIntegerArithmeticSafe<type, Lhs, Rhs>::value, "");
-  static const bool is_contained = true;
+struct FastIntegerArithmeticPromotionImpl {
+  using type = BigEnoughPromotion<Lhs, Rhs>;
+  static constexpr bool kContained = false;
 };
 
 template <typename Lhs, typename Rhs>
-struct FastIntegerArithmeticPromotion<Lhs, Rhs, false> {
-  using type = typename BigEnoughPromotion<Lhs, Rhs>::type;
-  static const bool is_contained = false;
+  requires(kIsIntegerArithmeticSafe<
+           std::conditional_t<std::is_signed_v<Lhs> || std::is_signed_v<Rhs>,
+                              intmax_t,
+                              uintmax_t>,
+           MaxExponentPromotion<Lhs, Rhs>>)
+struct FastIntegerArithmeticPromotionImpl<Lhs, Rhs> {
+  using type =
+      TwiceWiderInteger<MaxExponentPromotion<Lhs, Rhs>,
+                        std::is_signed_v<Lhs> || std::is_signed_v<Rhs>>;
+  static_assert(kIsIntegerArithmeticSafe<type, Lhs, Rhs>);
+  static constexpr bool kContained = true;
+};
+
+template <typename Lhs, typename Rhs>
+using FastIntegerArithmeticPromotion =
+    FastIntegerArithmeticPromotionImpl<Lhs, Rhs>::type;
+
+template <typename Lhs, typename Rhs>
+inline constexpr bool kIsFastIntegerArithmeticPromotionContained =
+    FastIntegerArithmeticPromotionImpl<Lhs, Rhs>::kContained;
+
+template <typename T>
+struct ArithmeticOrIntegralConstant {
+  using type = T;
+};
+
+template <typename T>
+  requires IntegralConstantLike<T>
+struct ArithmeticOrIntegralConstant<T> {
+  using type = T::value_type;
 };
 
 // Extracts the underlying type from an enum.
-template <typename T, bool is_enum = std::is_enum<T>::value>
-struct ArithmeticOrUnderlyingEnum;
-
 template <typename T>
-struct ArithmeticOrUnderlyingEnum<T, true> {
-  using type = typename std::underlying_type<T>::type;
-  static const bool value = std::is_arithmetic<type>::value;
-};
-
-template <typename T>
-struct ArithmeticOrUnderlyingEnum<T, false> {
-  using type = T;
-  static const bool value = std::is_arithmetic<type>::value;
-};
+using ArithmeticOrUnderlyingEnum =
+    typename std::conditional_t<std::is_enum_v<T>,
+                                std::underlying_type<T>,
+                                ArithmeticOrIntegralConstant<T>>::type;
 
 // The following are helper templates used in the CheckedNumeric class.
 template <typename T>
+  requires std::is_arithmetic_v<T>
 class CheckedNumeric;
 
 template <typename T>
+  requires std::is_arithmetic_v<T>
 class ClampedNumeric;
 
 template <typename T>
+  requires std::is_arithmetic_v<T>
 class StrictNumeric;
 
 // Used to treat CheckedNumeric and arithmetic underlying types the same.
 template <typename T>
-struct UnderlyingType {
-  using type = typename ArithmeticOrUnderlyingEnum<T>::type;
-  static const bool is_numeric = std::is_arithmetic<type>::value;
-  static const bool is_checked = false;
-  static const bool is_clamped = false;
-  static const bool is_strict = false;
-};
+inline constexpr bool kIsCheckedNumeric = false;
+template <typename T>
+inline constexpr bool kIsCheckedNumeric<CheckedNumeric<T>> = true;
+template <typename T>
+concept IsCheckedNumeric = kIsCheckedNumeric<T>;
 
 template <typename T>
-struct UnderlyingType<CheckedNumeric<T>> {
-  using type = T;
-  static const bool is_numeric = true;
-  static const bool is_checked = true;
-  static const bool is_clamped = false;
-  static const bool is_strict = false;
-};
+inline constexpr bool kIsClampedNumeric = false;
+template <typename T>
+inline constexpr bool kIsClampedNumeric<ClampedNumeric<T>> = true;
+template <typename T>
+concept IsClampedNumeric = kIsClampedNumeric<T>;
 
 template <typename T>
-struct UnderlyingType<ClampedNumeric<T>> {
-  using type = T;
-  static const bool is_numeric = true;
-  static const bool is_checked = false;
-  static const bool is_clamped = true;
-  static const bool is_strict = false;
-};
+inline constexpr bool kIsStrictNumeric = false;
+template <typename T>
+inline constexpr bool kIsStrictNumeric<StrictNumeric<T>> = true;
+template <typename T>
+concept IsStrictNumeric = kIsStrictNumeric<T>;
 
 template <typename T>
-struct UnderlyingType<StrictNumeric<T>> {
+struct UnderlyingTypeImpl {
+  using type = ArithmeticOrUnderlyingEnum<T>;
+};
+template <typename T>
+struct UnderlyingTypeImpl<CheckedNumeric<T>> {
   using type = T;
-  static const bool is_numeric = true;
-  static const bool is_checked = false;
-  static const bool is_clamped = false;
-  static const bool is_strict = true;
 };
+template <typename T>
+struct UnderlyingTypeImpl<ClampedNumeric<T>> {
+  using type = T;
+};
+template <typename T>
+struct UnderlyingTypeImpl<StrictNumeric<T>> {
+  using type = T;
+};
+template <typename T>
+using UnderlyingType = UnderlyingTypeImpl<T>::type;
+
+template <typename T>
+inline constexpr bool kIsNumeric = std::is_arithmetic_v<UnderlyingType<T>>;
+template <typename T>
+  requires(IsCheckedNumeric<T> || IsClampedNumeric<T> || IsStrictNumeric<T>)
+inline constexpr bool kIsNumeric<T> = true;
+template <typename T>
+concept IsNumeric = kIsNumeric<T>;
 
 template <typename L, typename R>
-struct IsCheckedOp {
-  static const bool value =
-      UnderlyingType<L>::is_numeric && UnderlyingType<R>::is_numeric &&
-      (UnderlyingType<L>::is_checked || UnderlyingType<R>::is_checked);
-};
+concept IsCheckedOp = (IsCheckedNumeric<L> && IsNumeric<R>) ||
+                      (IsCheckedNumeric<R> && IsNumeric<L>);
 
 template <typename L, typename R>
-struct IsClampedOp {
-  static const bool value =
-      UnderlyingType<L>::is_numeric && UnderlyingType<R>::is_numeric &&
-      (UnderlyingType<L>::is_clamped || UnderlyingType<R>::is_clamped) &&
-      !(UnderlyingType<L>::is_checked || UnderlyingType<R>::is_checked);
-};
+concept IsClampedOp =
+    !IsCheckedOp<L, R> && ((IsClampedNumeric<L> && IsNumeric<R>) ||
+                           (IsClampedNumeric<R> && IsNumeric<L>));
 
 template <typename L, typename R>
-struct IsStrictOp {
-  static const bool value =
-      UnderlyingType<L>::is_numeric && UnderlyingType<R>::is_numeric &&
-      (UnderlyingType<L>::is_strict || UnderlyingType<R>::is_strict) &&
-      !(UnderlyingType<L>::is_checked || UnderlyingType<R>::is_checked) &&
-      !(UnderlyingType<L>::is_clamped || UnderlyingType<R>::is_clamped);
-};
+concept IsStrictOp = !IsCheckedOp<L, R> && !IsClampedOp<L, R> &&
+                     ((IsStrictNumeric<L> && IsNumeric<R>) ||
+                      (IsStrictNumeric<R> && IsNumeric<L>));
 
 // as_signed<> returns the supplied integral value (or integral castable
 // Numeric template) cast as a signed integral of equivalent precision.
 // I.e. it's mostly an alias for: static_cast<std::make_signed<T>::type>(t)
-template <typename Src>
-constexpr typename std::make_signed<
-    typename internal::UnderlyingType<Src>::type>::type
-as_signed(const Src value) {
-  static_assert(std::is_integral<decltype(as_signed(value))>::value,
-                "Argument must be a signed or unsigned integer type.");
-  return static_cast<decltype(as_signed(value))>(value);
+template <typename Src, typename Dst = std::make_signed_t<UnderlyingType<Src>>>
+  requires std::integral<Dst>
+constexpr auto as_signed(Src value) {
+  return static_cast<Dst>(value);
 }
 
 // as_unsigned<> returns the supplied integral value (or integral castable
 // Numeric template) cast as an unsigned integral of equivalent precision.
 // I.e. it's mostly an alias for: static_cast<std::make_unsigned<T>::type>(t)
-template <typename Src>
-constexpr typename std::make_unsigned<
-    typename internal::UnderlyingType<Src>::type>::type
-as_unsigned(const Src value) {
-  static_assert(std::is_integral<decltype(as_unsigned(value))>::value,
-                "Argument must be a signed or unsigned integer type.");
-  return static_cast<decltype(as_unsigned(value))>(value);
+template <typename Src,
+          typename Dst = std::make_unsigned_t<UnderlyingType<Src>>>
+  requires std::integral<Dst>
+constexpr auto as_unsigned(Src value) {
+  return static_cast<Dst>(value);
 }
 
 template <typename L, typename R>
-constexpr bool IsLessImpl(const L lhs,
-                          const R rhs,
-                          const RangeCheck l_range,
-                          const RangeCheck r_range) {
-  return l_range.IsUnderflow() || r_range.IsOverflow() ||
-         (l_range == r_range && static_cast<decltype(lhs + rhs)>(lhs) <
-                                    static_cast<decltype(lhs + rhs)>(rhs));
-}
-
-template <typename L, typename R>
+  requires std::is_arithmetic_v<L> && std::is_arithmetic_v<R>
 struct IsLess {
-  static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value,
-                "Types must be numeric.");
-  static constexpr bool Test(const L lhs, const R rhs) {
-    return IsLessImpl(lhs, rhs, DstRangeRelationToSrcRange<R>(lhs),
-                      DstRangeRelationToSrcRange<L>(rhs));
+  using SumT = decltype(std::declval<L>() + std::declval<R>());
+  static constexpr bool Test(L lhs, R rhs) {
+    const RangeCheck l_range = DstRangeRelationToSrcRange<R>(lhs);
+    const RangeCheck r_range = DstRangeRelationToSrcRange<L>(rhs);
+    return l_range.IsUnderflow() || r_range.IsOverflow() ||
+           (l_range == r_range &&
+            static_cast<SumT>(lhs) < static_cast<SumT>(rhs));
   }
 };
 
 template <typename L, typename R>
-constexpr bool IsLessOrEqualImpl(const L lhs,
-                                 const R rhs,
-                                 const RangeCheck l_range,
-                                 const RangeCheck r_range) {
-  return l_range.IsUnderflow() || r_range.IsOverflow() ||
-         (l_range == r_range && static_cast<decltype(lhs + rhs)>(lhs) <=
-                                    static_cast<decltype(lhs + rhs)>(rhs));
-}
-
-template <typename L, typename R>
+  requires std::is_arithmetic_v<L> && std::is_arithmetic_v<R>
 struct IsLessOrEqual {
-  static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value,
-                "Types must be numeric.");
-  static constexpr bool Test(const L lhs, const R rhs) {
-    return IsLessOrEqualImpl(lhs, rhs, DstRangeRelationToSrcRange<R>(lhs),
-                             DstRangeRelationToSrcRange<L>(rhs));
+  using SumT = decltype(std::declval<L>() + std::declval<R>());
+  static constexpr bool Test(L lhs, R rhs) {
+    const RangeCheck l_range = DstRangeRelationToSrcRange<R>(lhs);
+    const RangeCheck r_range = DstRangeRelationToSrcRange<L>(rhs);
+    return l_range.IsUnderflow() || r_range.IsOverflow() ||
+           (l_range == r_range &&
+            static_cast<SumT>(lhs) <= static_cast<SumT>(rhs));
   }
 };
 
 template <typename L, typename R>
-constexpr bool IsGreaterImpl(const L lhs,
-                             const R rhs,
-                             const RangeCheck l_range,
-                             const RangeCheck r_range) {
-  return l_range.IsOverflow() || r_range.IsUnderflow() ||
-         (l_range == r_range && static_cast<decltype(lhs + rhs)>(lhs) >
-                                    static_cast<decltype(lhs + rhs)>(rhs));
-}
-
-template <typename L, typename R>
+  requires std::is_arithmetic_v<L> && std::is_arithmetic_v<R>
 struct IsGreater {
-  static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value,
-                "Types must be numeric.");
-  static constexpr bool Test(const L lhs, const R rhs) {
-    return IsGreaterImpl(lhs, rhs, DstRangeRelationToSrcRange<R>(lhs),
-                         DstRangeRelationToSrcRange<L>(rhs));
+  using SumT = decltype(std::declval<L>() + std::declval<R>());
+  static constexpr bool Test(L lhs, R rhs) {
+    const RangeCheck l_range = DstRangeRelationToSrcRange<R>(lhs);
+    const RangeCheck r_range = DstRangeRelationToSrcRange<L>(rhs);
+    return l_range.IsOverflow() || r_range.IsUnderflow() ||
+           (l_range == r_range &&
+            static_cast<SumT>(lhs) > static_cast<SumT>(rhs));
   }
 };
 
 template <typename L, typename R>
-constexpr bool IsGreaterOrEqualImpl(const L lhs,
-                                    const R rhs,
-                                    const RangeCheck l_range,
-                                    const RangeCheck r_range) {
-  return l_range.IsOverflow() || r_range.IsUnderflow() ||
-         (l_range == r_range && static_cast<decltype(lhs + rhs)>(lhs) >=
-                                    static_cast<decltype(lhs + rhs)>(rhs));
-}
-
-template <typename L, typename R>
+  requires std::is_arithmetic_v<L> && std::is_arithmetic_v<R>
 struct IsGreaterOrEqual {
-  static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value,
-                "Types must be numeric.");
-  static constexpr bool Test(const L lhs, const R rhs) {
-    return IsGreaterOrEqualImpl(lhs, rhs, DstRangeRelationToSrcRange<R>(lhs),
-                                DstRangeRelationToSrcRange<L>(rhs));
+  using SumT = decltype(std::declval<L>() + std::declval<R>());
+  static constexpr bool Test(L lhs, R rhs) {
+    const RangeCheck l_range = DstRangeRelationToSrcRange<R>(lhs);
+    const RangeCheck r_range = DstRangeRelationToSrcRange<L>(rhs);
+    return l_range.IsOverflow() || r_range.IsUnderflow() ||
+           (l_range == r_range &&
+            static_cast<SumT>(lhs) >= static_cast<SumT>(rhs));
   }
 };
 
 template <typename L, typename R>
+  requires std::is_arithmetic_v<L> && std::is_arithmetic_v<R>
 struct IsEqual {
-  static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value,
-                "Types must be numeric.");
-  static constexpr bool Test(const L lhs, const R rhs) {
+  using SumT = decltype(std::declval<L>() + std::declval<R>());
+  static constexpr bool Test(L lhs, R rhs) {
     return DstRangeRelationToSrcRange<R>(lhs) ==
                DstRangeRelationToSrcRange<L>(rhs) &&
-           static_cast<decltype(lhs + rhs)>(lhs) ==
-               static_cast<decltype(lhs + rhs)>(rhs);
+           static_cast<SumT>(lhs) == static_cast<SumT>(rhs);
   }
 };
 
 template <typename L, typename R>
+  requires std::is_arithmetic_v<L> && std::is_arithmetic_v<R>
 struct IsNotEqual {
-  static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value,
-                "Types must be numeric.");
-  static constexpr bool Test(const L lhs, const R rhs) {
+  using SumT = decltype(std::declval<L>() + std::declval<R>());
+  static constexpr bool Test(L lhs, R rhs) {
     return DstRangeRelationToSrcRange<R>(lhs) !=
                DstRangeRelationToSrcRange<L>(rhs) ||
-           static_cast<decltype(lhs + rhs)>(lhs) !=
-               static_cast<decltype(lhs + rhs)>(rhs);
+           static_cast<SumT>(lhs) != static_cast<SumT>(rhs);
   }
 };
 
 // These perform the actual math operations on the CheckedNumerics.
 // Binary arithmetic operations.
-template <template <typename, typename> class C, typename L, typename R>
-constexpr bool SafeCompare(const L lhs, const R rhs) {
-  static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value,
-                "Types must be numeric.");
-  using Promotion = BigEnoughPromotion<L, R>;
-  using BigType = typename Promotion::type;
-  return Promotion::is_contained
+template <template <typename, typename> typename C, typename L, typename R>
+  requires std::is_arithmetic_v<L> && std::is_arithmetic_v<R>
+constexpr bool SafeCompare(L lhs, R rhs) {
+  using BigType = BigEnoughPromotion<L, R>;
+  return kIsBigEnoughPromotionContained<L, R>
              // Force to a larger type for speed if both are contained.
-             ? C<BigType, BigType>::Test(
-                   static_cast<BigType>(static_cast<L>(lhs)),
-                   static_cast<BigType>(static_cast<R>(rhs)))
+             ? C<BigType, BigType>::Test(static_cast<BigType>(lhs),
+                                         static_cast<BigType>(rhs))
              // Let the template functions figure it out for mixed types.
              : C<L, R>::Test(lhs, rhs);
 }
 
 template <typename Dst, typename Src>
-constexpr bool IsMaxInRangeForNumericType() {
-  return IsGreaterOrEqual<Dst, Src>::Test(std::numeric_limits<Dst>::max(),
-                                          std::numeric_limits<Src>::max());
-}
+inline constexpr bool kIsMaxInRangeForNumericType =
+    IsGreaterOrEqual<Dst, Src>::Test(std::numeric_limits<Dst>::max(),
+                                     std::numeric_limits<Src>::max());
 
 template <typename Dst, typename Src>
-constexpr bool IsMinInRangeForNumericType() {
-  return IsLessOrEqual<Dst, Src>::Test(std::numeric_limits<Dst>::lowest(),
-                                       std::numeric_limits<Src>::lowest());
-}
+inline constexpr bool kIsMinInRangeForNumericType =
+    IsLessOrEqual<Dst, Src>::Test(std::numeric_limits<Dst>::lowest(),
+                                  std::numeric_limits<Src>::lowest());
 
 template <typename Dst, typename Src>
-constexpr Dst CommonMax() {
-  return !IsMaxInRangeForNumericType<Dst, Src>()
-             ? Dst(std::numeric_limits<Dst>::max())
-             : Dst(std::numeric_limits<Src>::max());
-}
+inline constexpr Dst kCommonMax =
+    kIsMaxInRangeForNumericType<Dst, Src>
+        ? static_cast<Dst>(std::numeric_limits<Src>::max())
+        : std::numeric_limits<Dst>::max();
 
 template <typename Dst, typename Src>
-constexpr Dst CommonMin() {
-  return !IsMinInRangeForNumericType<Dst, Src>()
-             ? Dst(std::numeric_limits<Dst>::lowest())
-             : Dst(std::numeric_limits<Src>::lowest());
-}
+inline constexpr Dst kCommonMin =
+    kIsMinInRangeForNumericType<Dst, Src>
+        ? static_cast<Dst>(std::numeric_limits<Src>::lowest())
+        : std::numeric_limits<Dst>::lowest();
 
 // This is a wrapper to generate return the max or min for a supplied type.
 // If the argument is false, the returned value is the maximum. If true the
 // returned value is the minimum.
 template <typename Dst, typename Src = Dst>
 constexpr Dst CommonMaxOrMin(bool is_min) {
-  return is_min ? CommonMin<Dst, Src>() : CommonMax<Dst, Src>();
+  return is_min ? kCommonMin<Dst, Src> : kCommonMax<Dst, Src>;
 }
 
-}  // namespace internal
-}  // namespace pdfium
+}  // namespace pdfium::internal
 
 #endif  // CORE_FXCRT_NUMERICS_SAFE_CONVERSIONS_IMPL_H_
diff --git a/core/fxcrt/numerics/safe_math.h b/core/fxcrt/numerics/safe_math.h
index f910027..c47b79b 100644
--- a/core/fxcrt/numerics/safe_math.h
+++ b/core/fxcrt/numerics/safe_math.h
@@ -5,8 +5,8 @@
 #ifndef CORE_FXCRT_NUMERICS_SAFE_MATH_H_
 #define CORE_FXCRT_NUMERICS_SAFE_MATH_H_
 
-#include "core/fxcrt/numerics/checked_math.h"
-#include "core/fxcrt/numerics/clamped_math.h"
-#include "core/fxcrt/numerics/safe_conversions.h"
+#include "core/fxcrt/numerics/checked_math.h"      // IWYU pragma: export
+#include "core/fxcrt/numerics/clamped_math.h"      // IWYU pragma: export
+#include "core/fxcrt/numerics/safe_conversions.h"  // IWYU pragma: export
 
 #endif  // CORE_FXCRT_NUMERICS_SAFE_MATH_H_
diff --git a/core/fxcrt/numerics/safe_math_arm_impl.h b/core/fxcrt/numerics/safe_math_arm_impl.h
index f2d83cb..00a38c6 100644
--- a/core/fxcrt/numerics/safe_math_arm_impl.h
+++ b/core/fxcrt/numerics/safe_math_arm_impl.h
@@ -5,18 +5,20 @@
 #ifndef CORE_FXCRT_NUMERICS_SAFE_MATH_ARM_IMPL_H_
 #define CORE_FXCRT_NUMERICS_SAFE_MATH_ARM_IMPL_H_
 
+// IWYU pragma: private
+
+#include <stdint.h>
+
 #include <cassert>
-#include <type_traits>
 
 #include "core/fxcrt/numerics/safe_conversions.h"
 
-namespace pdfium {
-namespace internal {
+namespace pdfium::internal {
 
 template <typename T, typename U>
 struct CheckedMulFastAsmOp {
-  static const bool is_supported =
-      kEnableAsmCode && FastIntegerArithmeticPromotion<T, U>::is_contained;
+  static constexpr bool is_supported =
+      kEnableAsmCode && kIsFastIntegerArithmeticPromotionContained<T, U>;
 
   // The following is not an assembler routine and is thus constexpr safe, it
   // just emits much more efficient code than the Clang and GCC builtins for
@@ -32,7 +34,7 @@
   //    cmp     r2, r1, asr #15
   template <typename V>
   static constexpr bool Do(T x, U y, V* result) {
-    using Promotion = typename FastIntegerArithmeticPromotion<T, U>::type;
+    using Promotion = FastIntegerArithmeticPromotion<T, U>;
     Promotion presult;
 
     presult = static_cast<Promotion>(x) * static_cast<Promotion>(y);
@@ -46,83 +48,78 @@
 
 template <typename T, typename U>
 struct ClampedAddFastAsmOp {
-  static const bool is_supported =
-      kEnableAsmCode && BigEnoughPromotion<T, U>::is_contained &&
-      IsTypeInRangeForNumericType<
-          int32_t,
-          typename BigEnoughPromotion<T, U>::type>::value;
+  static constexpr bool is_supported =
+      kEnableAsmCode && kIsBigEnoughPromotionContained<T, U> &&
+      kIsTypeInRangeForNumericType<int32_t, BigEnoughPromotion<T, U>>;
 
   template <typename V>
   __attribute__((always_inline)) static V Do(T x, U y) {
     // This will get promoted to an int, so let the compiler do whatever is
     // clever and rely on the saturated cast to bounds check.
-    if (IsIntegerArithmeticSafe<int, T, U>::value) {
+    if constexpr (kIsIntegerArithmeticSafe<int, T, U>) {
       return saturated_cast<V>(static_cast<int>(x) + static_cast<int>(y));
+    } else {
+      int32_t result;
+      int32_t x_i32 = checked_cast<int32_t>(x);
+      int32_t y_i32 = checked_cast<int32_t>(y);
+
+      asm("qadd %[result], %[first], %[second]"
+          : [result] "=r"(result)
+          : [first] "r"(x_i32), [second] "r"(y_i32));
+      return saturated_cast<V>(result);
     }
-
-    int32_t result;
-    int32_t x_i32 = checked_cast<int32_t>(x);
-    int32_t y_i32 = checked_cast<int32_t>(y);
-
-    asm("qadd %[result], %[first], %[second]"
-        : [result] "=r"(result)
-        : [first] "r"(x_i32), [second] "r"(y_i32));
-    return saturated_cast<V>(result);
   }
 };
 
 template <typename T, typename U>
 struct ClampedSubFastAsmOp {
-  static const bool is_supported =
-      kEnableAsmCode && BigEnoughPromotion<T, U>::is_contained &&
-      IsTypeInRangeForNumericType<
-          int32_t,
-          typename BigEnoughPromotion<T, U>::type>::value;
+  static constexpr bool is_supported =
+      kEnableAsmCode && kIsBigEnoughPromotionContained<T, U> &&
+      kIsTypeInRangeForNumericType<int32_t, BigEnoughPromotion<T, U>>;
 
   template <typename V>
   __attribute__((always_inline)) static V Do(T x, U y) {
     // This will get promoted to an int, so let the compiler do whatever is
     // clever and rely on the saturated cast to bounds check.
-    if (IsIntegerArithmeticSafe<int, T, U>::value) {
+    if constexpr (kIsIntegerArithmeticSafe<int, T, U>) {
       return saturated_cast<V>(static_cast<int>(x) - static_cast<int>(y));
+    } else {
+      int32_t result;
+      int32_t x_i32 = checked_cast<int32_t>(x);
+      int32_t y_i32 = checked_cast<int32_t>(y);
+
+      asm("qsub %[result], %[first], %[second]"
+          : [result] "=r"(result)
+          : [first] "r"(x_i32), [second] "r"(y_i32));
+      return saturated_cast<V>(result);
     }
-
-    int32_t result;
-    int32_t x_i32 = checked_cast<int32_t>(x);
-    int32_t y_i32 = checked_cast<int32_t>(y);
-
-    asm("qsub %[result], %[first], %[second]"
-        : [result] "=r"(result)
-        : [first] "r"(x_i32), [second] "r"(y_i32));
-    return saturated_cast<V>(result);
   }
 };
 
 template <typename T, typename U>
 struct ClampedMulFastAsmOp {
-  static const bool is_supported =
+  static constexpr bool is_supported =
       kEnableAsmCode && CheckedMulFastAsmOp<T, U>::is_supported;
 
   template <typename V>
   __attribute__((always_inline)) static V Do(T x, U y) {
     // Use the CheckedMulFastAsmOp for full-width 32-bit values, because
     // it's fewer instructions than promoting and then saturating.
-    if (!IsIntegerArithmeticSafe<int32_t, T, U>::value &&
-        !IsIntegerArithmeticSafe<uint32_t, T, U>::value) {
+    if constexpr (!kIsIntegerArithmeticSafe<int32_t, T, U> &&
+                  !kIsIntegerArithmeticSafe<uint32_t, T, U>) {
       V result;
       return CheckedMulFastAsmOp<T, U>::Do(x, y, &result)
                  ? result
                  : CommonMaxOrMin<V>(IsValueNegative(x) ^ IsValueNegative(y));
+    } else {
+      static_assert(kIsFastIntegerArithmeticPromotionContained<T, U>);
+      using Promotion = FastIntegerArithmeticPromotion<T, U>;
+      return saturated_cast<V>(static_cast<Promotion>(x) *
+                               static_cast<Promotion>(y));
     }
-
-    assert((FastIntegerArithmeticPromotion<T, U>::is_contained));
-    using Promotion = typename FastIntegerArithmeticPromotion<T, U>::type;
-    return saturated_cast<V>(static_cast<Promotion>(x) *
-                             static_cast<Promotion>(y));
   }
 };
 
-}  // namespace internal
-}  // namespace pdfium
+}  // namespace pdfium::internal
 
 #endif  // CORE_FXCRT_NUMERICS_SAFE_MATH_ARM_IMPL_H_
diff --git a/core/fxcrt/numerics/safe_math_clang_gcc_impl.h b/core/fxcrt/numerics/safe_math_clang_gcc_impl.h
index ca6b16a..ca21d40 100644
--- a/core/fxcrt/numerics/safe_math_clang_gcc_impl.h
+++ b/core/fxcrt/numerics/safe_math_clang_gcc_impl.h
@@ -5,14 +5,17 @@
 #ifndef CORE_FXCRT_NUMERICS_SAFE_MATH_CLANG_GCC_IMPL_H_
 #define CORE_FXCRT_NUMERICS_SAFE_MATH_CLANG_GCC_IMPL_H_
 
-#include <cassert>
+// IWYU pragma: private
+
+#include <stdint.h>
+
 #include <limits>
 #include <type_traits>
 
 #include "core/fxcrt/numerics/safe_conversions.h"
 
 #if !defined(__native_client__) && (defined(__ARMEL__) || defined(__arch64__))
-#include "core/fxcrt/numerics/safe_math_arm_impl.h"
+#include "core/fxcrt/numerics/safe_math_arm_impl.h"  // IWYU pragma: export
 #define BASE_HAS_ASSEMBLER_SAFE_MATH (1)
 #else
 #define BASE_HAS_ASSEMBLER_SAFE_MATH (0)
@@ -92,10 +95,10 @@
   // https://crbug.com/613003
   // We can support intptr_t, uintptr_t, or a smaller common type.
   static const bool is_supported =
-      (IsTypeInRangeForNumericType<intptr_t, T>::value &&
-       IsTypeInRangeForNumericType<intptr_t, U>::value) ||
-      (IsTypeInRangeForNumericType<uintptr_t, T>::value &&
-       IsTypeInRangeForNumericType<uintptr_t, U>::value);
+      (kIsTypeInRangeForNumericType<intptr_t, T> &&
+       kIsTypeInRangeForNumericType<intptr_t, U>) ||
+      (kIsTypeInRangeForNumericType<uintptr_t, T> &&
+       kIsTypeInRangeForNumericType<uintptr_t, U>);
 #else
   static const bool is_supported = true;
 #endif
@@ -136,7 +139,7 @@
 
 template <typename T>
 struct ClampedNegFastOp {
-  static const bool is_supported = std::is_signed<T>::value;
+  static const bool is_supported = std::is_signed_v<T>;
   __attribute__((always_inline)) static T Do(T value) {
     // Use this when there is no assembler path available.
     if (!ClampedSubFastAsmOp<T, T>::is_supported) {
diff --git a/core/fxcrt/numerics/safe_math_shared_impl.h b/core/fxcrt/numerics/safe_math_shared_impl.h
index 168d5f3..ffdef70 100644
--- a/core/fxcrt/numerics/safe_math_shared_impl.h
+++ b/core/fxcrt/numerics/safe_math_shared_impl.h
@@ -5,20 +5,15 @@
 #ifndef CORE_FXCRT_NUMERICS_SAFE_MATH_SHARED_IMPL_H_
 #define CORE_FXCRT_NUMERICS_SAFE_MATH_SHARED_IMPL_H_
 
-#include <stddef.h>
-#include <stdint.h>
+// IWYU pragma: private
 
-#include <cassert>
-#include <climits>
-#include <cmath>
-#include <cstdlib>
-#include <limits>
+#include <concepts>
 #include <type_traits>
 
 #include "build/build_config.h"
 #include "core/fxcrt/numerics/safe_conversions.h"
 
-#if BUILDFLAG(IS_ASMJS)
+#if defined(__asmjs__) || defined(__wasm__)
 // Optimized safe math instructions are incompatible with asmjs.
 #define BASE_HAS_OPTIMIZED_SAFE_MATH (0)
 // Where available use builtin math overflow support on Clang and GCC.
@@ -27,7 +22,7 @@
       ((__clang_major__ > 3) ||                            \
        (__clang_major__ == 3 && __clang_minor__ >= 4))) || \
      (defined(__GNUC__) && __GNUC__ >= 5))
-#include "core/fxcrt/numerics/safe_math_clang_gcc_impl.h"
+#include "core/fxcrt/numerics/safe_math_clang_gcc_impl.h"  // IWYU pragma: export
 #define BASE_HAS_OPTIMIZED_SAFE_MATH (1)
 #else
 #define BASE_HAS_OPTIMIZED_SAFE_MATH (0)
@@ -114,18 +109,18 @@
 // template instantiations even though we don't actually support the operations.
 // However, there is no corresponding implementation of e.g. SafeUnsignedAbs,
 // so the float versions will not compile.
-template <typename Numeric,
-          bool IsInteger = std::is_integral<Numeric>::value,
-          bool IsFloat = std::is_floating_point<Numeric>::value>
+template <typename Numeric>
 struct UnsignedOrFloatForSize;
 
 template <typename Numeric>
-struct UnsignedOrFloatForSize<Numeric, true, false> {
+  requires(std::integral<Numeric>)
+struct UnsignedOrFloatForSize<Numeric> {
   using type = typename std::make_unsigned<Numeric>::type;
 };
 
 template <typename Numeric>
-struct UnsignedOrFloatForSize<Numeric, false, true> {
+  requires(std::floating_point<Numeric>)
+struct UnsignedOrFloatForSize<Numeric> {
   using type = Numeric;
 };
 
@@ -134,47 +129,45 @@
 // exhibit well-defined overflow semantics and rely on the caller to detect
 // if an overflow occurred.
 
-template <typename T,
-          typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
+template <typename T>
+  requires(std::integral<T>)
 constexpr T NegateWrapper(T value) {
   using UnsignedT = typename std::make_unsigned<T>::type;
   // This will compile to a NEG on Intel, and is normal negation on ARM.
   return static_cast<T>(UnsignedT(0) - static_cast<UnsignedT>(value));
 }
 
-template <
-    typename T,
-    typename std::enable_if<std::is_floating_point<T>::value>::type* = nullptr>
+template <typename T>
+  requires(std::floating_point<T>)
 constexpr T NegateWrapper(T value) {
   return -value;
 }
 
-template <typename T,
-          typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
+template <typename T>
+  requires(std::integral<T>)
 constexpr typename std::make_unsigned<T>::type InvertWrapper(T value) {
   return ~value;
 }
 
-template <typename T,
-          typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
+template <typename T>
+  requires(std::integral<T>)
 constexpr T AbsWrapper(T value) {
   return static_cast<T>(SafeUnsignedAbs(value));
 }
 
-template <
-    typename T,
-    typename std::enable_if<std::is_floating_point<T>::value>::type* = nullptr>
+template <typename T>
+  requires(std::floating_point<T>)
 constexpr T AbsWrapper(T value) {
   return value < 0 ? -value : value;
 }
 
-template <template <typename, typename, typename> class M,
+template <template <typename, typename> class M,
           typename L,
-          typename R>
+          typename R,
+          typename Math = M<UnderlyingType<L>, UnderlyingType<R>>>
+  requires requires { typename Math::result_type; }
 struct MathWrapper {
-  using math = M<typename UnderlyingType<L>::type,
-                 typename UnderlyingType<R>::type,
-                 void>;
+  using math = Math;
   using type = typename math::result_type;
 };
 
@@ -183,28 +176,26 @@
 // solution, but it beats rewriting these over and over again.
 #define BASE_NUMERIC_ARITHMETIC_VARIADIC(CLASS, CL_ABBR, OP_NAME)       \
   template <typename L, typename R, typename... Args>                   \
-  constexpr auto CL_ABBR##OP_NAME(const L lhs, const R rhs,             \
-                                  const Args... args) {                 \
+  constexpr auto CL_ABBR##OP_NAME(L lhs, R rhs, Args... args) {         \
     return CL_ABBR##MathOp<CLASS##OP_NAME##Op, L, R, Args...>(lhs, rhs, \
                                                               args...); \
   }
 
 #define BASE_NUMERIC_ARITHMETIC_OPERATORS(CLASS, CL_ABBR, OP_NAME, OP, CMP_OP) \
   /* Binary arithmetic operator for all CLASS##Numeric operations. */          \
-  template <typename L, typename R,                                            \
-            typename std::enable_if<Is##CLASS##Op<L, R>::value>::type* =       \
-                nullptr>                                                       \
-  constexpr CLASS##Numeric<                                                    \
-      typename MathWrapper<CLASS##OP_NAME##Op, L, R>::type>                    \
-  operator OP(const L lhs, const R rhs) {                                      \
+  template <typename L, typename R>                                            \
+    requires(Is##CLASS##Op<L, R>)                                              \
+  constexpr CLASS##Numeric<typename MathWrapper<CLASS##OP_NAME##Op, L,         \
+                                                R>::type> operator OP(L lhs,   \
+                                                                      R rhs) { \
     return decltype(lhs OP rhs)::template MathOp<CLASS##OP_NAME##Op>(lhs,      \
                                                                      rhs);     \
   }                                                                            \
   /* Assignment arithmetic operator implementation from CLASS##Numeric. */     \
   template <typename L>                                                        \
+    requires std::is_arithmetic_v<L>                                           \
   template <typename R>                                                        \
-  constexpr CLASS##Numeric<L>& CLASS##Numeric<L>::operator CMP_OP(             \
-      const R rhs) {                                                           \
+  constexpr CLASS##Numeric<L>& CLASS##Numeric<L>::operator CMP_OP(R rhs) {     \
     return MathOp<CLASS##OP_NAME##Op>(rhs);                                    \
   }                                                                            \
   /* Variadic arithmetic functions that return CLASS##Numeric. */              \