1// Copyright 2014 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5// Slightly adapted for inclusion in V8.
6// Copyright 2014 the V8 project authors. All rights reserved.
7// List of adaptations:
8// - include guard names
9// - wrap in v8 namespace
10// - formatting (git cl format)
11
12#ifndef V8_BASE_SAFE_CONVERSIONS_IMPL_H_
13#define V8_BASE_SAFE_CONVERSIONS_IMPL_H_
14
15#include <stddef.h>
16#include <stdint.h>
17
18#include <limits>
19#include <type_traits>
20
21#if defined(__GNUC__) || defined(__clang__)
22#define BASE_NUMERICS_LIKELY(x) __builtin_expect(!!(x), 1)
23#define BASE_NUMERICS_UNLIKELY(x) __builtin_expect(!!(x), 0)
24#else
25#define BASE_NUMERICS_LIKELY(x) (x)
26#define BASE_NUMERICS_UNLIKELY(x) (x)
27#endif
28
29namespace v8 {
30namespace base {
31namespace internal {
32
33// The std library doesn't provide a binary max_exponent for integers, however
34// we can compute an analog using std::numeric_limits<>::digits.
35template <typename NumericType>
36struct MaxExponent {
37  static const int value = std::is_floating_point<NumericType>::value
38                               ? std::numeric_limits<NumericType>::max_exponent
39                               : std::numeric_limits<NumericType>::digits + 1;
40};
41
42// The number of bits (including the sign) in an integer. Eliminates sizeof
43// hacks.
44template <typename NumericType>
45struct IntegerBitsPlusSign {
46  static const int value = std::numeric_limits<NumericType>::digits +
47                           std::is_signed<NumericType>::value;
48};
49
50// Helper templates for integer manipulations.
51
52template <typename Integer>
53struct PositionOfSignBit {
54  static const size_t value = IntegerBitsPlusSign<Integer>::value - 1;
55};
56
57// Determines if a numeric value is negative without throwing compiler
58// warnings on: unsigned(value) < 0.
59template <typename T,
60          typename std::enable_if<std::is_signed<T>::value>::type* = nullptr>
61constexpr bool IsValueNegative(T value) {
62  static_assert(std::is_arithmetic<T>::value, "Argument must be numeric.");
63  return value < 0;
64}
65
66template <typename T,
67          typename std::enable_if<!std::is_signed<T>::value>::type* = nullptr>
68constexpr bool IsValueNegative(T) {
69  static_assert(std::is_arithmetic<T>::value, "Argument must be numeric.");
70  return false;
71}
72
73// This performs a fast negation, returning a signed value. It works on unsigned
74// arguments, but probably doesn't do what you want for any unsigned value
75// larger than max / 2 + 1 (i.e. signed min cast to unsigned).
76template <typename T>
77constexpr typename std::make_signed<T>::type ConditionalNegate(
78    T x, bool is_negative) {
79  static_assert(std::is_integral<T>::value, "Type must be integral");
80  using SignedT = typename std::make_signed<T>::type;
81  using UnsignedT = typename std::make_unsigned<T>::type;
82  return static_cast<SignedT>(
83      (static_cast<UnsignedT>(x) ^ -SignedT(is_negative)) + is_negative);
84}
85
86// This performs a safe, absolute value via unsigned overflow.
87template <typename T>
88constexpr typename std::make_unsigned<T>::type SafeUnsignedAbs(T value) {
89  static_assert(std::is_integral<T>::value, "Type must be integral");
90  using UnsignedT = typename std::make_unsigned<T>::type;
91  return IsValueNegative(value)
92             ? static_cast<UnsignedT>(0u - static_cast<UnsignedT>(value))
93             : static_cast<UnsignedT>(value);
94}
95
96// This allows us to switch paths on known compile-time constants.
97#if defined(__clang__) || defined(__GNUC__)
98constexpr bool CanDetectCompileTimeConstant() { return true; }
99template <typename T>
100constexpr bool IsCompileTimeConstant(const T v) {
101  return __builtin_constant_p(v);
102}
103#else
104constexpr bool CanDetectCompileTimeConstant() { return false; }
105template <typename T>
106constexpr bool IsCompileTimeConstant(const T) {
107  return false;
108}
109#endif
110template <typename T>
111constexpr bool MustTreatAsConstexpr(const T v) {
112  // Either we can't detect a compile-time constant, and must always use the
113  // constexpr path, or we know we have a compile-time constant.
114  return !CanDetectCompileTimeConstant() || IsCompileTimeConstant(v);
115}
116
117// Forces a crash, like a CHECK(false). Used for numeric boundary errors.
118// Also used in a constexpr template to trigger a compilation failure on
119// an error condition.
120struct CheckOnFailure {
121  template <typename T>
122  static T HandleFailure() {
123#if defined(_MSC_VER)
124    __debugbreak();
125#elif defined(__GNUC__) || defined(__clang__)
126    __builtin_trap();
127#else
128    ((void)(*(volatile char*)0 = 0));
129#endif
130    return T();
131  }
132};
133
134enum IntegerRepresentation {
135  INTEGER_REPRESENTATION_UNSIGNED,
136  INTEGER_REPRESENTATION_SIGNED
137};
138
139// A range for a given nunmeric Src type is contained for a given numeric Dst
140// type if both numeric_limits<Src>::max() <= numeric_limits<Dst>::max() and
141// numeric_limits<Src>::lowest() >= numeric_limits<Dst>::lowest() are true.
142// We implement this as template specializations rather than simple static
143// comparisons to ensure type correctness in our comparisons.
144enum NumericRangeRepresentation {
145  NUMERIC_RANGE_NOT_CONTAINED,
146  NUMERIC_RANGE_CONTAINED
147};
148
149// Helper templates to statically determine if our destination type can contain
150// maximum and minimum values represented by the source type.
151
152template <typename Dst, typename Src,
153          IntegerRepresentation DstSign = std::is_signed<Dst>::value
154                                              ? INTEGER_REPRESENTATION_SIGNED
155                                              : INTEGER_REPRESENTATION_UNSIGNED,
156          IntegerRepresentation SrcSign = std::is_signed<Src>::value
157                                              ? INTEGER_REPRESENTATION_SIGNED
158                                              : INTEGER_REPRESENTATION_UNSIGNED>
159struct StaticDstRangeRelationToSrcRange;
160
161// Same sign: Dst is guaranteed to contain Src only if its range is equal or
162// larger.
163template <typename Dst, typename Src, IntegerRepresentation Sign>
164struct StaticDstRangeRelationToSrcRange<Dst, Src, Sign, Sign> {
165  static const NumericRangeRepresentation value =
166      MaxExponent<Dst>::value >= MaxExponent<Src>::value
167          ? NUMERIC_RANGE_CONTAINED
168          : NUMERIC_RANGE_NOT_CONTAINED;
169};
170
171// Unsigned to signed: Dst is guaranteed to contain source only if its range is
172// larger.
173template <typename Dst, typename Src>
174struct StaticDstRangeRelationToSrcRange<Dst,
175                                        Src,
176                                        INTEGER_REPRESENTATION_SIGNED,
177                                        INTEGER_REPRESENTATION_UNSIGNED> {
178  static const NumericRangeRepresentation value =
179      MaxExponent<Dst>::value > MaxExponent<Src>::value
180          ? NUMERIC_RANGE_CONTAINED
181          : NUMERIC_RANGE_NOT_CONTAINED;
182};
183
184// Signed to unsigned: Dst cannot be statically determined to contain Src.
185template <typename Dst, typename Src>
186struct StaticDstRangeRelationToSrcRange<Dst,
187                                        Src,
188                                        INTEGER_REPRESENTATION_UNSIGNED,
189                                        INTEGER_REPRESENTATION_SIGNED> {
190  static const NumericRangeRepresentation value = NUMERIC_RANGE_NOT_CONTAINED;
191};
192
193// This class wraps the range constraints as separate booleans so the compiler
194// can identify constants and eliminate unused code paths.
195class RangeCheck {
196 public:
197  constexpr RangeCheck(bool is_in_lower_bound, bool is_in_upper_bound)
198      : is_underflow_(!is_in_lower_bound), is_overflow_(!is_in_upper_bound) {}
199  constexpr RangeCheck() : is_underflow_(false), is_overflow_(false) {}
200  constexpr bool IsValid() const { return !is_overflow_ && !is_underflow_; }
201  constexpr bool IsInvalid() const { return is_overflow_ && is_underflow_; }
202  constexpr bool IsOverflow() const { return is_overflow_ && !is_underflow_; }
203  constexpr bool IsUnderflow() const { return !is_overflow_ && is_underflow_; }
204  constexpr bool IsOverflowFlagSet() const { return is_overflow_; }
205  constexpr bool IsUnderflowFlagSet() const { return is_underflow_; }
206  constexpr bool operator==(const RangeCheck rhs) const {
207    return is_underflow_ == rhs.is_underflow_ &&
208           is_overflow_ == rhs.is_overflow_;
209  }
210  constexpr bool operator!=(const RangeCheck rhs) const {
211    return !(*this == rhs);
212  }
213
214 private:
215  // Do not change the order of these member variables. The integral conversion
216  // optimization depends on this exact order.
217  const bool is_underflow_;
218  const bool is_overflow_;
219};
220
221// The following helper template addresses a corner case in range checks for
222// conversion from a floating-point type to an integral type of smaller range
223// but larger precision (e.g. float -> unsigned). The problem is as follows:
224//   1. Integral maximum is always one less than a power of two, so it must be
225//      truncated to fit the mantissa of the floating point. The direction of
226//      rounding is implementation defined, but by default it's always IEEE
227//      floats, which round to nearest and thus result in a value of larger
228//      magnitude than the integral value.
229//      Example: float f = UINT_MAX; // f is 4294967296f but UINT_MAX
230//                                   // is 4294967295u.
231//   2. If the floating point value is equal to the promoted integral maximum
232//      value, a range check will erroneously pass.
233//      Example: (4294967296f <= 4294967295u) // This is true due to a precision
234//                                            // loss in rounding up to float.
235//   3. When the floating point value is then converted to an integral, the
236//      resulting value is out of range for the target integral type and
237//      thus is implementation defined.
238//      Example: unsigned u = (float)INT_MAX; // u will typically overflow to 0.
239// To fix this bug we manually truncate the maximum value when the destination
240// type is an integral of larger precision than the source floating-point type,
241// such that the resulting maximum is represented exactly as a floating point.
242template <typename Dst, typename Src, template <typename> class Bounds>
243struct NarrowingRange {
244  using SrcLimits = std::numeric_limits<Src>;
245  using DstLimits = typename std::numeric_limits<Dst>;
246
247  // Computes the mask required to make an accurate comparison between types.
248  static const int kShift =
249      (MaxExponent<Src>::value > MaxExponent<Dst>::value &&
250       SrcLimits::digits < DstLimits::digits)
251          ? (DstLimits::digits - SrcLimits::digits)
252          : 0;
253  template <typename T, typename std::enable_if<
254                            std::is_integral<T>::value>::type* = nullptr>
255
256  // Masks out the integer bits that are beyond the precision of the
257  // intermediate type used for comparison.
258  static constexpr T Adjust(T value) {
259    static_assert(std::is_same<T, Dst>::value, "");
260    static_assert(kShift < DstLimits::digits, "");
261    return static_cast<T>(
262        ConditionalNegate(SafeUnsignedAbs(value) & ~((T(1) << kShift) - T(1)),
263                          IsValueNegative(value)));
264  }
265
266  template <typename T, typename std::enable_if<
267                            std::is_floating_point<T>::value>::type* = nullptr>
268  static constexpr T Adjust(T value) {
269    static_assert(std::is_same<T, Dst>::value, "");
270    static_assert(kShift == 0, "");
271    return value;
272  }
273
274  static constexpr Dst max() { return Adjust(Bounds<Dst>::max()); }
275  static constexpr Dst lowest() { return Adjust(Bounds<Dst>::lowest()); }
276};
277
278template <typename Dst, typename Src, template <typename> class Bounds,
279          IntegerRepresentation DstSign = std::is_signed<Dst>::value
280                                              ? INTEGER_REPRESENTATION_SIGNED
281                                              : INTEGER_REPRESENTATION_UNSIGNED,
282          IntegerRepresentation SrcSign = std::is_signed<Src>::value
283                                              ? INTEGER_REPRESENTATION_SIGNED
284                                              : INTEGER_REPRESENTATION_UNSIGNED,
285          NumericRangeRepresentation DstRange =
286              StaticDstRangeRelationToSrcRange<Dst, Src>::value>
287struct DstRangeRelationToSrcRangeImpl;
288
289// The following templates are for ranges that must be verified at runtime. We
290// split it into checks based on signedness to avoid confusing casts and
291// compiler warnings on signed an unsigned comparisons.
292
293// Same sign narrowing: The range is contained for normal limits.
294template <typename Dst, typename Src, template <typename> class Bounds,
295          IntegerRepresentation DstSign, IntegerRepresentation SrcSign>
296struct DstRangeRelationToSrcRangeImpl<Dst, Src, Bounds, DstSign, SrcSign,
297                                      NUMERIC_RANGE_CONTAINED> {
298  static constexpr RangeCheck Check(Src value) {
299    using SrcLimits = std::numeric_limits<Src>;
300    using DstLimits = NarrowingRange<Dst, Src, Bounds>;
301    return RangeCheck(
302        static_cast<Dst>(SrcLimits::lowest()) >= DstLimits::lowest() ||
303            static_cast<Dst>(value) >= DstLimits::lowest(),
304        static_cast<Dst>(SrcLimits::max()) <= DstLimits::max() ||
305            static_cast<Dst>(value) <= DstLimits::max());
306  }
307};
308
309// Signed to signed narrowing: Both the upper and lower boundaries may be
310// exceeded for standard limits.
311template <typename Dst, typename Src, template <typename> class Bounds>
312struct DstRangeRelationToSrcRangeImpl<
313    Dst, Src, Bounds, INTEGER_REPRESENTATION_SIGNED,
314    INTEGER_REPRESENTATION_SIGNED, NUMERIC_RANGE_NOT_CONTAINED> {
315  static constexpr RangeCheck Check(Src value) {
316    using DstLimits = NarrowingRange<Dst, Src, Bounds>;
317    return RangeCheck(value >= DstLimits::lowest(), value <= DstLimits::max());
318  }
319};
320
321// Unsigned to unsigned narrowing: Only the upper bound can be exceeded for
322// standard limits.
323template <typename Dst, typename Src, template <typename> class Bounds>
324struct DstRangeRelationToSrcRangeImpl<
325    Dst, Src, Bounds, INTEGER_REPRESENTATION_UNSIGNED,
326    INTEGER_REPRESENTATION_UNSIGNED, NUMERIC_RANGE_NOT_CONTAINED> {
327  static constexpr RangeCheck Check(Src value) {
328    using DstLimits = NarrowingRange<Dst, Src, Bounds>;
329    return RangeCheck(
330        DstLimits::lowest() == Dst(0) || value >= DstLimits::lowest(),
331        value <= DstLimits::max());
332  }
333};
334
335// Unsigned to signed: Only the upper bound can be exceeded for standard limits.
336template <typename Dst, typename Src, template <typename> class Bounds>
337struct DstRangeRelationToSrcRangeImpl<
338    Dst, Src, Bounds, INTEGER_REPRESENTATION_SIGNED,
339    INTEGER_REPRESENTATION_UNSIGNED, NUMERIC_RANGE_NOT_CONTAINED> {
340  static constexpr RangeCheck Check(Src value) {
341    using DstLimits = NarrowingRange<Dst, Src, Bounds>;
342    using Promotion = decltype(Src() + Dst());
343    return RangeCheck(DstLimits::lowest() <= Dst(0) ||
344                          static_cast<Promotion>(value) >=
345                              static_cast<Promotion>(DstLimits::lowest()),
346                      static_cast<Promotion>(value) <=
347                          static_cast<Promotion>(DstLimits::max()));
348  }
349};
350
351// Signed to unsigned: The upper boundary may be exceeded for a narrower Dst,
352// and any negative value exceeds the lower boundary for standard limits.
353template <typename Dst, typename Src, template <typename> class Bounds>
354struct DstRangeRelationToSrcRangeImpl<
355    Dst, Src, Bounds, INTEGER_REPRESENTATION_UNSIGNED,
356    INTEGER_REPRESENTATION_SIGNED, NUMERIC_RANGE_NOT_CONTAINED> {
357  static constexpr RangeCheck Check(Src value) {
358    using SrcLimits = std::numeric_limits<Src>;
359    using DstLimits = NarrowingRange<Dst, Src, Bounds>;
360    using Promotion = decltype(Src() + Dst());
361    bool ge_zero = false;
362    // Converting floating-point to integer will discard fractional part, so
363    // values in (-1.0, -0.0) will truncate to 0 and fit in Dst.
364    if (std::is_floating_point<Src>::value) {
365      ge_zero = value > Src(-1);
366    } else {
367      ge_zero = value >= Src(0);
368    }
369    return RangeCheck(
370        ge_zero && (DstLimits::lowest() == 0 ||
371                    static_cast<Dst>(value) >= DstLimits::lowest()),
372        static_cast<Promotion>(SrcLimits::max()) <=
373                static_cast<Promotion>(DstLimits::max()) ||
374            static_cast<Promotion>(value) <=
375                static_cast<Promotion>(DstLimits::max()));
376  }
377};
378
379// Simple wrapper for statically checking if a type's range is contained.
380template <typename Dst, typename Src>
381struct IsTypeInRangeForNumericType {
382  static const bool value = StaticDstRangeRelationToSrcRange<Dst, Src>::value ==
383                            NUMERIC_RANGE_CONTAINED;
384};
385
386template <typename Dst, template <typename> class Bounds = std::numeric_limits,
387          typename Src>
388constexpr RangeCheck DstRangeRelationToSrcRange(Src value) {
389  static_assert(std::is_arithmetic<Src>::value, "Argument must be numeric.");
390  static_assert(std::is_arithmetic<Dst>::value, "Result must be numeric.");
391  static_assert(Bounds<Dst>::lowest() < Bounds<Dst>::max(), "");
392  return DstRangeRelationToSrcRangeImpl<Dst, Src, Bounds>::Check(value);
393}
394
395// Integer promotion templates used by the portable checked integer arithmetic.
396template <size_t Size, bool IsSigned>
397struct IntegerForDigitsAndSign;
398
399#define INTEGER_FOR_DIGITS_AND_SIGN(I)                          \
400  template <>                                                   \
401  struct IntegerForDigitsAndSign<IntegerBitsPlusSign<I>::value, \
402                                 std::is_signed<I>::value> {    \
403    using type = I;                                             \
404  }
405
406INTEGER_FOR_DIGITS_AND_SIGN(int8_t);
407INTEGER_FOR_DIGITS_AND_SIGN(uint8_t);
408INTEGER_FOR_DIGITS_AND_SIGN(int16_t);
409INTEGER_FOR_DIGITS_AND_SIGN(uint16_t);
410INTEGER_FOR_DIGITS_AND_SIGN(int32_t);
411INTEGER_FOR_DIGITS_AND_SIGN(uint32_t);
412INTEGER_FOR_DIGITS_AND_SIGN(int64_t);
413INTEGER_FOR_DIGITS_AND_SIGN(uint64_t);
414#undef INTEGER_FOR_DIGITS_AND_SIGN
415
416// WARNING: We have no IntegerForSizeAndSign<16, *>. If we ever add one to
417// support 128-bit math, then the ArithmeticPromotion template below will need
418// to be updated (or more likely replaced with a decltype expression).
419static_assert(IntegerBitsPlusSign<intmax_t>::value == 64,
420              "Max integer size not supported for this toolchain.");
421
422template <typename Integer, bool IsSigned = std::is_signed<Integer>::value>
423struct TwiceWiderInteger {
424  using type =
425      typename IntegerForDigitsAndSign<IntegerBitsPlusSign<Integer>::value * 2,
426                                       IsSigned>::type;
427};
428
429enum ArithmeticPromotionCategory {
430  LEFT_PROMOTION,  // Use the type of the left-hand argument.
431  RIGHT_PROMOTION  // Use the type of the right-hand argument.
432};
433
434// Determines the type that can represent the largest positive value.
435template <typename Lhs, typename Rhs,
436          ArithmeticPromotionCategory Promotion =
437              (MaxExponent<Lhs>::value > MaxExponent<Rhs>::value)
438                  ? LEFT_PROMOTION
439                  : RIGHT_PROMOTION>
440struct MaxExponentPromotion;
441
442template <typename Lhs, typename Rhs>
443struct MaxExponentPromotion<Lhs, Rhs, LEFT_PROMOTION> {
444  using type = Lhs;
445};
446
447template <typename Lhs, typename Rhs>
448struct MaxExponentPromotion<Lhs, Rhs, RIGHT_PROMOTION> {
449  using type = Rhs;
450};
451
452// Determines the type that can represent the lowest arithmetic value.
453template <typename Lhs, typename Rhs,
454          ArithmeticPromotionCategory Promotion =
455              std::is_signed<Lhs>::value
456                  ? (std::is_signed<Rhs>::value
457                         ? (MaxExponent<Lhs>::value > MaxExponent<Rhs>::value
458                                ? LEFT_PROMOTION
459                                : RIGHT_PROMOTION)
460                         : LEFT_PROMOTION)
461                  : (std::is_signed<Rhs>::value
462                         ? RIGHT_PROMOTION
463                         : (MaxExponent<Lhs>::value < MaxExponent<Rhs>::value
464                                ? LEFT_PROMOTION
465                                : RIGHT_PROMOTION))>
466struct LowestValuePromotion;
467
468template <typename Lhs, typename Rhs>
469struct LowestValuePromotion<Lhs, Rhs, LEFT_PROMOTION> {
470  using type = Lhs;
471};
472
473template <typename Lhs, typename Rhs>
474struct LowestValuePromotion<Lhs, Rhs, RIGHT_PROMOTION> {
475  using type = Rhs;
476};
477
478// Determines the type that is best able to represent an arithmetic result.
479template <
480    typename Lhs, typename Rhs = Lhs,
481    bool is_intmax_type =
482        std::is_integral<typename MaxExponentPromotion<Lhs, Rhs>::type>::value&&
483            IntegerBitsPlusSign<typename MaxExponentPromotion<Lhs, Rhs>::type>::
484                value == IntegerBitsPlusSign<intmax_t>::value,
485    bool is_max_exponent =
486        StaticDstRangeRelationToSrcRange<
487            typename MaxExponentPromotion<Lhs, Rhs>::type, Lhs>::value ==
488        NUMERIC_RANGE_CONTAINED&& StaticDstRangeRelationToSrcRange<
489            typename MaxExponentPromotion<Lhs, Rhs>::type, Rhs>::value ==
490        NUMERIC_RANGE_CONTAINED>
491struct BigEnoughPromotion;
492
493// The side with the max exponent is big enough.
494template <typename Lhs, typename Rhs, bool is_intmax_type>
495struct BigEnoughPromotion<Lhs, Rhs, is_intmax_type, true> {
496  using type = typename MaxExponentPromotion<Lhs, Rhs>::type;
497  static const bool is_contained = true;
498};
499
500// We can use a twice wider type to fit.
501template <typename Lhs, typename Rhs>
502struct BigEnoughPromotion<Lhs, Rhs, false, false> {
503  using type =
504      typename TwiceWiderInteger<typename MaxExponentPromotion<Lhs, Rhs>::type,
505                                 std::is_signed<Lhs>::value ||
506                                     std::is_signed<Rhs>::value>::type;
507  static const bool is_contained = true;
508};
509
510// No type is large enough.
511template <typename Lhs, typename Rhs>
512struct BigEnoughPromotion<Lhs, Rhs, true, false> {
513  using type = typename MaxExponentPromotion<Lhs, Rhs>::type;
514  static const bool is_contained = false;
515};
516
517// We can statically check if operations on the provided types can wrap, so we
518// can skip the checked operations if they're not needed. So, for an integer we
519// care if the destination type preserves the sign and is twice the width of
520// the source.
521template <typename T, typename Lhs, typename Rhs = Lhs>
522struct IsIntegerArithmeticSafe {
523  static const bool value =
524      !std::is_floating_point<T>::value &&
525      !std::is_floating_point<Lhs>::value &&
526      !std::is_floating_point<Rhs>::value &&
527      std::is_signed<T>::value >= std::is_signed<Lhs>::value &&
528      IntegerBitsPlusSign<T>::value >= (2 * IntegerBitsPlusSign<Lhs>::value) &&
529      std::is_signed<T>::value >= std::is_signed<Rhs>::value &&
530      IntegerBitsPlusSign<T>::value >= (2 * IntegerBitsPlusSign<Rhs>::value);
531};
532
533// Promotes to a type that can represent any possible result of a binary
534// arithmetic operation with the source types.
535template <typename Lhs, typename Rhs,
536          bool is_promotion_possible = IsIntegerArithmeticSafe<
537              typename std::conditional<std::is_signed<Lhs>::value ||
538                                            std::is_signed<Rhs>::value,
539                                        intmax_t, uintmax_t>::type,
540              typename MaxExponentPromotion<Lhs, Rhs>::type>::value>
541struct FastIntegerArithmeticPromotion;
542
543template <typename Lhs, typename Rhs>
544struct FastIntegerArithmeticPromotion<Lhs, Rhs, true> {
545  using type =
546      typename TwiceWiderInteger<typename MaxExponentPromotion<Lhs, Rhs>::type,
547                                 std::is_signed<Lhs>::value ||
548                                     std::is_signed<Rhs>::value>::type;
549  static_assert(IsIntegerArithmeticSafe<type, Lhs, Rhs>::value, "");
550  static const bool is_contained = true;
551};
552
553template <typename Lhs, typename Rhs>
554struct FastIntegerArithmeticPromotion<Lhs, Rhs, false> {
555  using type = typename BigEnoughPromotion<Lhs, Rhs>::type;
556  static const bool is_contained = false;
557};
558
559// Extracts the underlying type from an enum.
560template <typename T, bool is_enum = std::is_enum<T>::value>
561struct ArithmeticOrUnderlyingEnum;
562
563template <typename T>
564struct ArithmeticOrUnderlyingEnum<T, true> {
565  using type = typename std::underlying_type<T>::type;
566  static const bool value = std::is_arithmetic<type>::value;
567};
568
569template <typename T>
570struct ArithmeticOrUnderlyingEnum<T, false> {
571  using type = T;
572  static const bool value = std::is_arithmetic<type>::value;
573};
574
575// The following are helper templates used in the CheckedNumeric class.
576template <typename T>
577class CheckedNumeric;
578
579template <typename T>
580class ClampedNumeric;
581
582template <typename T>
583class StrictNumeric;
584
585// Used to treat CheckedNumeric and arithmetic underlying types the same.
586template <typename T>
587struct UnderlyingType {
588  using type = typename ArithmeticOrUnderlyingEnum<T>::type;
589  static const bool is_numeric = std::is_arithmetic<type>::value;
590  static const bool is_checked = false;
591  static const bool is_clamped = false;
592  static const bool is_strict = false;
593};
594
595template <typename T>
596struct UnderlyingType<CheckedNumeric<T>> {
597  using type = T;
598  static const bool is_numeric = true;
599  static const bool is_checked = true;
600  static const bool is_clamped = false;
601  static const bool is_strict = false;
602};
603
604template <typename T>
605struct UnderlyingType<ClampedNumeric<T>> {
606  using type = T;
607  static const bool is_numeric = true;
608  static const bool is_checked = false;
609  static const bool is_clamped = true;
610  static const bool is_strict = false;
611};
612
613template <typename T>
614struct UnderlyingType<StrictNumeric<T>> {
615  using type = T;
616  static const bool is_numeric = true;
617  static const bool is_checked = false;
618  static const bool is_clamped = false;
619  static const bool is_strict = true;
620};
621
622template <typename L, typename R>
623struct IsCheckedOp {
624  static const bool value =
625      UnderlyingType<L>::is_numeric && UnderlyingType<R>::is_numeric &&
626      (UnderlyingType<L>::is_checked || UnderlyingType<R>::is_checked);
627};
628
629template <typename L, typename R>
630struct IsClampedOp {
631  static const bool value =
632      UnderlyingType<L>::is_numeric && UnderlyingType<R>::is_numeric &&
633      (UnderlyingType<L>::is_clamped || UnderlyingType<R>::is_clamped) &&
634      !(UnderlyingType<L>::is_checked || UnderlyingType<R>::is_checked);
635};
636
637template <typename L, typename R>
638struct IsStrictOp {
639  static const bool value =
640      UnderlyingType<L>::is_numeric && UnderlyingType<R>::is_numeric &&
641      (UnderlyingType<L>::is_strict || UnderlyingType<R>::is_strict) &&
642      !(UnderlyingType<L>::is_checked || UnderlyingType<R>::is_checked) &&
643      !(UnderlyingType<L>::is_clamped || UnderlyingType<R>::is_clamped);
644};
645
646// as_signed<> returns the supplied integral value (or integral castable
647// Numeric template) cast as a signed integral of equivalent precision.
648// I.e. it's mostly an alias for: static_cast<std::make_signed<T>::type>(t)
649template <typename Src>
650constexpr typename std::make_signed<
651    typename base::internal::UnderlyingType<Src>::type>::type
652as_signed(const Src value) {
653  static_assert(std::is_integral<decltype(as_signed(value))>::value,
654                "Argument must be a signed or unsigned integer type.");
655  return static_cast<decltype(as_signed(value))>(value);
656}
657
658// as_unsigned<> returns the supplied integral value (or integral castable
659// Numeric template) cast as an unsigned integral of equivalent precision.
660// I.e. it's mostly an alias for: static_cast<std::make_unsigned<T>::type>(t)
661template <typename Src>
662constexpr typename std::make_unsigned<
663    typename base::internal::UnderlyingType<Src>::type>::type
664as_unsigned(const Src value) {
665  static_assert(std::is_integral<decltype(as_unsigned(value))>::value,
666                "Argument must be a signed or unsigned integer type.");
667  return static_cast<decltype(as_unsigned(value))>(value);
668}
669
670template <typename L, typename R>
671constexpr bool IsLessImpl(const L lhs, const R rhs, const RangeCheck l_range,
672                          const RangeCheck r_range) {
673  return l_range.IsUnderflow() || r_range.IsOverflow() ||
674         (l_range == r_range && static_cast<decltype(lhs + rhs)>(lhs) <
675                                    static_cast<decltype(lhs + rhs)>(rhs));
676}
677
678template <typename L, typename R>
679struct IsLess {
680  static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value,
681                "Types must be numeric.");
682  static constexpr bool Test(const L lhs, const R rhs) {
683    return IsLessImpl(lhs, rhs, DstRangeRelationToSrcRange<R>(lhs),
684                      DstRangeRelationToSrcRange<L>(rhs));
685  }
686};
687
688template <typename L, typename R>
689constexpr bool IsLessOrEqualImpl(const L lhs, const R rhs,
690                                 const RangeCheck l_range,
691                                 const RangeCheck r_range) {
692  return l_range.IsUnderflow() || r_range.IsOverflow() ||
693         (l_range == r_range && static_cast<decltype(lhs + rhs)>(lhs) <=
694                                    static_cast<decltype(lhs + rhs)>(rhs));
695}
696
697template <typename L, typename R>
698struct IsLessOrEqual {
699  static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value,
700                "Types must be numeric.");
701  static constexpr bool Test(const L lhs, const R rhs) {
702    return IsLessOrEqualImpl(lhs, rhs, DstRangeRelationToSrcRange<R>(lhs),
703                             DstRangeRelationToSrcRange<L>(rhs));
704  }
705};
706
707template <typename L, typename R>
708constexpr bool IsGreaterImpl(const L lhs, const R rhs, const RangeCheck l_range,
709                             const RangeCheck r_range) {
710  return l_range.IsOverflow() || r_range.IsUnderflow() ||
711         (l_range == r_range && static_cast<decltype(lhs + rhs)>(lhs) >
712                                    static_cast<decltype(lhs + rhs)>(rhs));
713}
714
715template <typename L, typename R>
716struct IsGreater {
717  static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value,
718                "Types must be numeric.");
719  static constexpr bool Test(const L lhs, const R rhs) {
720    return IsGreaterImpl(lhs, rhs, DstRangeRelationToSrcRange<R>(lhs),
721                         DstRangeRelationToSrcRange<L>(rhs));
722  }
723};
724
725template <typename L, typename R>
726constexpr bool IsGreaterOrEqualImpl(const L lhs, const R rhs,
727                                    const RangeCheck l_range,
728                                    const RangeCheck r_range) {
729  return l_range.IsOverflow() || r_range.IsUnderflow() ||
730         (l_range == r_range && static_cast<decltype(lhs + rhs)>(lhs) >=
731                                    static_cast<decltype(lhs + rhs)>(rhs));
732}
733
734template <typename L, typename R>
735struct IsGreaterOrEqual {
736  static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value,
737                "Types must be numeric.");
738  static constexpr bool Test(const L lhs, const R rhs) {
739    return IsGreaterOrEqualImpl(lhs, rhs, DstRangeRelationToSrcRange<R>(lhs),
740                                DstRangeRelationToSrcRange<L>(rhs));
741  }
742};
743
744template <typename L, typename R>
745struct IsEqual {
746  static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value,
747                "Types must be numeric.");
748  static constexpr bool Test(const L lhs, const R rhs) {
749    return DstRangeRelationToSrcRange<R>(lhs) ==
750               DstRangeRelationToSrcRange<L>(rhs) &&
751           static_cast<decltype(lhs + rhs)>(lhs) ==
752               static_cast<decltype(lhs + rhs)>(rhs);
753  }
754};
755
756template <typename L, typename R>
757struct IsNotEqual {
758  static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value,
759                "Types must be numeric.");
760  static constexpr bool Test(const L lhs, const R rhs) {
761    return DstRangeRelationToSrcRange<R>(lhs) !=
762               DstRangeRelationToSrcRange<L>(rhs) ||
763           static_cast<decltype(lhs + rhs)>(lhs) !=
764               static_cast<decltype(lhs + rhs)>(rhs);
765  }
766};
767
768// These perform the actual math operations on the CheckedNumerics.
769// Binary arithmetic operations.
770template <template <typename, typename> class C, typename L, typename R>
771constexpr bool SafeCompare(const L lhs, const R rhs) {
772  static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value,
773                "Types must be numeric.");
774  using Promotion = BigEnoughPromotion<L, R>;
775  using BigType = typename Promotion::type;
776  return Promotion::is_contained
777             // Force to a larger type for speed if both are contained.
778             ? C<BigType, BigType>::Test(
779                   static_cast<BigType>(static_cast<L>(lhs)),
780                   static_cast<BigType>(static_cast<R>(rhs)))
781             // Let the template functions figure it out for mixed types.
782             : C<L, R>::Test(lhs, rhs);
783}
784
785template <typename Dst, typename Src>
786constexpr bool IsMaxInRangeForNumericType() {
787  return IsGreaterOrEqual<Dst, Src>::Test(std::numeric_limits<Dst>::max(),
788                                          std::numeric_limits<Src>::max());
789}
790
791template <typename Dst, typename Src>
792constexpr bool IsMinInRangeForNumericType() {
793  return IsLessOrEqual<Dst, Src>::Test(std::numeric_limits<Dst>::lowest(),
794                                       std::numeric_limits<Src>::lowest());
795}
796
797template <typename Dst, typename Src>
798constexpr Dst CommonMax() {
799  return !IsMaxInRangeForNumericType<Dst, Src>()
800             ? Dst(std::numeric_limits<Dst>::max())
801             : Dst(std::numeric_limits<Src>::max());
802}
803
804template <typename Dst, typename Src>
805constexpr Dst CommonMin() {
806  return !IsMinInRangeForNumericType<Dst, Src>()
807             ? Dst(std::numeric_limits<Dst>::lowest())
808             : Dst(std::numeric_limits<Src>::lowest());
809}
810
811// This is a wrapper to generate return the max or min for a supplied type.
812// If the argument is false, the returned value is the maximum. If true the
813// returned value is the minimum.
814template <typename Dst, typename Src = Dst>
815constexpr Dst CommonMaxOrMin(bool is_min) {
816  return is_min ? CommonMin<Dst, Src>() : CommonMax<Dst, Src>();
817}
818
819}  // namespace internal
820}  // namespace base
821}  // namespace v8
822
823#endif  // V8_BASE_SAFE_CONVERSIONS_IMPL_H_
824