Chromium Code Reviews| Index: base/numerics/checked_math_impl.h |
| diff --git a/base/numerics/checked_math_impl.h b/base/numerics/checked_math_impl.h |
| index 0492d1dbb5540e4f7a90a51bf4217a0a07388b27..203b286282b2c779cc13fa4292e529a8ae4e654e 100644 |
| --- a/base/numerics/checked_math_impl.h |
| +++ b/base/numerics/checked_math_impl.h |
| @@ -182,17 +182,19 @@ struct CheckedMulOp<T, |
| return !__builtin_mul_overflow(x, y, result); |
| #endif |
| using Promotion = typename FastIntegerArithmeticPromotion<T, U>::type; |
| + // Verify the destination type can hold the result (always true for 0). |
| + if (!(IsValueInRangeForNumericType<Promotion>(x) && |
| + IsValueInRangeForNumericType<Promotion>(y)) && |
| + x && y) { |
| + return false; |
|
jschuh
2017/06/26 20:26:50
The only functional change here is supporting the
|
| + } |
| Promotion presult; |
| - // Fail if either operand is out of range for the promoted type. |
| - // TODO(jschuh): This could be made to work for a broader range of values. |
| - bool is_valid = IsValueInRangeForNumericType<Promotion>(x) && |
| - IsValueInRangeForNumericType<Promotion>(y); |
| - |
| + bool is_valid = true; |
| if (IsIntegerArithmeticSafe<Promotion, T, U>::value) { |
| presult = static_cast<Promotion>(x) * static_cast<Promotion>(y); |
| } else { |
| - is_valid &= CheckedMulImpl(static_cast<Promotion>(x), |
| - static_cast<Promotion>(y), &presult); |
| + is_valid = CheckedMulImpl(static_cast<Promotion>(x), |
| + static_cast<Promotion>(y), &presult); |
| } |
| *result = static_cast<V>(presult); |
| return is_valid && IsValueInRangeForNumericType<V>(presult); |
| @@ -242,7 +244,7 @@ struct CheckedDivOp<T, |
| template <typename T> |
| bool CheckedModImpl(T x, T y, T* result) { |
| static_assert(std::is_integral<T>::value, "Type must be integral"); |
| - if (y > 0) { |
| + if (y) { |
| *result = static_cast<T>(x % y); |
| return true; |
| } |
| @@ -272,9 +274,6 @@ struct CheckedModOp<T, |
| template <typename T, typename U, class Enable = void> |
| struct CheckedLshOp {}; |
| -// Left shift. Shifts less than 0 or greater than or equal to the number |
| -// of bits in the promoted type are undefined. Shifts of negative values |
| -// are undefined. Otherwise it is defined when the result fits. |
| template <typename T, typename U> |
| struct CheckedLshOp<T, |
| U, |
| @@ -283,27 +282,22 @@ struct CheckedLshOp<T, |
| using result_type = T; |
| template <typename V> |
| static bool Do(T x, U shift, V* result) { |
| - using ShiftType = typename std::make_unsigned<T>::type; |
| - static const ShiftType kBitWidth = IntegerBitsPlusSign<T>::value; |
| - const ShiftType real_shift = static_cast<ShiftType>(shift); |
| - // Signed shift is not legal on negative values. |
| - if (!IsValueNegative(x) && real_shift < kBitWidth) { |
| + if (!IsValueNegative(x) && |
| + as_unsigned(shift) < std::numeric_limits<T>::digits) { |
| // Just use a multiplication because it's easy. |
| // TODO(jschuh): This could probably be made more efficient. |
| - if (!std::is_signed<T>::value || real_shift != kBitWidth - 1) |
| - return CheckedMulOp<T, T>::Do(x, static_cast<T>(1) << shift, result); |
| - return !x; // Special case zero for a full width signed shift. |
| + return CheckedMulOp<T, T>::Do(x, T(1) << shift, result); |
| } |
| - return false; |
| + // The standard defines only the result of the shift, rather than the |
| + // values of source operands. So, shifting zero by any amount is legal. |
| + // Otherwise, we overflowed or have an undefined operation. |
| + return !x ? (*result = V(0)), true : false; |
|
jschuh
2017/06/26 20:26:50
Stumbled on a spec error in how I was handling zer
dcheng
2017/06/28 07:25:06
Acknowledged--though for future CLs, it would be n
jschuh
2017/06/28 12:32:37
Weird. I'm looking at it now and you're right. May
|
| } |
| }; |
| template <typename T, typename U, class Enable = void> |
| struct CheckedRshOp {}; |
| -// Right shift. Shifts less than 0 or greater than or equal to the number |
| -// of bits in the promoted type are undefined. Otherwise, it is always defined, |
| -// but a right shift of a negative value is implementation-dependent. |
| template <typename T, typename U> |
| struct CheckedRshOp<T, |
| U, |
| @@ -314,7 +308,7 @@ struct CheckedRshOp<T, |
| static bool Do(T x, U shift, V* result) { |
| // Use the type conversion push negative values out of range. |
| using ShiftType = typename std::make_unsigned<T>::type; |
| - if (static_cast<ShiftType>(shift) < IntegerBitsPlusSign<T>::value) { |
| + if (!x || static_cast<ShiftType>(shift) < IntegerBitsPlusSign<T>::value) { |
|
jschuh
2017/06/26 20:26:50
Stumbled on a spec error in how I was handling zer
dcheng
2017/06/28 07:25:06
Ditto.
jschuh
2017/06/28 12:32:37
Yup.
|
| T tmp = x >> shift; |
| *result = static_cast<V>(tmp); |
| return IsValueInRangeForNumericType<V>(tmp); |