Add the ability to use property-based serialization for "cased" types.
authorJohn McCall <rjmccall@apple.com>
Mon, 16 Dec 2019 07:10:15 +0000 (02:10 -0500)
committerJohn McCall <rjmccall@apple.com>
Mon, 16 Dec 2019 18:33:59 +0000 (13:33 -0500)
This patch doesn't actually use this serialization for anything,
but follow-ups will move the current handling of various standard
types over to this.

12 files changed:
clang/include/clang/AST/AbstractBasicReader.h
clang/include/clang/AST/AbstractBasicWriter.h
clang/include/clang/AST/PropertiesBase.td
clang/include/clang/Basic/ASTNode.td [new file with mode: 0644]
clang/include/clang/Basic/CommentNodes.td
clang/include/clang/Basic/DeclNodes.td
clang/include/clang/Basic/StmtNodes.td
clang/include/clang/Basic/TypeNodes.td
clang/utils/TableGen/ASTTableGen.cpp
clang/utils/TableGen/ASTTableGen.h
clang/utils/TableGen/ClangASTNodesEmitter.cpp
clang/utils/TableGen/ClangASTPropertiesEmitter.cpp

index e4e5fe8..b18e89b 100644 (file)
@@ -37,6 +37,34 @@ inline T *makePointerFromOptional(Optional<T *> value) {
 // where TypeName is the name of a PropertyType node from PropertiesBase.td
 // and ValueType is the corresponding C++ type name.  The read method may
 // require one or more buffer arguments.
+//
+// In addition to the concrete type names, BasicReader is expected to
+// implement these methods:
+//
+//   template <class ValueType>
+//   Optional<ValueType> writeOptional();
+//
+//     Reads an optional value from the current property.
+//
+//   template <class ValueType>
+//   ArrayRef<ValueType> readArray(llvm::SmallVectorImpl<ValueType> &buffer);
+//
+//     Reads an array of values from the current property.
+//
+//   PropertyReader readObject();
+//
+//     Reads an object from the current property; the returned property
+//     reader will be subjected to a sequence of property reads and then
+//     discarded before any other properties are reader from the "outer"
+//     property reader (which need not be the same type).  The sub-reader
+//     will be used as if with the following code:
+//
+//       {
+//         auto &&widget = W.find("widget").readObject();
+//         auto kind = widget.find("kind").readWidgetKind();
+//         auto declaration = widget.find("declaration").readDeclRef();
+//         return Widget(kind, declaration);
+//       }
 
 // ReadDispatcher does type-based forwarding to one of the read methods
 // on the BasicReader passed in:
@@ -99,6 +127,10 @@ public:
     return asImpl();
   }
 
+  // Implement object reading by forwarding to this, collapsing the
+  // structure into a single data stream.
+  Impl &readObject() { return asImpl(); }
+
   template <class T>
   llvm::ArrayRef<T> readArray(llvm::SmallVectorImpl<T> &buffer) {
     assert(buffer.empty());
index a15a49b..499e5a6 100644 (file)
@@ -38,6 +38,33 @@ inline llvm::Optional<T*> makeOptionalFromPointer(T *value) {
 //   void write##TypeName(ValueType value);
 // where TypeName is the name of a PropertyType node from PropertiesBase.td
 // and ValueType is the corresponding C++ type name.
+//
+// In addition to the concrete property types, BasicWriter is expected
+// to implement these methods:
+//
+//   template <class ValueType>
+//   void writeOptional(Optional<ValueType> value);
+//
+//     Writes an optional value as the current property.
+//
+//   template <class ValueType>
+//   void writeArray(ArrayRef<ValueType> value);
+//
+//     Writes an array of values as the current property.
+//
+//   PropertyWriter writeObject();
+//
+//     Writes an object as the current property; the returned property
+//     writer will be subjected to a sequence of property writes and then
+//     discarded before any other properties are written to the "outer"
+//     property writer (which need not be the same type).  The sub-writer
+//     will be used as if with the following code:
+//
+//       {
+//         auto &&widget = W.find("widget").writeObject();
+//         widget.find("kind").writeWidgetKind(...);
+//         widget.find("declaration").writeDeclRef(...);
+//       }
 
 // WriteDispatcher is a template which does type-based forwarding to one
 // of the write methods of the BasicWriter passed in:
@@ -95,6 +122,10 @@ public:
     return asImpl();
   }
 
+  // Implement object writing by forwarding to this, collapsing the
+  // structure into a single data stream.
+  Impl &writeObject() { return asImpl(); }
+
   template <class T>
   void writeArray(llvm::ArrayRef<T> array) {
     asImpl().writeUInt32(array.size());
index 7d804e4..cdab032 100644 (file)
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-class ASTNode;
+class HasProperties;
 
 /// The type of the property.
 class PropertyType<string typeName = ""> {
@@ -151,7 +151,7 @@ class Optional<PropertyType element> : PropertyType {
 
 /// A property of an AST node.
 class Property<string name, PropertyType type> {
-  ASTNode Class;
+  HasProperties Class;
   string Name = name;
   PropertyType Type = type;
 
@@ -162,7 +162,7 @@ class Property<string name, PropertyType type> {
 
 /// A rule for creating objects of this type.
 class Creator<code create> {
-  ASTNode Class;
+  HasProperties Class;
 
   /// A function for creating values of this kind, expressed in terms of a
   /// variable `ctx` of type `ASTContext &`.  Must also refer to all of the
@@ -172,8 +172,42 @@ class Creator<code create> {
 
 /// A rule which overrides some of the normal rules.
 class Override {
-  ASTNode Class;
+  HasProperties Class;
 
   /// Properties from base classes that should be ignored.
   list<string> IgnoredProperties = [];
 }
+
+/// A description of how to break a type into cases.  Providing this and
+/// an exhaustive list of the cases will cause AbstractBasic{Reader,Writer}
+/// to be generated with a default implementation of how to read the
+/// type.
+///
+/// Creator rules for the cases can additionally access a variable
+/// `kind` of the KindType.
+class PropertyTypeKind<PropertyType type,
+                       PropertyType kindType,
+                       string readCode> {
+  /// The type for which this describes cases.
+  PropertyType Type = type;
+
+  /// The type of this type's kind enum.
+  PropertyType KindType = kindType;
+
+  /// The property name to use for the kind.
+  string KindPropertyName = "kind";
+
+  /// An expression which reads the kind from a value, expressed in terms
+  /// of a variable `node`.
+  string Read = readCode;
+}
+
+/// One of the options for representing a particular type.
+class PropertyTypeCase<PropertyType type, string name> : HasProperties {
+  /// The type of which this is a case.
+  PropertyType Type = type;
+
+  /// The name of the case (a value of the type's kind enum).
+  string Name = name;
+}
+
diff --git a/clang/include/clang/Basic/ASTNode.td b/clang/include/clang/Basic/ASTNode.td
new file mode 100644 (file)
index 0000000..61ccc21
--- /dev/null
@@ -0,0 +1,8 @@
+#ifndef AST_NODE_TD
+#define AST_NODE_TD
+
+class HasProperties;
+class ASTNode : HasProperties;
+class AttrSubject;
+
+#endif
index e58ff4c..af2aacc 100644 (file)
@@ -1,4 +1,6 @@
-class CommentNode<CommentNode base, bit abstract = 0> {
+include "clang/Basic/ASTNode.td"
+
+class CommentNode<CommentNode base, bit abstract = 0> : ASTNode {
   CommentNode Base = base;
   bit Abstract = abstract;
 }
index 7b4c640..c2c2323 100644 (file)
@@ -1,5 +1,4 @@
-class ASTNode;
-class AttrSubject;
+include "clang/Basic/ASTNode.td"
 
 class DeclNode<DeclNode base, string diagSpelling = "", bit abstract = 0>
     : ASTNode, AttrSubject {
index b24199b..2949932 100644 (file)
@@ -1,5 +1,4 @@
-class ASTNode;
-class AttrSubject;
+include "clang/Basic/ASTNode.td"
 
 class StmtNode<StmtNode base, bit abstract = 0> : ASTNode, AttrSubject {
        StmtNode Base = base;
index 2a08dcc..96d9472 100644 (file)
@@ -1,4 +1,4 @@
-class ASTNode;
+include "clang/Basic/ASTNode.td"
 
 class TypeNode<TypeNode base, bit abstract = 0> : ASTNode {
        TypeNode Base = base;
index c49bcd9..3f6da40 100644 (file)
@@ -20,6 +20,16 @@ using namespace llvm;
 using namespace clang;
 using namespace clang::tblgen;
 
+llvm::StringRef clang::tblgen::HasProperties::getName() const {
+  if (auto node = getAs<ASTNode>()) {
+    return node.getName();
+  } else if (auto typeCase = getAs<TypeCase>()) {
+    return typeCase.getCaseName();
+  } else {
+    PrintFatalError(getLoc(), "unexpected node declaring properties");
+  }
+}
+
 static StringRef removeExpectedNodeNameSuffix(Record *node, StringRef suffix) {
   StringRef nodeName = node->getName();
   if (!nodeName.endswith(suffix)) {
index 87934f5..7b7e5f1 100644 (file)
 
 // These are spellings in the tblgen files.
 
-// Field names that are fortunately common across the hierarchies.
+#define HasPropertiesClassName "HasProperties"
+
+// ASTNodes and their common fields.  `Base` is actually defined
+// in subclasses, but it's still common across the hierarchies.
+#define ASTNodeClassName "ASTNode"
 #define BaseFieldName "Base"
 #define AbstractFieldName "Abstract"
 
 #define NeverCanonicalUnlessDependentClassName "NeverCanonicalUnlessDependent"
 #define LeafTypeClassName "LeafType"
 
+// Cases of various non-ASTNode structured types like DeclarationName.
+#define TypeKindClassName "PropertyTypeKind"
+#define KindTypeFieldName "KindType"
+#define KindPropertyNameFieldName "KindPropertyName"
+#define TypeCaseClassName "PropertyTypeCase"
+
 // Properties of AST nodes.
 #define PropertyClassName "Property"
 #define ClassFieldName "Class"
@@ -94,13 +104,54 @@ public:
   bool isSubClassOf(llvm::StringRef className) const {
     return get()->isSubClassOf(className);
   }
+
+  template <class NodeClass>
+  NodeClass getAs() const {
+    return (isSubClassOf(NodeClass::getTableGenNodeClassName())
+              ? NodeClass(get()) : NodeClass());
+  }
+
+  friend bool operator<(WrappedRecord lhs, WrappedRecord rhs) {
+    assert(lhs && rhs && "sorting null nodes");
+    return lhs.get()->getName() < rhs.get()->getName();
+  }
+  friend bool operator>(WrappedRecord lhs, WrappedRecord rhs) {
+    return rhs < lhs;
+  }
+  friend bool operator<=(WrappedRecord lhs, WrappedRecord rhs) {
+    return !(rhs < lhs);
+  }
+  friend bool operator>=(WrappedRecord lhs, WrappedRecord rhs) {
+    return !(lhs < rhs);
+  }
+  friend bool operator==(WrappedRecord lhs, WrappedRecord rhs) {
+    // This should handle null nodes.
+    return lhs.getRecord() == rhs.getRecord();
+  }
+  friend bool operator!=(WrappedRecord lhs, WrappedRecord rhs) {
+    return !(lhs == rhs);
+  }
+};
+
+/// Anything in the AST that has properties.
+class HasProperties : public WrappedRecord {
+public:
+  static constexpr llvm::StringRef ClassName = HasPropertiesClassName;
+
+  HasProperties(llvm::Record *record = nullptr) : WrappedRecord(record) {}
+
+  llvm::StringRef getName() const;
+
+  static llvm::StringRef getTableGenNodeClassName() {
+    return HasPropertiesClassName;
+  }
 };
 
 /// An (optional) reference to a TableGen node representing a class
 /// in one of Clang's AST hierarchies.
-class ASTNode : public WrappedRecord {
+class ASTNode : public HasProperties {
 public:
-  ASTNode(llvm::Record *record = nullptr) : WrappedRecord(record) {}
+  ASTNode(llvm::Record *record = nullptr) : HasProperties(record) {}
 
   llvm::StringRef getName() const {
     return get()->getName();
@@ -116,19 +167,9 @@ public:
     return get()->getValueAsBit(AbstractFieldName);
   }
 
-  friend bool operator<(ASTNode lhs, ASTNode rhs) {
-    assert(lhs && rhs && "sorting null nodes");
-    return lhs.getName() < rhs.getName();
-  }
-  friend bool operator>(ASTNode lhs, ASTNode rhs) { return rhs < lhs; }
-  friend bool operator<=(ASTNode lhs, ASTNode rhs) { return !(rhs < lhs); }
-  friend bool operator>=(ASTNode lhs, ASTNode rhs) { return !(lhs < rhs); }
-
-  friend bool operator==(ASTNode lhs, ASTNode rhs) {
-    // This should handle null nodes.
-    return lhs.getRecord() == rhs.getRecord();
+  static llvm::StringRef getTableGenNodeClassName() {
+    return ASTNodeClassName;
   }
-  friend bool operator!=(ASTNode lhs, ASTNode rhs) { return !(lhs == rhs); }
 };
 
 class DeclNode : public ASTNode {
@@ -275,6 +316,60 @@ public:
   std::vector<llvm::Record*> getBufferElementTypes() const {
     return get()->getValueAsListOfDefs(BufferElementTypesFieldName);
   }
+
+  static llvm::StringRef getTableGenNodeClassName() {
+    return PropertyTypeClassName;
+  }
+};
+
+/// A rule for returning the kind of a type.
+class TypeKindRule : public WrappedRecord {
+public:
+  TypeKindRule(llvm::Record *record = nullptr) : WrappedRecord(record) {}
+
+  /// Return the type to which this applies.
+  PropertyType getParentType() const {
+    return get()->getValueAsDef(TypeFieldName);
+  }
+
+  /// Return the type of the kind.
+  PropertyType getKindType() const {
+    return get()->getValueAsDef(KindTypeFieldName);
+  }
+
+  /// Return the name to use for the kind property.
+  llvm::StringRef getKindPropertyName() const {
+    return get()->getValueAsString(KindPropertyNameFieldName);
+  }
+
+  /// Return the code for reading the kind value.
+  llvm::StringRef getReadCode() const {
+    return get()->getValueAsString(ReadFieldName);
+  }
+
+  static llvm::StringRef getTableGenNodeClassName() {
+    return TypeKindClassName;
+  }
+};
+
+/// An implementation case of a property type.
+class TypeCase : public HasProperties {
+public:
+  TypeCase(llvm::Record *record = nullptr) : HasProperties(record) {}
+
+  /// Return the name of this case.
+  llvm::StringRef getCaseName() const {
+    return get()->getValueAsString(NameFieldName);
+  }
+
+  /// Return the type of which this is a case.
+  PropertyType getParentType() const {
+    return get()->getValueAsDef(TypeFieldName);
+  }
+
+  static llvm::StringRef getTableGenNodeClassName() {
+    return TypeCaseClassName;
+  }
 };
 
 /// A property of an AST node.
@@ -301,6 +396,10 @@ public:
   llvm::StringRef getReadCode() const {
     return get()->getValueAsString(ReadFieldName);
   }
+
+  static llvm::StringRef getTableGenNodeClassName() {
+    return PropertyClassName;
+  }
 };
 
 /// A rule for how to create an AST node from its properties.
@@ -317,6 +416,10 @@ public:
   llvm::StringRef getCreationCode() const {
     return get()->getValueAsString(CreateFieldName);
   }
+
+  static llvm::StringRef getTableGenNodeClassName() {
+    return CreationRuleClassName;
+  }
 };
 
 /// A rule which overrides the standard rules for serializing an AST node.
@@ -336,6 +439,10 @@ public:
   std::vector<llvm::StringRef> getIgnoredProperties() const {
     return get()->getValueAsListOfStrings(IgnoredPropertiesFieldName);
   }
+
+  static llvm::StringRef getTableGenNodeClassName() {
+    return OverrideRuleClassName;
+  }
 };
 
 /// A visitor for an AST node hierarchy.  Note that `base` can be null for
index c1bb00b..1cc46cb 100644 (file)
@@ -142,7 +142,7 @@ std::pair<ASTNode, ASTNode> ClangASTNodesEmitter::EmitNode(raw_ostream &OS,
 }
 
 void ClangASTNodesEmitter::deriveChildTree() {
-  assert(Root == nullptr && "already computed tree");
+  assert(!Root && "already computed tree");
 
   // Emit statements
   const std::vector<Record*> Stmts
index e14b9eb..81d880c 100644 (file)
@@ -80,11 +80,17 @@ struct NodeInfo {
   OverrideRule Override = nullptr;
 };
 
+struct CasedTypeInfo {
+  TypeKindRule KindRule;
+  std::vector<TypeCase> Cases;
+};
+
 class ASTPropsEmitter {
        raw_ostream &Out;
        RecordKeeper &Records;
-       std::map<ASTNode, NodeInfo> NodeInfos;
+       std::map<HasProperties, NodeInfo> NodeInfos;
   std::vector<PropertyType> AllPropertyTypes;
+  std::map<PropertyType, CasedTypeInfo> CasedTypeInfos;
 
 public:
        ASTPropsEmitter(RecordKeeper &records, raw_ostream &out)
@@ -125,6 +131,7 @@ public:
       info.Override = overrideRule;
     }
 
+    // Find all the concrete property types.
     for (PropertyType type :
            records.getAllDerivedDefinitions(PropertyTypeClassName)) {
       // Ignore generic specializations; they're generally not useful when
@@ -134,10 +141,29 @@ public:
       AllPropertyTypes.push_back(type);
     }
 
+    // Find all the type kind rules.
+    for (TypeKindRule kindRule :
+           records.getAllDerivedDefinitions(TypeKindClassName)) {
+      PropertyType type = kindRule.getParentType();
+      auto &info = CasedTypeInfos[type];
+      if (info.KindRule) {
+        PrintFatalError(kindRule.getLoc(),
+                        "multiple kind rules for \""
+                           + type.getCXXTypeName() + "\"");
+      }
+      info.KindRule = kindRule;
+    }
+
+    // Find all the type cases.
+    for (TypeCase typeCase :
+           records.getAllDerivedDefinitions(TypeCaseClassName)) {
+      CasedTypeInfos[typeCase.getParentType()].Cases.push_back(typeCase);
+    }
+
     Validator(*this).validate();
        }
 
-  void visitAllProperties(ASTNode derived, const NodeInfo &derivedInfo,
+  void visitAllProperties(HasProperties derived, const NodeInfo &derivedInfo,
                           function_ref<void (Property)> visit) {
     std::set<StringRef> ignoredProperties;
 
@@ -147,60 +173,88 @@ public:
       ignoredProperties.insert(list.begin(), list.end());
     }
 
-    for (ASTNode node = derived; node; node = node.getBase()) {
-      auto it = NodeInfos.find(node);
-
-      // Ignore intermediate nodes that don't add interesting properties.
-      if (it == NodeInfos.end()) continue;
-      auto &info = it->second;
-
+    visitAllNodesWithInfo(derived, derivedInfo,
+                          [&](HasProperties node, const NodeInfo &info) {
       for (Property prop : info.Properties) {
         if (ignoredProperties.count(prop.getName()))
           continue;
 
         visit(prop);
       }
+    });
+  }
+
+  void visitAllNodesWithInfo(HasProperties derivedNode,
+                             const NodeInfo &derivedNodeInfo,
+                             llvm::function_ref<void (HasProperties node,
+                                                      const NodeInfo &info)>
+                               visit) {
+    visit(derivedNode, derivedNodeInfo);
+
+    // Also walk the bases if appropriate.
+    if (ASTNode base = derivedNode.getAs<ASTNode>()) {
+      for (base = base.getBase(); base; base = base.getBase()) {
+        auto it = NodeInfos.find(base);
+
+        // Ignore intermediate nodes that don't add interesting properties.
+        if (it == NodeInfos.end()) continue;
+        auto &baseInfo = it->second;
+
+        visit(base, baseInfo);
+      }
     }
   }
 
   template <class NodeClass>
-  void emitReaderClass() {
+  void emitNodeReaderClass() {
     auto info = ReaderWriterInfo::forReader<NodeClass>();
-    emitReaderWriterClass<NodeClass>(info);
+    emitNodeReaderWriterClass<NodeClass>(info);
   }
 
   template <class NodeClass>
-  void emitWriterClass() {
+  void emitNodeWriterClass() {
     auto info = ReaderWriterInfo::forWriter<NodeClass>();
-    emitReaderWriterClass<NodeClass>(info);
+    emitNodeReaderWriterClass<NodeClass>(info);
   }
 
   template <class NodeClass>
-  void emitReaderWriterClass(const ReaderWriterInfo &info);
+  void emitNodeReaderWriterClass(const ReaderWriterInfo &info);
 
   template <class NodeClass>
   void emitNodeReaderWriterMethod(NodeClass node,
                                   const ReaderWriterInfo &info);
 
-  void emitReadOfProperty(Property property);
-  void emitWriteOfProperty(Property property);
+  void emitPropertiedReaderWriterBody(HasProperties node,
+                                      const ReaderWriterInfo &info);
+
+  void emitReadOfProperty(StringRef readerName, Property property);
+  void emitReadOfProperty(StringRef readerName, StringRef name,
+                          PropertyType type);
+
+  void emitWriteOfProperty(StringRef writerName, Property property);
+  void emitWriteOfProperty(StringRef writerName, StringRef name,
+                           PropertyType type, StringRef readCode);
 
   void emitBasicReaderWriterFile(const ReaderWriterInfo &info);
   void emitDispatcherTemplate(const ReaderWriterInfo &info);
   void emitPackUnpackOptionalTemplate(const ReaderWriterInfo &info);
   void emitBasicReaderWriterTemplate(const ReaderWriterInfo &info);
 
+  void emitCasedReaderWriterMethodBody(PropertyType type,
+                                       const CasedTypeInfo &typeCases,
+                                       const ReaderWriterInfo &info);
+
 private:
   class Validator {
-    const ASTPropsEmitter &Emitter;
-    std::set<ASTNode> ValidatedNodes;
+    ASTPropsEmitter &Emitter;
+    std::set<HasProperties> ValidatedNodes;
 
   public:
-    Validator(const ASTPropsEmitter &emitter) : Emitter(emitter) {}
+    Validator(ASTPropsEmitter &emitter) : Emitter(emitter) {}
     void validate();
 
   private:
-    void validateNode(ASTNode node, const NodeInfo &nodeInfo);
+    void validateNode(HasProperties node, const NodeInfo &nodeInfo);
     void validateType(PropertyType type, WrappedRecord context);
   };
 };
@@ -217,21 +271,17 @@ void ASTPropsEmitter::Validator::validate() {
   }
 }
 
-void ASTPropsEmitter::Validator::validateNode(ASTNode node,
-                                              const NodeInfo &nodeInfo) {
-  if (!ValidatedNodes.insert(node).second) return;
+void ASTPropsEmitter::Validator::validateNode(HasProperties derivedNode,
+                                              const NodeInfo &derivedNodeInfo) {
+  if (!ValidatedNodes.insert(derivedNode).second) return;
 
   // A map from property name to property.
   std::map<StringRef, Property> allProperties;
 
-  // Walk the hierarchy, ignoring nodes that don't declare anything
-  // interesting.
-  for (auto base = node; base; base = base.getBase()) {
-    auto it = Emitter.NodeInfos.find(base);
-    if (it == Emitter.NodeInfos.end()) continue;
-
-    auto &baseInfo = it->second;
-    for (Property property : baseInfo.Properties) {
+  Emitter.visitAllNodesWithInfo(derivedNode, derivedNodeInfo,
+                                [&](HasProperties node,
+                                    const NodeInfo &nodeInfo) {
+    for (Property property : nodeInfo.Properties) {
       validateType(property.getType(), property);
 
       auto result = allProperties.insert(
@@ -244,11 +294,11 @@ void ASTPropsEmitter::Validator::validateNode(ASTNode node,
         Property existingProperty = result.first->second;
         PrintError(existingProperty.getLoc(),
                    "multiple properties named \"" + property.getName()
-                      + "\" in hierarchy of " + node.getName());
+                      + "\" in hierarchy of " + derivedNode.getName());
         PrintNote(property.getLoc(), "existing property");
       }
     }
-  }
+  });
 }
 
 void ASTPropsEmitter::Validator::validateType(PropertyType type,
@@ -284,7 +334,7 @@ void ASTPropsEmitter::Validator::validateType(PropertyType type,
 /****************************************************************************/
 
 template <class NodeClass>
-void ASTPropsEmitter::emitReaderWriterClass(const ReaderWriterInfo &info) {
+void ASTPropsEmitter::emitNodeReaderWriterClass(const ReaderWriterInfo &info) {
   StringRef suffix = info.ClassSuffix;
   StringRef var = info.HelperVariable;
 
@@ -350,6 +400,14 @@ void ASTPropsEmitter::emitNodeReaderWriterMethod(NodeClass node,
   if (info.IsReader)
     Out << "    auto &ctx = " << info.HelperVariable << ".getASTContext();\n";
 
+  emitPropertiedReaderWriterBody(node, info);
+
+  // Finish the method declaration.
+  Out << "  }\n\n";
+}
+
+void ASTPropsEmitter::emitPropertiedReaderWriterBody(HasProperties node,
+                                               const ReaderWriterInfo &info) {
   // Find the information for this node.
   auto it = NodeInfos.find(node);
   if (it == NodeInfos.end())
@@ -380,17 +438,14 @@ void ASTPropsEmitter::emitNodeReaderWriterMethod(NodeClass node,
 
     // Emit code to read or write this property.
     if (info.IsReader)
-      emitReadOfProperty(prop);
+      emitReadOfProperty(info.HelperVariable, prop);
     else
-      emitWriteOfProperty(prop);
+      emitWriteOfProperty(info.HelperVariable, prop);
   });
 
   // Emit the final creation code.
   if (info.IsReader)
     Out << "    " << creationCode << "\n";
-
-  // Finish the method declaration.
-  Out << "  }\n\n";
 }
 
 static void emitBasicReaderWriterMethodSuffix(raw_ostream &out,
@@ -422,10 +477,14 @@ static void emitBasicReaderWriterMethodSuffix(raw_ostream &out,
 }
 
 /// Emit code to read the given property in a node-reader method.
-void ASTPropsEmitter::emitReadOfProperty(Property property) {
-  PropertyType type = property.getType();
-  auto name = property.getName();
+void ASTPropsEmitter::emitReadOfProperty(StringRef readerName,
+                                         Property property) {
+  emitReadOfProperty(readerName, property.getName(), property.getType());
+}
 
+void ASTPropsEmitter::emitReadOfProperty(StringRef readerName,
+                                         StringRef name,
+                                         PropertyType type) {
   // Declare all the necessary buffers.
   auto bufferTypes = type.getBufferElementTypes();
   for (size_t i = 0, e = bufferTypes.size(); i != e; ++i) {
@@ -440,7 +499,7 @@ void ASTPropsEmitter::emitReadOfProperty(Property property) {
   // that in the creation rule.
   Out << "    ";
   type.emitCXXValueTypeName(true, Out);
-  Out << " " << name << " = R.find(\"" << name << "\")."
+  Out << " " << name << " = " << readerName << ".find(\"" << name << "\")."
       << (type.isGenericSpecialization() ? "template " : "") << "read";
   emitBasicReaderWriterMethodSuffix(Out, type, /*for read*/ true);
   Out << "(";
@@ -451,13 +510,21 @@ void ASTPropsEmitter::emitReadOfProperty(Property property) {
 }
 
 /// Emit code to write the given property in a node-writer method.
-void ASTPropsEmitter::emitWriteOfProperty(Property property) {
+void ASTPropsEmitter::emitWriteOfProperty(StringRef writerName,
+                                          Property property) {
+  emitWriteOfProperty(writerName, property.getName(), property.getType(),
+                      property.getReadCode());
+}
+
+void ASTPropsEmitter::emitWriteOfProperty(StringRef writerName,
+                                          StringRef name,
+                                          PropertyType type,
+                                          StringRef readCode) {
   // Focus down to the property:
   //   W.find("prop").write##ValueType(value);
-  Out << "    W.find(\"" << property.getName() << "\").write";
-  emitBasicReaderWriterMethodSuffix(Out, property.getType(),
-                                    /*for read*/ false);
-  Out << "(" << property.getReadCode() << ");\n";
+  Out << "    " << writerName << ".find(\"" << name << "\").write";
+  emitBasicReaderWriterMethodSuffix(Out, type, /*for read*/ false);
+  Out << "(" << readCode << ");\n";
 }
 
 /// Emit an .inc file that defines the AbstractFooReader class
@@ -467,7 +534,7 @@ static void emitASTReader(RecordKeeper &records, raw_ostream &out,
                           StringRef description) {
   emitSourceFileHeader(description, out);
 
-  ASTPropsEmitter(records, out).emitReaderClass<NodeClass>();
+  ASTPropsEmitter(records, out).emitNodeReaderClass<NodeClass>();
 }
 
 void clang::EmitClangTypeReader(RecordKeeper &records, raw_ostream &out) {
@@ -481,7 +548,7 @@ static void emitASTWriter(RecordKeeper &records, raw_ostream &out,
                           StringRef description) {
   emitSourceFileHeader(description, out);
 
-  ASTPropsEmitter(records, out).emitWriterClass<NodeClass>();
+  ASTPropsEmitter(records, out).emitNodeWriterClass<NodeClass>();
 }
 
 void clang::EmitClangTypeWriter(RecordKeeper &records, raw_ostream &out) {
@@ -596,35 +663,49 @@ ASTPropsEmitter::emitBasicReaderWriterTemplate(const ReaderWriterInfo &info) {
   auto enterReaderWriterMethod = [&](StringRef cxxTypeName,
                                      StringRef abstractTypeName,
                                      bool shouldPassByReference,
-                                     bool constWhenWriting) {
+                                     bool constWhenWriting,
+                                     StringRef paramName) {
     Out << "  " << (info.IsReader ? cxxTypeName : "void")
                 << " " << info.MethodPrefix << abstractTypeName << "(";
     if (!info.IsReader)
       Out       << (shouldPassByReference || constWhenWriting ? "const " : "")
                 << cxxTypeName
-                << (shouldPassByReference ? " &" : "") << " value";
+                << (shouldPassByReference ? " &" : "") << " " << paramName;
     Out         << ") {\n";
   };
 
   // Emit {read,write}ValueType methods for all the enum and subclass types
   // that default to using the integer/base-class implementations.
   for (PropertyType type : AllPropertyTypes) {
-    if (type.isEnum()) {
+    auto enterMethod = [&](StringRef paramName) {
       enterReaderWriterMethod(type.getCXXTypeName(),
                               type.getAbstractTypeName(),
-                              /*pass by reference*/ false,
-                              /*const when writing*/ false);
+                              type.shouldPassByReference(),
+                              type.isConstWhenWriting(),
+                              paramName);
+    };
+    auto exitMethod = [&] {
+      Out << "  }\n";
+    };
+
+    // Handled cased types.
+    auto casedIter = CasedTypeInfos.find(type);
+    if (casedIter != CasedTypeInfos.end()) {
+      enterMethod("node");
+      emitCasedReaderWriterMethodBody(type, casedIter->second, info);
+      exitMethod();
+
+    } else if (type.isEnum()) {
+      enterMethod("value");
       if (info.IsReader)
         Out << "    return " << type.getCXXTypeName()
                              << "(asImpl().readUInt32());\n";
       else
         Out << "    asImpl().writeUInt32(uint32_t(value));\n";
-      Out << "  }\n";
+      exitMethod();
+
     } else if (PropertyType superclass = type.getSuperclassType()) {
-      enterReaderWriterMethod(type.getCXXTypeName(),
-                              type.getAbstractTypeName(),
-                              /*pass by reference*/ false,
-                              /*const when writing*/ type.isConstWhenWriting());
+      enterMethod("value");
       if (info.IsReader)
         Out << "    return cast_or_null<" << type.getSubclassClassName()
                                           << ">(asImpl().read"
@@ -633,7 +714,8 @@ ASTPropsEmitter::emitBasicReaderWriterTemplate(const ReaderWriterInfo &info) {
       else
         Out << "    asImpl().write" << superclass.getAbstractTypeName()
                                     << "(value);\n";
-      Out << "  }\n";
+      exitMethod();
+
     } else {
       // The other types can't be handled as trivially.
     }
@@ -641,9 +723,66 @@ ASTPropsEmitter::emitBasicReaderWriterTemplate(const ReaderWriterInfo &info) {
   Out << "};\n\n";
 }
 
-void ASTPropsEmitter::emitBasicReaderWriterFile(const ReaderWriterInfo &info) {
-  auto types = Records.getAllDerivedDefinitions(PropertyTypeClassName);
+void ASTPropsEmitter::emitCasedReaderWriterMethodBody(PropertyType type,
+                                             const CasedTypeInfo &typeCases,
+                                             const ReaderWriterInfo &info) {
+  if (typeCases.Cases.empty()) {
+    assert(typeCases.KindRule);
+    PrintFatalError(typeCases.KindRule.getLoc(),
+                    "no cases found for \"" + type.getCXXTypeName() + "\"");
+  }
+  if (!typeCases.KindRule) {
+    assert(!typeCases.Cases.empty());
+    PrintFatalError(typeCases.Cases.front().getLoc(),
+                    "no kind rule for \"" + type.getCXXTypeName() + "\"");
+  }
+
+  auto var = info.HelperVariable;
+  std::string subvar = ("sub" + var).str();
+
+  // Bind `ctx` for readers.
+  if (info.IsReader)
+    Out << "    auto &ctx = asImpl().getASTContext();\n";
 
+  // Start an object.
+  Out << "    auto &&" << subvar << " = asImpl()."
+                       << info.MethodPrefix << "Object();\n";
+
+  // Read/write the kind property;
+  TypeKindRule kindRule = typeCases.KindRule;
+  StringRef kindProperty = kindRule.getKindPropertyName();
+  PropertyType kindType = kindRule.getKindType();
+  if (info.IsReader) {
+    emitReadOfProperty(subvar, kindProperty, kindType);
+  } else {
+    // Read the kind into a local variable.
+    Out << "    ";
+    kindType.emitCXXValueTypeName(/*for read*/ false, Out);
+    Out << " " << kindProperty << " = " << kindRule.getReadCode() << ";\n";
+    emitWriteOfProperty(subvar, kindProperty, kindType, kindProperty);
+  }
+
+  // Prepare a ReaderWriterInfo with a helper variable that will use
+  // the sub-reader/writer.
+  ReaderWriterInfo subInfo = info;
+  subInfo.HelperVariable = subvar;
+
+  // Switch on the kind.
+  Out << "    switch (" << kindProperty << ") {\n";
+  for (TypeCase typeCase : typeCases.Cases) {
+    Out << "    case " << type.getCXXTypeName() << "::"
+                       << typeCase.getCaseName() << ": {\n";
+    emitPropertiedReaderWriterBody(typeCase, subInfo);
+    if (!info.IsReader)
+      Out << "    return;\n";
+    Out << "    }\n\n";
+  }
+  Out << "    }\n"
+         "    llvm_unreachable(\"bad " << kindType.getCXXTypeName()
+                                       << "\");\n";
+}
+
+void ASTPropsEmitter::emitBasicReaderWriterFile(const ReaderWriterInfo &info) {
   emitDispatcherTemplate(info);
   emitPackUnpackOptionalTemplate(info);
   emitBasicReaderWriterTemplate(info);