11cb0ef41Sopenharmony_ci// Copyright 2014 The Chromium Authors. All rights reserved.
21cb0ef41Sopenharmony_ci// Use of this source code is governed by a BSD-style license that can be
31cb0ef41Sopenharmony_ci// found in the LICENSE file.
41cb0ef41Sopenharmony_ci
51cb0ef41Sopenharmony_ci// Slightly adapted for inclusion in V8.
61cb0ef41Sopenharmony_ci// Copyright 2014 the V8 project authors. All rights reserved.
71cb0ef41Sopenharmony_ci// List of adaptations:
81cb0ef41Sopenharmony_ci// - include guard names
91cb0ef41Sopenharmony_ci// - wrap in v8 namespace
101cb0ef41Sopenharmony_ci// - formatting (git cl format)
111cb0ef41Sopenharmony_ci
121cb0ef41Sopenharmony_ci#ifndef V8_BASE_SAFE_CONVERSIONS_IMPL_H_
131cb0ef41Sopenharmony_ci#define V8_BASE_SAFE_CONVERSIONS_IMPL_H_
141cb0ef41Sopenharmony_ci
151cb0ef41Sopenharmony_ci#include <stddef.h>
161cb0ef41Sopenharmony_ci#include <stdint.h>
171cb0ef41Sopenharmony_ci
181cb0ef41Sopenharmony_ci#include <limits>
191cb0ef41Sopenharmony_ci#include <type_traits>
201cb0ef41Sopenharmony_ci
211cb0ef41Sopenharmony_ci#if defined(__GNUC__) || defined(__clang__)
221cb0ef41Sopenharmony_ci#define BASE_NUMERICS_LIKELY(x) __builtin_expect(!!(x), 1)
231cb0ef41Sopenharmony_ci#define BASE_NUMERICS_UNLIKELY(x) __builtin_expect(!!(x), 0)
241cb0ef41Sopenharmony_ci#else
251cb0ef41Sopenharmony_ci#define BASE_NUMERICS_LIKELY(x) (x)
261cb0ef41Sopenharmony_ci#define BASE_NUMERICS_UNLIKELY(x) (x)
271cb0ef41Sopenharmony_ci#endif
281cb0ef41Sopenharmony_ci
291cb0ef41Sopenharmony_cinamespace v8 {
301cb0ef41Sopenharmony_cinamespace base {
311cb0ef41Sopenharmony_cinamespace internal {
321cb0ef41Sopenharmony_ci
331cb0ef41Sopenharmony_ci// The std library doesn't provide a binary max_exponent for integers, however
341cb0ef41Sopenharmony_ci// we can compute an analog using std::numeric_limits<>::digits.
351cb0ef41Sopenharmony_citemplate <typename NumericType>
361cb0ef41Sopenharmony_cistruct MaxExponent {
371cb0ef41Sopenharmony_ci  static const int value = std::is_floating_point<NumericType>::value
381cb0ef41Sopenharmony_ci                               ? std::numeric_limits<NumericType>::max_exponent
391cb0ef41Sopenharmony_ci                               : std::numeric_limits<NumericType>::digits + 1;
401cb0ef41Sopenharmony_ci};
411cb0ef41Sopenharmony_ci
421cb0ef41Sopenharmony_ci// The number of bits (including the sign) in an integer. Eliminates sizeof
431cb0ef41Sopenharmony_ci// hacks.
441cb0ef41Sopenharmony_citemplate <typename NumericType>
451cb0ef41Sopenharmony_cistruct IntegerBitsPlusSign {
461cb0ef41Sopenharmony_ci  static const int value = std::numeric_limits<NumericType>::digits +
471cb0ef41Sopenharmony_ci                           std::is_signed<NumericType>::value;
481cb0ef41Sopenharmony_ci};
491cb0ef41Sopenharmony_ci
501cb0ef41Sopenharmony_ci// Helper templates for integer manipulations.
511cb0ef41Sopenharmony_ci
521cb0ef41Sopenharmony_citemplate <typename Integer>
531cb0ef41Sopenharmony_cistruct PositionOfSignBit {
541cb0ef41Sopenharmony_ci  static const size_t value = IntegerBitsPlusSign<Integer>::value - 1;
551cb0ef41Sopenharmony_ci};
561cb0ef41Sopenharmony_ci
571cb0ef41Sopenharmony_ci// Determines if a numeric value is negative without throwing compiler
581cb0ef41Sopenharmony_ci// warnings on: unsigned(value) < 0.
591cb0ef41Sopenharmony_citemplate <typename T,
601cb0ef41Sopenharmony_ci          typename std::enable_if<std::is_signed<T>::value>::type* = nullptr>
611cb0ef41Sopenharmony_ciconstexpr bool IsValueNegative(T value) {
621cb0ef41Sopenharmony_ci  static_assert(std::is_arithmetic<T>::value, "Argument must be numeric.");
631cb0ef41Sopenharmony_ci  return value < 0;
641cb0ef41Sopenharmony_ci}
651cb0ef41Sopenharmony_ci
661cb0ef41Sopenharmony_citemplate <typename T,
671cb0ef41Sopenharmony_ci          typename std::enable_if<!std::is_signed<T>::value>::type* = nullptr>
681cb0ef41Sopenharmony_ciconstexpr bool IsValueNegative(T) {
691cb0ef41Sopenharmony_ci  static_assert(std::is_arithmetic<T>::value, "Argument must be numeric.");
701cb0ef41Sopenharmony_ci  return false;
711cb0ef41Sopenharmony_ci}
721cb0ef41Sopenharmony_ci
731cb0ef41Sopenharmony_ci// This performs a fast negation, returning a signed value. It works on unsigned
741cb0ef41Sopenharmony_ci// arguments, but probably doesn't do what you want for any unsigned value
751cb0ef41Sopenharmony_ci// larger than max / 2 + 1 (i.e. signed min cast to unsigned).
761cb0ef41Sopenharmony_citemplate <typename T>
771cb0ef41Sopenharmony_ciconstexpr typename std::make_signed<T>::type ConditionalNegate(
781cb0ef41Sopenharmony_ci    T x, bool is_negative) {
791cb0ef41Sopenharmony_ci  static_assert(std::is_integral<T>::value, "Type must be integral");
801cb0ef41Sopenharmony_ci  using SignedT = typename std::make_signed<T>::type;
811cb0ef41Sopenharmony_ci  using UnsignedT = typename std::make_unsigned<T>::type;
821cb0ef41Sopenharmony_ci  return static_cast<SignedT>(
831cb0ef41Sopenharmony_ci      (static_cast<UnsignedT>(x) ^ -SignedT(is_negative)) + is_negative);
841cb0ef41Sopenharmony_ci}
851cb0ef41Sopenharmony_ci
861cb0ef41Sopenharmony_ci// This performs a safe, absolute value via unsigned overflow.
871cb0ef41Sopenharmony_citemplate <typename T>
881cb0ef41Sopenharmony_ciconstexpr typename std::make_unsigned<T>::type SafeUnsignedAbs(T value) {
891cb0ef41Sopenharmony_ci  static_assert(std::is_integral<T>::value, "Type must be integral");
901cb0ef41Sopenharmony_ci  using UnsignedT = typename std::make_unsigned<T>::type;
911cb0ef41Sopenharmony_ci  return IsValueNegative(value)
921cb0ef41Sopenharmony_ci             ? static_cast<UnsignedT>(0u - static_cast<UnsignedT>(value))
931cb0ef41Sopenharmony_ci             : static_cast<UnsignedT>(value);
941cb0ef41Sopenharmony_ci}
951cb0ef41Sopenharmony_ci
961cb0ef41Sopenharmony_ci// This allows us to switch paths on known compile-time constants.
971cb0ef41Sopenharmony_ci#if defined(__clang__) || defined(__GNUC__)
981cb0ef41Sopenharmony_ciconstexpr bool CanDetectCompileTimeConstant() { return true; }
991cb0ef41Sopenharmony_citemplate <typename T>
1001cb0ef41Sopenharmony_ciconstexpr bool IsCompileTimeConstant(const T v) {
1011cb0ef41Sopenharmony_ci  return __builtin_constant_p(v);
1021cb0ef41Sopenharmony_ci}
1031cb0ef41Sopenharmony_ci#else
1041cb0ef41Sopenharmony_ciconstexpr bool CanDetectCompileTimeConstant() { return false; }
1051cb0ef41Sopenharmony_citemplate <typename T>
1061cb0ef41Sopenharmony_ciconstexpr bool IsCompileTimeConstant(const T) {
1071cb0ef41Sopenharmony_ci  return false;
1081cb0ef41Sopenharmony_ci}
1091cb0ef41Sopenharmony_ci#endif
1101cb0ef41Sopenharmony_citemplate <typename T>
1111cb0ef41Sopenharmony_ciconstexpr bool MustTreatAsConstexpr(const T v) {
1121cb0ef41Sopenharmony_ci  // Either we can't detect a compile-time constant, and must always use the
1131cb0ef41Sopenharmony_ci  // constexpr path, or we know we have a compile-time constant.
1141cb0ef41Sopenharmony_ci  return !CanDetectCompileTimeConstant() || IsCompileTimeConstant(v);
1151cb0ef41Sopenharmony_ci}
1161cb0ef41Sopenharmony_ci
1171cb0ef41Sopenharmony_ci// Forces a crash, like a CHECK(false). Used for numeric boundary errors.
1181cb0ef41Sopenharmony_ci// Also used in a constexpr template to trigger a compilation failure on
1191cb0ef41Sopenharmony_ci// an error condition.
1201cb0ef41Sopenharmony_cistruct CheckOnFailure {
1211cb0ef41Sopenharmony_ci  template <typename T>
1221cb0ef41Sopenharmony_ci  static T HandleFailure() {
1231cb0ef41Sopenharmony_ci#if defined(_MSC_VER)
1241cb0ef41Sopenharmony_ci    __debugbreak();
1251cb0ef41Sopenharmony_ci#elif defined(__GNUC__) || defined(__clang__)
1261cb0ef41Sopenharmony_ci    __builtin_trap();
1271cb0ef41Sopenharmony_ci#else
1281cb0ef41Sopenharmony_ci    ((void)(*(volatile char*)0 = 0));
1291cb0ef41Sopenharmony_ci#endif
1301cb0ef41Sopenharmony_ci    return T();
1311cb0ef41Sopenharmony_ci  }
1321cb0ef41Sopenharmony_ci};
1331cb0ef41Sopenharmony_ci
1341cb0ef41Sopenharmony_cienum IntegerRepresentation {
1351cb0ef41Sopenharmony_ci  INTEGER_REPRESENTATION_UNSIGNED,
1361cb0ef41Sopenharmony_ci  INTEGER_REPRESENTATION_SIGNED
1371cb0ef41Sopenharmony_ci};
1381cb0ef41Sopenharmony_ci
1391cb0ef41Sopenharmony_ci// A range for a given nunmeric Src type is contained for a given numeric Dst
1401cb0ef41Sopenharmony_ci// type if both numeric_limits<Src>::max() <= numeric_limits<Dst>::max() and
1411cb0ef41Sopenharmony_ci// numeric_limits<Src>::lowest() >= numeric_limits<Dst>::lowest() are true.
1421cb0ef41Sopenharmony_ci// We implement this as template specializations rather than simple static
1431cb0ef41Sopenharmony_ci// comparisons to ensure type correctness in our comparisons.
1441cb0ef41Sopenharmony_cienum NumericRangeRepresentation {
1451cb0ef41Sopenharmony_ci  NUMERIC_RANGE_NOT_CONTAINED,
1461cb0ef41Sopenharmony_ci  NUMERIC_RANGE_CONTAINED
1471cb0ef41Sopenharmony_ci};
1481cb0ef41Sopenharmony_ci
1491cb0ef41Sopenharmony_ci// Helper templates to statically determine if our destination type can contain
1501cb0ef41Sopenharmony_ci// maximum and minimum values represented by the source type.
1511cb0ef41Sopenharmony_ci
1521cb0ef41Sopenharmony_citemplate <typename Dst, typename Src,
1531cb0ef41Sopenharmony_ci          IntegerRepresentation DstSign = std::is_signed<Dst>::value
1541cb0ef41Sopenharmony_ci                                              ? INTEGER_REPRESENTATION_SIGNED
1551cb0ef41Sopenharmony_ci                                              : INTEGER_REPRESENTATION_UNSIGNED,
1561cb0ef41Sopenharmony_ci          IntegerRepresentation SrcSign = std::is_signed<Src>::value
1571cb0ef41Sopenharmony_ci                                              ? INTEGER_REPRESENTATION_SIGNED
1581cb0ef41Sopenharmony_ci                                              : INTEGER_REPRESENTATION_UNSIGNED>
1591cb0ef41Sopenharmony_cistruct StaticDstRangeRelationToSrcRange;
1601cb0ef41Sopenharmony_ci
1611cb0ef41Sopenharmony_ci// Same sign: Dst is guaranteed to contain Src only if its range is equal or
1621cb0ef41Sopenharmony_ci// larger.
1631cb0ef41Sopenharmony_citemplate <typename Dst, typename Src, IntegerRepresentation Sign>
1641cb0ef41Sopenharmony_cistruct StaticDstRangeRelationToSrcRange<Dst, Src, Sign, Sign> {
1651cb0ef41Sopenharmony_ci  static const NumericRangeRepresentation value =
1661cb0ef41Sopenharmony_ci      MaxExponent<Dst>::value >= MaxExponent<Src>::value
1671cb0ef41Sopenharmony_ci          ? NUMERIC_RANGE_CONTAINED
1681cb0ef41Sopenharmony_ci          : NUMERIC_RANGE_NOT_CONTAINED;
1691cb0ef41Sopenharmony_ci};
1701cb0ef41Sopenharmony_ci
1711cb0ef41Sopenharmony_ci// Unsigned to signed: Dst is guaranteed to contain source only if its range is
1721cb0ef41Sopenharmony_ci// larger.
1731cb0ef41Sopenharmony_citemplate <typename Dst, typename Src>
1741cb0ef41Sopenharmony_cistruct StaticDstRangeRelationToSrcRange<Dst,
1751cb0ef41Sopenharmony_ci                                        Src,
1761cb0ef41Sopenharmony_ci                                        INTEGER_REPRESENTATION_SIGNED,
1771cb0ef41Sopenharmony_ci                                        INTEGER_REPRESENTATION_UNSIGNED> {
1781cb0ef41Sopenharmony_ci  static const NumericRangeRepresentation value =
1791cb0ef41Sopenharmony_ci      MaxExponent<Dst>::value > MaxExponent<Src>::value
1801cb0ef41Sopenharmony_ci          ? NUMERIC_RANGE_CONTAINED
1811cb0ef41Sopenharmony_ci          : NUMERIC_RANGE_NOT_CONTAINED;
1821cb0ef41Sopenharmony_ci};
1831cb0ef41Sopenharmony_ci
1841cb0ef41Sopenharmony_ci// Signed to unsigned: Dst cannot be statically determined to contain Src.
1851cb0ef41Sopenharmony_citemplate <typename Dst, typename Src>
1861cb0ef41Sopenharmony_cistruct StaticDstRangeRelationToSrcRange<Dst,
1871cb0ef41Sopenharmony_ci                                        Src,
1881cb0ef41Sopenharmony_ci                                        INTEGER_REPRESENTATION_UNSIGNED,
1891cb0ef41Sopenharmony_ci                                        INTEGER_REPRESENTATION_SIGNED> {
1901cb0ef41Sopenharmony_ci  static const NumericRangeRepresentation value = NUMERIC_RANGE_NOT_CONTAINED;
1911cb0ef41Sopenharmony_ci};
1921cb0ef41Sopenharmony_ci
1931cb0ef41Sopenharmony_ci// This class wraps the range constraints as separate booleans so the compiler
1941cb0ef41Sopenharmony_ci// can identify constants and eliminate unused code paths.
1951cb0ef41Sopenharmony_ciclass RangeCheck {
1961cb0ef41Sopenharmony_ci public:
1971cb0ef41Sopenharmony_ci  constexpr RangeCheck(bool is_in_lower_bound, bool is_in_upper_bound)
1981cb0ef41Sopenharmony_ci      : is_underflow_(!is_in_lower_bound), is_overflow_(!is_in_upper_bound) {}
1991cb0ef41Sopenharmony_ci  constexpr RangeCheck() : is_underflow_(false), is_overflow_(false) {}
2001cb0ef41Sopenharmony_ci  constexpr bool IsValid() const { return !is_overflow_ && !is_underflow_; }
2011cb0ef41Sopenharmony_ci  constexpr bool IsInvalid() const { return is_overflow_ && is_underflow_; }
2021cb0ef41Sopenharmony_ci  constexpr bool IsOverflow() const { return is_overflow_ && !is_underflow_; }
2031cb0ef41Sopenharmony_ci  constexpr bool IsUnderflow() const { return !is_overflow_ && is_underflow_; }
2041cb0ef41Sopenharmony_ci  constexpr bool IsOverflowFlagSet() const { return is_overflow_; }
2051cb0ef41Sopenharmony_ci  constexpr bool IsUnderflowFlagSet() const { return is_underflow_; }
2061cb0ef41Sopenharmony_ci  constexpr bool operator==(const RangeCheck rhs) const {
2071cb0ef41Sopenharmony_ci    return is_underflow_ == rhs.is_underflow_ &&
2081cb0ef41Sopenharmony_ci           is_overflow_ == rhs.is_overflow_;
2091cb0ef41Sopenharmony_ci  }
2101cb0ef41Sopenharmony_ci  constexpr bool operator!=(const RangeCheck rhs) const {
2111cb0ef41Sopenharmony_ci    return !(*this == rhs);
2121cb0ef41Sopenharmony_ci  }
2131cb0ef41Sopenharmony_ci
2141cb0ef41Sopenharmony_ci private:
2151cb0ef41Sopenharmony_ci  // Do not change the order of these member variables. The integral conversion
2161cb0ef41Sopenharmony_ci  // optimization depends on this exact order.
2171cb0ef41Sopenharmony_ci  const bool is_underflow_;
2181cb0ef41Sopenharmony_ci  const bool is_overflow_;
2191cb0ef41Sopenharmony_ci};
2201cb0ef41Sopenharmony_ci
2211cb0ef41Sopenharmony_ci// The following helper template addresses a corner case in range checks for
2221cb0ef41Sopenharmony_ci// conversion from a floating-point type to an integral type of smaller range
2231cb0ef41Sopenharmony_ci// but larger precision (e.g. float -> unsigned). The problem is as follows:
2241cb0ef41Sopenharmony_ci//   1. Integral maximum is always one less than a power of two, so it must be
2251cb0ef41Sopenharmony_ci//      truncated to fit the mantissa of the floating point. The direction of
2261cb0ef41Sopenharmony_ci//      rounding is implementation defined, but by default it's always IEEE
2271cb0ef41Sopenharmony_ci//      floats, which round to nearest and thus result in a value of larger
2281cb0ef41Sopenharmony_ci//      magnitude than the integral value.
2291cb0ef41Sopenharmony_ci//      Example: float f = UINT_MAX; // f is 4294967296f but UINT_MAX
2301cb0ef41Sopenharmony_ci//                                   // is 4294967295u.
2311cb0ef41Sopenharmony_ci//   2. If the floating point value is equal to the promoted integral maximum
2321cb0ef41Sopenharmony_ci//      value, a range check will erroneously pass.
2331cb0ef41Sopenharmony_ci//      Example: (4294967296f <= 4294967295u) // This is true due to a precision
2341cb0ef41Sopenharmony_ci//                                            // loss in rounding up to float.
2351cb0ef41Sopenharmony_ci//   3. When the floating point value is then converted to an integral, the
2361cb0ef41Sopenharmony_ci//      resulting value is out of range for the target integral type and
2371cb0ef41Sopenharmony_ci//      thus is implementation defined.
2381cb0ef41Sopenharmony_ci//      Example: unsigned u = (float)INT_MAX; // u will typically overflow to 0.
2391cb0ef41Sopenharmony_ci// To fix this bug we manually truncate the maximum value when the destination
2401cb0ef41Sopenharmony_ci// type is an integral of larger precision than the source floating-point type,
2411cb0ef41Sopenharmony_ci// such that the resulting maximum is represented exactly as a floating point.
2421cb0ef41Sopenharmony_citemplate <typename Dst, typename Src, template <typename> class Bounds>
2431cb0ef41Sopenharmony_cistruct NarrowingRange {
2441cb0ef41Sopenharmony_ci  using SrcLimits = std::numeric_limits<Src>;
2451cb0ef41Sopenharmony_ci  using DstLimits = typename std::numeric_limits<Dst>;
2461cb0ef41Sopenharmony_ci
2471cb0ef41Sopenharmony_ci  // Computes the mask required to make an accurate comparison between types.
2481cb0ef41Sopenharmony_ci  static const int kShift =
2491cb0ef41Sopenharmony_ci      (MaxExponent<Src>::value > MaxExponent<Dst>::value &&
2501cb0ef41Sopenharmony_ci       SrcLimits::digits < DstLimits::digits)
2511cb0ef41Sopenharmony_ci          ? (DstLimits::digits - SrcLimits::digits)
2521cb0ef41Sopenharmony_ci          : 0;
2531cb0ef41Sopenharmony_ci  template <typename T, typename std::enable_if<
2541cb0ef41Sopenharmony_ci                            std::is_integral<T>::value>::type* = nullptr>
2551cb0ef41Sopenharmony_ci
2561cb0ef41Sopenharmony_ci  // Masks out the integer bits that are beyond the precision of the
2571cb0ef41Sopenharmony_ci  // intermediate type used for comparison.
2581cb0ef41Sopenharmony_ci  static constexpr T Adjust(T value) {
2591cb0ef41Sopenharmony_ci    static_assert(std::is_same<T, Dst>::value, "");
2601cb0ef41Sopenharmony_ci    static_assert(kShift < DstLimits::digits, "");
2611cb0ef41Sopenharmony_ci    return static_cast<T>(
2621cb0ef41Sopenharmony_ci        ConditionalNegate(SafeUnsignedAbs(value) & ~((T(1) << kShift) - T(1)),
2631cb0ef41Sopenharmony_ci                          IsValueNegative(value)));
2641cb0ef41Sopenharmony_ci  }
2651cb0ef41Sopenharmony_ci
2661cb0ef41Sopenharmony_ci  template <typename T, typename std::enable_if<
2671cb0ef41Sopenharmony_ci                            std::is_floating_point<T>::value>::type* = nullptr>
2681cb0ef41Sopenharmony_ci  static constexpr T Adjust(T value) {
2691cb0ef41Sopenharmony_ci    static_assert(std::is_same<T, Dst>::value, "");
2701cb0ef41Sopenharmony_ci    static_assert(kShift == 0, "");
2711cb0ef41Sopenharmony_ci    return value;
2721cb0ef41Sopenharmony_ci  }
2731cb0ef41Sopenharmony_ci
2741cb0ef41Sopenharmony_ci  static constexpr Dst max() { return Adjust(Bounds<Dst>::max()); }
2751cb0ef41Sopenharmony_ci  static constexpr Dst lowest() { return Adjust(Bounds<Dst>::lowest()); }
2761cb0ef41Sopenharmony_ci};
2771cb0ef41Sopenharmony_ci
2781cb0ef41Sopenharmony_citemplate <typename Dst, typename Src, template <typename> class Bounds,
2791cb0ef41Sopenharmony_ci          IntegerRepresentation DstSign = std::is_signed<Dst>::value
2801cb0ef41Sopenharmony_ci                                              ? INTEGER_REPRESENTATION_SIGNED
2811cb0ef41Sopenharmony_ci                                              : INTEGER_REPRESENTATION_UNSIGNED,
2821cb0ef41Sopenharmony_ci          IntegerRepresentation SrcSign = std::is_signed<Src>::value
2831cb0ef41Sopenharmony_ci                                              ? INTEGER_REPRESENTATION_SIGNED
2841cb0ef41Sopenharmony_ci                                              : INTEGER_REPRESENTATION_UNSIGNED,
2851cb0ef41Sopenharmony_ci          NumericRangeRepresentation DstRange =
2861cb0ef41Sopenharmony_ci              StaticDstRangeRelationToSrcRange<Dst, Src>::value>
2871cb0ef41Sopenharmony_cistruct DstRangeRelationToSrcRangeImpl;
2881cb0ef41Sopenharmony_ci
2891cb0ef41Sopenharmony_ci// The following templates are for ranges that must be verified at runtime. We
2901cb0ef41Sopenharmony_ci// split it into checks based on signedness to avoid confusing casts and
2911cb0ef41Sopenharmony_ci// compiler warnings on signed an unsigned comparisons.
2921cb0ef41Sopenharmony_ci
2931cb0ef41Sopenharmony_ci// Same sign narrowing: The range is contained for normal limits.
2941cb0ef41Sopenharmony_citemplate <typename Dst, typename Src, template <typename> class Bounds,
2951cb0ef41Sopenharmony_ci          IntegerRepresentation DstSign, IntegerRepresentation SrcSign>
2961cb0ef41Sopenharmony_cistruct DstRangeRelationToSrcRangeImpl<Dst, Src, Bounds, DstSign, SrcSign,
2971cb0ef41Sopenharmony_ci                                      NUMERIC_RANGE_CONTAINED> {
2981cb0ef41Sopenharmony_ci  static constexpr RangeCheck Check(Src value) {
2991cb0ef41Sopenharmony_ci    using SrcLimits = std::numeric_limits<Src>;
3001cb0ef41Sopenharmony_ci    using DstLimits = NarrowingRange<Dst, Src, Bounds>;
3011cb0ef41Sopenharmony_ci    return RangeCheck(
3021cb0ef41Sopenharmony_ci        static_cast<Dst>(SrcLimits::lowest()) >= DstLimits::lowest() ||
3031cb0ef41Sopenharmony_ci            static_cast<Dst>(value) >= DstLimits::lowest(),
3041cb0ef41Sopenharmony_ci        static_cast<Dst>(SrcLimits::max()) <= DstLimits::max() ||
3051cb0ef41Sopenharmony_ci            static_cast<Dst>(value) <= DstLimits::max());
3061cb0ef41Sopenharmony_ci  }
3071cb0ef41Sopenharmony_ci};
3081cb0ef41Sopenharmony_ci
3091cb0ef41Sopenharmony_ci// Signed to signed narrowing: Both the upper and lower boundaries may be
3101cb0ef41Sopenharmony_ci// exceeded for standard limits.
3111cb0ef41Sopenharmony_citemplate <typename Dst, typename Src, template <typename> class Bounds>
3121cb0ef41Sopenharmony_cistruct DstRangeRelationToSrcRangeImpl<
3131cb0ef41Sopenharmony_ci    Dst, Src, Bounds, INTEGER_REPRESENTATION_SIGNED,
3141cb0ef41Sopenharmony_ci    INTEGER_REPRESENTATION_SIGNED, NUMERIC_RANGE_NOT_CONTAINED> {
3151cb0ef41Sopenharmony_ci  static constexpr RangeCheck Check(Src value) {
3161cb0ef41Sopenharmony_ci    using DstLimits = NarrowingRange<Dst, Src, Bounds>;
3171cb0ef41Sopenharmony_ci    return RangeCheck(value >= DstLimits::lowest(), value <= DstLimits::max());
3181cb0ef41Sopenharmony_ci  }
3191cb0ef41Sopenharmony_ci};
3201cb0ef41Sopenharmony_ci
3211cb0ef41Sopenharmony_ci// Unsigned to unsigned narrowing: Only the upper bound can be exceeded for
3221cb0ef41Sopenharmony_ci// standard limits.
3231cb0ef41Sopenharmony_citemplate <typename Dst, typename Src, template <typename> class Bounds>
3241cb0ef41Sopenharmony_cistruct DstRangeRelationToSrcRangeImpl<
3251cb0ef41Sopenharmony_ci    Dst, Src, Bounds, INTEGER_REPRESENTATION_UNSIGNED,
3261cb0ef41Sopenharmony_ci    INTEGER_REPRESENTATION_UNSIGNED, NUMERIC_RANGE_NOT_CONTAINED> {
3271cb0ef41Sopenharmony_ci  static constexpr RangeCheck Check(Src value) {
3281cb0ef41Sopenharmony_ci    using DstLimits = NarrowingRange<Dst, Src, Bounds>;
3291cb0ef41Sopenharmony_ci    return RangeCheck(
3301cb0ef41Sopenharmony_ci        DstLimits::lowest() == Dst(0) || value >= DstLimits::lowest(),
3311cb0ef41Sopenharmony_ci        value <= DstLimits::max());
3321cb0ef41Sopenharmony_ci  }
3331cb0ef41Sopenharmony_ci};
3341cb0ef41Sopenharmony_ci
3351cb0ef41Sopenharmony_ci// Unsigned to signed: Only the upper bound can be exceeded for standard limits.
3361cb0ef41Sopenharmony_citemplate <typename Dst, typename Src, template <typename> class Bounds>
3371cb0ef41Sopenharmony_cistruct DstRangeRelationToSrcRangeImpl<
3381cb0ef41Sopenharmony_ci    Dst, Src, Bounds, INTEGER_REPRESENTATION_SIGNED,
3391cb0ef41Sopenharmony_ci    INTEGER_REPRESENTATION_UNSIGNED, NUMERIC_RANGE_NOT_CONTAINED> {
3401cb0ef41Sopenharmony_ci  static constexpr RangeCheck Check(Src value) {
3411cb0ef41Sopenharmony_ci    using DstLimits = NarrowingRange<Dst, Src, Bounds>;
3421cb0ef41Sopenharmony_ci    using Promotion = decltype(Src() + Dst());
3431cb0ef41Sopenharmony_ci    return RangeCheck(DstLimits::lowest() <= Dst(0) ||
3441cb0ef41Sopenharmony_ci                          static_cast<Promotion>(value) >=
3451cb0ef41Sopenharmony_ci                              static_cast<Promotion>(DstLimits::lowest()),
3461cb0ef41Sopenharmony_ci                      static_cast<Promotion>(value) <=
3471cb0ef41Sopenharmony_ci                          static_cast<Promotion>(DstLimits::max()));
3481cb0ef41Sopenharmony_ci  }
3491cb0ef41Sopenharmony_ci};
3501cb0ef41Sopenharmony_ci
3511cb0ef41Sopenharmony_ci// Signed to unsigned: The upper boundary may be exceeded for a narrower Dst,
3521cb0ef41Sopenharmony_ci// and any negative value exceeds the lower boundary for standard limits.
3531cb0ef41Sopenharmony_citemplate <typename Dst, typename Src, template <typename> class Bounds>
3541cb0ef41Sopenharmony_cistruct DstRangeRelationToSrcRangeImpl<
3551cb0ef41Sopenharmony_ci    Dst, Src, Bounds, INTEGER_REPRESENTATION_UNSIGNED,
3561cb0ef41Sopenharmony_ci    INTEGER_REPRESENTATION_SIGNED, NUMERIC_RANGE_NOT_CONTAINED> {
3571cb0ef41Sopenharmony_ci  static constexpr RangeCheck Check(Src value) {
3581cb0ef41Sopenharmony_ci    using SrcLimits = std::numeric_limits<Src>;
3591cb0ef41Sopenharmony_ci    using DstLimits = NarrowingRange<Dst, Src, Bounds>;
3601cb0ef41Sopenharmony_ci    using Promotion = decltype(Src() + Dst());
3611cb0ef41Sopenharmony_ci    bool ge_zero = false;
3621cb0ef41Sopenharmony_ci    // Converting floating-point to integer will discard fractional part, so
3631cb0ef41Sopenharmony_ci    // values in (-1.0, -0.0) will truncate to 0 and fit in Dst.
3641cb0ef41Sopenharmony_ci    if (std::is_floating_point<Src>::value) {
3651cb0ef41Sopenharmony_ci      ge_zero = value > Src(-1);
3661cb0ef41Sopenharmony_ci    } else {
3671cb0ef41Sopenharmony_ci      ge_zero = value >= Src(0);
3681cb0ef41Sopenharmony_ci    }
3691cb0ef41Sopenharmony_ci    return RangeCheck(
3701cb0ef41Sopenharmony_ci        ge_zero && (DstLimits::lowest() == 0 ||
3711cb0ef41Sopenharmony_ci                    static_cast<Dst>(value) >= DstLimits::lowest()),
3721cb0ef41Sopenharmony_ci        static_cast<Promotion>(SrcLimits::max()) <=
3731cb0ef41Sopenharmony_ci                static_cast<Promotion>(DstLimits::max()) ||
3741cb0ef41Sopenharmony_ci            static_cast<Promotion>(value) <=
3751cb0ef41Sopenharmony_ci                static_cast<Promotion>(DstLimits::max()));
3761cb0ef41Sopenharmony_ci  }
3771cb0ef41Sopenharmony_ci};
3781cb0ef41Sopenharmony_ci
3791cb0ef41Sopenharmony_ci// Simple wrapper for statically checking if a type's range is contained.
3801cb0ef41Sopenharmony_citemplate <typename Dst, typename Src>
3811cb0ef41Sopenharmony_cistruct IsTypeInRangeForNumericType {
3821cb0ef41Sopenharmony_ci  static const bool value = StaticDstRangeRelationToSrcRange<Dst, Src>::value ==
3831cb0ef41Sopenharmony_ci                            NUMERIC_RANGE_CONTAINED;
3841cb0ef41Sopenharmony_ci};
3851cb0ef41Sopenharmony_ci
3861cb0ef41Sopenharmony_citemplate <typename Dst, template <typename> class Bounds = std::numeric_limits,
3871cb0ef41Sopenharmony_ci          typename Src>
3881cb0ef41Sopenharmony_ciconstexpr RangeCheck DstRangeRelationToSrcRange(Src value) {
3891cb0ef41Sopenharmony_ci  static_assert(std::is_arithmetic<Src>::value, "Argument must be numeric.");
3901cb0ef41Sopenharmony_ci  static_assert(std::is_arithmetic<Dst>::value, "Result must be numeric.");
3911cb0ef41Sopenharmony_ci  static_assert(Bounds<Dst>::lowest() < Bounds<Dst>::max(), "");
3921cb0ef41Sopenharmony_ci  return DstRangeRelationToSrcRangeImpl<Dst, Src, Bounds>::Check(value);
3931cb0ef41Sopenharmony_ci}
3941cb0ef41Sopenharmony_ci
3951cb0ef41Sopenharmony_ci// Integer promotion templates used by the portable checked integer arithmetic.
3961cb0ef41Sopenharmony_citemplate <size_t Size, bool IsSigned>
3971cb0ef41Sopenharmony_cistruct IntegerForDigitsAndSign;
3981cb0ef41Sopenharmony_ci
3991cb0ef41Sopenharmony_ci#define INTEGER_FOR_DIGITS_AND_SIGN(I)                          \
4001cb0ef41Sopenharmony_ci  template <>                                                   \
4011cb0ef41Sopenharmony_ci  struct IntegerForDigitsAndSign<IntegerBitsPlusSign<I>::value, \
4021cb0ef41Sopenharmony_ci                                 std::is_signed<I>::value> {    \
4031cb0ef41Sopenharmony_ci    using type = I;                                             \
4041cb0ef41Sopenharmony_ci  }
4051cb0ef41Sopenharmony_ci
4061cb0ef41Sopenharmony_ciINTEGER_FOR_DIGITS_AND_SIGN(int8_t);
4071cb0ef41Sopenharmony_ciINTEGER_FOR_DIGITS_AND_SIGN(uint8_t);
4081cb0ef41Sopenharmony_ciINTEGER_FOR_DIGITS_AND_SIGN(int16_t);
4091cb0ef41Sopenharmony_ciINTEGER_FOR_DIGITS_AND_SIGN(uint16_t);
4101cb0ef41Sopenharmony_ciINTEGER_FOR_DIGITS_AND_SIGN(int32_t);
4111cb0ef41Sopenharmony_ciINTEGER_FOR_DIGITS_AND_SIGN(uint32_t);
4121cb0ef41Sopenharmony_ciINTEGER_FOR_DIGITS_AND_SIGN(int64_t);
4131cb0ef41Sopenharmony_ciINTEGER_FOR_DIGITS_AND_SIGN(uint64_t);
4141cb0ef41Sopenharmony_ci#undef INTEGER_FOR_DIGITS_AND_SIGN
4151cb0ef41Sopenharmony_ci
4161cb0ef41Sopenharmony_ci// WARNING: We have no IntegerForSizeAndSign<16, *>. If we ever add one to
4171cb0ef41Sopenharmony_ci// support 128-bit math, then the ArithmeticPromotion template below will need
4181cb0ef41Sopenharmony_ci// to be updated (or more likely replaced with a decltype expression).
4191cb0ef41Sopenharmony_cistatic_assert(IntegerBitsPlusSign<intmax_t>::value == 64,
4201cb0ef41Sopenharmony_ci              "Max integer size not supported for this toolchain.");
4211cb0ef41Sopenharmony_ci
4221cb0ef41Sopenharmony_citemplate <typename Integer, bool IsSigned = std::is_signed<Integer>::value>
4231cb0ef41Sopenharmony_cistruct TwiceWiderInteger {
4241cb0ef41Sopenharmony_ci  using type =
4251cb0ef41Sopenharmony_ci      typename IntegerForDigitsAndSign<IntegerBitsPlusSign<Integer>::value * 2,
4261cb0ef41Sopenharmony_ci                                       IsSigned>::type;
4271cb0ef41Sopenharmony_ci};
4281cb0ef41Sopenharmony_ci
4291cb0ef41Sopenharmony_cienum ArithmeticPromotionCategory {
4301cb0ef41Sopenharmony_ci  LEFT_PROMOTION,  // Use the type of the left-hand argument.
4311cb0ef41Sopenharmony_ci  RIGHT_PROMOTION  // Use the type of the right-hand argument.
4321cb0ef41Sopenharmony_ci};
4331cb0ef41Sopenharmony_ci
4341cb0ef41Sopenharmony_ci// Determines the type that can represent the largest positive value.
4351cb0ef41Sopenharmony_citemplate <typename Lhs, typename Rhs,
4361cb0ef41Sopenharmony_ci          ArithmeticPromotionCategory Promotion =
4371cb0ef41Sopenharmony_ci              (MaxExponent<Lhs>::value > MaxExponent<Rhs>::value)
4381cb0ef41Sopenharmony_ci                  ? LEFT_PROMOTION
4391cb0ef41Sopenharmony_ci                  : RIGHT_PROMOTION>
4401cb0ef41Sopenharmony_cistruct MaxExponentPromotion;
4411cb0ef41Sopenharmony_ci
4421cb0ef41Sopenharmony_citemplate <typename Lhs, typename Rhs>
4431cb0ef41Sopenharmony_cistruct MaxExponentPromotion<Lhs, Rhs, LEFT_PROMOTION> {
4441cb0ef41Sopenharmony_ci  using type = Lhs;
4451cb0ef41Sopenharmony_ci};
4461cb0ef41Sopenharmony_ci
4471cb0ef41Sopenharmony_citemplate <typename Lhs, typename Rhs>
4481cb0ef41Sopenharmony_cistruct MaxExponentPromotion<Lhs, Rhs, RIGHT_PROMOTION> {
4491cb0ef41Sopenharmony_ci  using type = Rhs;
4501cb0ef41Sopenharmony_ci};
4511cb0ef41Sopenharmony_ci
4521cb0ef41Sopenharmony_ci// Determines the type that can represent the lowest arithmetic value.
4531cb0ef41Sopenharmony_citemplate <typename Lhs, typename Rhs,
4541cb0ef41Sopenharmony_ci          ArithmeticPromotionCategory Promotion =
4551cb0ef41Sopenharmony_ci              std::is_signed<Lhs>::value
4561cb0ef41Sopenharmony_ci                  ? (std::is_signed<Rhs>::value
4571cb0ef41Sopenharmony_ci                         ? (MaxExponent<Lhs>::value > MaxExponent<Rhs>::value
4581cb0ef41Sopenharmony_ci                                ? LEFT_PROMOTION
4591cb0ef41Sopenharmony_ci                                : RIGHT_PROMOTION)
4601cb0ef41Sopenharmony_ci                         : LEFT_PROMOTION)
4611cb0ef41Sopenharmony_ci                  : (std::is_signed<Rhs>::value
4621cb0ef41Sopenharmony_ci                         ? RIGHT_PROMOTION
4631cb0ef41Sopenharmony_ci                         : (MaxExponent<Lhs>::value < MaxExponent<Rhs>::value
4641cb0ef41Sopenharmony_ci                                ? LEFT_PROMOTION
4651cb0ef41Sopenharmony_ci                                : RIGHT_PROMOTION))>
4661cb0ef41Sopenharmony_cistruct LowestValuePromotion;
4671cb0ef41Sopenharmony_ci
4681cb0ef41Sopenharmony_citemplate <typename Lhs, typename Rhs>
4691cb0ef41Sopenharmony_cistruct LowestValuePromotion<Lhs, Rhs, LEFT_PROMOTION> {
4701cb0ef41Sopenharmony_ci  using type = Lhs;
4711cb0ef41Sopenharmony_ci};
4721cb0ef41Sopenharmony_ci
4731cb0ef41Sopenharmony_citemplate <typename Lhs, typename Rhs>
4741cb0ef41Sopenharmony_cistruct LowestValuePromotion<Lhs, Rhs, RIGHT_PROMOTION> {
4751cb0ef41Sopenharmony_ci  using type = Rhs;
4761cb0ef41Sopenharmony_ci};
4771cb0ef41Sopenharmony_ci
4781cb0ef41Sopenharmony_ci// Determines the type that is best able to represent an arithmetic result.
4791cb0ef41Sopenharmony_citemplate <
4801cb0ef41Sopenharmony_ci    typename Lhs, typename Rhs = Lhs,
4811cb0ef41Sopenharmony_ci    bool is_intmax_type =
4821cb0ef41Sopenharmony_ci        std::is_integral<typename MaxExponentPromotion<Lhs, Rhs>::type>::value&&
4831cb0ef41Sopenharmony_ci            IntegerBitsPlusSign<typename MaxExponentPromotion<Lhs, Rhs>::type>::
4841cb0ef41Sopenharmony_ci                value == IntegerBitsPlusSign<intmax_t>::value,
4851cb0ef41Sopenharmony_ci    bool is_max_exponent =
4861cb0ef41Sopenharmony_ci        StaticDstRangeRelationToSrcRange<
4871cb0ef41Sopenharmony_ci            typename MaxExponentPromotion<Lhs, Rhs>::type, Lhs>::value ==
4881cb0ef41Sopenharmony_ci        NUMERIC_RANGE_CONTAINED&& StaticDstRangeRelationToSrcRange<
4891cb0ef41Sopenharmony_ci            typename MaxExponentPromotion<Lhs, Rhs>::type, Rhs>::value ==
4901cb0ef41Sopenharmony_ci        NUMERIC_RANGE_CONTAINED>
4911cb0ef41Sopenharmony_cistruct BigEnoughPromotion;
4921cb0ef41Sopenharmony_ci
4931cb0ef41Sopenharmony_ci// The side with the max exponent is big enough.
4941cb0ef41Sopenharmony_citemplate <typename Lhs, typename Rhs, bool is_intmax_type>
4951cb0ef41Sopenharmony_cistruct BigEnoughPromotion<Lhs, Rhs, is_intmax_type, true> {
4961cb0ef41Sopenharmony_ci  using type = typename MaxExponentPromotion<Lhs, Rhs>::type;
4971cb0ef41Sopenharmony_ci  static const bool is_contained = true;
4981cb0ef41Sopenharmony_ci};
4991cb0ef41Sopenharmony_ci
5001cb0ef41Sopenharmony_ci// We can use a twice wider type to fit.
5011cb0ef41Sopenharmony_citemplate <typename Lhs, typename Rhs>
5021cb0ef41Sopenharmony_cistruct BigEnoughPromotion<Lhs, Rhs, false, false> {
5031cb0ef41Sopenharmony_ci  using type =
5041cb0ef41Sopenharmony_ci      typename TwiceWiderInteger<typename MaxExponentPromotion<Lhs, Rhs>::type,
5051cb0ef41Sopenharmony_ci                                 std::is_signed<Lhs>::value ||
5061cb0ef41Sopenharmony_ci                                     std::is_signed<Rhs>::value>::type;
5071cb0ef41Sopenharmony_ci  static const bool is_contained = true;
5081cb0ef41Sopenharmony_ci};
5091cb0ef41Sopenharmony_ci
5101cb0ef41Sopenharmony_ci// No type is large enough.
5111cb0ef41Sopenharmony_citemplate <typename Lhs, typename Rhs>
5121cb0ef41Sopenharmony_cistruct BigEnoughPromotion<Lhs, Rhs, true, false> {
5131cb0ef41Sopenharmony_ci  using type = typename MaxExponentPromotion<Lhs, Rhs>::type;
5141cb0ef41Sopenharmony_ci  static const bool is_contained = false;
5151cb0ef41Sopenharmony_ci};
5161cb0ef41Sopenharmony_ci
5171cb0ef41Sopenharmony_ci// We can statically check if operations on the provided types can wrap, so we
5181cb0ef41Sopenharmony_ci// can skip the checked operations if they're not needed. So, for an integer we
5191cb0ef41Sopenharmony_ci// care if the destination type preserves the sign and is twice the width of
5201cb0ef41Sopenharmony_ci// the source.
5211cb0ef41Sopenharmony_citemplate <typename T, typename Lhs, typename Rhs = Lhs>
5221cb0ef41Sopenharmony_cistruct IsIntegerArithmeticSafe {
5231cb0ef41Sopenharmony_ci  static const bool value =
5241cb0ef41Sopenharmony_ci      !std::is_floating_point<T>::value &&
5251cb0ef41Sopenharmony_ci      !std::is_floating_point<Lhs>::value &&
5261cb0ef41Sopenharmony_ci      !std::is_floating_point<Rhs>::value &&
5271cb0ef41Sopenharmony_ci      std::is_signed<T>::value >= std::is_signed<Lhs>::value &&
5281cb0ef41Sopenharmony_ci      IntegerBitsPlusSign<T>::value >= (2 * IntegerBitsPlusSign<Lhs>::value) &&
5291cb0ef41Sopenharmony_ci      std::is_signed<T>::value >= std::is_signed<Rhs>::value &&
5301cb0ef41Sopenharmony_ci      IntegerBitsPlusSign<T>::value >= (2 * IntegerBitsPlusSign<Rhs>::value);
5311cb0ef41Sopenharmony_ci};
5321cb0ef41Sopenharmony_ci
5331cb0ef41Sopenharmony_ci// Promotes to a type that can represent any possible result of a binary
5341cb0ef41Sopenharmony_ci// arithmetic operation with the source types.
5351cb0ef41Sopenharmony_citemplate <typename Lhs, typename Rhs,
5361cb0ef41Sopenharmony_ci          bool is_promotion_possible = IsIntegerArithmeticSafe<
5371cb0ef41Sopenharmony_ci              typename std::conditional<std::is_signed<Lhs>::value ||
5381cb0ef41Sopenharmony_ci                                            std::is_signed<Rhs>::value,
5391cb0ef41Sopenharmony_ci                                        intmax_t, uintmax_t>::type,
5401cb0ef41Sopenharmony_ci              typename MaxExponentPromotion<Lhs, Rhs>::type>::value>
5411cb0ef41Sopenharmony_cistruct FastIntegerArithmeticPromotion;
5421cb0ef41Sopenharmony_ci
5431cb0ef41Sopenharmony_citemplate <typename Lhs, typename Rhs>
5441cb0ef41Sopenharmony_cistruct FastIntegerArithmeticPromotion<Lhs, Rhs, true> {
5451cb0ef41Sopenharmony_ci  using type =
5461cb0ef41Sopenharmony_ci      typename TwiceWiderInteger<typename MaxExponentPromotion<Lhs, Rhs>::type,
5471cb0ef41Sopenharmony_ci                                 std::is_signed<Lhs>::value ||
5481cb0ef41Sopenharmony_ci                                     std::is_signed<Rhs>::value>::type;
5491cb0ef41Sopenharmony_ci  static_assert(IsIntegerArithmeticSafe<type, Lhs, Rhs>::value, "");
5501cb0ef41Sopenharmony_ci  static const bool is_contained = true;
5511cb0ef41Sopenharmony_ci};
5521cb0ef41Sopenharmony_ci
5531cb0ef41Sopenharmony_citemplate <typename Lhs, typename Rhs>
5541cb0ef41Sopenharmony_cistruct FastIntegerArithmeticPromotion<Lhs, Rhs, false> {
5551cb0ef41Sopenharmony_ci  using type = typename BigEnoughPromotion<Lhs, Rhs>::type;
5561cb0ef41Sopenharmony_ci  static const bool is_contained = false;
5571cb0ef41Sopenharmony_ci};
5581cb0ef41Sopenharmony_ci
5591cb0ef41Sopenharmony_ci// Extracts the underlying type from an enum.
5601cb0ef41Sopenharmony_citemplate <typename T, bool is_enum = std::is_enum<T>::value>
5611cb0ef41Sopenharmony_cistruct ArithmeticOrUnderlyingEnum;
5621cb0ef41Sopenharmony_ci
5631cb0ef41Sopenharmony_citemplate <typename T>
5641cb0ef41Sopenharmony_cistruct ArithmeticOrUnderlyingEnum<T, true> {
5651cb0ef41Sopenharmony_ci  using type = typename std::underlying_type<T>::type;
5661cb0ef41Sopenharmony_ci  static const bool value = std::is_arithmetic<type>::value;
5671cb0ef41Sopenharmony_ci};
5681cb0ef41Sopenharmony_ci
5691cb0ef41Sopenharmony_citemplate <typename T>
5701cb0ef41Sopenharmony_cistruct ArithmeticOrUnderlyingEnum<T, false> {
5711cb0ef41Sopenharmony_ci  using type = T;
5721cb0ef41Sopenharmony_ci  static const bool value = std::is_arithmetic<type>::value;
5731cb0ef41Sopenharmony_ci};
5741cb0ef41Sopenharmony_ci
5751cb0ef41Sopenharmony_ci// The following are helper templates used in the CheckedNumeric class.
5761cb0ef41Sopenharmony_citemplate <typename T>
5771cb0ef41Sopenharmony_ciclass CheckedNumeric;
5781cb0ef41Sopenharmony_ci
5791cb0ef41Sopenharmony_citemplate <typename T>
5801cb0ef41Sopenharmony_ciclass ClampedNumeric;
5811cb0ef41Sopenharmony_ci
5821cb0ef41Sopenharmony_citemplate <typename T>
5831cb0ef41Sopenharmony_ciclass StrictNumeric;
5841cb0ef41Sopenharmony_ci
5851cb0ef41Sopenharmony_ci// Used to treat CheckedNumeric and arithmetic underlying types the same.
5861cb0ef41Sopenharmony_citemplate <typename T>
5871cb0ef41Sopenharmony_cistruct UnderlyingType {
5881cb0ef41Sopenharmony_ci  using type = typename ArithmeticOrUnderlyingEnum<T>::type;
5891cb0ef41Sopenharmony_ci  static const bool is_numeric = std::is_arithmetic<type>::value;
5901cb0ef41Sopenharmony_ci  static const bool is_checked = false;
5911cb0ef41Sopenharmony_ci  static const bool is_clamped = false;
5921cb0ef41Sopenharmony_ci  static const bool is_strict = false;
5931cb0ef41Sopenharmony_ci};
5941cb0ef41Sopenharmony_ci
5951cb0ef41Sopenharmony_citemplate <typename T>
5961cb0ef41Sopenharmony_cistruct UnderlyingType<CheckedNumeric<T>> {
5971cb0ef41Sopenharmony_ci  using type = T;
5981cb0ef41Sopenharmony_ci  static const bool is_numeric = true;
5991cb0ef41Sopenharmony_ci  static const bool is_checked = true;
6001cb0ef41Sopenharmony_ci  static const bool is_clamped = false;
6011cb0ef41Sopenharmony_ci  static const bool is_strict = false;
6021cb0ef41Sopenharmony_ci};
6031cb0ef41Sopenharmony_ci
6041cb0ef41Sopenharmony_citemplate <typename T>
6051cb0ef41Sopenharmony_cistruct UnderlyingType<ClampedNumeric<T>> {
6061cb0ef41Sopenharmony_ci  using type = T;
6071cb0ef41Sopenharmony_ci  static const bool is_numeric = true;
6081cb0ef41Sopenharmony_ci  static const bool is_checked = false;
6091cb0ef41Sopenharmony_ci  static const bool is_clamped = true;
6101cb0ef41Sopenharmony_ci  static const bool is_strict = false;
6111cb0ef41Sopenharmony_ci};
6121cb0ef41Sopenharmony_ci
6131cb0ef41Sopenharmony_citemplate <typename T>
6141cb0ef41Sopenharmony_cistruct UnderlyingType<StrictNumeric<T>> {
6151cb0ef41Sopenharmony_ci  using type = T;
6161cb0ef41Sopenharmony_ci  static const bool is_numeric = true;
6171cb0ef41Sopenharmony_ci  static const bool is_checked = false;
6181cb0ef41Sopenharmony_ci  static const bool is_clamped = false;
6191cb0ef41Sopenharmony_ci  static const bool is_strict = true;
6201cb0ef41Sopenharmony_ci};
6211cb0ef41Sopenharmony_ci
6221cb0ef41Sopenharmony_citemplate <typename L, typename R>
6231cb0ef41Sopenharmony_cistruct IsCheckedOp {
6241cb0ef41Sopenharmony_ci  static const bool value =
6251cb0ef41Sopenharmony_ci      UnderlyingType<L>::is_numeric && UnderlyingType<R>::is_numeric &&
6261cb0ef41Sopenharmony_ci      (UnderlyingType<L>::is_checked || UnderlyingType<R>::is_checked);
6271cb0ef41Sopenharmony_ci};
6281cb0ef41Sopenharmony_ci
6291cb0ef41Sopenharmony_citemplate <typename L, typename R>
6301cb0ef41Sopenharmony_cistruct IsClampedOp {
6311cb0ef41Sopenharmony_ci  static const bool value =
6321cb0ef41Sopenharmony_ci      UnderlyingType<L>::is_numeric && UnderlyingType<R>::is_numeric &&
6331cb0ef41Sopenharmony_ci      (UnderlyingType<L>::is_clamped || UnderlyingType<R>::is_clamped) &&
6341cb0ef41Sopenharmony_ci      !(UnderlyingType<L>::is_checked || UnderlyingType<R>::is_checked);
6351cb0ef41Sopenharmony_ci};
6361cb0ef41Sopenharmony_ci
6371cb0ef41Sopenharmony_citemplate <typename L, typename R>
6381cb0ef41Sopenharmony_cistruct IsStrictOp {
6391cb0ef41Sopenharmony_ci  static const bool value =
6401cb0ef41Sopenharmony_ci      UnderlyingType<L>::is_numeric && UnderlyingType<R>::is_numeric &&
6411cb0ef41Sopenharmony_ci      (UnderlyingType<L>::is_strict || UnderlyingType<R>::is_strict) &&
6421cb0ef41Sopenharmony_ci      !(UnderlyingType<L>::is_checked || UnderlyingType<R>::is_checked) &&
6431cb0ef41Sopenharmony_ci      !(UnderlyingType<L>::is_clamped || UnderlyingType<R>::is_clamped);
6441cb0ef41Sopenharmony_ci};
6451cb0ef41Sopenharmony_ci
6461cb0ef41Sopenharmony_ci// as_signed<> returns the supplied integral value (or integral castable
6471cb0ef41Sopenharmony_ci// Numeric template) cast as a signed integral of equivalent precision.
6481cb0ef41Sopenharmony_ci// I.e. it's mostly an alias for: static_cast<std::make_signed<T>::type>(t)
6491cb0ef41Sopenharmony_citemplate <typename Src>
6501cb0ef41Sopenharmony_ciconstexpr typename std::make_signed<
6511cb0ef41Sopenharmony_ci    typename base::internal::UnderlyingType<Src>::type>::type
6521cb0ef41Sopenharmony_cias_signed(const Src value) {
6531cb0ef41Sopenharmony_ci  static_assert(std::is_integral<decltype(as_signed(value))>::value,
6541cb0ef41Sopenharmony_ci                "Argument must be a signed or unsigned integer type.");
6551cb0ef41Sopenharmony_ci  return static_cast<decltype(as_signed(value))>(value);
6561cb0ef41Sopenharmony_ci}
6571cb0ef41Sopenharmony_ci
6581cb0ef41Sopenharmony_ci// as_unsigned<> returns the supplied integral value (or integral castable
6591cb0ef41Sopenharmony_ci// Numeric template) cast as an unsigned integral of equivalent precision.
6601cb0ef41Sopenharmony_ci// I.e. it's mostly an alias for: static_cast<std::make_unsigned<T>::type>(t)
6611cb0ef41Sopenharmony_citemplate <typename Src>
6621cb0ef41Sopenharmony_ciconstexpr typename std::make_unsigned<
6631cb0ef41Sopenharmony_ci    typename base::internal::UnderlyingType<Src>::type>::type
6641cb0ef41Sopenharmony_cias_unsigned(const Src value) {
6651cb0ef41Sopenharmony_ci  static_assert(std::is_integral<decltype(as_unsigned(value))>::value,
6661cb0ef41Sopenharmony_ci                "Argument must be a signed or unsigned integer type.");
6671cb0ef41Sopenharmony_ci  return static_cast<decltype(as_unsigned(value))>(value);
6681cb0ef41Sopenharmony_ci}
6691cb0ef41Sopenharmony_ci
6701cb0ef41Sopenharmony_citemplate <typename L, typename R>
6711cb0ef41Sopenharmony_ciconstexpr bool IsLessImpl(const L lhs, const R rhs, const RangeCheck l_range,
6721cb0ef41Sopenharmony_ci                          const RangeCheck r_range) {
6731cb0ef41Sopenharmony_ci  return l_range.IsUnderflow() || r_range.IsOverflow() ||
6741cb0ef41Sopenharmony_ci         (l_range == r_range && static_cast<decltype(lhs + rhs)>(lhs) <
6751cb0ef41Sopenharmony_ci                                    static_cast<decltype(lhs + rhs)>(rhs));
6761cb0ef41Sopenharmony_ci}
6771cb0ef41Sopenharmony_ci
6781cb0ef41Sopenharmony_citemplate <typename L, typename R>
6791cb0ef41Sopenharmony_cistruct IsLess {
6801cb0ef41Sopenharmony_ci  static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value,
6811cb0ef41Sopenharmony_ci                "Types must be numeric.");
6821cb0ef41Sopenharmony_ci  static constexpr bool Test(const L lhs, const R rhs) {
6831cb0ef41Sopenharmony_ci    return IsLessImpl(lhs, rhs, DstRangeRelationToSrcRange<R>(lhs),
6841cb0ef41Sopenharmony_ci                      DstRangeRelationToSrcRange<L>(rhs));
6851cb0ef41Sopenharmony_ci  }
6861cb0ef41Sopenharmony_ci};
6871cb0ef41Sopenharmony_ci
6881cb0ef41Sopenharmony_citemplate <typename L, typename R>
6891cb0ef41Sopenharmony_ciconstexpr bool IsLessOrEqualImpl(const L lhs, const R rhs,
6901cb0ef41Sopenharmony_ci                                 const RangeCheck l_range,
6911cb0ef41Sopenharmony_ci                                 const RangeCheck r_range) {
6921cb0ef41Sopenharmony_ci  return l_range.IsUnderflow() || r_range.IsOverflow() ||
6931cb0ef41Sopenharmony_ci         (l_range == r_range && static_cast<decltype(lhs + rhs)>(lhs) <=
6941cb0ef41Sopenharmony_ci                                    static_cast<decltype(lhs + rhs)>(rhs));
6951cb0ef41Sopenharmony_ci}
6961cb0ef41Sopenharmony_ci
6971cb0ef41Sopenharmony_citemplate <typename L, typename R>
6981cb0ef41Sopenharmony_cistruct IsLessOrEqual {
6991cb0ef41Sopenharmony_ci  static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value,
7001cb0ef41Sopenharmony_ci                "Types must be numeric.");
7011cb0ef41Sopenharmony_ci  static constexpr bool Test(const L lhs, const R rhs) {
7021cb0ef41Sopenharmony_ci    return IsLessOrEqualImpl(lhs, rhs, DstRangeRelationToSrcRange<R>(lhs),
7031cb0ef41Sopenharmony_ci                             DstRangeRelationToSrcRange<L>(rhs));
7041cb0ef41Sopenharmony_ci  }
7051cb0ef41Sopenharmony_ci};
7061cb0ef41Sopenharmony_ci
7071cb0ef41Sopenharmony_citemplate <typename L, typename R>
7081cb0ef41Sopenharmony_ciconstexpr bool IsGreaterImpl(const L lhs, const R rhs, const RangeCheck l_range,
7091cb0ef41Sopenharmony_ci                             const RangeCheck r_range) {
7101cb0ef41Sopenharmony_ci  return l_range.IsOverflow() || r_range.IsUnderflow() ||
7111cb0ef41Sopenharmony_ci         (l_range == r_range && static_cast<decltype(lhs + rhs)>(lhs) >
7121cb0ef41Sopenharmony_ci                                    static_cast<decltype(lhs + rhs)>(rhs));
7131cb0ef41Sopenharmony_ci}
7141cb0ef41Sopenharmony_ci
7151cb0ef41Sopenharmony_citemplate <typename L, typename R>
7161cb0ef41Sopenharmony_cistruct IsGreater {
7171cb0ef41Sopenharmony_ci  static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value,
7181cb0ef41Sopenharmony_ci                "Types must be numeric.");
7191cb0ef41Sopenharmony_ci  static constexpr bool Test(const L lhs, const R rhs) {
7201cb0ef41Sopenharmony_ci    return IsGreaterImpl(lhs, rhs, DstRangeRelationToSrcRange<R>(lhs),
7211cb0ef41Sopenharmony_ci                         DstRangeRelationToSrcRange<L>(rhs));
7221cb0ef41Sopenharmony_ci  }
7231cb0ef41Sopenharmony_ci};
7241cb0ef41Sopenharmony_ci
7251cb0ef41Sopenharmony_citemplate <typename L, typename R>
7261cb0ef41Sopenharmony_ciconstexpr bool IsGreaterOrEqualImpl(const L lhs, const R rhs,
7271cb0ef41Sopenharmony_ci                                    const RangeCheck l_range,
7281cb0ef41Sopenharmony_ci                                    const RangeCheck r_range) {
7291cb0ef41Sopenharmony_ci  return l_range.IsOverflow() || r_range.IsUnderflow() ||
7301cb0ef41Sopenharmony_ci         (l_range == r_range && static_cast<decltype(lhs + rhs)>(lhs) >=
7311cb0ef41Sopenharmony_ci                                    static_cast<decltype(lhs + rhs)>(rhs));
7321cb0ef41Sopenharmony_ci}
7331cb0ef41Sopenharmony_ci
7341cb0ef41Sopenharmony_citemplate <typename L, typename R>
7351cb0ef41Sopenharmony_cistruct IsGreaterOrEqual {
7361cb0ef41Sopenharmony_ci  static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value,
7371cb0ef41Sopenharmony_ci                "Types must be numeric.");
7381cb0ef41Sopenharmony_ci  static constexpr bool Test(const L lhs, const R rhs) {
7391cb0ef41Sopenharmony_ci    return IsGreaterOrEqualImpl(lhs, rhs, DstRangeRelationToSrcRange<R>(lhs),
7401cb0ef41Sopenharmony_ci                                DstRangeRelationToSrcRange<L>(rhs));
7411cb0ef41Sopenharmony_ci  }
7421cb0ef41Sopenharmony_ci};
7431cb0ef41Sopenharmony_ci
7441cb0ef41Sopenharmony_citemplate <typename L, typename R>
7451cb0ef41Sopenharmony_cistruct IsEqual {
7461cb0ef41Sopenharmony_ci  static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value,
7471cb0ef41Sopenharmony_ci                "Types must be numeric.");
7481cb0ef41Sopenharmony_ci  static constexpr bool Test(const L lhs, const R rhs) {
7491cb0ef41Sopenharmony_ci    return DstRangeRelationToSrcRange<R>(lhs) ==
7501cb0ef41Sopenharmony_ci               DstRangeRelationToSrcRange<L>(rhs) &&
7511cb0ef41Sopenharmony_ci           static_cast<decltype(lhs + rhs)>(lhs) ==
7521cb0ef41Sopenharmony_ci               static_cast<decltype(lhs + rhs)>(rhs);
7531cb0ef41Sopenharmony_ci  }
7541cb0ef41Sopenharmony_ci};
7551cb0ef41Sopenharmony_ci
7561cb0ef41Sopenharmony_citemplate <typename L, typename R>
7571cb0ef41Sopenharmony_cistruct IsNotEqual {
7581cb0ef41Sopenharmony_ci  static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value,
7591cb0ef41Sopenharmony_ci                "Types must be numeric.");
7601cb0ef41Sopenharmony_ci  static constexpr bool Test(const L lhs, const R rhs) {
7611cb0ef41Sopenharmony_ci    return DstRangeRelationToSrcRange<R>(lhs) !=
7621cb0ef41Sopenharmony_ci               DstRangeRelationToSrcRange<L>(rhs) ||
7631cb0ef41Sopenharmony_ci           static_cast<decltype(lhs + rhs)>(lhs) !=
7641cb0ef41Sopenharmony_ci               static_cast<decltype(lhs + rhs)>(rhs);
7651cb0ef41Sopenharmony_ci  }
7661cb0ef41Sopenharmony_ci};
7671cb0ef41Sopenharmony_ci
7681cb0ef41Sopenharmony_ci// These perform the actual math operations on the CheckedNumerics.
7691cb0ef41Sopenharmony_ci// Binary arithmetic operations.
7701cb0ef41Sopenharmony_citemplate <template <typename, typename> class C, typename L, typename R>
7711cb0ef41Sopenharmony_ciconstexpr bool SafeCompare(const L lhs, const R rhs) {
7721cb0ef41Sopenharmony_ci  static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value,
7731cb0ef41Sopenharmony_ci                "Types must be numeric.");
7741cb0ef41Sopenharmony_ci  using Promotion = BigEnoughPromotion<L, R>;
7751cb0ef41Sopenharmony_ci  using BigType = typename Promotion::type;
7761cb0ef41Sopenharmony_ci  return Promotion::is_contained
7771cb0ef41Sopenharmony_ci             // Force to a larger type for speed if both are contained.
7781cb0ef41Sopenharmony_ci             ? C<BigType, BigType>::Test(
7791cb0ef41Sopenharmony_ci                   static_cast<BigType>(static_cast<L>(lhs)),
7801cb0ef41Sopenharmony_ci                   static_cast<BigType>(static_cast<R>(rhs)))
7811cb0ef41Sopenharmony_ci             // Let the template functions figure it out for mixed types.
7821cb0ef41Sopenharmony_ci             : C<L, R>::Test(lhs, rhs);
7831cb0ef41Sopenharmony_ci}
7841cb0ef41Sopenharmony_ci
7851cb0ef41Sopenharmony_citemplate <typename Dst, typename Src>
7861cb0ef41Sopenharmony_ciconstexpr bool IsMaxInRangeForNumericType() {
7871cb0ef41Sopenharmony_ci  return IsGreaterOrEqual<Dst, Src>::Test(std::numeric_limits<Dst>::max(),
7881cb0ef41Sopenharmony_ci                                          std::numeric_limits<Src>::max());
7891cb0ef41Sopenharmony_ci}
7901cb0ef41Sopenharmony_ci
7911cb0ef41Sopenharmony_citemplate <typename Dst, typename Src>
7921cb0ef41Sopenharmony_ciconstexpr bool IsMinInRangeForNumericType() {
7931cb0ef41Sopenharmony_ci  return IsLessOrEqual<Dst, Src>::Test(std::numeric_limits<Dst>::lowest(),
7941cb0ef41Sopenharmony_ci                                       std::numeric_limits<Src>::lowest());
7951cb0ef41Sopenharmony_ci}
7961cb0ef41Sopenharmony_ci
7971cb0ef41Sopenharmony_citemplate <typename Dst, typename Src>
7981cb0ef41Sopenharmony_ciconstexpr Dst CommonMax() {
7991cb0ef41Sopenharmony_ci  return !IsMaxInRangeForNumericType<Dst, Src>()
8001cb0ef41Sopenharmony_ci             ? Dst(std::numeric_limits<Dst>::max())
8011cb0ef41Sopenharmony_ci             : Dst(std::numeric_limits<Src>::max());
8021cb0ef41Sopenharmony_ci}
8031cb0ef41Sopenharmony_ci
8041cb0ef41Sopenharmony_citemplate <typename Dst, typename Src>
8051cb0ef41Sopenharmony_ciconstexpr Dst CommonMin() {
8061cb0ef41Sopenharmony_ci  return !IsMinInRangeForNumericType<Dst, Src>()
8071cb0ef41Sopenharmony_ci             ? Dst(std::numeric_limits<Dst>::lowest())
8081cb0ef41Sopenharmony_ci             : Dst(std::numeric_limits<Src>::lowest());
8091cb0ef41Sopenharmony_ci}
8101cb0ef41Sopenharmony_ci
8111cb0ef41Sopenharmony_ci// This is a wrapper to generate return the max or min for a supplied type.
8121cb0ef41Sopenharmony_ci// If the argument is false, the returned value is the maximum. If true the
8131cb0ef41Sopenharmony_ci// returned value is the minimum.
8141cb0ef41Sopenharmony_citemplate <typename Dst, typename Src = Dst>
8151cb0ef41Sopenharmony_ciconstexpr Dst CommonMaxOrMin(bool is_min) {
8161cb0ef41Sopenharmony_ci  return is_min ? CommonMin<Dst, Src>() : CommonMax<Dst, Src>();
8171cb0ef41Sopenharmony_ci}
8181cb0ef41Sopenharmony_ci
8191cb0ef41Sopenharmony_ci}  // namespace internal
8201cb0ef41Sopenharmony_ci}  // namespace base
8211cb0ef41Sopenharmony_ci}  // namespace v8
8221cb0ef41Sopenharmony_ci
8231cb0ef41Sopenharmony_ci#endif  // V8_BASE_SAFE_CONVERSIONS_IMPL_H_
824