• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
29 namespace v8 {
30 namespace base {
31 namespace 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.
35 template <typename NumericType>
36 struct 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.
44 template <typename NumericType>
45 struct 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 
52 template <typename Integer>
53 struct 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.
59 template <typename T,
60           typename std::enable_if<std::is_signed<T>::value>::type* = nullptr>
IsValueNegative(T value)61 constexpr bool IsValueNegative(T value) {
62   static_assert(std::is_arithmetic<T>::value, "Argument must be numeric.");
63   return value < 0;
64 }
65 
66 template <typename T,
67           typename std::enable_if<!std::is_signed<T>::value>::type* = nullptr>
IsValueNegative(T)68 constexpr 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).
76 template <typename T>
ConditionalNegate(T x,bool is_negative)77 constexpr 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.
87 template <typename T>
SafeUnsignedAbs(T value)88 constexpr 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__)
CanDetectCompileTimeConstant()98 constexpr bool CanDetectCompileTimeConstant() { return true; }
99 template <typename T>
IsCompileTimeConstant(const T v)100 constexpr bool IsCompileTimeConstant(const T v) {
101   return __builtin_constant_p(v);
102 }
103 #else
CanDetectCompileTimeConstant()104 constexpr bool CanDetectCompileTimeConstant() { return false; }
105 template <typename T>
IsCompileTimeConstant(const T)106 constexpr bool IsCompileTimeConstant(const T) {
107   return false;
108 }
109 #endif
110 template <typename T>
MustTreatAsConstexpr(const T v)111 constexpr 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.
120 struct CheckOnFailure {
121   template <typename T>
HandleFailureCheckOnFailure122   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 
134 enum 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.
144 enum 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 
152 template <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>
159 struct StaticDstRangeRelationToSrcRange;
160 
161 // Same sign: Dst is guaranteed to contain Src only if its range is equal or
162 // larger.
163 template <typename Dst, typename Src, IntegerRepresentation Sign>
164 struct 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.
173 template <typename Dst, typename Src>
174 struct 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.
185 template <typename Dst, typename Src>
186 struct 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.
195 class 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.
242 template <typename Dst, typename Src, template <typename> class Bounds>
243 struct 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 
278 template <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>
287 struct 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.
294 template <typename Dst, typename Src, template <typename> class Bounds,
295           IntegerRepresentation DstSign, IntegerRepresentation SrcSign>
296 struct 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.
311 template <typename Dst, typename Src, template <typename> class Bounds>
312 struct 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.
323 template <typename Dst, typename Src, template <typename> class Bounds>
324 struct 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.
336 template <typename Dst, typename Src, template <typename> class Bounds>
337 struct 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.
353 template <typename Dst, typename Src, template <typename> class Bounds>
354 struct 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.
380 template <typename Dst, typename Src>
381 struct IsTypeInRangeForNumericType {
382   static const bool value = StaticDstRangeRelationToSrcRange<Dst, Src>::value ==
383                             NUMERIC_RANGE_CONTAINED;
384 };
385 
386 template <typename Dst, template <typename> class Bounds = std::numeric_limits,
387           typename Src>
388 constexpr 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.
396 template <size_t Size, bool IsSigned>
397 struct 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 
406 INTEGER_FOR_DIGITS_AND_SIGN(int8_t);
407 INTEGER_FOR_DIGITS_AND_SIGN(uint8_t);
408 INTEGER_FOR_DIGITS_AND_SIGN(int16_t);
409 INTEGER_FOR_DIGITS_AND_SIGN(uint16_t);
410 INTEGER_FOR_DIGITS_AND_SIGN(int32_t);
411 INTEGER_FOR_DIGITS_AND_SIGN(uint32_t);
412 INTEGER_FOR_DIGITS_AND_SIGN(int64_t);
413 INTEGER_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).
419 static_assert(IntegerBitsPlusSign<intmax_t>::value == 64,
420               "Max integer size not supported for this toolchain.");
421 
422 template <typename Integer, bool IsSigned = std::is_signed<Integer>::value>
423 struct TwiceWiderInteger {
424   using type =
425       typename IntegerForDigitsAndSign<IntegerBitsPlusSign<Integer>::value * 2,
426                                        IsSigned>::type;
427 };
428 
429 enum 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.
435 template <typename Lhs, typename Rhs,
436           ArithmeticPromotionCategory Promotion =
437               (MaxExponent<Lhs>::value > MaxExponent<Rhs>::value)
438                   ? LEFT_PROMOTION
439                   : RIGHT_PROMOTION>
440 struct MaxExponentPromotion;
441 
442 template <typename Lhs, typename Rhs>
443 struct MaxExponentPromotion<Lhs, Rhs, LEFT_PROMOTION> {
444   using type = Lhs;
445 };
446 
447 template <typename Lhs, typename Rhs>
448 struct MaxExponentPromotion<Lhs, Rhs, RIGHT_PROMOTION> {
449   using type = Rhs;
450 };
451 
452 // Determines the type that can represent the lowest arithmetic value.
453 template <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))>
466 struct LowestValuePromotion;
467 
468 template <typename Lhs, typename Rhs>
469 struct LowestValuePromotion<Lhs, Rhs, LEFT_PROMOTION> {
470   using type = Lhs;
471 };
472 
473 template <typename Lhs, typename Rhs>
474 struct LowestValuePromotion<Lhs, Rhs, RIGHT_PROMOTION> {
475   using type = Rhs;
476 };
477 
478 // Determines the type that is best able to represent an arithmetic result.
479 template <
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>
491 struct BigEnoughPromotion;
492 
493 // The side with the max exponent is big enough.
494 template <typename Lhs, typename Rhs, bool is_intmax_type>
495 struct 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.
501 template <typename Lhs, typename Rhs>
502 struct 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.
511 template <typename Lhs, typename Rhs>
512 struct 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.
521 template <typename T, typename Lhs, typename Rhs = Lhs>
522 struct 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.
535 template <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>
541 struct FastIntegerArithmeticPromotion;
542 
543 template <typename Lhs, typename Rhs>
544 struct 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 
553 template <typename Lhs, typename Rhs>
554 struct 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.
560 template <typename T, bool is_enum = std::is_enum<T>::value>
561 struct ArithmeticOrUnderlyingEnum;
562 
563 template <typename T>
564 struct ArithmeticOrUnderlyingEnum<T, true> {
565   using type = typename std::underlying_type<T>::type;
566   static const bool value = std::is_arithmetic<type>::value;
567 };
568 
569 template <typename T>
570 struct 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.
576 template <typename T>
577 class CheckedNumeric;
578 
579 template <typename T>
580 class ClampedNumeric;
581 
582 template <typename T>
583 class StrictNumeric;
584 
585 // Used to treat CheckedNumeric and arithmetic underlying types the same.
586 template <typename T>
587 struct 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 
595 template <typename T>
596 struct 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 
604 template <typename T>
605 struct 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 
613 template <typename T>
614 struct 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 
622 template <typename L, typename R>
623 struct 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 
629 template <typename L, typename R>
630 struct 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 
637 template <typename L, typename R>
638 struct 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)
649 template <typename Src>
650 constexpr typename std::make_signed<
651     typename base::internal::UnderlyingType<Src>::type>::type
652 as_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)
661 template <typename Src>
662 constexpr typename std::make_unsigned<
663     typename base::internal::UnderlyingType<Src>::type>::type
664 as_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 
670 template <typename L, typename R>
671 constexpr 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 
678 template <typename L, typename R>
679 struct 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 
688 template <typename L, typename R>
689 constexpr 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 
697 template <typename L, typename R>
698 struct 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 
707 template <typename L, typename R>
708 constexpr 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 
715 template <typename L, typename R>
716 struct 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 
725 template <typename L, typename R>
726 constexpr 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 
734 template <typename L, typename R>
735 struct 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 
744 template <typename L, typename R>
745 struct 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 
756 template <typename L, typename R>
757 struct 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.
770 template <template <typename, typename> class C, typename L, typename R>
771 constexpr 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 
785 template <typename Dst, typename Src>
786 constexpr bool IsMaxInRangeForNumericType() {
787   return IsGreaterOrEqual<Dst, Src>::Test(std::numeric_limits<Dst>::max(),
788                                           std::numeric_limits<Src>::max());
789 }
790 
791 template <typename Dst, typename Src>
792 constexpr bool IsMinInRangeForNumericType() {
793   return IsLessOrEqual<Dst, Src>::Test(std::numeric_limits<Dst>::lowest(),
794                                        std::numeric_limits<Src>::lowest());
795 }
796 
797 template <typename Dst, typename Src>
798 constexpr Dst CommonMax() {
799   return !IsMaxInRangeForNumericType<Dst, Src>()
800              ? Dst(std::numeric_limits<Dst>::max())
801              : Dst(std::numeric_limits<Src>::max());
802 }
803 
804 template <typename Dst, typename Src>
805 constexpr 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.
814 template <typename Dst, typename Src = Dst>
815 constexpr 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