Index: base/numerics/safe_numerics_unittest.cc |
diff --git a/base/numerics/safe_numerics_unittest.cc b/base/numerics/safe_numerics_unittest.cc |
index 8ced4bdb7b244e0637dc18f8c2f296d84d49f671..f1522ba3800060851fb23686f9010b99bfdbf98c 100644 |
--- a/base/numerics/safe_numerics_unittest.cc |
+++ b/base/numerics/safe_numerics_unittest.cc |
@@ -22,6 +22,9 @@ |
using std::numeric_limits; |
using base::CheckedNumeric; |
+using base::IsValidForType; |
+using base::ValueOrDieForType; |
+using base::ValueOrDefaultForType; |
using base::CheckNum; |
using base::CheckAdd; |
using base::CheckSub; |
@@ -37,6 +40,7 @@ using base::SizeT; |
using base::StrictNumeric; |
using base::saturated_cast; |
using base::strict_cast; |
+using base::StrictNumeric; |
using base::internal::MaxExponent; |
using base::internal::RANGE_VALID; |
using base::internal::RANGE_INVALID; |
@@ -108,6 +112,23 @@ struct LogOnFailure { |
<< "Result test: Value " << GetNumericValueForTest(actual) << " as " \ |
<< dst << " on line " << line |
+// Test the simple pointer arithmetic overrides. |
+template <typename Dst> |
+void TestStrictPointerMath() { |
+ Dst dummy_value = 0; |
+ Dst* dummy_ptr = &dummy_value; |
+ static const Dst kDummyOffset = 2; // Don't want to go too far. |
+ EXPECT_EQ(dummy_ptr + kDummyOffset, |
+ dummy_ptr + StrictNumeric<Dst>(kDummyOffset)); |
+ EXPECT_EQ(dummy_ptr - kDummyOffset, |
+ dummy_ptr - StrictNumeric<Dst>(kDummyOffset)); |
+ EXPECT_NE(dummy_ptr, dummy_ptr + StrictNumeric<Dst>(kDummyOffset)); |
+ EXPECT_NE(dummy_ptr, dummy_ptr - StrictNumeric<Dst>(kDummyOffset)); |
+ EXPECT_DEATH_IF_SUPPORTED( |
+ dummy_ptr + StrictNumeric<size_t>(std::numeric_limits<size_t>::max()), |
+ ""); |
+} |
+ |
// Signed integer arithmetic. |
template <typename Dst> |
static void TestSpecializedArithmetic( |
@@ -170,6 +191,8 @@ static void TestSpecializedArithmetic( |
TEST_EXPECTED_VALUE(0, |
CheckedNumeric<Dst>(1) >> (sizeof(Dst) * CHAR_BIT - 1)); |
TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(1) >> negative_one); |
+ |
+ TestStrictPointerMath<Dst>(); |
} |
// Unsigned integer arithmetic. |
@@ -241,6 +264,8 @@ static void TestSpecializedArithmetic( |
TEST_EXPECTED_VALUE(std::numeric_limits<Dst>::max(), |
CheckedNumeric<Dst>(0) ^ static_cast<Dst>(-1)); |
TEST_EXPECTED_VALUE(DstLimits::max(), ~CheckedNumeric<Dst>(0)); |
+ |
+ TestStrictPointerMath<Dst>(); |
} |
// Floating point arithmetic. |
@@ -268,7 +293,6 @@ void TestSpecializedArithmetic( |
TEST_EXPECTED_SUCCESS(CheckedNumeric<Dst>(DstLimits::min()) * 2); |
TEST_EXPECTED_VALUE(-0.5, CheckedNumeric<Dst>(-1.0) / 2); |
- EXPECT_EQ(static_cast<Dst>(1.0), CheckedNumeric<Dst>(1.0).ValueFloating()); |
} |
// Generic arithmetic tests. |
@@ -411,6 +435,43 @@ struct TestNumericConversion {}; |
<< " on line " << line |
template <typename Dst, typename Src> |
+void TestStrictComparison() { |
+ typedef numeric_limits<Dst> DstLimits; |
+ typedef numeric_limits<Src> SrcLimits; |
+ static_assert(StrictNumeric<Src>(SrcLimits::min()) < DstLimits::max(), ""); |
+ static_assert(StrictNumeric<Src>(SrcLimits::min()) < SrcLimits::max(), ""); |
+ static_assert(!(StrictNumeric<Src>(SrcLimits::min()) >= DstLimits::max()), |
+ ""); |
+ static_assert(!(StrictNumeric<Src>(SrcLimits::min()) >= SrcLimits::max()), |
+ ""); |
+ static_assert(StrictNumeric<Src>(SrcLimits::min()) <= DstLimits::max(), ""); |
+ static_assert(StrictNumeric<Src>(SrcLimits::min()) <= SrcLimits::max(), ""); |
+ static_assert(!(StrictNumeric<Src>(SrcLimits::min()) > DstLimits::max()), ""); |
+ static_assert(!(StrictNumeric<Src>(SrcLimits::min()) > SrcLimits::max()), ""); |
+ static_assert(StrictNumeric<Src>(SrcLimits::max()) > DstLimits::min(), ""); |
+ static_assert(StrictNumeric<Src>(SrcLimits::max()) > SrcLimits::min(), ""); |
+ static_assert(!(StrictNumeric<Src>(SrcLimits::max()) <= DstLimits::min()), |
+ ""); |
+ static_assert(!(StrictNumeric<Src>(SrcLimits::max()) <= SrcLimits::min()), |
+ ""); |
+ static_assert(StrictNumeric<Src>(SrcLimits::max()) >= DstLimits::min(), ""); |
+ static_assert(StrictNumeric<Src>(SrcLimits::max()) >= SrcLimits::min(), ""); |
+ static_assert(!(StrictNumeric<Src>(SrcLimits::max()) < DstLimits::min()), ""); |
+ static_assert(!(StrictNumeric<Src>(SrcLimits::max()) < SrcLimits::min()), ""); |
+ static_assert(StrictNumeric<Src>(static_cast<Src>(1)) == static_cast<Dst>(1), |
+ ""); |
+ static_assert(StrictNumeric<Src>(static_cast<Src>(1)) != static_cast<Dst>(0), |
+ ""); |
+ static_assert(StrictNumeric<Src>(SrcLimits::max()) != static_cast<Dst>(0), |
+ ""); |
+ static_assert(StrictNumeric<Src>(SrcLimits::max()) != DstLimits::min(), ""); |
+ static_assert( |
+ !(StrictNumeric<Src>(static_cast<Src>(1)) != static_cast<Dst>(1)), ""); |
+ static_assert( |
+ !(StrictNumeric<Src>(static_cast<Src>(1)) == static_cast<Dst>(0)), ""); |
+} |
+ |
+template <typename Dst, typename Src> |
struct TestNumericConversion<Dst, Src, SIGN_PRESERVING_VALUE_PRESERVING> { |
static void Test(const char *dst, const char *src, int line) { |
typedef numeric_limits<Src> SrcLimits; |
@@ -426,6 +487,8 @@ struct TestNumericConversion<Dst, Src, SIGN_PRESERVING_VALUE_PRESERVING> { |
(DstLimits::is_signed && sizeof(Dst) > sizeof(Src)))), |
"Comparison must be sign preserving and value preserving"); |
+ TestStrictComparison<Dst, Src>(); |
+ |
const CheckedNumeric<Dst> checked_dst = SrcLimits::max(); |
TEST_EXPECTED_SUCCESS(checked_dst); |
if (MaxExponent<Dst>::value > MaxExponent<Src>::value) { |
@@ -466,6 +529,8 @@ struct TestNumericConversion<Dst, Src, SIGN_PRESERVING_NARROW> { |
(DstLimits::is_integer && SrcLimits::is_iec559), |
"Destination must be narrower than source"); |
+ TestStrictComparison<Dst, Src>(); |
+ |
const CheckedNumeric<Dst> checked_dst; |
TEST_EXPECTED_FAILURE(checked_dst + SrcLimits::max()); |
TEST_EXPECTED_VALUE(1, checked_dst + static_cast<Src>(1)); |
@@ -512,6 +577,8 @@ struct TestNumericConversion<Dst, Src, SIGN_TO_UNSIGN_WIDEN_OR_EQUAL> { |
static_assert(SrcLimits::is_signed, "Source must be signed"); |
static_assert(!DstLimits::is_signed, "Destination must be unsigned"); |
+ TestStrictComparison<Dst, Src>(); |
+ |
const CheckedNumeric<Dst> checked_dst; |
TEST_EXPECTED_VALUE(SrcLimits::max(), checked_dst + SrcLimits::max()); |
TEST_EXPECTED_FAILURE(checked_dst + static_cast<Src>(-1)); |
@@ -535,6 +602,8 @@ struct TestNumericConversion<Dst, Src, SIGN_TO_UNSIGN_NARROW> { |
static_assert(SrcLimits::is_signed, "Source must be signed."); |
static_assert(!DstLimits::is_signed, "Destination must be unsigned."); |
+ TestStrictComparison<Dst, Src>(); |
+ |
const CheckedNumeric<Dst> checked_dst; |
TEST_EXPECTED_VALUE(1, checked_dst + static_cast<Src>(1)); |
TEST_EXPECTED_FAILURE(checked_dst + SrcLimits::max()); |
@@ -577,6 +646,8 @@ struct TestNumericConversion<Dst, Src, UNSIGN_TO_SIGN_NARROW_OR_EQUAL> { |
static_assert(!SrcLimits::is_signed, "Source must be unsigned."); |
static_assert(DstLimits::is_signed, "Destination must be signed."); |
+ TestStrictComparison<Dst, Src>(); |
+ |
const CheckedNumeric<Dst> checked_dst; |
TEST_EXPECTED_VALUE(1, checked_dst + static_cast<Src>(1)); |
TEST_EXPECTED_FAILURE(checked_dst + SrcLimits::max()); |
@@ -683,6 +754,25 @@ TEST(SafeNumerics, SizeTOperations) { |
TEST_NUMERIC_CONVERSION(int, size_t, UNSIGN_TO_SIGN_NARROW_OR_EQUAL); |
} |
+// A one-off test to ensure StrictNumeric won't resolve to an incorrect type. |
+// If this fails we'll just get a compiler error on an ambiguous overload. |
+int TestOverload(int) { // Overload fails. |
+ return 0; |
+} |
+uint8_t TestOverload(uint8_t) { // Overload fails. |
+ return 0; |
+} |
+size_t TestOverload(size_t) { // Overload succeeds. |
+ return 0; |
+} |
+ |
+static_assert( |
+ std::is_same<decltype(TestOverload(StrictNumeric<int>())), int>::value, |
+ ""); |
+static_assert(std::is_same<decltype(TestOverload(StrictNumeric<size_t>())), |
+ size_t>::value, |
+ ""); |
+ |
TEST(SafeNumerics, CastTests) { |
// MSVC catches and warns that we're forcing saturation in these tests. |
// Since that's intentional, we need to shut this warning off. |
@@ -754,22 +844,21 @@ TEST(SafeNumerics, CastTests) { |
auto int8_max = CheckNum(numeric_limits<int8_t>::max()); |
auto double_max = CheckNum(numeric_limits<double>::max()); |
static_assert( |
- std::is_same<int16_t, decltype(int8_min.ValueOrDie<int16_t>())>::value, |
+ std::is_same<int16_t, |
+ decltype(int8_min.ValueOrDie<int16_t>())::type>::value, |
"ValueOrDie returning incorrect type."); |
static_assert( |
std::is_same<int16_t, |
- decltype(int8_min.ValueOrDefault<int16_t>(0))>::value, |
+ decltype(int8_min.ValueOrDefault<int16_t>(0))::type>::value, |
"ValueOrDefault returning incorrect type."); |
- static_assert( |
- std::is_same<float, decltype(double_max.ValueFloating<float>())>::value, |
- "ValueFloating returning incorrect type."); |
- EXPECT_FALSE(int8_min.template IsValid<uint8_t>()); |
- EXPECT_TRUE(int8_max.template IsValid<uint8_t>()); |
+ EXPECT_FALSE(IsValidForType<uint8_t>(int8_min)); |
+ EXPECT_TRUE(IsValidForType<uint8_t>(int8_max)); |
EXPECT_EQ(static_cast<int>(numeric_limits<int8_t>::min()), |
- int8_min.template ValueOrDie<int>()); |
- EXPECT_TRUE(int8_max.template IsValid<uint32_t>()); |
+ ValueOrDieForType<int>(int8_min)); |
+ EXPECT_TRUE(IsValidForType<uint32_t>(int8_max)); |
EXPECT_EQ(static_cast<int>(numeric_limits<int8_t>::max()), |
- int8_max.template ValueOrDie<int>()); |
+ ValueOrDieForType<int>(int8_max)); |
+ EXPECT_EQ(0, ValueOrDefaultForType<int>(double_max, 0)); |
uint8_t uint8_dest = 0; |
int16_t int16_dest = 0; |
double double_dest = 0; |
@@ -784,6 +873,9 @@ TEST(SafeNumerics, CastTests) { |
EXPECT_FALSE(double_max.AssignIfValid(&int16_dest)); |
EXPECT_TRUE(double_max.AssignIfValid(&double_dest)); |
EXPECT_EQ(numeric_limits<double>::max(), double_dest); |
+ EXPECT_EQ(1, checked_cast<int>(StrictNumeric<int>(1))); |
+ EXPECT_EQ(1, saturated_cast<int>(StrictNumeric<int>(1))); |
+ EXPECT_EQ(1, strict_cast<int>(StrictNumeric<int>(1))); |
} |
TEST(SafeNumerics, SaturatedCastChecks) { |
@@ -896,19 +988,19 @@ TEST(SafeNumerics, CompoundNumericOperations) { |
TEST(SafeNumerics, VariadicNumericOperations) { |
auto a = CheckAdd(1, 2UL, CheckNum(3LL), 4).ValueOrDie(); |
- EXPECT_EQ(static_cast<decltype(a)>(10), a); |
+ EXPECT_EQ(static_cast<decltype(a)::type>(10), a); |
auto b = CheckSub(CheckNum(20.0), 2UL, 4).ValueOrDie(); |
- EXPECT_EQ(static_cast<decltype(b)>(14.0), b); |
+ EXPECT_EQ(static_cast<decltype(b)::type>(14.0), b); |
auto c = CheckMul(20.0, CheckNum(1), 5, 3UL).ValueOrDie(); |
- EXPECT_EQ(static_cast<decltype(c)>(300.0), c); |
+ EXPECT_EQ(static_cast<decltype(c)::type>(300.0), c); |
auto d = CheckDiv(20.0, 2.0, CheckNum(5LL), -4).ValueOrDie(); |
- EXPECT_EQ(static_cast<decltype(d)>(-.5), d); |
+ EXPECT_EQ(static_cast<decltype(d)::type>(-.5), d); |
auto e = CheckMod(CheckNum(20), 3).ValueOrDie(); |
- EXPECT_EQ(static_cast<decltype(e)>(2), e); |
+ EXPECT_EQ(static_cast<decltype(e)::type>(2), e); |
auto f = CheckLsh(1, CheckNum(2)).ValueOrDie(); |
- EXPECT_EQ(static_cast<decltype(f)>(4), f); |
+ EXPECT_EQ(static_cast<decltype(f)::type>(4), f); |
auto g = CheckRsh(4, CheckNum(2)).ValueOrDie(); |
- EXPECT_EQ(static_cast<decltype(g)>(1), g); |
+ EXPECT_EQ(static_cast<decltype(g)::type>(1), g); |
auto h = CheckRsh(CheckAdd(1, 1, 1, 1), CheckSub(4, 2)).ValueOrDie(); |
- EXPECT_EQ(static_cast<decltype(h)>(1), h); |
+ EXPECT_EQ(static_cast<decltype(h)::type>(1), h); |
} |