Add Castsable::IsAnyOf<T1, T2, ...>()

This makes it a little easier to check if an object is one of any of the
types provided. Updated Type query functions to make use of IsAnyOf.
Added tests.

Change-Id: I12ea62b32042b6675d998ab85b86f2fe15861330
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/44462
Commit-Queue: Antonio Maiorano <amaiorano@google.com>
Reviewed-by: Ben Clayton <bclayton@google.com>
diff --git a/src/castable.h b/src/castable.h
index 072872a..0dada0d 100644
--- a/src/castable.h
+++ b/src/castable.h
@@ -83,6 +83,11 @@
   /// The unique TypeInfo for the type T.
   static const TypeInfo info;
 };
+
+// Forward declaration
+template <typename TO_FIRST, typename... TO_REST>
+struct IsAnyOf;
+
 }  // namespace detail
 
 /// @returns true if `obj` is a valid pointer, and is of, or derives from the
@@ -106,6 +111,14 @@
   return obj->TypeInfo().Is(TypeInfo::Of<std::remove_const_t<TO>>());
 }
 
+/// @returns true if `obj` is of, or derives from any of the `TO`
+/// classes.
+/// @param obj the object to cast from
+template <typename... TO, typename FROM>
+inline bool IsAnyOf(FROM* obj) {
+  return detail::IsAnyOf<TO...>::Exec(obj);
+}
+
 /// @returns obj dynamically cast to the type `TO` or `nullptr` if
 /// this object does not derive from `TO`.
 /// @param obj the object to cast from
@@ -137,6 +150,13 @@
     return tint::Is<TO>(this);
   }
 
+  /// @returns true if this object is of, or derives from any of the `TO`
+  /// classes.
+  template <typename... TO>
+  inline bool IsAnyOf() const {
+    return tint::IsAnyOf<TO...>(this);
+  }
+
   /// @returns this object dynamically cast to the type `TO` or `nullptr` if
   /// this object does not derive from `TO`.
   template <typename TO>
@@ -199,6 +219,13 @@
     return tint::Is<TO>(static_cast<const CLASS*>(this));
   }
 
+  /// @returns true if this object is of, or derives from any of the `TO`
+  /// classes.
+  template <typename... TO>
+  inline bool IsAnyOf() const {
+    return tint::IsAnyOf<TO...>(static_cast<const CLASS*>(this));
+  }
+
   /// @returns this object dynamically cast to the type `TO` or `nullptr` if
   /// this object does not derive from `TO`.
   template <typename TO>
@@ -214,6 +241,30 @@
   }
 };
 
+namespace detail {
+/// Helper for Castable::IsAnyOf
+template <typename TO_FIRST, typename... TO_REST>
+struct IsAnyOf {
+  /// @param obj castable object to test
+  /// @returns true if `obj` is of, or derives from any of `[TO_FIRST,
+  /// ...TO_REST]`
+  template <typename FROM>
+  static bool Exec(FROM* obj) {
+    return Is<TO_FIRST>(obj) || IsAnyOf<TO_REST...>::Exec(obj);
+  }
+};
+/// Terminal specialization
+template <typename TO>
+struct IsAnyOf<TO> {
+  /// @param obj castable object to test
+  /// @returns true if `obj` is of, or derives from TO
+  template <typename FROM>
+  static bool Exec(FROM* obj) {
+    return Is<TO>(obj);
+  }
+};
+}  // namespace detail
+
 }  // namespace tint
 
 TINT_CASTABLE_POP_DISABLE_WARNINGS();
diff --git a/src/castable_test.cc b/src/castable_test.cc
index 3330757..05f5e4a 100644
--- a/src/castable_test.cc
+++ b/src/castable_test.cc
@@ -73,6 +73,27 @@
   ASSERT_TRUE(gecko->Is<Reptile>());
 }
 
+TEST(CastableBase, IsAnyOf) {
+  std::unique_ptr<CastableBase> frog = std::make_unique<Frog>();
+  std::unique_ptr<CastableBase> bear = std::make_unique<Bear>();
+  std::unique_ptr<CastableBase> gecko = std::make_unique<Gecko>();
+
+  ASSERT_TRUE((frog->IsAnyOf<Animal, Mammal, Amphibian, Reptile>()));
+  ASSERT_TRUE((frog->IsAnyOf<Mammal, Amphibian>()));
+  ASSERT_TRUE((frog->IsAnyOf<Amphibian, Reptile>()));
+  ASSERT_FALSE((frog->IsAnyOf<Mammal, Reptile>()));
+
+  ASSERT_TRUE((bear->IsAnyOf<Animal, Mammal, Amphibian, Reptile>()));
+  ASSERT_TRUE((bear->IsAnyOf<Mammal, Amphibian>()));
+  ASSERT_TRUE((bear->IsAnyOf<Mammal, Reptile>()));
+  ASSERT_FALSE((bear->IsAnyOf<Amphibian, Reptile>()));
+
+  ASSERT_TRUE((gecko->IsAnyOf<Animal, Mammal, Amphibian, Reptile>()));
+  ASSERT_TRUE((gecko->IsAnyOf<Mammal, Reptile>()));
+  ASSERT_TRUE((gecko->IsAnyOf<Amphibian, Reptile>()));
+  ASSERT_FALSE((gecko->IsAnyOf<Mammal, Amphibian>()));
+}
+
 TEST(CastableBase, As) {
   std::unique_ptr<CastableBase> frog = std::make_unique<Frog>();
   std::unique_ptr<CastableBase> bear = std::make_unique<Bear>();
diff --git a/src/type/type.cc b/src/type/type.cc
index 39f309c..ecd79ea 100644
--- a/src/type/type.cc
+++ b/src/type/type.cc
@@ -71,7 +71,7 @@
 }
 
 bool Type::is_scalar() const {
-  return is_float_scalar() || is_integer_scalar() || Is<Bool>();
+  return IsAnyOf<F32, U32, I32, Bool>();
 }
 
 bool Type::is_float_scalar() const {
@@ -91,7 +91,7 @@
 }
 
 bool Type::is_integer_scalar() const {
-  return Is<U32>() || Is<I32>();
+  return IsAnyOf<U32, I32>();
 }
 
 bool Type::is_unsigned_integer_vector() const {
@@ -123,7 +123,7 @@
 }
 
 bool Type::is_handle() const {
-  return Is<type::Sampler>() || Is<type::Texture>();
+  return IsAnyOf<Sampler, Texture>();
 }
 
 }  // namespace type