Castable: factor out Is and As to free standing functions

These can now also be called with nullptr and will return false or
nullptr respectively.

Change-Id: I5fcf292503dd718f8d3771c7c39c204ce03ff4f7
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/44461
Reviewed-by: Ben Clayton <bclayton@google.com>
Commit-Queue: Antonio Maiorano <amaiorano@google.com>
diff --git a/src/castable.h b/src/castable.h
index 956f4f6..072872a 100644
--- a/src/castable.h
+++ b/src/castable.h
@@ -83,9 +83,37 @@
   /// The unique TypeInfo for the type T.
   static const TypeInfo info;
 };
-
 }  // namespace detail
 
+/// @returns true if `obj` is a valid pointer, and is of, or derives from the
+/// class `TO`
+/// @param obj the object to test from
+template <typename TO, typename FROM>
+bool Is(FROM* obj) {
+  constexpr const bool downcast = std::is_base_of<FROM, TO>::value;
+  constexpr const bool upcast = std::is_base_of<TO, FROM>::value;
+  constexpr const bool nocast = std::is_same<FROM, TO>::value;
+  static_assert(upcast || downcast || nocast, "impossible cast");
+
+  if (obj == nullptr) {
+    return false;
+  }
+
+  if (upcast || nocast) {
+    return true;
+  }
+
+  return obj->TypeInfo().Is(TypeInfo::Of<std::remove_const_t<TO>>());
+}
+
+/// @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
+template <typename TO, typename FROM>
+inline TO* As(FROM* obj) {
+  return Is<TO>(obj) ? static_cast<TO*>(obj) : nullptr;
+}
+
 /// CastableBase is the base class for all Castable objects.
 /// It is not encouraged to directly derive from CastableBase without using the
 /// Castable helper template.
@@ -106,31 +134,21 @@
   /// @returns true if this object is of, or derives from the class `TO`
   template <typename TO>
   inline bool Is() const {
-    using FROM = CastableBase;
-    constexpr const bool downcast = std::is_base_of<FROM, TO>::value;
-    constexpr const bool upcast = std::is_base_of<TO, FROM>::value;
-    constexpr const bool nocast = std::is_same<FROM, TO>::value;
-    static_assert(upcast || downcast || nocast, "impossible cast");
-
-    if (upcast || nocast) {
-      return true;
-    }
-
-    return TypeInfo().Is(TypeInfo::Of<TO>());
+    return tint::Is<TO>(this);
   }
 
   /// @returns this object dynamically cast to the type `TO` or `nullptr` if
   /// this object does not derive from `TO`.
   template <typename TO>
   inline TO* As() {
-    return Is<TO>() ? static_cast<TO*>(this) : nullptr;
+    return tint::As<TO>(this);
   }
 
   /// @returns this object dynamically cast to the type `TO` or `nullptr` if
   /// this object does not derive from `TO`.
   template <typename TO>
   inline const TO* As() const {
-    return Is<TO>() ? static_cast<const TO*>(this) : nullptr;
+    return tint::As<const TO>(this);
   }
 
  protected:
@@ -178,46 +196,24 @@
   /// @returns true if this object is of, or derives from the class `TO`
   template <typename TO>
   inline bool Is() const {
-    using FROM = Castable;
-    constexpr const bool downcast = std::is_base_of<FROM, TO>::value;
-    constexpr const bool upcast = std::is_base_of<TO, FROM>::value;
-    constexpr const bool nocast = std::is_same<FROM, TO>::value;
-    static_assert(upcast || downcast || nocast, "impossible cast");
-
-    if (upcast || nocast) {
-      return true;
-    }
-
-    return TypeInfo().Is(TypeInfo::Of<TO>());
+    return tint::Is<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>
   inline TO* As() {
-    return Is<TO>() ? static_cast<TO*>(this) : nullptr;
+    return tint::As<TO>(this);
   }
 
   /// @returns this object dynamically cast to the type `TO` or `nullptr` if
   /// this object does not derive from `TO`.
   template <typename TO>
   inline const TO* As() const {
-    return Is<TO>() ? static_cast<const TO*>(this) : nullptr;
+    return tint::As<const TO>(this);
   }
 };
 
-/// As() dynamically casts `obj` to the target type `TO`.
-/// @returns the cast object, or nullptr if `obj` is `nullptr` or not of the
-/// type `TO`.
-/// @param obj the object to cast
-template <typename TO, typename FROM>
-inline TO* As(FROM* obj) {
-  if (obj == nullptr) {
-    return nullptr;
-  }
-  return obj->template As<TO>();
-}
-
 }  // namespace tint
 
 TINT_CASTABLE_POP_DISABLE_WARNINGS();