[clang] e37726b - [analyzer] Implemented RangeSet::Factory::castTo function to perform promotions, truncations and conversions.
Denys Petrov via cfe-commits
cfe-commits at lists.llvm.org
Tue Apr 19 12:34:12 PDT 2022
Author: Denys Petrov
Date: 2022-04-19T22:34:03+03:00
New Revision: e37726beb22a8e3865e1f6fcdbb5cd4262786903
URL: https://github.com/llvm/llvm-project/commit/e37726beb22a8e3865e1f6fcdbb5cd4262786903
DIFF: https://github.com/llvm/llvm-project/commit/e37726beb22a8e3865e1f6fcdbb5cd4262786903.diff
LOG: [analyzer] Implemented RangeSet::Factory::castTo function to perform promotions, truncations and conversions.
Summary: Handle casts for ranges working similarly to APSIntType::apply function but for the whole range set. Support promotions, truncations and conversions.
Example:
promotion: char [0, 42] -> short [0, 42] -> int [0, 42] -> llong [0, 42]
truncation: llong [4295033088, 4295033130] -> int [65792, 65834] -> short [256, 298] -> char [0, 42]
conversion: char [-42, 42] -> uint [0, 42]U[4294967254, 4294967295] -> short[-42, 42]
Differential Revision: https://reviews.llvm.org/D103094
Added:
Modified:
clang/include/clang/StaticAnalyzer/Core/PathSensitive/APSIntType.h
clang/include/clang/StaticAnalyzer/Core/PathSensitive/RangedConstraintManager.h
clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp
clang/unittests/StaticAnalyzer/RangeSetTest.cpp
Removed:
################################################################################
diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/APSIntType.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/APSIntType.h
index 4b7d6054cd877..f1c50e721937b 100644
--- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/APSIntType.h
+++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/APSIntType.h
@@ -21,8 +21,8 @@ class APSIntType {
bool IsUnsigned;
public:
- APSIntType(uint32_t Width, bool Unsigned)
- : BitWidth(Width), IsUnsigned(Unsigned) {}
+ constexpr APSIntType(uint32_t Width, bool Unsigned)
+ : BitWidth(Width), IsUnsigned(Unsigned) {}
/* implicit */ APSIntType(const llvm::APSInt &Value)
: BitWidth(Value.getBitWidth()), IsUnsigned(Value.isUnsigned()) {}
diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/RangedConstraintManager.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/RangedConstraintManager.h
index 6c487697bc551..49ea006e27aa5 100644
--- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/RangedConstraintManager.h
+++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/RangedConstraintManager.h
@@ -237,6 +237,29 @@ class RangeSet {
/// Complexity: O(N)
/// where N = size(What)
RangeSet negate(RangeSet What);
+ /// Performs promotions, truncations and conversions of the given set.
+ ///
+ /// This function is optimized for each of the six cast cases:
+ /// - noop
+ /// - conversion
+ /// - truncation
+ /// - truncation-conversion
+ /// - promotion
+ /// - promotion-conversion
+ ///
+ /// NOTE: This function is NOT self-inverse for truncations, because of
+ /// the higher bits loss:
+ /// - castTo(castTo(OrigRangeOfInt, char), int) != OrigRangeOfInt.
+ /// - castTo(castTo(OrigRangeOfChar, int), char) == OrigRangeOfChar.
+ /// But it is self-inverse for all the rest casts.
+ ///
+ /// Complexity:
+ /// - Noop O(1);
+ /// - Truncation O(N^2);
+ /// - Another case O(N);
+ /// where N = size(What)
+ RangeSet castTo(RangeSet What, APSIntType Ty);
+ RangeSet castTo(RangeSet What, QualType T);
/// Return associated value factory.
BasicValueFactory &getValueFactory() const { return ValueFactory; }
@@ -252,6 +275,22 @@ class RangeSet {
/// containers are persistent (created via BasicValueFactory::getValue).
ContainerType unite(const ContainerType &LHS, const ContainerType &RHS);
+ /// This is a helper function for `castTo` method. Implies not to be used
+ /// separately.
+ /// Performs a truncation case of a cast operation.
+ ContainerType truncateTo(RangeSet What, APSIntType Ty);
+
+ /// This is a helper function for `castTo` method. Implies not to be used
+ /// separately.
+ /// Performs a conversion case and a promotion-conversion case for signeds
+ /// of a cast operation.
+ ContainerType convertTo(RangeSet What, APSIntType Ty);
+
+ /// This is a helper function for `castTo` method. Implies not to be used
+ /// separately.
+ /// Performs a promotion for unsigneds only.
+ ContainerType promoteTo(RangeSet What, APSIntType Ty);
+
// Many operations include producing new APSInt values and that's why
// we need this factory.
BasicValueFactory &ValueFactory;
@@ -303,6 +342,10 @@ class RangeSet {
/// Complexity: O(1)
const llvm::APSInt &getMaxValue() const;
+ bool isUnsigned() const;
+ uint32_t getBitWidth() const;
+ APSIntType getAPSIntType() const;
+
/// Test whether the given point is contained by any of the ranges.
///
/// Complexity: O(logN)
diff --git a/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp b/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp
index 4b0d4942e5287..1c17d48844ea3 100644
--- a/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp
+++ b/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp
@@ -353,6 +353,21 @@ const llvm::APSInt &RangeSet::getMaxValue() const {
return std::prev(end())->To();
}
+bool clang::ento::RangeSet::isUnsigned() const {
+ assert(!isEmpty());
+ return begin()->From().isUnsigned();
+}
+
+uint32_t clang::ento::RangeSet::getBitWidth() const {
+ assert(!isEmpty());
+ return begin()->From().getBitWidth();
+}
+
+APSIntType clang::ento::RangeSet::getAPSIntType() const {
+ assert(!isEmpty());
+ return APSIntType(begin()->From());
+}
+
bool RangeSet::containsImpl(llvm::APSInt &Point) const {
if (isEmpty() || !pin(Point))
return false;
@@ -655,6 +670,181 @@ RangeSet RangeSet::Factory::negate(RangeSet What) {
return makePersistent(std::move(Result));
}
+// Convert range set to the given integral type using truncation and promotion.
+// This works similar to APSIntType::apply function but for the range set.
+RangeSet RangeSet::Factory::castTo(RangeSet What, APSIntType Ty) {
+ // Set is empty or NOOP (aka cast to the same type).
+ if (What.isEmpty() || What.getAPSIntType() == Ty)
+ return What;
+
+ const bool IsConversion = What.isUnsigned() != Ty.isUnsigned();
+ const bool IsTruncation = What.getBitWidth() > Ty.getBitWidth();
+ const bool IsPromotion = What.getBitWidth() < Ty.getBitWidth();
+
+ if (IsTruncation)
+ return makePersistent(truncateTo(What, Ty));
+
+ // Here we handle 2 cases:
+ // - IsConversion && !IsPromotion.
+ // In this case we handle changing a sign with same bitwidth: char -> uchar,
+ // uint -> int. Here we convert negatives to positives and positives which
+ // is out of range to negatives. We use convertTo function for that.
+ // - IsConversion && IsPromotion && !What.isUnsigned().
+ // In this case we handle changing a sign from signeds to unsigneds with
+ // higher bitwidth: char -> uint, int-> uint64. The point is that we also
+ // need convert negatives to positives and use convertTo function as well.
+ // For example, we don't need such a convertion when converting unsigned to
+ // signed with higher bitwidth, because all the values of unsigned is valid
+ // for the such signed.
+ if (IsConversion && (!IsPromotion || !What.isUnsigned()))
+ return makePersistent(convertTo(What, Ty));
+
+ assert(IsPromotion && "Only promotion operation from unsigneds left.");
+ return makePersistent(promoteTo(What, Ty));
+}
+
+RangeSet RangeSet::Factory::castTo(RangeSet What, QualType T) {
+ assert(T->isIntegralOrEnumerationType() && "T shall be an integral type.");
+ return castTo(What, ValueFactory.getAPSIntType(T));
+}
+
+RangeSet::ContainerType RangeSet::Factory::truncateTo(RangeSet What,
+ APSIntType Ty) {
+ using llvm::APInt;
+ using llvm::APSInt;
+ ContainerType Result;
+ ContainerType Dummy;
+ // CastRangeSize is an amount of all possible values of cast type.
+ // Example: `char` has 256 values; `short` has 65536 values.
+ // But in fact we use `amount of values` - 1, because
+ // we can't keep `amount of values of UINT64` inside uint64_t.
+ // E.g. 256 is an amount of all possible values of `char` and we can't keep
+ // it inside `char`.
+ // And it's OK, it's enough to do correct calculations.
+ uint64_t CastRangeSize = APInt::getMaxValue(Ty.getBitWidth()).getZExtValue();
+ for (const Range &R : What) {
+ // Get bounds of the given range.
+ APSInt FromInt = R.From();
+ APSInt ToInt = R.To();
+ // CurrentRangeSize is an amount of all possible values of the current
+ // range minus one.
+ uint64_t CurrentRangeSize = (ToInt - FromInt).getZExtValue();
+ // This is an optimization for a specific case when this Range covers
+ // the whole range of the target type.
+ Dummy.clear();
+ if (CurrentRangeSize >= CastRangeSize) {
+ Dummy.emplace_back(ValueFactory.getMinValue(Ty),
+ ValueFactory.getMaxValue(Ty));
+ Result = std::move(Dummy);
+ break;
+ }
+ // Cast the bounds.
+ Ty.apply(FromInt);
+ Ty.apply(ToInt);
+ const APSInt &PersistentFrom = ValueFactory.getValue(FromInt);
+ const APSInt &PersistentTo = ValueFactory.getValue(ToInt);
+ if (FromInt > ToInt) {
+ Dummy.emplace_back(ValueFactory.getMinValue(Ty), PersistentTo);
+ Dummy.emplace_back(PersistentFrom, ValueFactory.getMaxValue(Ty));
+ } else
+ Dummy.emplace_back(PersistentFrom, PersistentTo);
+ // Every range retrieved after truncation potentialy has garbage values.
+ // So, we have to unite every next range with the previouses.
+ Result = unite(Result, Dummy);
+ }
+
+ return Result;
+}
+
+// Divide the convertion into two phases (presented as loops here).
+// First phase(loop) works when casted values go in ascending order.
+// E.g. char{1,3,5,127} -> uint{1,3,5,127}
+// Interrupt the first phase and go to second one when casted values start
+// go in descending order. That means that we crossed over the middle of
+// the type value set (aka 0 for signeds and MAX/2+1 for unsigneds).
+// For instance:
+// 1: uchar{1,3,5,128,255} -> char{1,3,5,-128,-1}
+// Here we put {1,3,5} to one array and {-128, -1} to another
+// 2: char{-128,-127,-1,0,1,2} -> uchar{128,129,255,0,1,3}
+// Here we put {128,129,255} to one array and {0,1,3} to another.
+// After that we unite both arrays.
+// NOTE: We don't just concatenate the arrays, because they may have
+// adjacent ranges, e.g.:
+// 1: char(-128, 127) -> uchar -> arr1(128, 255), arr2(0, 127) ->
+// unite -> uchar(0, 255)
+// 2: uchar(0, 1)U(254, 255) -> char -> arr1(0, 1), arr2(-2, -1) ->
+// unite -> uchar(-2, 1)
+RangeSet::ContainerType RangeSet::Factory::convertTo(RangeSet What,
+ APSIntType Ty) {
+ using llvm::APInt;
+ using llvm::APSInt;
+ using Bounds = std::pair<const APSInt &, const APSInt &>;
+ ContainerType AscendArray;
+ ContainerType DescendArray;
+ auto CastRange = [Ty, &VF = ValueFactory](const Range &R) -> Bounds {
+ // Get bounds of the given range.
+ APSInt FromInt = R.From();
+ APSInt ToInt = R.To();
+ // Cast the bounds.
+ Ty.apply(FromInt);
+ Ty.apply(ToInt);
+ return {VF.getValue(FromInt), VF.getValue(ToInt)};
+ };
+ // Phase 1. Fill the first array.
+ APSInt LastConvertedInt = Ty.getMinValue();
+ const auto *It = What.begin();
+ const auto *E = What.end();
+ while (It != E) {
+ Bounds NewBounds = CastRange(*(It++));
+ // If values stop going acsending order, go to the second phase(loop).
+ if (NewBounds.first < LastConvertedInt) {
+ DescendArray.emplace_back(NewBounds.first, NewBounds.second);
+ break;
+ }
+ // If the range contains a midpoint, then split the range.
+ // E.g. char(-5, 5) -> uchar(251, 5)
+ // Here we shall add a range (251, 255) to the first array and (0, 5) to the
+ // second one.
+ if (NewBounds.first > NewBounds.second) {
+ DescendArray.emplace_back(ValueFactory.getMinValue(Ty), NewBounds.second);
+ AscendArray.emplace_back(NewBounds.first, ValueFactory.getMaxValue(Ty));
+ } else
+ // Values are going acsending order.
+ AscendArray.emplace_back(NewBounds.first, NewBounds.second);
+ LastConvertedInt = NewBounds.first;
+ }
+ // Phase 2. Fill the second array.
+ while (It != E) {
+ Bounds NewBounds = CastRange(*(It++));
+ DescendArray.emplace_back(NewBounds.first, NewBounds.second);
+ }
+ // Unite both arrays.
+ return unite(AscendArray, DescendArray);
+}
+
+/// Promotion from unsigneds to signeds/unsigneds left.
+RangeSet::ContainerType RangeSet::Factory::promoteTo(RangeSet What,
+ APSIntType Ty) {
+ ContainerType Result;
+ // We definitely know the size of the result set.
+ Result.reserve(What.size());
+
+ // Each unsigned value fits every larger type without any changes,
+ // whether the larger type is signed or unsigned. So just promote and push
+ // back each range one by one.
+ for (const Range &R : What) {
+ // Get bounds of the given range.
+ llvm::APSInt FromInt = R.From();
+ llvm::APSInt ToInt = R.To();
+ // Cast the bounds.
+ Ty.apply(FromInt);
+ Ty.apply(ToInt);
+ Result.emplace_back(ValueFactory.getValue(FromInt),
+ ValueFactory.getValue(ToInt));
+ }
+ return Result;
+}
+
RangeSet RangeSet::Factory::deletePoint(RangeSet From,
const llvm::APSInt &Point) {
if (!From.contains(Point))
diff --git a/clang/unittests/StaticAnalyzer/RangeSetTest.cpp b/clang/unittests/StaticAnalyzer/RangeSetTest.cpp
index eb1c053e06c2a..91a6351c8e3b3 100644
--- a/clang/unittests/StaticAnalyzer/RangeSetTest.cpp
+++ b/clang/unittests/StaticAnalyzer/RangeSetTest.cpp
@@ -40,12 +40,18 @@ LLVM_ATTRIBUTE_UNUSED static std::ostream &operator<<(std::ostream &OS,
const Range &R) {
return OS << toString(R);
}
+LLVM_ATTRIBUTE_UNUSED static std::ostream &operator<<(std::ostream &OS,
+ APSIntType Ty) {
+ return OS << (Ty.isUnsigned() ? "u" : "s") << Ty.getBitWidth();
+}
} // namespace ento
} // namespace clang
namespace {
+template <class T> constexpr bool is_signed_v = std::is_signed<T>::value;
+
template <typename T> struct TestValues {
static constexpr T MIN = std::numeric_limits<T>::min();
static constexpr T MAX = std::numeric_limits<T>::max();
@@ -53,7 +59,7 @@ template <typename T> struct TestValues {
// which unary minus does not affect on,
// e.g. int8/int32(0), uint8(128), uint32(2147483648).
static constexpr T MID =
- std::is_signed<T>::value ? 0 : ~(static_cast<T>(-1) / static_cast<T>(2));
+ is_signed_v<T> ? 0 : ~(static_cast<T>(-1) / static_cast<T>(2));
static constexpr T A = MID - (MAX - MID) / 3 * 2;
static constexpr T B = MID - (MAX - MID) / 3;
static constexpr T C = -B;
@@ -61,8 +67,40 @@ template <typename T> struct TestValues {
static_assert(MIN < A && A < B && B < MID && MID < C && C < D && D < MAX,
"Values shall be in an ascending order");
+ // Clear bits in low bytes by the given amount.
+ template <T Value, size_t Bytes>
+ static constexpr T ClearLowBytes =
+ static_cast<T>(static_cast<uint64_t>(Value)
+ << ((Bytes >= CHAR_BIT) ? 0 : Bytes) * CHAR_BIT);
+
+ template <T Value, typename Base>
+ static constexpr T TruncZeroOf = ClearLowBytes<Value + 1, sizeof(Base)>;
+
+ // Random number with active bits in every byte. 0xAAAA'AAAA
+ static constexpr T XAAA = static_cast<T>(
+ 0b10101010'10101010'10101010'10101010'10101010'10101010'10101010'10101010);
+ template <typename Base>
+ static constexpr T XAAATruncZeroOf = TruncZeroOf<XAAA, Base>; // 0xAAAA'AB00
+
+ // Random number with active bits in every byte. 0x5555'5555
+ static constexpr T X555 = static_cast<T>(
+ 0b01010101'01010101'01010101'01010101'01010101'01010101'01010101'01010101);
+ template <typename Base>
+ static constexpr T X555TruncZeroOf = TruncZeroOf<X555, Base>; // 0x5555'5600
+
+ // Numbers for ranges with the same bits in the lowest byte.
+ // 0xAAAA'AA2A
+ static constexpr T FromA = ClearLowBytes<XAAA, sizeof(T) - 1> + 42;
+ static constexpr T ToA = FromA + 2; // 0xAAAA'AA2C
+ // 0x5555'552A
+ static constexpr T FromB = ClearLowBytes<X555, sizeof(T) - 1> + 42;
+ static constexpr T ToB = FromB + 2; // 0x5555'552C
};
+template <typename T>
+static constexpr APSIntType APSIntTy =
+ APSIntType(sizeof(T) * CHAR_BIT, !is_signed_v<T>);
+
template <typename BaseType> class RangeSetTest : public testing::Test {
public:
// Init block
@@ -74,21 +112,24 @@ template <typename BaseType> class RangeSetTest : public testing::Test {
// End init block
using Self = RangeSetTest<BaseType>;
- using RawRange = std::pair<BaseType, BaseType>;
- using RawRangeSet = std::initializer_list<RawRange>;
-
- const llvm::APSInt &from(BaseType X) {
- static llvm::APSInt Base{sizeof(BaseType) * CHAR_BIT,
- std::is_unsigned<BaseType>::value};
- Base = X;
- return BVF.getValue(Base);
+ template <typename T> using RawRangeT = std::pair<T, T>;
+ template <typename T>
+ using RawRangeSetT = std::initializer_list<RawRangeT<T>>;
+ using RawRange = RawRangeT<BaseType>;
+ using RawRangeSet = RawRangeSetT<BaseType>;
+
+ template <typename T> const llvm::APSInt &from(T X) {
+ static llvm::APSInt Int = APSIntTy<T>.getZeroValue();
+ Int = X;
+ return BVF.getValue(Int);
}
- Range from(const RawRange &Init) {
+ template <typename T> Range from(const RawRangeT<T> &Init) {
return Range(from(Init.first), from(Init.second));
}
- RangeSet from(const RawRangeSet &Init) {
+ template <typename T>
+ RangeSet from(RawRangeSetT<T> Init, APSIntType Ty = APSIntTy<BaseType>) {
RangeSet RangeSet = F.getEmptySet();
for (const auto &Raw : Init) {
RangeSet = F.add(RangeSet, from(Raw));
@@ -211,9 +252,20 @@ template <typename BaseType> class RangeSetTest : public testing::Test {
RawRangeSet RawExpected) {
wrap(&Self::checkDeleteImpl, Point, RawFrom, RawExpected);
}
-};
-} // namespace
+ void checkCastToImpl(RangeSet What, APSIntType Ty, RangeSet Expected) {
+ RangeSet Result = F.castTo(What, Ty);
+ EXPECT_EQ(Result, Expected)
+ << "while casting " << toString(What) << " to " << Ty;
+ }
+
+ template <typename From, typename To>
+ void checkCastTo(RawRangeSetT<From> What, RawRangeSetT<To> Expected) {
+ static constexpr APSIntType FromTy = APSIntTy<From>;
+ static constexpr APSIntType ToTy = APSIntTy<To>;
+ this->checkCastToImpl(from(What, FromTy), ToTy, from(Expected, ToTy));
+ }
+};
using IntTypes = ::testing::Types<int8_t, uint8_t, int16_t, uint16_t, int32_t,
uint32_t, int64_t, uint64_t>;
@@ -594,3 +646,437 @@ TYPED_TEST(RangeSetTest, RangeSetUniteTest) {
{{MIN, MIN}, {A, C}, {C + 2, D}, {MAX - 1, MAX}});
// clang-format on
}
+
+template <typename From, typename To> struct CastType {
+ using FromType = From;
+ using ToType = To;
+};
+
+template <typename Type>
+class RangeSetCastToNoopTest : public RangeSetTest<typename Type::FromType> {};
+template <typename Type>
+class RangeSetCastToPromotionTest
+ : public RangeSetTest<typename Type::FromType> {};
+template <typename Type>
+class RangeSetCastToTruncationTest
+ : public RangeSetTest<typename Type::FromType> {};
+template <typename Type>
+class RangeSetCastToConversionTest
+ : public RangeSetTest<typename Type::FromType> {};
+template <typename Type>
+class RangeSetCastToPromotionConversionTest
+ : public RangeSetTest<typename Type::FromType> {};
+template <typename Type>
+class RangeSetCastToTruncationConversionTest
+ : public RangeSetTest<typename Type::FromType> {};
+
+using NoopCastTypes =
+ ::testing::Types<CastType<int8_t, int8_t>, CastType<uint8_t, uint8_t>,
+ CastType<int16_t, int16_t>, CastType<uint16_t, uint16_t>,
+ CastType<int32_t, int32_t>, CastType<uint32_t, uint32_t>,
+ CastType<int64_t, int64_t>, CastType<uint64_t, uint64_t>>;
+
+using PromotionCastTypes =
+ ::testing::Types<CastType<int8_t, int16_t>, CastType<int8_t, int32_t>,
+ CastType<int8_t, int64_t>, CastType<uint8_t, uint16_t>,
+ CastType<uint8_t, uint32_t>, CastType<uint8_t, uint64_t>,
+ CastType<int16_t, int32_t>, CastType<int16_t, int64_t>,
+ CastType<uint16_t, uint32_t>, CastType<uint16_t, uint64_t>,
+ CastType<int32_t, int64_t>, CastType<uint32_t, uint64_t>>;
+
+using TruncationCastTypes =
+ ::testing::Types<CastType<int16_t, int8_t>, CastType<uint16_t, uint8_t>,
+ CastType<int32_t, int16_t>, CastType<int32_t, int8_t>,
+ CastType<uint32_t, uint16_t>, CastType<uint32_t, uint8_t>,
+ CastType<int64_t, int32_t>, CastType<int64_t, int16_t>,
+ CastType<int64_t, int8_t>, CastType<uint64_t, uint32_t>,
+ CastType<uint64_t, uint16_t>, CastType<uint64_t, uint8_t>>;
+
+using ConversionCastTypes =
+ ::testing::Types<CastType<int8_t, uint8_t>, CastType<uint8_t, int8_t>,
+ CastType<int16_t, uint16_t>, CastType<uint16_t, int16_t>,
+ CastType<int32_t, uint32_t>, CastType<uint32_t, int32_t>,
+ CastType<int64_t, uint64_t>, CastType<uint64_t, int64_t>>;
+
+using PromotionConversionCastTypes =
+ ::testing::Types<CastType<int8_t, uint16_t>, CastType<int8_t, uint32_t>,
+ CastType<int8_t, uint64_t>, CastType<uint8_t, int16_t>,
+ CastType<uint8_t, int32_t>, CastType<uint8_t, int64_t>,
+ CastType<int16_t, uint32_t>, CastType<int16_t, uint64_t>,
+ CastType<uint16_t, int32_t>, CastType<uint16_t, int64_t>,
+ CastType<int32_t, uint64_t>, CastType<uint32_t, int64_t>>;
+
+using TruncationConversionCastTypes =
+ ::testing::Types<CastType<int16_t, uint8_t>, CastType<uint16_t, int8_t>,
+ CastType<int32_t, uint16_t>, CastType<int32_t, uint8_t>,
+ CastType<uint32_t, int16_t>, CastType<uint32_t, int8_t>,
+ CastType<int64_t, uint32_t>, CastType<int64_t, uint16_t>,
+ CastType<int64_t, uint8_t>, CastType<uint64_t, int32_t>,
+ CastType<uint64_t, int16_t>, CastType<uint64_t, int8_t>>;
+
+TYPED_TEST_SUITE(RangeSetCastToNoopTest, NoopCastTypes);
+TYPED_TEST_SUITE(RangeSetCastToPromotionTest, PromotionCastTypes);
+TYPED_TEST_SUITE(RangeSetCastToTruncationTest, TruncationCastTypes);
+TYPED_TEST_SUITE(RangeSetCastToConversionTest, ConversionCastTypes);
+TYPED_TEST_SUITE(RangeSetCastToPromotionConversionTest,
+ PromotionConversionCastTypes);
+TYPED_TEST_SUITE(RangeSetCastToTruncationConversionTest,
+ TruncationConversionCastTypes);
+
+TYPED_TEST(RangeSetCastToNoopTest, RangeSetCastToNoopTest) {
+ // Just to reduce the verbosity.
+ using F = typename TypeParam::FromType; // From
+ using T = typename TypeParam::ToType; // To
+
+ using TV = TestValues<F>;
+ constexpr auto MIN = TV::MIN;
+ constexpr auto MAX = TV::MAX;
+ constexpr auto MID = TV::MID;
+ constexpr auto B = TV::B;
+ constexpr auto C = TV::C;
+ // One point
+ this->template checkCastTo<F, T>({{MIN, MIN}}, {{MIN, MIN}});
+ this->template checkCastTo<F, T>({{MAX, MAX}}, {{MAX, MAX}});
+ this->template checkCastTo<F, T>({{MID, MID}}, {{MID, MID}});
+ this->template checkCastTo<F, T>({{B, B}}, {{B, B}});
+ this->template checkCastTo<F, T>({{C, C}}, {{C, C}});
+ // Two points
+ this->template checkCastTo<F, T>({{MIN, MIN}, {MAX, MAX}},
+ {{MIN, MIN}, {MAX, MAX}});
+ this->template checkCastTo<F, T>({{MIN, MIN}, {B, B}}, {{MIN, MIN}, {B, B}});
+ this->template checkCastTo<F, T>({{MID, MID}, {MAX, MAX}},
+ {{MID, MID}, {MAX, MAX}});
+ this->template checkCastTo<F, T>({{C, C}, {MAX, MAX}}, {{C, C}, {MAX, MAX}});
+ this->template checkCastTo<F, T>({{MID, MID}, {C, C}}, {{MID, MID}, {C, C}});
+ this->template checkCastTo<F, T>({{B, B}, {MID, MID}}, {{B, B}, {MID, MID}});
+ this->template checkCastTo<F, T>({{B, B}, {C, C}}, {{B, B}, {C, C}});
+ // One range
+ this->template checkCastTo<F, T>({{MIN, MAX}}, {{MIN, MAX}});
+ this->template checkCastTo<F, T>({{MIN, MID}}, {{MIN, MID}});
+ this->template checkCastTo<F, T>({{MID, MAX}}, {{MID, MAX}});
+ this->template checkCastTo<F, T>({{B, MAX}}, {{B, MAX}});
+ this->template checkCastTo<F, T>({{C, MAX}}, {{C, MAX}});
+ this->template checkCastTo<F, T>({{MIN, C}}, {{MIN, C}});
+ this->template checkCastTo<F, T>({{MIN, B}}, {{MIN, B}});
+ this->template checkCastTo<F, T>({{B, C}}, {{B, C}});
+ // Two ranges
+ this->template checkCastTo<F, T>({{MIN, B}, {C, MAX}}, {{MIN, B}, {C, MAX}});
+ this->template checkCastTo<F, T>({{B, MID}, {C, MAX}}, {{B, MID}, {C, MAX}});
+ this->template checkCastTo<F, T>({{MIN, B}, {MID, C}}, {{MIN, B}, {MID, C}});
+}
+
+TYPED_TEST(RangeSetCastToPromotionTest, Test) {
+ // Just to reduce the verbosity.
+ using F = typename TypeParam::FromType; // From
+ using T = typename TypeParam::ToType; // To
+
+ using TV = TestValues<F>;
+ constexpr auto MIN = TV::MIN;
+ constexpr auto MAX = TV::MAX;
+ constexpr auto MID = TV::MID;
+ constexpr auto B = TV::B;
+ constexpr auto C = TV::C;
+ // One point
+ this->template checkCastTo<F, T>({{MIN, MIN}}, {{MIN, MIN}});
+ this->template checkCastTo<F, T>({{MAX, MAX}}, {{MAX, MAX}});
+ this->template checkCastTo<F, T>({{MID, MID}}, {{MID, MID}});
+ this->template checkCastTo<F, T>({{B, B}}, {{B, B}});
+ this->template checkCastTo<F, T>({{C, C}}, {{C, C}});
+ // Two points
+ this->template checkCastTo<F, T>({{MIN, MIN}, {MAX, MAX}},
+ {{MIN, MIN}, {MAX, MAX}});
+ this->template checkCastTo<F, T>({{MIN, MIN}, {B, B}}, {{MIN, MIN}, {B, B}});
+ this->template checkCastTo<F, T>({{MID, MID}, {MAX, MAX}},
+ {{MID, MID}, {MAX, MAX}});
+ this->template checkCastTo<F, T>({{C, C}, {MAX, MAX}}, {{C, C}, {MAX, MAX}});
+ this->template checkCastTo<F, T>({{MID, MID}, {C, C}}, {{MID, MID}, {C, C}});
+ this->template checkCastTo<F, T>({{B, B}, {MID, MID}}, {{B, B}, {MID, MID}});
+ this->template checkCastTo<F, T>({{B, B}, {C, C}}, {{B, B}, {C, C}});
+ // One range
+ this->template checkCastTo<F, T>({{MIN, MAX}}, {{MIN, MAX}});
+ this->template checkCastTo<F, T>({{MIN, MID}}, {{MIN, MID}});
+ this->template checkCastTo<F, T>({{MID, MAX}}, {{MID, MAX}});
+ this->template checkCastTo<F, T>({{B, MAX}}, {{B, MAX}});
+ this->template checkCastTo<F, T>({{C, MAX}}, {{C, MAX}});
+ this->template checkCastTo<F, T>({{MIN, C}}, {{MIN, C}});
+ this->template checkCastTo<F, T>({{MIN, B}}, {{MIN, B}});
+ this->template checkCastTo<F, T>({{B, C}}, {{B, C}});
+ // Two ranges
+ this->template checkCastTo<F, T>({{MIN, B}, {C, MAX}}, {{MIN, B}, {C, MAX}});
+ this->template checkCastTo<F, T>({{B, MID}, {C, MAX}}, {{B, MID}, {C, MAX}});
+ this->template checkCastTo<F, T>({{MIN, B}, {MID, C}}, {{MIN, B}, {MID, C}});
+}
+
+TYPED_TEST(RangeSetCastToTruncationTest, Test) {
+ // Just to reduce the verbosity.
+ using F = typename TypeParam::FromType; // From
+ using T = typename TypeParam::ToType; // To
+
+ using TV = TestValues<F>;
+ constexpr auto MIN = TV::MIN;
+ constexpr auto MAX = TV::MAX;
+ constexpr auto MID = TV::MID;
+ constexpr auto B = TV::B;
+ constexpr auto C = TV::C;
+ // One point
+ //
+ // NOTE: We can't use ToMIN, ToMAX, ... everywhere. That would be incorrect:
+ // int16(-32768, 32767) -> int8(-128, 127),
+ // aka (MIN, MAX) -> (ToMIN, ToMAX) // OK.
+ // int16(-32768, -32768) -> int8(-128, -128),
+ // aka (MIN, MIN) -> (ToMIN, ToMIN) // NOK.
+ // int16(-32768,-32768) -> int8(0, 0),
+ // aka (MIN, MIN) -> ((int8)MIN, (int8)MIN) // OK.
+ this->template checkCastTo<F, T>({{MIN, MIN}}, {{MIN, MIN}});
+ this->template checkCastTo<F, T>({{MAX, MAX}}, {{MAX, MAX}});
+ this->template checkCastTo<F, T>({{MID, MID}}, {{MID, MID}});
+ this->template checkCastTo<F, T>({{B, B}}, {{B, B}});
+ this->template checkCastTo<F, T>({{C, C}}, {{C, C}});
+ // Two points
+ // Use `if constexpr` here.
+ if (is_signed_v<F>) {
+ this->template checkCastTo<F, T>({{MIN, MIN}, {MAX, MAX}}, {{MAX, MIN}});
+ this->template checkCastTo<F, T>({{MID, MID}, {MAX, MAX}}, {{MAX, MID}});
+ } else {
+ this->template checkCastTo<F, T>({{MIN, MIN}, {MAX, MAX}},
+ {{MIN, MIN}, {MAX, MAX}});
+ this->template checkCastTo<F, T>({{MID, MID}, {MAX, MAX}},
+ {{MID, MID}, {MAX, MAX}});
+ }
+ this->template checkCastTo<F, T>({{MIN, MIN}, {B, B}}, {{MIN, MIN}, {B, B}});
+ this->template checkCastTo<F, T>({{C, C}, {MAX, MAX}}, {{C, C}, {MAX, MAX}});
+ this->template checkCastTo<F, T>({{MID, MID}, {C, C}}, {{MID, MID}, {C, C}});
+ this->template checkCastTo<F, T>({{B, B}, {MID, MID}}, {{B, B}, {MID, MID}});
+ this->template checkCastTo<F, T>({{B, B}, {C, C}}, {{B, B}, {C, C}});
+ // One range
+ constexpr auto ToMIN = TestValues<T>::MIN;
+ constexpr auto ToMAX = TestValues<T>::MAX;
+ this->template checkCastTo<F, T>({{MIN, MAX}}, {{ToMIN, ToMAX}});
+ this->template checkCastTo<F, T>({{MIN, MID}}, {{ToMIN, ToMAX}});
+ this->template checkCastTo<F, T>({{MID, MAX}}, {{ToMIN, ToMAX}});
+ this->template checkCastTo<F, T>({{B, MAX}}, {{ToMIN, ToMAX}});
+ this->template checkCastTo<F, T>({{C, MAX}}, {{ToMIN, ToMAX}});
+ this->template checkCastTo<F, T>({{MIN, C}}, {{ToMIN, ToMAX}});
+ this->template checkCastTo<F, T>({{MIN, B}}, {{ToMIN, ToMAX}});
+ this->template checkCastTo<F, T>({{B, C}}, {{ToMIN, ToMAX}});
+ // Two ranges
+ this->template checkCastTo<F, T>({{MIN, B}, {C, MAX}}, {{ToMIN, ToMAX}});
+ this->template checkCastTo<F, T>({{B, MID}, {C, MAX}}, {{ToMIN, ToMAX}});
+ this->template checkCastTo<F, T>({{MIN, B}, {MID, C}}, {{ToMIN, ToMAX}});
+ constexpr auto XAAA = TV::XAAA;
+ constexpr auto X555 = TV::X555;
+ constexpr auto ZA = TV::template XAAATruncZeroOf<T>;
+ constexpr auto Z5 = TV::template X555TruncZeroOf<T>;
+ this->template checkCastTo<F, T>({{XAAA, ZA}, {X555, Z5}},
+ {{ToMIN, 0}, {X555, ToMAX}});
+ // Use `if constexpr` here.
+ if (is_signed_v<F>) {
+ // One range
+ this->template checkCastTo<F, T>({{XAAA, ZA}}, {{XAAA, 0}});
+ // Two ranges
+ this->template checkCastTo<F, T>({{XAAA, ZA}, {1, 42}}, {{XAAA, 42}});
+ } else {
+ // One range
+ this->template checkCastTo<F, T>({{XAAA, ZA}}, {{0, 0}, {XAAA, ToMAX}});
+ // Two ranges
+ this->template checkCastTo<F, T>({{1, 42}, {XAAA, ZA}},
+ {{0, 42}, {XAAA, ToMAX}});
+ }
+ constexpr auto FromA = TV::FromA;
+ constexpr auto ToA = TV::ToA;
+ constexpr auto FromB = TV::FromB;
+ constexpr auto ToB = TV::ToB;
+ // int16 -> int8
+ // (0x00'01, 0x00'05)U(0xFF'01, 0xFF'05) casts to
+ // (0x01, 0x05)U(0x01, 0x05) unites to
+ // (0x01, 0x05)
+ this->template checkCastTo<F, T>({{FromA, ToA}, {FromB, ToB}},
+ {{FromA, ToA}});
+}
+
+TYPED_TEST(RangeSetCastToConversionTest, Test) {
+ // Just to reduce the verbosity.
+ using F = typename TypeParam::FromType; // From
+ using T = typename TypeParam::ToType; // To
+
+ using TV = TestValues<F>;
+ constexpr auto MIN = TV::MIN;
+ constexpr auto MAX = TV::MAX;
+ constexpr auto MID = TV::MID;
+ constexpr auto B = TV::B;
+ constexpr auto C = TV::C;
+ // One point
+ this->template checkCastTo<F, T>({{MIN, MIN}}, {{MIN, MIN}});
+ this->template checkCastTo<F, T>({{MAX, MAX}}, {{MAX, MAX}});
+ this->template checkCastTo<F, T>({{MID, MID}}, {{MID, MID}});
+ this->template checkCastTo<F, T>({{B, B}}, {{B, B}});
+ this->template checkCastTo<F, T>({{C, C}}, {{C, C}});
+ // Two points
+ this->template checkCastTo<F, T>({{MIN, MIN}, {MAX, MAX}}, {{MAX, MIN}});
+ this->template checkCastTo<F, T>({{MID, MID}, {MAX, MAX}},
+ {{MID, MID}, {MAX, MAX}});
+ this->template checkCastTo<F, T>({{MIN, MIN}, {B, B}}, {{MIN, MIN}, {B, B}});
+ this->template checkCastTo<F, T>({{C, C}, {MAX, MAX}}, {{C, C}, {MAX, MAX}});
+ this->template checkCastTo<F, T>({{MID, MID}, {C, C}}, {{MID, MID}, {C, C}});
+ this->template checkCastTo<F, T>({{B, B}, {MID, MID}}, {{B, B}, {MID, MID}});
+ this->template checkCastTo<F, T>({{B, B}, {C, C}}, {{B, B}, {C, C}});
+ // One range
+ constexpr auto ToMIN = TestValues<T>::MIN;
+ constexpr auto ToMAX = TestValues<T>::MAX;
+ this->template checkCastTo<F, T>({{MIN, MAX}}, {{ToMIN, ToMAX}});
+ this->template checkCastTo<F, T>({{MIN, MID}},
+ {{ToMIN, ToMIN}, {MIN, ToMAX}});
+ this->template checkCastTo<F, T>({{MID, MAX}}, {{MID, MAX}});
+ this->template checkCastTo<F, T>({{B, MAX}}, {{ToMIN, MAX}, {B, ToMAX}});
+ this->template checkCastTo<F, T>({{C, MAX}}, {{C, MAX}});
+ this->template checkCastTo<F, T>({{MIN, C}}, {{ToMIN, C}, {MIN, ToMAX}});
+ this->template checkCastTo<F, T>({{MIN, B}}, {{MIN, B}});
+ this->template checkCastTo<F, T>({{B, C}}, {{ToMIN, C}, {B, ToMAX}});
+ // Two ranges
+ this->template checkCastTo<F, T>({{MIN, B}, {C, MAX}}, {{C, B}});
+ this->template checkCastTo<F, T>({{B, MID}, {C, MAX}},
+ {{MID, MID}, {C, MAX}, {B, ToMAX}});
+ this->template checkCastTo<F, T>({{MIN, B}, {MID, C}}, {{MID, C}, {MIN, B}});
+}
+
+TYPED_TEST(RangeSetCastToPromotionConversionTest, Test) {
+ // Just to reduce the verbosity.
+ using F = typename TypeParam::FromType; // From
+ using T = typename TypeParam::ToType; // To
+
+ using TV = TestValues<F>;
+ constexpr auto MIN = TV::MIN;
+ constexpr auto MAX = TV::MAX;
+ constexpr auto MID = TV::MID;
+ constexpr auto B = TV::B;
+ constexpr auto C = TV::C;
+ // One point
+ this->template checkCastTo<F, T>({{MIN, MIN}}, {{MIN, MIN}});
+ this->template checkCastTo<F, T>({{MAX, MAX}}, {{MAX, MAX}});
+ this->template checkCastTo<F, T>({{MID, MID}}, {{MID, MID}});
+ this->template checkCastTo<F, T>({{B, B}}, {{B, B}});
+ this->template checkCastTo<F, T>({{C, C}}, {{C, C}});
+ // Two points
+ this->template checkCastTo<F, T>({{MIN, MIN}, {MAX, MAX}},
+ {{MAX, MAX}, {MIN, MIN}});
+ this->template checkCastTo<F, T>({{MIN, MIN}, {B, B}}, {{MIN, MIN}, {B, B}});
+ this->template checkCastTo<F, T>({{MID, MID}, {MAX, MAX}},
+ {{MID, MID}, {MAX, MAX}});
+ this->template checkCastTo<F, T>({{C, C}, {MAX, MAX}}, {{C, C}, {MAX, MAX}});
+ this->template checkCastTo<F, T>({{MID, MID}, {C, C}}, {{MID, MID}, {C, C}});
+ this->template checkCastTo<F, T>({{B, B}, {MID, MID}}, {{B, B}, {MID, MID}});
+ this->template checkCastTo<F, T>({{B, B}, {C, C}}, {{B, B}, {C, C}});
+
+ // Use `if constexpr` here.
+ if (is_signed_v<F>) {
+ // One range
+ this->template checkCastTo<F, T>({{MIN, MAX}}, {{0, MAX}, {MIN, -1}});
+ this->template checkCastTo<F, T>({{MIN, MID}}, {{0, 0}, {MIN, -1}});
+ this->template checkCastTo<F, T>({{MID, MAX}}, {{0, MAX}});
+ this->template checkCastTo<F, T>({{B, MAX}}, {{0, MAX}, {B, -1}});
+ this->template checkCastTo<F, T>({{C, MAX}}, {{C, MAX}});
+ this->template checkCastTo<F, T>({{MIN, C}}, {{0, C}, {MIN, -1}});
+ this->template checkCastTo<F, T>({{MIN, B}}, {{MIN, B}});
+ this->template checkCastTo<F, T>({{B, C}}, {{0, C}, {B, -1}});
+ // Two ranges
+ this->template checkCastTo<F, T>({{MIN, B}, {C, MAX}},
+ {{C, MAX}, {MIN, B}});
+ this->template checkCastTo<F, T>({{B, MID}, {C, MAX}},
+ {{0, 0}, {C, MAX}, {B, -1}});
+ this->template checkCastTo<F, T>({{MIN, B}, {MID, C}}, {{0, C}, {MIN, B}});
+ } else {
+ // One range
+ this->template checkCastTo<F, T>({{MIN, MAX}}, {{MIN, MAX}});
+ this->template checkCastTo<F, T>({{MIN, MID}}, {{MIN, MID}});
+ this->template checkCastTo<F, T>({{MID, MAX}}, {{MID, MAX}});
+ this->template checkCastTo<F, T>({{B, MAX}}, {{B, MAX}});
+ this->template checkCastTo<F, T>({{C, MAX}}, {{C, MAX}});
+ this->template checkCastTo<F, T>({{MIN, C}}, {{MIN, C}});
+ this->template checkCastTo<F, T>({{MIN, B}}, {{MIN, B}});
+ this->template checkCastTo<F, T>({{B, C}}, {{B, C}});
+ // Two ranges
+ this->template checkCastTo<F, T>({{MIN, B}, {C, MAX}},
+ {{MIN, B}, {C, MAX}});
+ this->template checkCastTo<F, T>({{B, MID}, {C, MAX}},
+ {{B, MID}, {C, MAX}});
+ this->template checkCastTo<F, T>({{MIN, B}, {MID, C}},
+ {{MIN, B}, {MID, C}});
+ }
+}
+
+TYPED_TEST(RangeSetCastToTruncationConversionTest, Test) {
+ // Just to reduce the verbosity.
+ using F = typename TypeParam::FromType; // From
+ using T = typename TypeParam::ToType; // To
+
+ using TV = TestValues<F>;
+ constexpr auto MIN = TV::MIN;
+ constexpr auto MAX = TV::MAX;
+ constexpr auto MID = TV::MID;
+ constexpr auto B = TV::B;
+ constexpr auto C = TV::C;
+ // One point
+ this->template checkCastTo<F, T>({{MIN, MIN}}, {{MIN, MIN}});
+ this->template checkCastTo<F, T>({{MAX, MAX}}, {{MAX, MAX}});
+ this->template checkCastTo<F, T>({{MID, MID}}, {{MID, MID}});
+ this->template checkCastTo<F, T>({{B, B}}, {{B, B}});
+ this->template checkCastTo<F, T>({{C, C}}, {{C, C}});
+ // Two points
+ // Use `if constexpr` here.
+ if (is_signed_v<F>) {
+ this->template checkCastTo<F, T>({{MIN, MIN}, {MAX, MAX}},
+ {{MIN, MIN}, {MAX, MAX}});
+ this->template checkCastTo<F, T>({{MID, MID}, {MAX, MAX}},
+ {{MID, MID}, {MAX, MAX}});
+ } else {
+ this->template checkCastTo<F, T>({{MIN, MIN}, {MAX, MAX}}, {{MAX, MIN}});
+ this->template checkCastTo<F, T>({{MID, MID}, {MAX, MAX}}, {{MAX, MIN}});
+ }
+ this->template checkCastTo<F, T>({{MIN, MIN}, {B, B}}, {{MIN, MIN}, {B, B}});
+ this->template checkCastTo<F, T>({{C, C}, {MAX, MAX}}, {{C, C}, {MAX, MAX}});
+ this->template checkCastTo<F, T>({{MID, MID}, {C, C}}, {{MID, MID}, {C, C}});
+ this->template checkCastTo<F, T>({{B, B}, {MID, MID}}, {{B, B}, {MID, MID}});
+ this->template checkCastTo<F, T>({{B, B}, {C, C}}, {{B, B}, {C, C}});
+ // One range
+ constexpr auto ToMIN = TestValues<T>::MIN;
+ constexpr auto ToMAX = TestValues<T>::MAX;
+ this->template checkCastTo<F, T>({{MIN, MAX}}, {{ToMIN, ToMAX}});
+ this->template checkCastTo<F, T>({{MIN, MID}}, {{ToMIN, ToMAX}});
+ this->template checkCastTo<F, T>({{MID, MAX}}, {{ToMIN, ToMAX}});
+ this->template checkCastTo<F, T>({{B, MAX}}, {{ToMIN, ToMAX}});
+ this->template checkCastTo<F, T>({{C, MAX}}, {{ToMIN, ToMAX}});
+ this->template checkCastTo<F, T>({{MIN, C}}, {{ToMIN, ToMAX}});
+ this->template checkCastTo<F, T>({{MIN, B}}, {{ToMIN, ToMAX}});
+ this->template checkCastTo<F, T>({{B, C}}, {{ToMIN, ToMAX}});
+ // Two ranges
+ this->template checkCastTo<F, T>({{MIN, B}, {C, MAX}}, {{ToMIN, ToMAX}});
+ this->template checkCastTo<F, T>({{B, MID}, {C, MAX}}, {{ToMIN, ToMAX}});
+ this->template checkCastTo<F, T>({{MIN, B}, {MID, C}}, {{ToMIN, ToMAX}});
+ constexpr auto XAAA = TV::XAAA;
+ constexpr auto X555 = TV::X555;
+ constexpr auto ZA = TV::template XAAATruncZeroOf<T>;
+ constexpr auto Z5 = TV::template X555TruncZeroOf<T>;
+ this->template checkCastTo<F, T>({{XAAA, ZA}, {X555, Z5}},
+ {{ToMIN, 0}, {X555, ToMAX}});
+ // Use `if constexpr` here.
+ if (is_signed_v<F>) {
+ // One range
+ this->template checkCastTo<F, T>({{XAAA, ZA}}, {{0, 0}, {XAAA, ToMAX}});
+ // Two ranges
+ this->template checkCastTo<F, T>({{XAAA, ZA}, {1, 42}},
+ {{0, 42}, {XAAA, ToMAX}});
+ } else {
+ // One range
+ this->template checkCastTo<F, T>({{XAAA, ZA}}, {{XAAA, 0}});
+ // Two ranges
+ this->template checkCastTo<F, T>({{1, 42}, {XAAA, ZA}}, {{XAAA, 42}});
+ }
+ constexpr auto FromA = TV::FromA;
+ constexpr auto ToA = TV::ToA;
+ constexpr auto FromB = TV::FromB;
+ constexpr auto ToB = TV::ToB;
+ this->template checkCastTo<F, T>({{FromA, ToA}, {FromB, ToB}},
+ {{FromA, ToA}});
+}
+
+} // namespace
More information about the cfe-commits
mailing list