Fix ClassIDs for DLL usage

If ClassID::Of<T>() is used inside tint and used outside tint for the same type, and tint is built as a DLL, then the address of the Unique<T>::token can resolve to different addresses, entirely breaking Castable.

Replace address-of for a unique symbol with a single static counter that's incremented for each use of TINT_INSTANTIATE_CLASS_ID().

Change-Id: I40dc81b1273110291d90a1d5ec05428f7e703c6a
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/42460
Reviewed-by: Antonio Maiorano <amaiorano@google.com>
Commit-Queue: Ben Clayton <bclayton@google.com>
diff --git a/src/castable.h b/src/castable.h
index 3599c34..9528d96 100644
--- a/src/castable.h
+++ b/src/castable.h
@@ -22,28 +22,36 @@
 
 namespace tint {
 
-/// Helper macro to instantiate the ClassID for `CLASS`.
-#define TINT_INSTANTIATE_CLASS_ID(CLASS) \
-  template <>                            \
-  const char ::tint::ClassID::Unique<CLASS>::token = 0
+class ClassID;
+
+/// Helper macro to instantiate the TypeInfo<T> template for `CLASS`.
+#define TINT_INSTANTIATE_CLASS_ID(CLASS)                \
+  template <>                                           \
+  const tint::ClassID tint::TypeInfo<CLASS>::class_id { \
+    tint::ClassID::New()                                \
+  }
+
+/// TypeInfo holds type information for the type T.
+/// TINT_INSTANTIATE_CLASS_ID() must be defined in a .cpp file for each type
+/// `T`.
+template <typename T>
+struct TypeInfo {
+  static const ClassID class_id;
+};
 
 /// ClassID represents a unique, comparable identifier for a C++ type.
 class ClassID {
- private:
-  /// Helper template that holds a single static field, which is used by Of()
-  /// to obtain a unique identifier by taking the field's address.
-  template <typename T>
-  struct Unique {
-    static const char token;
-  };
-
  public:
+  /// @returns a new and unique ClassID
+  static inline ClassID New() {
+    static uintptr_t next(0);
+    return ClassID(next++);
+  }
+
   /// @returns the unique ClassID for the type T.
   template <typename T>
-  static ClassID Of() {
-    // Take the address of a static variable to produce a unique identifier for
-    // the type T.
-    return ClassID{reinterpret_cast<uintptr_t>(&Unique<T>::token)};
+  static inline ClassID Of() {
+    return TypeInfo<T>::class_id;
   }
 
   /// Equality operator
@@ -81,7 +89,7 @@
 
   /// @returns true if this object is of, or derives from the class `TO`
   template <typename TO>
-  bool Is() const {
+  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;
@@ -146,13 +154,13 @@
   /// @returns true if this object is of, or derives from a class with the
   /// ClassID `id`.
   /// @param id the ClassID to test for
-  bool Is(ClassID id) const override {
+  inline bool Is(ClassID id) const override {
     return ClassID::Of<CLASS>() == id || BASE::Is(id);
   }
 
   /// @returns true if this object is of, or derives from the class `TO`
   template <typename TO>
-  bool Is() const {
+  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;