ast: Support decoration kind hierarchies

Instead of just having a single `DecorationKind` for the first
derivation from `Decoration`, have a `DecorationKind` for every
decoration type.

Add `Decoration::IsKind()` to test whether the decoration is of, or
derives from the given kind.

Note, this change is originally by bclayton@ from
https://dawn-review.googlesource.com/c/tint/+/33201

R=bclayton@google.com

Bug: tint:287
Change-Id: I69b51dfaa3f82ef4d61cda383b2f98f401013429
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/33280
Commit-Queue: dan sinclair <dsinclair@chromium.org>
Commit-Queue: Ben Clayton <bclayton@google.com>
Auto-Submit: dan sinclair <dsinclair@chromium.org>
Reviewed-by: Ben Clayton <bclayton@google.com>
diff --git a/src/ast/array_decoration.cc b/src/ast/array_decoration.cc
index 8a0753d..d7f22f3 100644
--- a/src/ast/array_decoration.cc
+++ b/src/ast/array_decoration.cc
@@ -21,11 +21,17 @@
 namespace tint {
 namespace ast {
 
-ArrayDecoration::ArrayDecoration(const Source& source)
-    : Decoration(Kind, source) {}
+constexpr const DecorationKind ArrayDecoration::Kind;
+
+ArrayDecoration::ArrayDecoration(DecorationKind kind, const Source& source)
+    : Decoration(kind, source) {}
 
 ArrayDecoration::~ArrayDecoration() = default;
 
+bool ArrayDecoration::IsKind(DecorationKind kind) const {
+  return kind == Kind;
+}
+
 bool ArrayDecoration::IsStride() const {
   return false;
 }
diff --git a/src/ast/array_decoration.h b/src/ast/array_decoration.h
index 473dc5b..77b6164 100644
--- a/src/ast/array_decoration.h
+++ b/src/ast/array_decoration.h
@@ -30,10 +30,15 @@
 class ArrayDecoration : public Decoration {
  public:
   /// The kind of decoration that this type represents
-  static constexpr DecorationKind Kind = DecorationKind::kArray;
+  static constexpr const DecorationKind Kind = DecorationKind::kArray;
 
   ~ArrayDecoration() override;
 
+  /// @param kind the decoration kind
+  /// @return true if this Decoration is of the (or derives from) the given
+  /// kind.
+  bool IsKind(DecorationKind kind) const override;
+
   /// @returns true if this is a stride decoration
   virtual bool IsStride() const;
 
@@ -42,8 +47,9 @@
 
  protected:
   /// Constructor
+  /// @param kind the decoration kind
   /// @param source the source of this decoration
-  explicit ArrayDecoration(const Source& source);
+  ArrayDecoration(DecorationKind kind, const Source& source);
 };
 
 /// A list of array decorations
diff --git a/src/ast/binding_decoration.cc b/src/ast/binding_decoration.cc
index 64632a6..d0c5063 100644
--- a/src/ast/binding_decoration.cc
+++ b/src/ast/binding_decoration.cc
@@ -17,11 +17,17 @@
 namespace tint {
 namespace ast {
 
+constexpr const DecorationKind BindingDecoration::Kind;
+
 BindingDecoration::BindingDecoration(uint32_t val, const Source& source)
-    : VariableDecoration(source), value_(val) {}
+    : VariableDecoration(Kind, source), value_(val) {}
 
 BindingDecoration::~BindingDecoration() = default;
 
+bool BindingDecoration::IsKind(DecorationKind kind) const {
+  return kind == Kind || VariableDecoration::IsKind(kind);
+}
+
 bool BindingDecoration::IsBinding() const {
   return true;
 }
diff --git a/src/ast/binding_decoration.h b/src/ast/binding_decoration.h
index b6633c2..4f65eab 100644
--- a/src/ast/binding_decoration.h
+++ b/src/ast/binding_decoration.h
@@ -25,12 +25,20 @@
 /// A binding decoration
 class BindingDecoration : public VariableDecoration {
  public:
+  /// The kind of decoration that this type represents
+  static constexpr const DecorationKind Kind = DecorationKind::kBinding;
+
   /// constructor
   /// @param value the binding value
   /// @param source the source of this decoration
   BindingDecoration(uint32_t value, const Source& source);
   ~BindingDecoration() override;
 
+  /// @param kind the decoration kind
+  /// @return true if this Decoration is of the (or derives from) the given
+  /// kind.
+  bool IsKind(DecorationKind kind) const override;
+
   /// @returns true if this is a binding decoration
   bool IsBinding() const override;
 
diff --git a/src/ast/builtin_decoration.cc b/src/ast/builtin_decoration.cc
index 6dc0bbd..7cb49c7 100644
--- a/src/ast/builtin_decoration.cc
+++ b/src/ast/builtin_decoration.cc
@@ -17,11 +17,17 @@
 namespace tint {
 namespace ast {
 
+constexpr const DecorationKind BuiltinDecoration::Kind;
+
 BuiltinDecoration::BuiltinDecoration(Builtin builtin, const Source& source)
-    : VariableDecoration(source), builtin_(builtin) {}
+    : VariableDecoration(Kind, source), builtin_(builtin) {}
 
 BuiltinDecoration::~BuiltinDecoration() = default;
 
+bool BuiltinDecoration::IsKind(DecorationKind kind) const {
+  return kind == Kind || VariableDecoration::IsKind(kind);
+}
+
 bool BuiltinDecoration::IsBuiltin() const {
   return true;
 }
diff --git a/src/ast/builtin_decoration.h b/src/ast/builtin_decoration.h
index b8169cb..75d40a5 100644
--- a/src/ast/builtin_decoration.h
+++ b/src/ast/builtin_decoration.h
@@ -24,12 +24,20 @@
 /// A builtin decoration
 class BuiltinDecoration : public VariableDecoration {
  public:
+  /// The kind of decoration that this type represents
+  static constexpr const DecorationKind Kind = DecorationKind::kBuiltin;
+
   /// constructor
   /// @param builtin the builtin value
   /// @param source the source of this decoration
   BuiltinDecoration(Builtin builtin, const Source& source);
   ~BuiltinDecoration() override;
 
+  /// @param kind the decoration kind
+  /// @return true if this Decoration is of the (or derives from) the given
+  /// kind.
+  bool IsKind(DecorationKind kind) const override;
+
   /// @returns true if this is a builtin decoration
   bool IsBuiltin() const override;
 
diff --git a/src/ast/constant_id_decoration.cc b/src/ast/constant_id_decoration.cc
index 9be39fd..145b80d 100644
--- a/src/ast/constant_id_decoration.cc
+++ b/src/ast/constant_id_decoration.cc
@@ -17,11 +17,17 @@
 namespace tint {
 namespace ast {
 
+constexpr const DecorationKind ConstantIdDecoration::Kind;
+
 ConstantIdDecoration::ConstantIdDecoration(uint32_t val, const Source& source)
-    : VariableDecoration(source), value_(val) {}
+    : VariableDecoration(Kind, source), value_(val) {}
 
 ConstantIdDecoration::~ConstantIdDecoration() = default;
 
+bool ConstantIdDecoration::IsKind(DecorationKind kind) const {
+  return kind == Kind || VariableDecoration::IsKind(kind);
+}
+
 bool ConstantIdDecoration::IsConstantId() const {
   return true;
 }
diff --git a/src/ast/constant_id_decoration.h b/src/ast/constant_id_decoration.h
index fbd8ba0..3fb8ef8 100644
--- a/src/ast/constant_id_decoration.h
+++ b/src/ast/constant_id_decoration.h
@@ -24,12 +24,20 @@
 /// A constant id decoration
 class ConstantIdDecoration : public VariableDecoration {
  public:
+  /// The kind of decoration that this type represents
+  static constexpr const DecorationKind Kind = DecorationKind::kConstantId;
+
   /// constructor
   /// @param val the constant_id value
   /// @param source the source of this decoration
   ConstantIdDecoration(uint32_t val, const Source& source);
   ~ConstantIdDecoration() override;
 
+  /// @param kind the decoration kind
+  /// @return true if this Decoration is of the (or derives from) the given
+  /// kind.
+  bool IsKind(DecorationKind kind) const override;
+
   /// @returns true if this is a constant_id decoration
   bool IsConstantId() const override;
 
diff --git a/src/ast/decoration.cc b/src/ast/decoration.cc
index 1b17eb8..2ce8037 100644
--- a/src/ast/decoration.cc
+++ b/src/ast/decoration.cc
@@ -23,14 +23,30 @@
   switch (data) {
     case DecorationKind::kArray:
       return out << "array";
+    case DecorationKind::kStride:
+      return out << "stride";
     case DecorationKind::kFunction:
       return out << "function";
+    case DecorationKind::kStage:
+      return out << "stage";
+    case DecorationKind::kWorkgroup:
+      return out << "workgroup";
     case DecorationKind::kStruct:
       return out << "struct";
     case DecorationKind::kStructMember:
       return out << "struct member";
+    case DecorationKind::kStructMemberOffset:
+      return out << "offset";
     case DecorationKind::kVariable:
       return out << "variable";
+    case DecorationKind::kBinding:
+      return out << "binding";
+    case DecorationKind::kBuiltin:
+      return out << "builtin";
+    case DecorationKind::kConstantId:
+      return out << "constant_id";
+    case DecorationKind::kLocation:
+      return out << "location";
   }
   return out << "<unknown>";
 }
diff --git a/src/ast/decoration.h b/src/ast/decoration.h
index 9b43a08..a07c0bf 100644
--- a/src/ast/decoration.h
+++ b/src/ast/decoration.h
@@ -28,10 +28,18 @@
 /// The decoration kind enumerator
 enum class DecorationKind {
   kArray,
+  /*|*/ kStride,
   kFunction,
+  /*|*/ kStage,
+  /*|*/ kWorkgroup,
   kStruct,
   kStructMember,
-  kVariable
+  /*|*/ kStructMemberOffset,
+  kVariable,
+  /*|*/ kBinding,
+  /*|*/ kBuiltin,
+  /*|*/ kConstantId,
+  /*|*/ kLocation,
 };
 
 std::ostream& operator<<(std::ostream& out, DecorationKind data);
@@ -41,15 +49,20 @@
  public:
   ~Decoration() override;
 
-  /// @return the decoration kind
-  DecorationKind GetKind() const { return kind_; }
+  /// @param kind the decoration kind
+  /// @return true if this Decoration is of the (or derives from) the given
+  /// kind.
+  virtual bool IsKind(DecorationKind kind) const = 0;
 
   /// @return true if this decoration is of (or derives from) type |TO|
   template <typename TO>
   bool Is() const {
-    return GetKind() == TO::Kind;
+    return IsKind(TO::Kind);
   }
 
+  /// @return the decoration kind
+  DecorationKind GetKind() const { return kind_; }
+
   /// @returns true if the node is valid
   bool IsValid() const override;
 
diff --git a/src/ast/decoration_test.cc b/src/ast/decoration_test.cc
index 104dbb7..3548654 100644
--- a/src/ast/decoration_test.cc
+++ b/src/ast/decoration_test.cc
@@ -15,11 +15,24 @@
 #include "src/ast/decoration.h"
 
 #include <sstream>
+#include <unordered_map>
+#include <unordered_set>
 #include <utility>
 
 #include "src/ast/array_decoration.h"
+#include "src/ast/binding_decoration.h"
+#include "src/ast/builtin_decoration.h"
 #include "src/ast/constant_id_decoration.h"
+#include "src/ast/function_decoration.h"
+#include "src/ast/location_decoration.h"
+#include "src/ast/stage_decoration.h"
+#include "src/ast/stride_decoration.h"
+#include "src/ast/struct_decoration.h"
+#include "src/ast/struct_member_decoration.h"
+#include "src/ast/struct_member_offset_decoration.h"
 #include "src/ast/test_helper.h"
+#include "src/ast/variable_decoration.h"
+#include "src/ast/workgroup_decoration.h"
 
 namespace tint {
 namespace ast {
@@ -47,6 +60,120 @@
   EXPECT_FALSE(decoration->Is<ArrayDecoration>());
 }
 
+TEST_F(DecorationTest, Kinds) {
+  EXPECT_EQ(ArrayDecoration::Kind, DecorationKind::kArray);
+  EXPECT_EQ(StrideDecoration::Kind, DecorationKind::kStride);
+  EXPECT_EQ(FunctionDecoration::Kind, DecorationKind::kFunction);
+  EXPECT_EQ(StageDecoration::Kind, DecorationKind::kStage);
+  EXPECT_EQ(WorkgroupDecoration::Kind, DecorationKind::kWorkgroup);
+  EXPECT_EQ(StructDecoration::Kind, DecorationKind::kStruct);
+  EXPECT_EQ(StructMemberDecoration::Kind, DecorationKind::kStructMember);
+  EXPECT_EQ(StructMemberOffsetDecoration::Kind,
+            DecorationKind::kStructMemberOffset);
+  EXPECT_EQ(VariableDecoration::Kind, DecorationKind::kVariable);
+  EXPECT_EQ(BindingDecoration::Kind, DecorationKind::kBinding);
+  EXPECT_EQ(BuiltinDecoration::Kind, DecorationKind::kBuiltin);
+  EXPECT_EQ(ConstantIdDecoration::Kind, DecorationKind::kConstantId);
+  EXPECT_EQ(LocationDecoration::Kind, DecorationKind::kLocation);
+}
+
+TEST_F(DecorationTest, IsKind) {
+  std::vector<DecorationKind> all_kinds{
+      DecorationKind::kArray,        DecorationKind::kStride,
+      DecorationKind::kFunction,     DecorationKind::kStage,
+      DecorationKind::kWorkgroup,    DecorationKind::kStruct,
+      DecorationKind::kStructMember, DecorationKind::kStructMemberOffset,
+      DecorationKind::kVariable,     DecorationKind::kBinding,
+      DecorationKind::kBuiltin,      DecorationKind::kConstantId,
+      DecorationKind::kLocation,
+  };
+
+  struct ExpectedKinds {
+    DecorationKind kind;
+    std::unordered_set<DecorationKind> expect_true;
+  };
+
+  //  kArray
+  //   | kStride
+  //  kFunction
+  //   | kStage
+  //   | kWorkgroup
+  //  kStruct
+  //  kStructMember
+  //   | kStructMemberOffset
+  //  kVariable
+  //   | kBinding
+  //   | kBuiltin
+  //   | kConstantId
+  //   | kLocation
+  std::unordered_map<DecorationKind, std::unordered_set<DecorationKind>>
+      kind_is{
+          {
+              DecorationKind::kStride,
+              {DecorationKind::kArray, DecorationKind::kStride},
+          },
+          {
+              DecorationKind::kStage,
+              {DecorationKind::kFunction, DecorationKind::kStage},
+          },
+          {
+              DecorationKind::kWorkgroup,
+              {DecorationKind::kFunction, DecorationKind::kWorkgroup},
+          },
+          {
+              DecorationKind::kStruct,
+              {DecorationKind::kStruct},
+          },
+          {
+              DecorationKind::kStructMemberOffset,
+              {DecorationKind::kStructMember,
+               DecorationKind::kStructMemberOffset},
+          },
+          {
+              DecorationKind::kBinding,
+              {DecorationKind::kVariable, DecorationKind::kBinding},
+          },
+          {
+              DecorationKind::kBuiltin,
+              {DecorationKind::kVariable, DecorationKind::kBuiltin},
+          },
+          {
+              DecorationKind::kConstantId,
+              {DecorationKind::kVariable, DecorationKind::kConstantId},
+          },
+          {
+              DecorationKind::kLocation,
+              {DecorationKind::kVariable, DecorationKind::kLocation},
+          },
+      };
+
+  auto check = [&](Decoration* d) {
+    auto& is_set = kind_is[d->GetKind()];
+    for (auto test : all_kinds) {
+      bool is_kind = is_set.find(test) != is_set.end();
+      EXPECT_EQ(d->IsKind(test), is_kind)
+          << "decoration: " << d->GetKind() << " IsKind(" << test << ")";
+    }
+  };
+  StrideDecoration stride(0, {});
+  StageDecoration stage(PipelineStage::kNone, {});
+  WorkgroupDecoration workgroup(0, {});
+  StructMemberOffsetDecoration struct_member_offset(0, {});
+  BindingDecoration binding(0, {});
+  BuiltinDecoration builtin(Builtin::kNone, {});
+  ConstantIdDecoration constant_id(0, {});
+  LocationDecoration location(0, {});
+
+  check(&stride);
+  check(&stage);
+  check(&workgroup);
+  check(&struct_member_offset);
+  check(&binding);
+  check(&builtin);
+  check(&constant_id);
+  check(&location);
+}
+
 }  // namespace
 }  // namespace ast
 }  // namespace tint
diff --git a/src/ast/function_decoration.cc b/src/ast/function_decoration.cc
index f414c9d..fc1409a 100644
--- a/src/ast/function_decoration.cc
+++ b/src/ast/function_decoration.cc
@@ -22,11 +22,18 @@
 namespace tint {
 namespace ast {
 
-FunctionDecoration::FunctionDecoration(const Source& source)
-    : Decoration(Kind, source) {}
+constexpr const DecorationKind FunctionDecoration::Kind;
+
+FunctionDecoration::FunctionDecoration(DecorationKind kind,
+                                       const Source& source)
+    : Decoration(kind, source) {}
 
 FunctionDecoration::~FunctionDecoration() = default;
 
+bool FunctionDecoration::IsKind(DecorationKind kind) const {
+  return kind == Kind;
+}
+
 bool FunctionDecoration::IsStage() const {
   return false;
 }
diff --git a/src/ast/function_decoration.h b/src/ast/function_decoration.h
index 360991d..1d778fa 100644
--- a/src/ast/function_decoration.h
+++ b/src/ast/function_decoration.h
@@ -31,10 +31,15 @@
 class FunctionDecoration : public Decoration {
  public:
   /// The kind of decoration that this type represents
-  static constexpr DecorationKind Kind = DecorationKind::kFunction;
+  static constexpr const DecorationKind Kind = DecorationKind::kFunction;
 
   ~FunctionDecoration() override;
 
+  /// @param kind the decoration kind
+  /// @return true if this Decoration is of the (or derives from) the given
+  /// kind.
+  bool IsKind(DecorationKind kind) const override;
+
   /// @returns true if this is a stage decoration
   virtual bool IsStage() const;
   /// @returns true if this is a workgroup decoration
@@ -47,8 +52,9 @@
 
  protected:
   /// Constructor
+  /// @param kind the decoration kind
   /// @param source the source of this decoration
-  explicit FunctionDecoration(const Source& source);
+  FunctionDecoration(DecorationKind kind, const Source& source);
 };
 
 /// A list of function decorations
diff --git a/src/ast/location_decoration.cc b/src/ast/location_decoration.cc
index 948fc8e..0aa7f1a 100644
--- a/src/ast/location_decoration.cc
+++ b/src/ast/location_decoration.cc
@@ -17,11 +17,17 @@
 namespace tint {
 namespace ast {
 
+constexpr const DecorationKind LocationDecoration::Kind;
+
 LocationDecoration::LocationDecoration(uint32_t val, const Source& source)
-    : VariableDecoration(source), value_(val) {}
+    : VariableDecoration(Kind, source), value_(val) {}
 
 LocationDecoration::~LocationDecoration() = default;
 
+bool LocationDecoration::IsKind(DecorationKind kind) const {
+  return kind == Kind || VariableDecoration::IsKind(kind);
+}
+
 bool LocationDecoration::IsLocation() const {
   return true;
 }
diff --git a/src/ast/location_decoration.h b/src/ast/location_decoration.h
index d5474da..abbfefe 100644
--- a/src/ast/location_decoration.h
+++ b/src/ast/location_decoration.h
@@ -25,12 +25,20 @@
 /// A location decoration
 class LocationDecoration : public VariableDecoration {
  public:
+  /// The kind of decoration that this type represents
+  static constexpr const DecorationKind Kind = DecorationKind::kLocation;
+
   /// constructor
   /// @param value the location value
   /// @param source the source of this decoration
   explicit LocationDecoration(uint32_t value, const Source& source);
   ~LocationDecoration() override;
 
+  /// @param kind the decoration kind
+  /// @return true if this Decoration is of the (or derives from) the given
+  /// kind.
+  bool IsKind(DecorationKind kind) const override;
+
   /// @returns true if this is a location decoration
   bool IsLocation() const override;
 
diff --git a/src/ast/set_decoration.cc b/src/ast/set_decoration.cc
index 08d5312..9d1d502 100644
--- a/src/ast/set_decoration.cc
+++ b/src/ast/set_decoration.cc
@@ -18,7 +18,7 @@
 namespace ast {
 
 SetDecoration::SetDecoration(uint32_t val, const Source& source)
-    : VariableDecoration(source), value_(val) {}
+    : VariableDecoration(Kind, source), value_(val) {}
 
 SetDecoration::~SetDecoration() = default;
 
diff --git a/src/ast/stage_decoration.cc b/src/ast/stage_decoration.cc
index 838042d..d1f20a2 100644
--- a/src/ast/stage_decoration.cc
+++ b/src/ast/stage_decoration.cc
@@ -17,11 +17,17 @@
 namespace tint {
 namespace ast {
 
+constexpr const DecorationKind StageDecoration::Kind;
+
 StageDecoration::StageDecoration(ast::PipelineStage stage, const Source& source)
-    : FunctionDecoration(source), stage_(stage) {}
+    : FunctionDecoration(Kind, source), stage_(stage) {}
 
 StageDecoration::~StageDecoration() = default;
 
+bool StageDecoration::IsKind(DecorationKind kind) const {
+  return kind == Kind || FunctionDecoration::IsKind(kind);
+}
+
 bool StageDecoration::IsStage() const {
   return true;
 }
diff --git a/src/ast/stage_decoration.h b/src/ast/stage_decoration.h
index 188170b..b7aa3b8 100644
--- a/src/ast/stage_decoration.h
+++ b/src/ast/stage_decoration.h
@@ -24,12 +24,20 @@
 /// A workgroup decoration
 class StageDecoration : public FunctionDecoration {
  public:
+  /// The kind of decoration that this type represents
+  static constexpr const DecorationKind Kind = DecorationKind::kStage;
+
   /// constructor
   /// @param stage the pipeline stage
   /// @param source the source of this decoration
   StageDecoration(ast::PipelineStage stage, const Source& source);
   ~StageDecoration() override;
 
+  /// @param kind the decoration kind
+  /// @return true if this Decoration is of the (or derives from) the given
+  /// kind.
+  bool IsKind(DecorationKind kind) const override;
+
   /// @returns true if this is a stage decoration
   bool IsStage() const override;
 
diff --git a/src/ast/stride_decoration.cc b/src/ast/stride_decoration.cc
index 879fd5d..58ac1a5 100644
--- a/src/ast/stride_decoration.cc
+++ b/src/ast/stride_decoration.cc
@@ -17,8 +17,14 @@
 namespace tint {
 namespace ast {
 
+constexpr const DecorationKind StrideDecoration::Kind;
+
 StrideDecoration::StrideDecoration(uint32_t stride, const Source& source)
-    : ArrayDecoration(source), stride_(stride) {}
+    : ArrayDecoration(Kind, source), stride_(stride) {}
+
+bool StrideDecoration::IsKind(DecorationKind kind) const {
+  return kind == Kind || ArrayDecoration::IsKind(kind);
+}
 
 bool StrideDecoration::IsStride() const {
   return true;
diff --git a/src/ast/stride_decoration.h b/src/ast/stride_decoration.h
index f6ee3ff..9b00563 100644
--- a/src/ast/stride_decoration.h
+++ b/src/ast/stride_decoration.h
@@ -27,12 +27,20 @@
 /// A stride decoration
 class StrideDecoration : public ArrayDecoration {
  public:
+  /// The kind of decoration that this type represents
+  static constexpr const DecorationKind Kind = DecorationKind::kStride;
+
   /// constructor
   /// @param stride the stride value
   /// @param source the source of this decoration
   StrideDecoration(uint32_t stride, const Source& source);
   ~StrideDecoration() override;
 
+  /// @param kind the decoration kind
+  /// @return true if this Decoration is of the (or derives from) the given
+  /// kind.
+  bool IsKind(DecorationKind kind) const override;
+
   /// @returns true if this is a stride decoration
   bool IsStride() const override;
 
diff --git a/src/ast/struct_block_decoration.cc b/src/ast/struct_block_decoration.cc
index 585646d..b95299e 100644
--- a/src/ast/struct_block_decoration.cc
+++ b/src/ast/struct_block_decoration.cc
@@ -18,7 +18,7 @@
 namespace ast {
 
 StructBlockDecoration::StructBlockDecoration(const Source& source)
-    : StructDecoration(source) {}
+    : StructDecoration(Kind, source) {}
 
 StructBlockDecoration::~StructBlockDecoration() = default;
 
diff --git a/src/ast/struct_decoration.cc b/src/ast/struct_decoration.cc
index 99c5cc1..340ac14 100644
--- a/src/ast/struct_decoration.cc
+++ b/src/ast/struct_decoration.cc
@@ -17,10 +17,16 @@
 namespace tint {
 namespace ast {
 
-StructDecoration::StructDecoration(const Source& source)
-    : Decoration(Kind, source) {}
+constexpr const DecorationKind StructDecoration::Kind;
+
+StructDecoration::StructDecoration(DecorationKind kind, const Source& source)
+    : Decoration(kind, source) {}
 
 StructDecoration::~StructDecoration() = default;
 
+bool StructDecoration::IsKind(DecorationKind kind) const {
+  return kind == Kind;
+}
+
 }  // namespace ast
 }  // namespace tint
diff --git a/src/ast/struct_decoration.h b/src/ast/struct_decoration.h
index 0b889c2..26b1901 100644
--- a/src/ast/struct_decoration.h
+++ b/src/ast/struct_decoration.h
@@ -28,17 +28,23 @@
 class StructDecoration : public Decoration {
  public:
   /// The kind of decoration that this type represents
-  static constexpr DecorationKind Kind = DecorationKind::kStruct;
+  static constexpr const DecorationKind Kind = DecorationKind::kStruct;
 
   ~StructDecoration() override;
 
+  /// @param kind the decoration kind
+  /// @return true if this Decoration is of the (or derives from) the given
+  /// kind.
+  bool IsKind(DecorationKind kind) const override;
+
   /// @returns true if this is a block struct
   virtual bool IsBlock() const = 0;
 
  protected:
   /// Constructor
+  /// @param kind the decoration kind
   /// @param source the source of this decoration
-  explicit StructDecoration(const Source& source);
+  explicit StructDecoration(DecorationKind kind, const Source& source);
 };
 
 /// List of struct decorations
diff --git a/src/ast/struct_member_decoration.cc b/src/ast/struct_member_decoration.cc
index aaf8d34..efbae62 100644
--- a/src/ast/struct_member_decoration.cc
+++ b/src/ast/struct_member_decoration.cc
@@ -21,11 +21,18 @@
 namespace tint {
 namespace ast {
 
-StructMemberDecoration::StructMemberDecoration(const Source& source)
-    : Decoration(DecorationKind::kStructMember, source) {}
+constexpr const DecorationKind StructMemberDecoration::Kind;
+
+StructMemberDecoration::StructMemberDecoration(DecorationKind kind,
+                                               const Source& source)
+    : Decoration(kind, source) {}
 
 StructMemberDecoration::~StructMemberDecoration() = default;
 
+bool StructMemberDecoration::IsKind(DecorationKind kind) const {
+  return kind == Kind;
+}
+
 bool StructMemberDecoration::IsOffset() const {
   return false;
 }
diff --git a/src/ast/struct_member_decoration.h b/src/ast/struct_member_decoration.h
index f31e6d6..188c777 100644
--- a/src/ast/struct_member_decoration.h
+++ b/src/ast/struct_member_decoration.h
@@ -30,10 +30,15 @@
 class StructMemberDecoration : public Decoration {
  public:
   /// The kind of decoration that this type represents
-  static constexpr DecorationKind Kind = DecorationKind::kStructMember;
+  static constexpr const DecorationKind Kind = DecorationKind::kStructMember;
 
   ~StructMemberDecoration() override;
 
+  /// @param kind the decoration kind
+  /// @return true if this Decoration is of the (or derives from) the given
+  /// kind.
+  bool IsKind(DecorationKind kind) const override;
+
   /// @returns true if this is an offset decoration
   virtual bool IsOffset() const;
 
@@ -42,8 +47,9 @@
 
  protected:
   /// Constructor
+  /// @param kind the decoration kind
   /// @param source the source of this decoration
-  explicit StructMemberDecoration(const Source& source);
+  explicit StructMemberDecoration(DecorationKind kind, const Source& source);
 };
 
 /// A list of struct member decorations
diff --git a/src/ast/struct_member_offset_decoration.cc b/src/ast/struct_member_offset_decoration.cc
index b3dec36..b2ad2b4 100644
--- a/src/ast/struct_member_offset_decoration.cc
+++ b/src/ast/struct_member_offset_decoration.cc
@@ -17,9 +17,15 @@
 namespace tint {
 namespace ast {
 
+constexpr const DecorationKind StructMemberOffsetDecoration::Kind;
+
 StructMemberOffsetDecoration::StructMemberOffsetDecoration(uint32_t offset,
                                                            const Source& source)
-    : StructMemberDecoration(source), offset_(offset) {}
+    : StructMemberDecoration(Kind, source), offset_(offset) {}
+
+bool StructMemberOffsetDecoration::IsKind(DecorationKind kind) const {
+  return kind == Kind || StructMemberDecoration::IsKind(kind);
+}
 
 bool StructMemberOffsetDecoration::IsOffset() const {
   return true;
diff --git a/src/ast/struct_member_offset_decoration.h b/src/ast/struct_member_offset_decoration.h
index 8e679fb..6e5a5a6 100644
--- a/src/ast/struct_member_offset_decoration.h
+++ b/src/ast/struct_member_offset_decoration.h
@@ -27,12 +27,21 @@
 /// A struct member offset decoration
 class StructMemberOffsetDecoration : public StructMemberDecoration {
  public:
+  /// The kind of decoration that this type represents
+  static constexpr const DecorationKind Kind =
+      DecorationKind::kStructMemberOffset;
+
   /// constructor
   /// @param offset the offset value
   /// @param source the source of this decoration
   StructMemberOffsetDecoration(uint32_t offset, const Source& source);
   ~StructMemberOffsetDecoration() override;
 
+  /// @param kind the decoration kind
+  /// @return true if this Decoration is of the (or derives from) the given
+  /// kind.
+  bool IsKind(DecorationKind kind) const override;
+
   /// @returns true if this is an offset decoration
   bool IsOffset() const override;
 
diff --git a/src/ast/variable_decoration.cc b/src/ast/variable_decoration.cc
index da68d27..e37d5bd 100644
--- a/src/ast/variable_decoration.cc
+++ b/src/ast/variable_decoration.cc
@@ -25,11 +25,18 @@
 namespace tint {
 namespace ast {
 
-VariableDecoration::VariableDecoration(const Source& source)
-    : Decoration(Kind, source) {}
+constexpr const DecorationKind VariableDecoration::Kind;
+
+VariableDecoration::VariableDecoration(DecorationKind kind,
+                                       const Source& source)
+    : Decoration(kind, source) {}
 
 VariableDecoration::~VariableDecoration() = default;
 
+bool VariableDecoration::IsKind(DecorationKind kind) const {
+  return kind == Kind;
+}
+
 bool VariableDecoration::IsBinding() const {
   return false;
 }
diff --git a/src/ast/variable_decoration.h b/src/ast/variable_decoration.h
index b1c1fa0..6c44242 100644
--- a/src/ast/variable_decoration.h
+++ b/src/ast/variable_decoration.h
@@ -35,10 +35,15 @@
 class VariableDecoration : public Decoration {
  public:
   /// The kind of decoration that this type represents
-  static constexpr DecorationKind Kind = DecorationKind::kVariable;
+  static constexpr const DecorationKind Kind = DecorationKind::kVariable;
 
   ~VariableDecoration() override;
 
+  /// @param kind the decoration kind
+  /// @return true if this Decoration is of the (or derives from) the given
+  /// kind.
+  bool IsKind(DecorationKind kind) const override;
+
   /// @returns true if this is a binding decoration
   virtual bool IsBinding() const;
   /// @returns true if this is a builtin decoration
@@ -63,8 +68,9 @@
 
  protected:
   /// Constructor
+  /// @param kind the decoration kind
   /// @param source the source of this decoration
-  explicit VariableDecoration(const Source& source);
+  VariableDecoration(DecorationKind kind, const Source& source);
 };
 
 /// A list of variable decorations
diff --git a/src/ast/workgroup_decoration.cc b/src/ast/workgroup_decoration.cc
index 7f6b38c..e9ecab6 100644
--- a/src/ast/workgroup_decoration.cc
+++ b/src/ast/workgroup_decoration.cc
@@ -17,22 +17,28 @@
 namespace tint {
 namespace ast {
 
+constexpr const DecorationKind WorkgroupDecoration::Kind;
+
 WorkgroupDecoration::WorkgroupDecoration(uint32_t x, const Source& source)
-    : FunctionDecoration(source), x_(x) {}
+    : FunctionDecoration(Kind, source), x_(x) {}
 
 WorkgroupDecoration::WorkgroupDecoration(uint32_t x,
                                          uint32_t y,
                                          const Source& source)
-    : FunctionDecoration(source), x_(x), y_(y) {}
+    : FunctionDecoration(Kind, source), x_(x), y_(y) {}
 
 WorkgroupDecoration::WorkgroupDecoration(uint32_t x,
                                          uint32_t y,
                                          uint32_t z,
                                          const Source& source)
-    : FunctionDecoration(source), x_(x), y_(y), z_(z) {}
+    : FunctionDecoration(Kind, source), x_(x), y_(y), z_(z) {}
 
 WorkgroupDecoration::~WorkgroupDecoration() = default;
 
+bool WorkgroupDecoration::IsKind(DecorationKind kind) const {
+  return kind == Kind || FunctionDecoration::IsKind(kind);
+}
+
 bool WorkgroupDecoration::IsWorkgroup() const {
   return true;
 }
diff --git a/src/ast/workgroup_decoration.h b/src/ast/workgroup_decoration.h
index d5e6226..cbfb5aa 100644
--- a/src/ast/workgroup_decoration.h
+++ b/src/ast/workgroup_decoration.h
@@ -27,6 +27,9 @@
 /// A workgroup decoration
 class WorkgroupDecoration : public FunctionDecoration {
  public:
+  /// The kind of decoration that this type represents
+  static constexpr const DecorationKind Kind = DecorationKind::kWorkgroup;
+
   /// constructor
   /// @param x the workgroup x dimension size
   /// @param source the source of this decoration
@@ -44,6 +47,11 @@
   WorkgroupDecoration(uint32_t x, uint32_t y, uint32_t z, const Source& source);
   ~WorkgroupDecoration() override;
 
+  /// @param kind the decoration kind
+  /// @return true if this Decoration is of the (or derives from) the given
+  /// kind.
+  bool IsKind(DecorationKind kind) const override;
+
   /// @returns true if this is a workgroup decoration
   bool IsWorkgroup() const override;