v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
safe_conversions_impl.h
Go to the documentation of this file.
1// Copyright 2014 The Chromium Authors
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 2025 the V8 project authors. All rights reserved.
7
8#ifndef V8_BASE_NUMERICS_SAFE_CONVERSIONS_IMPL_H_
9#define V8_BASE_NUMERICS_SAFE_CONVERSIONS_IMPL_H_
10
11// IWYU pragma: private, include "src/base/numerics/safe_conversions.h"
12
13#include <stddef.h>
14#include <stdint.h>
15
16#include <concepts>
17#include <limits>
18#include <type_traits>
19#include <utility>
20
22
23namespace v8::base::internal {
24
25// The std library doesn't provide a binary max_exponent for integers, however
26// we can compute an analog using std::numeric_limits<>::digits.
27template <typename NumericType>
28inline constexpr int kMaxExponent =
29 std::is_floating_point_v<NumericType>
30 ? std::numeric_limits<NumericType>::max_exponent
31 : std::numeric_limits<NumericType>::digits + 1;
32
33// The number of bits (including the sign) in an integer. Eliminates sizeof
34// hacks.
35template <typename NumericType>
36inline constexpr int kIntegerBitsPlusSign =
37 std::numeric_limits<NumericType>::digits + std::is_signed_v<NumericType>;
38
39// Determines if a numeric value is negative without throwing compiler
40// warnings on: unsigned(value) < 0.
41template <typename T>
42 requires(std::is_arithmetic_v<T>)
43constexpr bool IsValueNegative(T value) {
44 if constexpr (std::is_signed_v<T>) {
45 return value < 0;
46 } else {
47 return false;
48 }
49}
50
51// This performs a fast negation, returning a signed value. It works on unsigned
52// arguments, but probably doesn't do what you want for any unsigned value
53// larger than max / 2 + 1 (i.e. signed min cast to unsigned).
54template <typename T>
55 requires std::is_integral_v<T>
56constexpr auto ConditionalNegate(T x, bool is_negative) {
57 using SignedT = std::make_signed_t<T>;
58 using UnsignedT = std::make_unsigned_t<T>;
59 return static_cast<SignedT>((static_cast<UnsignedT>(x) ^
60 static_cast<UnsignedT>(-SignedT(is_negative))) +
61 is_negative);
62}
63
64// This performs a safe, absolute value via unsigned overflow.
65template <typename T>
66 requires std::is_integral_v<T>
67constexpr auto SafeUnsignedAbs(T value) {
68 using UnsignedT = std::make_unsigned_t<T>;
69 return IsValueNegative(value)
70 ? static_cast<UnsignedT>(0u - static_cast<UnsignedT>(value))
71 : static_cast<UnsignedT>(value);
72}
73
74// TODO(jschuh): Debug builds don't reliably propagate constants, so we restrict
75// some accelerated runtime paths to release builds until this can be forced
76// with consteval support in C++20 or C++23.
77#if defined(NDEBUG)
78inline constexpr bool kEnableAsmCode = true;
79#else
80inline constexpr bool kEnableAsmCode = false;
81#endif
82
83// Forces a crash, like a NOTREACHED(). Used for numeric boundary errors.
84// Also used in a constexpr template to trigger a compilation failure on
85// an error condition.
87 template <typename T>
88 static T HandleFailure() {
89#if defined(_MSC_VER)
90 __debugbreak();
91#elif defined(__GNUC__) || defined(__clang__)
92 __builtin_trap();
93#else
94 ((void)(*(volatile char*)0 = 0));
95#endif
96 return T();
97 }
98};
99
101
102// A range for a given nunmeric Src type is contained for a given numeric Dst
103// type if both numeric_limits<Src>::max() <= numeric_limits<Dst>::max() and
104// numeric_limits<Src>::lowest() >= numeric_limits<Dst>::lowest() are true.
105// We implement this as template specializations rather than simple static
106// comparisons to ensure type correctness in our comparisons.
108
109// Helper templates to statically determine if our destination type can contain
110// maximum and minimum values represented by the source type.
111
112// Default case, used for same sign: Dst is guaranteed to contain Src only if
113// its range is equal or larger.
114template <typename Dst, typename Src,
115 IntegerRepresentation DstSign =
116 std::is_signed_v<Dst> ? IntegerRepresentation::kSigned
118 IntegerRepresentation SrcSign =
119 std::is_signed_v<Src> ? IntegerRepresentation::kSigned
125
126// Unsigned to signed: Dst is guaranteed to contain source only if its range is
127// larger.
128template <typename Dst, typename Src>
129inline constexpr auto
135
136// Signed to unsigned: Dst cannot be statically determined to contain Src.
137template <typename Dst, typename Src>
138inline constexpr auto kStaticDstRangeRelationToSrcRange<
141
142// This class wraps the range constraints as separate booleans so the compiler
143// can identify constants and eliminate unused code paths.
145 public:
146 constexpr RangeCheck() = default;
147 constexpr RangeCheck(bool is_in_lower_bound, bool is_in_upper_bound)
148 : is_underflow_(!is_in_lower_bound), is_overflow_(!is_in_upper_bound) {}
149
150 constexpr bool operator==(const RangeCheck& rhs) const = default;
151
152 constexpr bool IsValid() const { return !is_overflow_ && !is_underflow_; }
153 constexpr bool IsInvalid() const { return is_overflow_ && is_underflow_; }
154 constexpr bool IsOverflow() const { return is_overflow_ && !is_underflow_; }
155 constexpr bool IsUnderflow() const { return !is_overflow_ && is_underflow_; }
156 constexpr bool IsOverflowFlagSet() const { return is_overflow_; }
157 constexpr bool IsUnderflowFlagSet() const { return is_underflow_; }
158
159 private:
160 // Do not change the order of these member variables. The integral conversion
161 // optimization depends on this exact order.
162 const bool is_underflow_ = false;
163 const bool is_overflow_ = false;
164};
165
166// The following helper template addresses a corner case in range checks for
167// conversion from a floating-point type to an integral type of smaller range
168// but larger precision (e.g. float -> unsigned). The problem is as follows:
169// 1. Integral maximum is always one less than a power of two, so it must be
170// truncated to fit the mantissa of the floating point. The direction of
171// rounding is implementation defined, but by default it's always IEEE
172// floats, which round to nearest and thus result in a value of larger
173// magnitude than the integral value.
174// Example: float f = UINT_MAX; // f is 4294967296f but UINT_MAX
175// // is 4294967295u.
176// 2. If the floating point value is equal to the promoted integral maximum
177// value, a range check will erroneously pass.
178// Example: (4294967296f <= 4294967295u) // This is true due to a precision
179// // loss in rounding up to float.
180// 3. When the floating point value is then converted to an integral, the
181// resulting value is out of range for the target integral type and
182// thus is implementation defined.
183// Example: unsigned u = (float)INT_MAX; // u will typically overflow to 0.
184// To fix this bug we manually truncate the maximum value when the destination
185// type is an integral of larger precision than the source floating-point type,
186// such that the resulting maximum is represented exactly as a floating point.
187template <typename Dst, typename Src, template <typename> class Bounds>
189 using SrcLimits = std::numeric_limits<Src>;
190 using DstLimits = std::numeric_limits<Dst>;
191
192 // Computes the mask required to make an accurate comparison between types.
193 static constexpr int kShift = (kMaxExponent<Src> > kMaxExponent<Dst> &&
194 SrcLimits::digits < DstLimits::digits)
195 ? (DstLimits::digits - SrcLimits::digits)
196 : 0;
197
198 template <typename T>
199 requires(std::same_as<T, Dst> &&
200 ((std::integral<T> && kShift < DstLimits::digits) ||
201 (std::floating_point<T> && kShift == 0)))
202 // Masks out the integer bits that are beyond the precision of the
203 // intermediate type used for comparison.
204 static constexpr T Adjust(T value) {
205 if constexpr (std::integral<T>) {
206 using UnsignedDst = typename std::make_unsigned_t<T>;
207 return static_cast<T>(
209 ~((UnsignedDst{1} << kShift) - UnsignedDst{1}),
210 IsValueNegative(value)));
211 } else {
212 return value;
213 }
214 }
215
216 static constexpr Dst max() { return Adjust(Bounds<Dst>::max()); }
217 static constexpr Dst lowest() { return Adjust(Bounds<Dst>::lowest()); }
218};
219
220// The following templates are for ranges that must be verified at runtime. We
221// split it into checks based on signedness to avoid confusing casts and
222// compiler warnings on signed an unsigned comparisons.
223
224// Default case, used for same sign narrowing: The range is contained for normal
225// limits.
226template <typename Dst, typename Src, template <typename> class Bounds,
227 IntegerRepresentation DstSign =
228 std::is_signed_v<Dst> ? IntegerRepresentation::kSigned
230 IntegerRepresentation SrcSign =
231 std::is_signed_v<Src> ? IntegerRepresentation::kSigned
236 static constexpr RangeCheck Check(Src value) {
237 using SrcLimits = std::numeric_limits<Src>;
238 using DstLimits = NarrowingRange<Dst, Src, Bounds>;
239 return RangeCheck(
240 static_cast<Dst>(SrcLimits::lowest()) >= DstLimits::lowest() ||
241 static_cast<Dst>(value) >= DstLimits::lowest(),
242 static_cast<Dst>(SrcLimits::max()) <= DstLimits::max() ||
243 static_cast<Dst>(value) <= DstLimits::max());
244 }
245};
246
247// Signed to signed narrowing: Both the upper and lower boundaries may be
248// exceeded for standard limits.
249template <typename Dst, typename Src, template <typename> class Bounds>
251 Dst, Src, Bounds, IntegerRepresentation::kSigned,
253 static constexpr RangeCheck Check(Src value) {
254 using DstLimits = NarrowingRange<Dst, Src, Bounds>;
255 return RangeCheck(value >= DstLimits::lowest(), value <= DstLimits::max());
256 }
257};
258
259// Unsigned to unsigned narrowing: Only the upper bound can be exceeded for
260// standard limits.
261template <typename Dst, typename Src, template <typename> class Bounds>
263 Dst, Src, Bounds, IntegerRepresentation::kUnsigned,
266 static constexpr RangeCheck Check(Src value) {
267 using DstLimits = NarrowingRange<Dst, Src, Bounds>;
268 return RangeCheck(
269 DstLimits::lowest() == Dst{0} || value >= DstLimits::lowest(),
270 value <= DstLimits::max());
271 }
272};
273
274// Unsigned to signed: Only the upper bound can be exceeded for standard limits.
275template <typename Dst, typename Src, template <typename> class Bounds>
277 Dst, Src, Bounds, IntegerRepresentation::kSigned,
280 static constexpr RangeCheck Check(Src value) {
281 using DstLimits = NarrowingRange<Dst, Src, Bounds>;
282 using Promotion = decltype(Src() + Dst());
283 return RangeCheck(DstLimits::lowest() <= Dst{0} ||
284 static_cast<Promotion>(value) >=
285 static_cast<Promotion>(DstLimits::lowest()),
286 static_cast<Promotion>(value) <=
287 static_cast<Promotion>(DstLimits::max()));
288 }
289};
290
291// Signed to unsigned: The upper boundary may be exceeded for a narrower Dst,
292// and any negative value exceeds the lower boundary for standard limits.
293template <typename Dst, typename Src, template <typename> class Bounds>
295 Dst, Src, Bounds, IntegerRepresentation::kUnsigned,
297 static constexpr RangeCheck Check(Src value) {
298 using SrcLimits = std::numeric_limits<Src>;
299 using DstLimits = NarrowingRange<Dst, Src, Bounds>;
300 using Promotion = decltype(Src() + Dst());
301 bool ge_zero;
302 // Converting floating-point to integer will discard fractional part, so
303 // values in (-1.0, -0.0) will truncate to 0 and fit in Dst.
304 if constexpr (std::is_floating_point_v<Src>) {
305 ge_zero = value > Src{-1};
306 } else {
307 ge_zero = value >= Src{0};
308 }
309 return RangeCheck(
310 ge_zero && (DstLimits::lowest() == 0 ||
311 static_cast<Dst>(value) >= DstLimits::lowest()),
312 static_cast<Promotion>(SrcLimits::max()) <=
313 static_cast<Promotion>(DstLimits::max()) ||
314 static_cast<Promotion>(value) <=
315 static_cast<Promotion>(DstLimits::max()));
316 }
317};
318
319// Simple wrapper for statically checking if a type's range is contained.
320template <typename Dst, typename Src>
321inline constexpr bool kIsTypeInRangeForNumericType =
324
325template <typename Dst, template <typename> class Bounds = std::numeric_limits,
326 typename Src>
327 requires(std::is_arithmetic_v<Src> && std::is_arithmetic_v<Dst> &&
328 Bounds<Dst>::lowest() < Bounds<Dst>::max())
332
333// Integer promotion templates used by the portable checked integer arithmetic.
334template <size_t Size, bool IsSigned>
336
337#define INTEGER_FOR_DIGITS_AND_SIGN(I) \
338 template <> \
339 struct IntegerForDigitsAndSignImpl<kIntegerBitsPlusSign<I>, \
340 std::is_signed_v<I>> { \
341 using type = I; \
342 }
343
352#undef INTEGER_FOR_DIGITS_AND_SIGN
353
354template <size_t Size, bool IsSigned>
357
358// WARNING: We have no IntegerForSizeAndSign<16, *>. If we ever add one to
359// support 128-bit math, then the ArithmeticPromotion template below will need
360// to be updated (or more likely replaced with a decltype expression).
361static_assert(kIntegerBitsPlusSign<intmax_t> == 64,
362 "Max integer size not supported for this toolchain.");
363
364template <typename Integer, bool IsSigned = std::is_signed_v<Integer>>
367
368// Determines the type that can represent the largest positive value.
369template <typename Lhs, typename Rhs>
371 std::conditional_t<(kMaxExponent<Lhs> > kMaxExponent<Rhs>), Lhs, Rhs>;
372
373// Determines the type that can represent the lowest arithmetic value.
374template <typename Lhs, typename Rhs>
375using LowestValuePromotion = std::conditional_t<
376 std::is_signed_v<Lhs>
377 ? (!std::is_signed_v<Rhs> || kMaxExponent<Lhs> > kMaxExponent<Rhs>)
378 : (!std::is_signed_v<Rhs> && kMaxExponent<Lhs> < kMaxExponent<Rhs>),
379 Lhs, Rhs>;
380
381// Determines the type that is best able to represent an arithmetic result.
382
383// Default case, used when the side with the max exponent is big enough.
384template <typename Lhs, typename Rhs = Lhs,
385 bool is_intmax_type =
386 std::is_integral_v<MaxExponentPromotion<Lhs, Rhs>> &&
389 bool is_max_exponent = kStaticDstRangeRelationToSrcRange<
397 static constexpr bool kContained = true;
398};
399
400// We can use a twice wider type to fit.
401template <typename Lhs, typename Rhs>
402struct BigEnoughPromotionImpl<Lhs, Rhs, false, false> {
403 using type =
405 std::is_signed_v<Lhs> || std::is_signed_v<Rhs>>;
406 static constexpr bool kContained = true;
407};
408
409// No type is large enough.
410template <typename Lhs, typename Rhs>
411struct BigEnoughPromotionImpl<Lhs, Rhs, true, false> {
413 static constexpr bool kContained = false;
414};
415
416template <typename Lhs, typename Rhs>
418
419template <typename Lhs, typename Rhs>
420inline constexpr bool kIsBigEnoughPromotionContained =
422
423// We can statically check if operations on the provided types can wrap, so we
424// can skip the checked operations if they're not needed. So, for an integer we
425// care if the destination type preserves the sign and is twice the width of
426// the source.
427template <typename T, typename Lhs, typename Rhs = Lhs>
428inline constexpr bool kIsIntegerArithmeticSafe =
429 !std::is_floating_point_v<T> && !std::is_floating_point_v<Lhs> &&
430 !std::is_floating_point_v<Rhs> &&
431 std::is_signed_v<T> >= std::is_signed_v<Lhs> &&
433 std::is_signed_v<T> >= std::is_signed_v<Rhs> &&
435
436// Promotes to a type that can represent any possible result of a binary
437// arithmetic operation with the source types.
438template <typename Lhs, typename Rhs>
441 static constexpr bool kContained = false;
442};
443
444template <typename Lhs, typename Rhs>
446 std::conditional_t<std::is_signed_v<Lhs> || std::is_signed_v<Rhs>,
447 intmax_t, uintmax_t>,
450 using type =
452 std::is_signed_v<Lhs> || std::is_signed_v<Rhs>>;
454 static constexpr bool kContained = true;
455};
456
457template <typename Lhs, typename Rhs>
460
461template <typename Lhs, typename Rhs>
464
465template <typename T>
469
470template <typename T>
473 using type = T::value_type;
474};
475
476// Extracts the underlying type from an enum.
477template <typename T>
479 typename std::conditional_t<std::is_enum_v<T>, std::underlying_type<T>,
481
482// The following are helper templates used in the CheckedNumeric class.
483template <typename T>
484 requires std::is_arithmetic_v<T>
486
487template <typename T>
488 requires std::is_arithmetic_v<T>
490
491template <typename T>
492 requires std::is_arithmetic_v<T>
494
495// Used to treat CheckedNumeric and arithmetic underlying types the same.
496template <typename T>
497inline constexpr bool kIsCheckedNumeric = false;
498template <typename T>
499inline constexpr bool kIsCheckedNumeric<CheckedNumeric<T>> = true;
500template <typename T>
502
503template <typename T>
504inline constexpr bool kIsClampedNumeric = false;
505template <typename T>
506inline constexpr bool kIsClampedNumeric<ClampedNumeric<T>> = true;
507template <typename T>
509
510template <typename T>
511inline constexpr bool kIsStrictNumeric = false;
512template <typename T>
513inline constexpr bool kIsStrictNumeric<StrictNumeric<T>> = true;
514template <typename T>
516
517template <typename T>
521template <typename T>
523 using type = T;
524};
525template <typename T>
527 using type = T;
528};
529template <typename T>
531 using type = T;
532};
533template <typename T>
535
536template <typename T>
537inline constexpr bool kIsNumeric = std::is_arithmetic_v<UnderlyingType<T>>;
538template <typename T>
540inline constexpr bool kIsNumeric<T> = true;
541template <typename T>
543
544template <typename L, typename R>
547
548template <typename L, typename R>
549concept IsClampedOp =
552
553template <typename L, typename R>
557
558// as_signed<> returns the supplied integral value (or integral castable
559// Numeric template) cast as a signed integral of equivalent precision.
560// I.e. it's mostly an alias for: static_cast<std::make_signed<T>::type>(t)
561template <typename Src, typename Dst = std::make_signed_t<UnderlyingType<Src>>>
562 requires std::integral<Dst>
563constexpr auto as_signed(Src value) {
564 return static_cast<Dst>(value);
565}
566
567// as_unsigned<> returns the supplied integral value (or integral castable
568// Numeric template) cast as an unsigned integral of equivalent precision.
569// I.e. it's mostly an alias for: static_cast<std::make_unsigned<T>::type>(t)
570template <typename Src,
571 typename Dst = std::make_unsigned_t<UnderlyingType<Src>>>
572 requires std::integral<Dst>
573constexpr auto as_unsigned(Src value) {
574 return static_cast<Dst>(value);
575}
576
577template <typename L, typename R>
578 requires std::is_arithmetic_v<L> && std::is_arithmetic_v<R>
579struct IsLess {
580 using SumT = decltype(std::declval<L>() + std::declval<R>());
581 static constexpr bool Test(L lhs, R rhs) {
582 const RangeCheck l_range = DstRangeRelationToSrcRange<R>(lhs);
583 const RangeCheck r_range = DstRangeRelationToSrcRange<L>(rhs);
584 return l_range.IsUnderflow() || r_range.IsOverflow() ||
585 (l_range == r_range &&
586 static_cast<SumT>(lhs) < static_cast<SumT>(rhs));
587 }
588};
589
590template <typename L, typename R>
591 requires std::is_arithmetic_v<L> && std::is_arithmetic_v<R>
593 using SumT = decltype(std::declval<L>() + std::declval<R>());
594 static constexpr bool Test(L lhs, R rhs) {
595 const RangeCheck l_range = DstRangeRelationToSrcRange<R>(lhs);
596 const RangeCheck r_range = DstRangeRelationToSrcRange<L>(rhs);
597 return l_range.IsUnderflow() || r_range.IsOverflow() ||
598 (l_range == r_range &&
599 static_cast<SumT>(lhs) <= static_cast<SumT>(rhs));
600 }
601};
602
603template <typename L, typename R>
604 requires std::is_arithmetic_v<L> && std::is_arithmetic_v<R>
605struct IsGreater {
606 using SumT = decltype(std::declval<L>() + std::declval<R>());
607 static constexpr bool Test(L lhs, R rhs) {
608 const RangeCheck l_range = DstRangeRelationToSrcRange<R>(lhs);
609 const RangeCheck r_range = DstRangeRelationToSrcRange<L>(rhs);
610 return l_range.IsOverflow() || r_range.IsUnderflow() ||
611 (l_range == r_range &&
612 static_cast<SumT>(lhs) > static_cast<SumT>(rhs));
613 }
614};
615
616template <typename L, typename R>
617 requires std::is_arithmetic_v<L> && std::is_arithmetic_v<R>
619 using SumT = decltype(std::declval<L>() + std::declval<R>());
620 static constexpr bool Test(L lhs, R rhs) {
621 const RangeCheck l_range = DstRangeRelationToSrcRange<R>(lhs);
622 const RangeCheck r_range = DstRangeRelationToSrcRange<L>(rhs);
623 return l_range.IsOverflow() || r_range.IsUnderflow() ||
624 (l_range == r_range &&
625 static_cast<SumT>(lhs) >= static_cast<SumT>(rhs));
626 }
627};
628
629template <typename L, typename R>
630 requires std::is_arithmetic_v<L> && std::is_arithmetic_v<R>
631struct IsEqual {
632 using SumT = decltype(std::declval<L>() + std::declval<R>());
633 static constexpr bool Test(L lhs, R rhs) {
634 return DstRangeRelationToSrcRange<R>(lhs) ==
636 static_cast<SumT>(lhs) == static_cast<SumT>(rhs);
637 }
638};
639
640template <typename L, typename R>
641 requires std::is_arithmetic_v<L> && std::is_arithmetic_v<R>
643 using SumT = decltype(std::declval<L>() + std::declval<R>());
644 static constexpr bool Test(L lhs, R rhs) {
645 return DstRangeRelationToSrcRange<R>(lhs) !=
647 static_cast<SumT>(lhs) != static_cast<SumT>(rhs);
648 }
649};
650
651// These perform the actual math operations on the CheckedNumerics.
652// Binary arithmetic operations.
653template <template <typename, typename> typename C, typename L, typename R>
654 requires std::is_arithmetic_v<L> && std::is_arithmetic_v<R>
655constexpr bool SafeCompare(L lhs, R rhs) {
656 using BigType = BigEnoughPromotion<L, R>;
658 // Force to a larger type for speed if both are contained.
659 ? C<BigType, BigType>::Test(static_cast<BigType>(lhs),
660 static_cast<BigType>(rhs))
661 // Let the template functions figure it out for mixed types.
662 : C<L, R>::Test(lhs, rhs);
663}
664
665template <typename Dst, typename Src>
666inline constexpr bool kIsMaxInRangeForNumericType =
667 IsGreaterOrEqual<Dst, Src>::Test(std::numeric_limits<Dst>::max(),
668 std::numeric_limits<Src>::max());
669
670template <typename Dst, typename Src>
671inline constexpr bool kIsMinInRangeForNumericType =
672 IsLessOrEqual<Dst, Src>::Test(std::numeric_limits<Dst>::lowest(),
673 std::numeric_limits<Src>::lowest());
674
675template <typename Dst, typename Src>
676inline constexpr Dst kCommonMax =
678 ? static_cast<Dst>(std::numeric_limits<Src>::max())
679 : std::numeric_limits<Dst>::max();
680
681template <typename Dst, typename Src>
682inline constexpr Dst kCommonMin =
684 ? static_cast<Dst>(std::numeric_limits<Src>::lowest())
685 : std::numeric_limits<Dst>::lowest();
686
687// This is a wrapper to generate return the max or min for a supplied type.
688// If the argument is false, the returned value is the maximum. If true the
689// returned value is the minimum.
690template <typename Dst, typename Src = Dst>
691constexpr Dst CommonMaxOrMin(bool is_min) {
693}
694
695} // namespace v8::base::internal
696
697#endif // V8_BASE_NUMERICS_SAFE_CONVERSIONS_IMPL_H_
#define T
constexpr bool IsUnderflowFlagSet() const
constexpr bool IsOverflowFlagSet() const
constexpr RangeCheck(bool is_in_lower_bound, bool is_in_upper_bound)
constexpr RangeCheck()=default
constexpr bool operator==(const RangeCheck &rhs) const =default
int x
STL namespace.
constexpr auto ConditionalNegate(T x, bool is_negative)
constexpr auto SafeUnsignedAbs(T value)
constexpr bool IsValueNegative(T value)
constexpr bool kEnableAsmCode
constexpr bool SafeCompare(L lhs, R rhs)
constexpr int kIntegerBitsPlusSign
constexpr auto kStaticDstRangeRelationToSrcRange
constexpr auto as_unsigned(Src value)
UnderlyingTypeImpl< T >::type UnderlyingType
constexpr bool kIsClampedNumeric
BigEnoughPromotionImpl< Lhs, Rhs >::type BigEnoughPromotion
constexpr bool kIsBigEnoughPromotionContained
constexpr bool kIsCheckedNumeric
constexpr bool kIsMaxInRangeForNumericType
constexpr bool kIsIntegerArithmeticSafe
std::conditional_t< std::is_signed_v< Lhs > ?(!std::is_signed_v< Rhs >||kMaxExponent< Lhs > > kMaxExponent< Rhs >) :(!std::is_signed_v< Rhs > &&kMaxExponent< Lhs >< kMaxExponent< Rhs >), Lhs, Rhs > LowestValuePromotion
constexpr bool kIsFastIntegerArithmeticPromotionContained
IntegerForDigitsAndSign< kIntegerBitsPlusSign< Integer > *2, IsSigned > TwiceWiderInteger
constexpr bool kIsStrictNumeric
constexpr Dst CommonMaxOrMin(bool is_min)
FastIntegerArithmeticPromotionImpl< Lhs, Rhs >::type FastIntegerArithmeticPromotion
typename std::conditional_t< std::is_enum_v< T >, std::underlying_type< T >, ArithmeticOrIntegralConstant< T > >::type ArithmeticOrUnderlyingEnum
constexpr RangeCheck DstRangeRelationToSrcRange(Src value)
constexpr auto as_signed(Src value)
constexpr bool kIsMinInRangeForNumericType
std::conditional_t<(kMaxExponent< Lhs > > kMaxExponent< Rhs >), Lhs, Rhs > MaxExponentPromotion
IntegerForDigitsAndSignImpl< Size, IsSigned >::type IntegerForDigitsAndSign
#define INTEGER_FOR_DIGITS_AND_SIGN(I)
TwiceWiderInteger< MaxExponentPromotion< Lhs, Rhs >, std::is_signed_v< Lhs >||std::is_signed_v< Rhs > > type
TwiceWiderInteger< MaxExponentPromotion< Lhs, Rhs >, std::is_signed_v< Lhs >||std::is_signed_v< Rhs > > type
decltype(std::declval< L >()+std::declval< R >()) SumT
static constexpr bool Test(L lhs, R rhs)
static constexpr bool Test(L lhs, R rhs)
decltype(std::declval< L >()+std::declval< R >()) SumT
static constexpr bool Test(L lhs, R rhs)
decltype(std::declval< L >()+std::declval< R >()) SumT
static constexpr bool Test(L lhs, R rhs)
decltype(std::declval< L >()+std::declval< R >()) SumT
static constexpr bool Test(L lhs, R rhs)
decltype(std::declval< L >()+std::declval< R >()) SumT
static constexpr bool Test(L lhs, R rhs)
decltype(std::declval< L >()+std::declval< R >()) SumT
std::unique_ptr< ValueMirror > value
wasm::ValueType type