Abstract serialization: TableGen "basic" reader/writer CRTP
authorJohn McCall <rjmccall@apple.com>
Sat, 14 Dec 2019 02:52:16 +0000 (21:52 -0500)
committerJohn McCall <rjmccall@apple.com>
Sat, 14 Dec 2019 05:16:48 +0000 (00:16 -0500)
classes that serialize basic values

clang/include/clang/AST/CMakeLists.txt
clang/include/clang/AST/PropertiesBase.td [new file with mode: 0644]
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/CMakeLists.txt
clang/utils/TableGen/ClangASTPropertiesEmitter.cpp [new file with mode: 0644]
clang/utils/TableGen/TableGen.cpp
clang/utils/TableGen/TableGenBackends.h

index fc98398..8ba823f 100644 (file)
@@ -35,6 +35,14 @@ clang_tablegen(TypeNodes.inc -gen-clang-type-nodes
   SOURCE ../Basic/TypeNodes.td
   TARGET ClangTypeNodes)
 
+clang_tablegen(AbstractBasicReader.inc -gen-clang-basic-reader
+  SOURCE PropertiesBase.td
+  TARGET ClangAbstractBasicReader)
+
+clang_tablegen(AbstractBasicWriter.inc -gen-clang-basic-writer
+  SOURCE PropertiesBase.td
+  TARGET ClangAbstractBasicWriter)
+
 clang_tablegen(CommentNodes.inc -gen-clang-comment-nodes
   SOURCE ../Basic/CommentNodes.td
   TARGET ClangCommentNodes)
diff --git a/clang/include/clang/AST/PropertiesBase.td b/clang/include/clang/AST/PropertiesBase.td
new file mode 100644 (file)
index 0000000..c38bf7c
--- /dev/null
@@ -0,0 +1,179 @@
+//==--- PropertiesBase.td - Baseline definitions for AST properties -------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+class ASTNode;
+
+/// The type of the property.
+class PropertyType<string typeName = ""> {
+       /// The C++ type name for the type.
+       string CXXName = !if(!ne(typeName, ""), typeName, NAME);
+
+       /// Whether the C++ type should generally be passed around by reference.
+       bit PassByReference = 0;
+
+       /// Whether `const` should be prepended to the type when writing.
+       bit ConstWhenWriting = 0;
+
+       /// Given a value of type Optional<CXXName> bound as 'value', yield a
+       /// CXXName that can be serialized into a DataStreamTypeWriter.
+       string PackOptional = "";
+
+       /// Given a value of type CXXName bound as 'value' that was deserialized
+       /// by a DataStreamTypeReader, yield an Optional<CXXName>.
+       string UnpackOptional = "";
+
+       /// A list of types for which buffeers must be passed to the read
+       /// operations.
+       list<PropertyType> BufferElementTypes = [];
+}
+
+/// Property types that correspond to specific C++ enums.
+class EnumPropertyType<string typeName = ""> : PropertyType<typeName> {}
+
+/// Property types that correspond to a specific C++ class.
+/// Supports optional values by using the null representation.
+class RefPropertyType<string className> : PropertyType<className # "*"> {
+       let PackOptional =
+               "value ? *value : nullptr";
+       let UnpackOptional =
+               "value ? llvm::Optional<" # CXXName # ">(value) : llvm::None";
+}
+
+/// Property types that correspond to a specific subclass of another type.
+class SubclassPropertyType<string className, PropertyType base>
+               : RefPropertyType<className> {
+       PropertyType Base = base;
+       string SubclassName = className;
+       let ConstWhenWriting = base.ConstWhenWriting;
+}
+
+/// Property types that support optional values by using their
+/// default value.
+class DefaultValuePropertyType<string typeName = ""> : PropertyType<typeName> {
+       let PackOptional =
+               "value ? *value : " # CXXName # "()";
+       let UnpackOptional =
+               "value.isNull() ? llvm::None : llvm::Optional<" # CXXName # ">(value)";
+}
+
+/// Property types that correspond to integer types and support optional
+/// values by shifting the value over by 1.
+class CountPropertyType<string typeName = ""> : PropertyType<typeName> {
+       let PackOptional =
+               "value ? *value + 1 : 0";
+       let UnpackOptional =
+               "value ? llvm::Optional<" # CXXName # ">(value - 1) : llvm::None";
+}
+
+def APInt : PropertyType<"llvm::APInt"> { let PassByReference = 1; }
+def APSInt : PropertyType<"llvm::APSInt"> { let PassByReference = 1; }
+def ArraySizeModifier : EnumPropertyType<"ArrayType::ArraySizeModifier">;
+def AttrKind : EnumPropertyType<"attr::Kind">;
+def AutoTypeKeyword : EnumPropertyType;
+def Bool : PropertyType<"bool">;
+def BuiltinTypeKind : EnumPropertyType<"BuiltinType::Kind">;
+def CallingConv : EnumPropertyType;
+def DeclarationName : PropertyType;
+def DeclarationNameKind : EnumPropertyType<"DeclarationName::NameKind">;
+def DeclRef : RefPropertyType<"Decl"> { let ConstWhenWriting = 1; }
+       def CXXRecordDeclRef :
+               SubclassPropertyType<"CXXRecordDecl", DeclRef>;
+       def FunctionDeclRef :
+               SubclassPropertyType<"FunctionDecl", DeclRef>;
+       def NamedDeclRef :
+               SubclassPropertyType<"NamedDecl", DeclRef>;
+       def NamespaceDeclRef :
+               SubclassPropertyType<"NamespaceDecl", DeclRef>;
+       def NamespaceAliasDeclRef :
+               SubclassPropertyType<"NamespaceAliasDecl", DeclRef>;
+       def ObjCProtocolDeclRef :
+               SubclassPropertyType<"ObjCProtocolDecl", DeclRef>;
+       def ObjCTypeParamDeclRef :
+               SubclassPropertyType<"ObjCTypeParamDecl", DeclRef>;
+       def TagDeclRef :
+               SubclassPropertyType<"TagDecl", DeclRef>;
+       def TemplateDeclRef :
+               SubclassPropertyType<"TemplateDecl", DeclRef>;
+       def TemplateTypeParmDeclRef :
+               SubclassPropertyType<"TemplateTypeParmDecl", DeclRef>;
+       def TemplateTemplateParmDeclRef :
+               SubclassPropertyType<"TemplateTemplateParmDecl", DeclRef>;
+       def ValueDeclRef :
+               SubclassPropertyType<"ValueDecl", DeclRef>;
+def ElaboratedTypeKeyword : EnumPropertyType;
+def ExtParameterInfo : PropertyType<"FunctionProtoType::ExtParameterInfo">;
+def Identifier : PropertyType<"IdentifierInfo*">;
+def NestedNameSpecifier : PropertyType<"NestedNameSpecifier *">;
+def NestedNameSpecifierKind :
+       EnumPropertyType<"NestedNameSpecifier::SpecifierKind">;
+def OverloadedOperatorKind : EnumPropertyType;
+def Qualifiers : PropertyType;
+def QualType : DefaultValuePropertyType;
+def RefQualifierKind : EnumPropertyType;
+def Selector : PropertyType;
+def SourceLocation : PropertyType;
+def StmtRef : RefPropertyType<"Stmt"> { let ConstWhenWriting = 1; }
+       def ExprRef : SubclassPropertyType<"Expr", StmtRef>;
+def TemplateArgument : PropertyType;
+def TemplateArgumentKind : EnumPropertyType<"TemplateArgument::ArgKind">;
+def TemplateName : DefaultValuePropertyType;
+def TemplateNameKind : EnumPropertyType<"TemplateName::NameKind">;
+def UInt32 : CountPropertyType<"uint32_t">;
+def UInt64 : CountPropertyType<"uint64_t">;
+def UnaryTypeTransformKind : EnumPropertyType<"UnaryTransformType::UTTKind">;
+def VectorKind : EnumPropertyType<"VectorType::VectorKind">;
+
+def ExceptionSpecInfo : PropertyType<"FunctionProtoType::ExceptionSpecInfo"> {
+       let BufferElementTypes = [ QualType ];
+}
+
+/// Arrays.  The corresponding C++ type is ArrayRef of the corresponding
+/// C++ type of the element.
+class Array<PropertyType element> : PropertyType {
+       PropertyType Element = element;
+       let BufferElementTypes = [ element ];
+}
+
+/// llvm::Optional<T>.  The corresponding C++ type is generally just the
+/// corresponding C++ type of the element.
+///
+/// Optional<Unsigned> may restrict the range of the operand for some
+/// serialization clients.
+class Optional<PropertyType element> : PropertyType {
+       PropertyType Element = element;
+       let PassByReference = element.PassByReference;
+}
+
+/// A property of an AST node.
+class Property<string name, PropertyType type> {
+       ASTNode Class;
+       string Name = name;
+       PropertyType Type = type;
+
+       /// A function for reading the property, expressed in terms of a variable
+       /// "node".
+       code Read;
+}
+
+/// A rule for creating objects of this type.
+class Creator<code create> {
+       ASTNode 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
+       /// properties by name.
+       code Create = create;
+}
+
+/// A rule which overrides some of the normal rules.
+class Override {
+       ASTNode Class;
+
+       /// Properties from base classes that should be ignored.
+       list<string> IgnoredProperties = [];
+}
index 25d49fc..7b4c640 100644 (file)
@@ -1,7 +1,8 @@
+class ASTNode;
 class AttrSubject;
 
 class DeclNode<DeclNode base, string diagSpelling = "", bit abstract = 0>
-    : AttrSubject {
+    : ASTNode, AttrSubject {
   DeclNode Base = base;
   bit Abstract = abstract;
   string DiagSpelling = diagSpelling;
index 755cd0b..b24199b 100644 (file)
@@ -1,6 +1,7 @@
+class ASTNode;
 class AttrSubject;
 
-class StmtNode<StmtNode base, bit abstract = 0> : AttrSubject {
+class StmtNode<StmtNode base, bit abstract = 0> : ASTNode, AttrSubject {
        StmtNode Base = base;
   bit Abstract = abstract;
 }
index 835db0b..2a08dcc 100644 (file)
@@ -1,4 +1,6 @@
-class TypeNode<TypeNode base, bit abstract = 0> {
+class ASTNode;
+
+class TypeNode<TypeNode base, bit abstract = 0> : ASTNode {
        TypeNode Base = base;
   bit Abstract = abstract;
 } 
index 11e0e42..c49bcd9 100644 (file)
@@ -60,6 +60,26 @@ std::string clang::tblgen::StmtNode::getId() const {
   return (Twine(getName()) + "Class").str();
 }
 
+/// Emit a string spelling out the C++ value type.
+void PropertyType::emitCXXValueTypeName(bool forRead, raw_ostream &out) const {
+  if (!isGenericSpecialization()) {
+    if (!forRead && isConstWhenWriting())
+      out << "const ";
+    out << getCXXTypeName();
+  } else if (auto elementType = getArrayElementType()) {
+    out << "llvm::ArrayRef<";
+    elementType.emitCXXValueTypeName(forRead, out);
+    out << ">";
+  } else if (auto valueType = getOptionalElementType()) {
+    out << "llvm::Optional<";
+    valueType.emitCXXValueTypeName(forRead, out);
+    out << ">";
+  } else {
+    //PrintFatalError(getLoc(), "unexpected generic property type");
+    abort();
+  }
+}
+
 // A map from a node to each of its child nodes.
 using ChildMap = std::multimap<ASTNode, ASTNode>;
 
index 185f809..87934f5 100644 (file)
 #define NeverCanonicalUnlessDependentClassName "NeverCanonicalUnlessDependent"
 #define LeafTypeClassName "LeafType"
 
-// Property node hierarchy.
+// Properties of AST nodes.
 #define PropertyClassName "Property"
 #define ClassFieldName "Class"
+#define NameFieldName "Name"
+#define TypeFieldName "Type"
+#define ReadFieldName "Read"
+
+// Types of properties.
+#define PropertyTypeClassName "PropertyType"
+#define CXXTypeNameFieldName "CXXName"
+#define PassByReferenceFieldName "PassByReference"
+#define ConstWhenWritingFieldName "ConstWhenWriting"
+#define PackOptionalCodeFieldName "PackOptional"
+#define UnpackOptionalCodeFieldName "UnpackOptional"
+#define BufferElementTypesFieldName "BufferElementTypes"
+#define ArrayTypeClassName "Array"
+#define ArrayElementTypeFieldName "Element"
+#define OptionalTypeClassName "Optional"
+#define OptionalElementTypeFieldName "Element"
+#define SubclassPropertyTypeClassName "SubclassPropertyType"
+#define SubclassBaseTypeFieldName "Base"
+#define SubclassClassNameFieldName "SubclassName"
+#define EnumPropertyTypeClassName "EnumPropertyType"
+
+// Creation rules.
+#define CreationRuleClassName "Creator"
+#define CreateFieldName "Create"
+
+// Override rules.
+#define OverrideRuleClassName "Override"
+#define IgnoredPropertiesFieldName "IgnoredProperties"
 
 namespace clang {
 namespace tblgen {
@@ -169,6 +197,147 @@ public:
   }
 };
 
+/// The type of a property.
+class PropertyType : public WrappedRecord {
+public:
+  PropertyType(llvm::Record *record = nullptr) : WrappedRecord(record) {}
+
+  /// Is this a generic specialization (i.e. `Array<T>` or `Optional<T>`)?
+  bool isGenericSpecialization() const {
+    return get()->isAnonymous();
+  }
+
+  /// The abstract type name of the property.  Doesn't work for generic
+  /// specializations.
+  llvm::StringRef getAbstractTypeName() const {
+    return get()->getName();
+  }
+
+  /// The C++ type name of the property.  Doesn't work for generic
+  /// specializations.
+  llvm::StringRef getCXXTypeName() const {
+    return get()->getValueAsString(CXXTypeNameFieldName);
+  }
+  void emitCXXValueTypeName(bool forRead, llvm::raw_ostream &out) const;
+
+  /// Whether the C++ type should be passed around by reference.
+  bool shouldPassByReference() const {
+    return get()->getValueAsBit(PassByReferenceFieldName);
+  }
+
+  /// Whether the C++ type should have 'const' prepended when working with
+  /// a value of the type being written.
+  bool isConstWhenWriting() const {
+    return get()->getValueAsBit(ConstWhenWritingFieldName);
+  }
+
+  /// If this is `Array<T>`, return `T`; otherwise return null.
+  PropertyType getArrayElementType() const {
+    if (isSubClassOf(ArrayTypeClassName))
+      return get()->getValueAsDef(ArrayElementTypeFieldName);
+    return nullptr;
+  }
+
+  /// If this is `Optional<T>`, return `T`; otherwise return null.
+  PropertyType getOptionalElementType() const {
+    if (isSubClassOf(OptionalTypeClassName))
+      return get()->getValueAsDef(OptionalElementTypeFieldName);
+    return nullptr;
+  }
+
+  /// If this is a subclass type, return its superclass type.
+  PropertyType getSuperclassType() const {
+    if (isSubClassOf(SubclassPropertyTypeClassName))
+      return get()->getValueAsDef(SubclassBaseTypeFieldName);
+    return nullptr;
+  }
+
+  // Given that this is a subclass type, return the C++ name of its
+  // subclass type.  This is just the bare class name, suitable for
+  // use in `cast<>`.
+  llvm::StringRef getSubclassClassName() const {
+    return get()->getValueAsString(SubclassClassNameFieldName);
+  }
+
+  /// Does this represent an enum type?
+  bool isEnum() const {
+    return isSubClassOf(EnumPropertyTypeClassName);
+  }
+
+  llvm::StringRef getPackOptionalCode() const {
+    return get()->getValueAsString(PackOptionalCodeFieldName);
+  }
+
+  llvm::StringRef getUnpackOptionalCode() const {
+    return get()->getValueAsString(UnpackOptionalCodeFieldName);
+  }
+
+  std::vector<llvm::Record*> getBufferElementTypes() const {
+    return get()->getValueAsListOfDefs(BufferElementTypesFieldName);
+  }
+};
+
+/// A property of an AST node.
+class Property : public WrappedRecord {
+public:
+  Property(llvm::Record *record = nullptr) : WrappedRecord(record) {}
+
+  /// Return the name of this property.
+  llvm::StringRef getName() const {
+    return get()->getValueAsString(NameFieldName);
+  }
+
+  /// Return the type of this property.
+  PropertyType getType() const {
+    return get()->getValueAsDef(TypeFieldName);
+  }
+
+  /// Return the class of which this is a property.
+  ASTNode getClass() const {
+    return get()->getValueAsDef(ClassFieldName);
+  }
+
+  /// Return the code for reading this property.
+  llvm::StringRef getReadCode() const {
+    return get()->getValueAsString(ReadFieldName);
+  }
+};
+
+/// A rule for how to create an AST node from its properties.
+class CreationRule : public WrappedRecord {
+public:
+  CreationRule(llvm::Record *record = nullptr) : WrappedRecord(record) {}
+
+  /// Return the class for which this is a creation rule.
+  /// Should never be abstract.
+  ASTNode getClass() const {
+    return get()->getValueAsDef(ClassFieldName);
+  }
+
+  llvm::StringRef getCreationCode() const {
+    return get()->getValueAsString(CreateFieldName);
+  }
+};
+
+/// A rule which overrides the standard rules for serializing an AST node.
+class OverrideRule : public WrappedRecord {
+public:
+  OverrideRule(llvm::Record *record = nullptr) : WrappedRecord(record) {}
+
+  /// Return the class for which this is an override rule.
+  /// Should never be abstract.
+  ASTNode getClass() const {
+    return get()->getValueAsDef(ClassFieldName);
+  }
+
+  /// Return a set of properties that are unnecessary when serializing
+  /// this AST node.  Generally this is used for inherited properties
+  /// that are derived for this subclass.
+  std::vector<llvm::StringRef> getIgnoredProperties() const {
+    return get()->getValueAsListOfStrings(IgnoredPropertiesFieldName);
+  }
+};
+
 /// A visitor for an AST node hierarchy.  Note that `base` can be null for
 /// the root class.
 template <class NodeClass>
index 84d5488..7deca99 100644 (file)
@@ -3,6 +3,7 @@ set(LLVM_LINK_COMPONENTS Support)
 add_tablegen(clang-tblgen CLANG
   ASTTableGen.cpp
   ClangASTNodesEmitter.cpp
+  ClangASTPropertiesEmitter.cpp
   ClangAttrEmitter.cpp
   ClangCommentCommandInfoEmitter.cpp
   ClangCommentHTMLNamedCharacterReferenceEmitter.cpp
diff --git a/clang/utils/TableGen/ClangASTPropertiesEmitter.cpp b/clang/utils/TableGen/ClangASTPropertiesEmitter.cpp
new file mode 100644 (file)
index 0000000..c80b10e
--- /dev/null
@@ -0,0 +1,658 @@
+//=== ClangASTPropsEmitter.cpp - Generate Clang AST properties --*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This tablegen backend emits code for working with Clang AST properties.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ASTTableGen.h"
+#include "TableGenBackends.h"
+
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/Twine.h"
+#include "llvm/TableGen/Error.h"
+#include "llvm/TableGen/Record.h"
+#include "llvm/TableGen/TableGenBackend.h"
+#include <cctype>
+#include <map>
+#include <set>
+#include <string>
+using namespace llvm;
+using namespace clang;
+using namespace clang::tblgen;
+
+static StringRef getReaderResultType(TypeNode _) { return "QualType"; }
+
+namespace {
+
+struct ReaderWriterInfo {
+  bool IsReader;
+
+  /// The name of the node hierarchy.  Not actually sensitive to IsReader,
+  /// but useful to cache here anyway.
+  StringRef HierarchyName;
+
+  /// The suffix on classes: Reader/Writer
+  StringRef ClassSuffix;
+
+  /// The base name of methods: read/write
+  StringRef MethodPrefix;
+
+  /// The name of the property helper member: R/W
+  StringRef HelperVariable;
+
+  /// The result type of methods on the class.
+  StringRef ResultType;
+
+  template <class NodeClass>
+  static ReaderWriterInfo forReader() {
+    return ReaderWriterInfo{
+      true,
+      NodeClass::getASTHierarchyName(),
+      "Reader",
+      "read",
+      "R",
+      getReaderResultType(NodeClass())
+    };
+  }
+
+  template <class NodeClass>
+  static ReaderWriterInfo forWriter() {
+    return ReaderWriterInfo{
+      false,
+      NodeClass::getASTHierarchyName(),
+      "Writer",
+      "write",
+      "W",
+      "void"
+    };
+  }
+};
+
+struct NodeInfo {
+  std::vector<Property> Properties;
+  CreationRule Creator = nullptr;
+  OverrideRule Override = nullptr;
+};
+
+class ASTPropsEmitter {
+       raw_ostream &Out;
+       RecordKeeper &Records;
+       std::map<ASTNode, NodeInfo> NodeInfos;
+
+public:
+       ASTPropsEmitter(RecordKeeper &records, raw_ostream &out)
+               : Out(out), Records(records) {
+
+               // Find all the properties.
+               for (Property property :
+           records.getAllDerivedDefinitions(PropertyClassName)) {
+                       ASTNode node = property.getClass();
+                       NodeInfos[node].Properties.push_back(property);
+               }
+
+    // Find all the creation rules.
+    for (CreationRule creationRule :
+           records.getAllDerivedDefinitions(CreationRuleClassName)) {
+      ASTNode node = creationRule.getClass();
+
+      auto &info = NodeInfos[node];
+      if (info.Creator) {
+        PrintFatalError(creationRule.getLoc(),
+                        "multiple creator rules for \"" + node.getName()
+                          + "\"");
+      }
+      info.Creator = creationRule;
+    }
+
+    // Find all the override rules.
+    for (OverrideRule overrideRule :
+           records.getAllDerivedDefinitions(OverrideRuleClassName)) {
+      ASTNode node = overrideRule.getClass();
+
+      auto &info = NodeInfos[node];
+      if (info.Override) {
+        PrintFatalError(overrideRule.getLoc(),
+                        "multiple override rules for \"" + node.getName()
+                          + "\"");
+      }
+      info.Override = overrideRule;
+    }
+
+    Validator(*this).validate();
+       }
+
+  void visitAllProperties(ASTNode derived, const NodeInfo &derivedInfo,
+                          function_ref<void (Property)> visit) {
+    std::set<StringRef> ignoredProperties;
+
+    auto overrideRule = derivedInfo.Override;
+    if (overrideRule) {
+      auto list = overrideRule.getIgnoredProperties();
+      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;
+
+      for (Property prop : info.Properties) {
+        if (ignoredProperties.count(prop.getName()))
+          continue;
+
+        visit(prop);
+      }
+    }
+  }
+
+  template <class NodeClass>
+  void emitReaderClass() {
+    auto info = ReaderWriterInfo::forReader<NodeClass>();
+    emitReaderWriterClass<NodeClass>(info);
+  }
+
+  template <class NodeClass>
+  void emitWriterClass() {
+    auto info = ReaderWriterInfo::forWriter<NodeClass>();
+    emitReaderWriterClass<NodeClass>(info);
+  }
+
+  template <class NodeClass>
+  void emitReaderWriterClass(const ReaderWriterInfo &info);
+
+  template <class NodeClass>
+  void emitNodeReaderWriterMethod(NodeClass node,
+                                  const ReaderWriterInfo &info);
+
+  void emitReadOfProperty(Property property);
+  void emitWriteOfProperty(Property property);
+
+private:
+  class Validator {
+    const ASTPropsEmitter &Emitter;
+    std::set<ASTNode> ValidatedNodes;
+
+  public:
+    Validator(const ASTPropsEmitter &emitter) : Emitter(emitter) {}
+    void validate();
+
+  private:
+    void validateNode(ASTNode node, const NodeInfo &nodeInfo);
+    void validateType(PropertyType type, WrappedRecord context);
+  };
+};
+
+} // end anonymous namespace
+
+void ASTPropsEmitter::Validator::validate() {
+  for (auto &entry : Emitter.NodeInfos) {
+    validateNode(entry.first, entry.second);
+  }
+
+  if (ErrorsPrinted > 0) {
+    PrintFatalError("property validation failed");
+  }
+}
+
+void ASTPropsEmitter::Validator::validateNode(ASTNode node,
+                                              const NodeInfo &nodeInfo) {
+  if (!ValidatedNodes.insert(node).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) {
+      validateType(property.getType(), property);
+
+      auto result = allProperties.insert(
+                      std::make_pair(property.getName(), property));
+
+      // Diagnose non-unique properties.
+      if (!result.second) {
+        // The existing property is more likely to be associated with a
+        // derived node, so use it as the error.
+        Property existingProperty = result.first->second;
+        PrintError(existingProperty.getLoc(),
+                   "multiple properties named \"" + property.getName()
+                      + "\" in hierarchy of " + node.getName());
+        PrintNote(property.getLoc(), "existing property");
+      }
+    }
+  }
+}
+
+void ASTPropsEmitter::Validator::validateType(PropertyType type,
+                                              WrappedRecord context) {
+  if (!type.isGenericSpecialization()) {
+    if (type.getCXXTypeName() == "") {
+      PrintError(type.getLoc(),
+                 "type is not generic but has no C++ type name");
+      if (context) PrintNote(context.getLoc(), "type used here");
+    }
+  } else if (auto eltType = type.getArrayElementType()) {
+    validateType(eltType, context);
+  } else if (auto valueType = type.getOptionalElementType()) {
+    validateType(valueType, context);
+
+    if (valueType.getPackOptionalCode().empty()) {
+      PrintError(valueType.getLoc(),
+                 "type doesn't provide optional-packing code");
+      if (context) PrintNote(context.getLoc(), "type used here");
+    } else if (valueType.getUnpackOptionalCode().empty()) {
+      PrintError(valueType.getLoc(),
+                 "type doesn't provide optional-unpacking code");
+      if (context) PrintNote(context.getLoc(), "type used here");
+    }
+  } else {
+    PrintError(type.getLoc(), "unknown generic property type");
+    if (context) PrintNote(context.getLoc(), "type used here");
+  }
+}
+
+/****************************************************************************/
+/**************************** AST READER/WRITERS ****************************/
+/****************************************************************************/
+
+template <class NodeClass>
+void ASTPropsEmitter::emitReaderWriterClass(const ReaderWriterInfo &info) {
+  StringRef suffix = info.ClassSuffix;
+  StringRef var = info.HelperVariable;
+
+  // Enter the class declaration.
+  Out << "template <class Property" << suffix << ">\n"
+         "class Abstract" << info.HierarchyName << suffix << " {\n"
+         "public:\n"
+         "  Property" << suffix << " &" << var << ";\n\n";
+
+  // Emit the constructor.
+  Out << "  Abstract" << info.HierarchyName << suffix
+                      << "(Property" << suffix << " &" << var << ") : "
+                      << var << "(" << var << ") {}\n\n";
+
+  // Emit a method that dispatches on a kind to the appropriate node-specific
+  // method.
+  Out << "  " << info.ResultType << " " << info.MethodPrefix << "(";
+  if (info.IsReader)
+    Out       << NodeClass::getASTIdTypeName() << " kind";
+  else
+    Out       << "const " << info.HierarchyName << " *node";
+  Out         << ") {\n"
+         "    switch (";
+  if (info.IsReader)
+    Out         << "kind";
+  else
+    Out         << "node->" << NodeClass::getASTIdAccessorName() << "()";
+  Out           << ") {\n";
+  visitASTNodeHierarchy<NodeClass>(Records, [&](NodeClass node, NodeClass _) {
+    if (node.isAbstract()) return;
+    Out << "    case " << info.HierarchyName << "::" << node.getId() << ":\n"
+           "      return " << info.MethodPrefix << node.getClassName() << "(";
+    if (!info.IsReader)
+      Out                  << "static_cast<const " << node.getClassName()
+                           << " *>(node)";
+    Out                    << ");\n";
+  });
+  Out << "    }\n"
+         "    llvm_unreachable(\"bad kind\");\n"
+         "  }\n\n";
+
+  // Emit node-specific methods for all the concrete nodes.
+  visitASTNodeHierarchy<NodeClass>(Records,
+                                   [&](NodeClass node, NodeClass base) {
+    if (node.isAbstract()) return;
+    emitNodeReaderWriterMethod(node, info);
+  });
+
+  // Finish the class.
+  Out << "};\n\n";
+}
+
+/// Emit a reader method for the given concrete AST node class.
+template <class NodeClass>
+void ASTPropsEmitter::emitNodeReaderWriterMethod(NodeClass node,
+                                           const ReaderWriterInfo &info) {
+  // Declare and start the method.
+  Out << "  " << info.ResultType << " "
+              << info.MethodPrefix << node.getClassName() << "(";
+  if (!info.IsReader)
+    Out <<       "const " << node.getClassName() << " *node";
+  Out <<         ") {\n";
+  if (info.IsReader)
+    Out << "    auto &ctx = " << info.HelperVariable << ".getASTContext();\n";
+
+  // Find the information for this node.
+  auto it = NodeInfos.find(node);
+  if (it == NodeInfos.end())
+    PrintFatalError(node.getLoc(),
+                    "no information about how to deserialize \""
+                      + node.getName() + "\"");
+  auto &nodeInfo = it->second;
+
+  StringRef creationCode;
+  if (info.IsReader) {
+    // We should have a creation rule.
+    if (!nodeInfo.Creator)
+      PrintFatalError(node.getLoc(),
+                      "no " CreationRuleClassName " for \""
+                        + node.getName() + "\"");
+
+    creationCode = nodeInfo.Creator.getCreationCode();
+  }
+
+  // Emit code to read all the properties.
+  visitAllProperties(node, nodeInfo, [&](Property prop) {
+    // Verify that the creation code refers to this property.
+    if (info.IsReader && creationCode.find(prop.getName()) == StringRef::npos)
+      PrintFatalError(nodeInfo.Creator.getLoc(),
+                      "creation code for " + node.getName()
+                        + " doesn't refer to property \""
+                        + prop.getName() + "\"");
+
+    // Emit code to read or write this property.
+    if (info.IsReader)
+      emitReadOfProperty(prop);
+    else
+      emitWriteOfProperty(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,
+                                              PropertyType type,
+                                              bool isForRead) {
+  if (!type.isGenericSpecialization()) {
+    out << type.getAbstractTypeName();
+  } else if (auto eltType = type.getArrayElementType()) {
+    out << "Array";
+    // We only include an explicit template argument for reads so that
+    // we don't cause spurious const mismatches.
+    if (isForRead) {
+      out << "<";
+      eltType.emitCXXValueTypeName(isForRead, out);
+      out << ">";
+    }
+  } else if (auto valueType = type.getOptionalElementType()) {
+    out << "Optional";
+    // We only include an explicit template argument for reads so that
+    // we don't cause spurious const mismatches.
+    if (isForRead) {
+      out << "<";
+      valueType.emitCXXValueTypeName(isForRead, out);
+      out << ">";
+    }
+  } else {
+    PrintFatalError(type.getLoc(), "unexpected generic property type");
+  }
+}
+
+/// 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();
+
+  // Declare all the necessary buffers.
+  auto bufferTypes = type.getBufferElementTypes();
+  for (size_t i = 0, e = bufferTypes.size(); i != e; ++i) {
+    Out << "    llvm::SmallVector<";
+    PropertyType(bufferTypes[i]).emitCXXValueTypeName(/*for read*/ true, Out);
+    Out << ", 8> " << name << "_buffer_" << i << ";\n";
+  }
+
+  //   T prop = R.find("prop").read##ValueType(buffers...);
+  // We intentionally ignore shouldPassByReference here: we're going to
+  // get a pr-value back from read(), and we should be able to forward
+  // that in the creation rule.
+  Out << "    ";
+  type.emitCXXValueTypeName(true, Out);
+  Out << " " << name << " = R.find(\"" << name << "\")."
+      << (type.isGenericSpecialization() ? "template " : "") << "read";
+  emitBasicReaderWriterMethodSuffix(Out, type, /*for read*/ true);
+  Out << "(";
+  for (size_t i = 0, e = bufferTypes.size(); i != e; ++i) {
+    Out << (i > 0 ? ", " : "") << name << "_buffer_" << i;
+  }
+  Out << ");\n";
+}
+
+/// Emit code to write the given property in a node-writer method.
+void ASTPropsEmitter::emitWriteOfProperty(Property property) {
+  // 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";
+}
+
+/// Emit an .inc file that defines the AbstractFooReader class
+/// for the given AST class hierarchy.
+template <class NodeClass>
+static void emitASTReader(RecordKeeper &records, raw_ostream &out,
+                          StringRef description) {
+  emitSourceFileHeader(description, out);
+
+  ASTPropsEmitter(records, out).emitReaderClass<NodeClass>();
+}
+
+void clang::EmitClangTypeReader(RecordKeeper &records, raw_ostream &out) {
+  emitASTReader<TypeNode>(records, out, "A CRTP reader for Clang Type nodes");
+}
+
+/// Emit an .inc file that defines the AbstractFooWriter class
+/// for the given AST class hierarchy.
+template <class NodeClass>
+static void emitASTWriter(RecordKeeper &records, raw_ostream &out,
+                          StringRef description) {
+  emitSourceFileHeader(description, out);
+
+  ASTPropsEmitter(records, out).emitWriterClass<NodeClass>();
+}
+
+void clang::EmitClangTypeWriter(RecordKeeper &records, raw_ostream &out) {
+  emitASTWriter<TypeNode>(records, out, "A CRTP writer for Clang Type nodes");
+}
+
+/****************************************************************************/
+/*************************** BASIC READER/WRITERS ***************************/
+/****************************************************************************/
+
+static void emitDispatcherTemplate(ArrayRef<Record*> types, raw_ostream &out,
+                                   const ReaderWriterInfo &info) {
+  // Declare the {Read,Write}Dispatcher template.
+  StringRef dispatcherPrefix = (info.IsReader ? "Read" : "Write");
+  out << "template <class ValueType>\n"
+         "struct " << dispatcherPrefix << "Dispatcher;\n";
+
+  // Declare a specific specialization of the dispatcher template.
+  auto declareSpecialization =
+    [&](StringRef specializationParameters,
+        const Twine &cxxTypeName,
+        StringRef methodSuffix) {
+    StringRef var = info.HelperVariable;
+    out << "template " << specializationParameters << "\n"
+           "struct " << dispatcherPrefix << "Dispatcher<"
+                     << cxxTypeName << "> {\n";
+    out << "  template <class Basic" << info.ClassSuffix << ", class... Args>\n"
+           "  static " << (info.IsReader ? cxxTypeName : "void") << " "
+                       << info.MethodPrefix
+                       << "(Basic" << info.ClassSuffix << " &" << var
+                       << ", Args &&... args) {\n"
+           "    return " << var << "."
+                         << info.MethodPrefix << methodSuffix
+                         << "(std::forward<Args>(args)...);\n"
+           "  }\n"
+           "};\n";
+  };
+
+  // Declare explicit specializations for each of the concrete types.
+  for (PropertyType type : types) {
+    declareSpecialization("<>",
+                          type.getCXXTypeName(),
+                          type.getAbstractTypeName());
+    // Also declare a specialization for the const type when appropriate.
+    if (!info.IsReader && type.isConstWhenWriting()) {
+      declareSpecialization("<>",
+                            "const " + type.getCXXTypeName(),
+                            type.getAbstractTypeName());
+    }
+  }
+  // Declare partial specializations for ArrayRef and Optional.
+  declareSpecialization("<class T>",
+                        "llvm::ArrayRef<T>",
+                        "Array");
+  declareSpecialization("<class T>",
+                        "llvm::Optional<T>",
+                        "Optional");
+  out << "\n";
+}
+
+static void emitPackUnpackOptionalTemplate(ArrayRef<Record*> types,
+                                           raw_ostream &out,
+                                           const ReaderWriterInfo &info) {
+  StringRef classPrefix = (info.IsReader ? "Unpack" : "Pack");
+  StringRef methodName = (info.IsReader ? "unpack" : "pack");
+
+  // Declare the {Pack,Unpack}OptionalValue template.
+  out << "template <class ValueType>\n"
+         "struct " << classPrefix << "OptionalValue;\n";
+
+  auto declareSpecialization = [&](const Twine &typeName,
+                                   StringRef code) {
+    out << "template <>\n"
+           "struct " << classPrefix << "OptionalValue<" << typeName << "> {\n"
+           "  static " << (info.IsReader ? "Optional<" : "") << typeName
+                       << (info.IsReader ? "> " : " ") << methodName << "("
+                       << (info.IsReader ? "" : "Optional<") << typeName
+                       << (info.IsReader ? "" : ">") << " value) {\n"
+           "    return " << code << ";\n"
+           "  }\n"
+           "};\n";
+  };
+
+  for (PropertyType type : types) {
+    StringRef code = (info.IsReader ? type.getUnpackOptionalCode()
+                                    : type.getPackOptionalCode());
+    if (code.empty()) continue;
+
+    StringRef typeName = type.getCXXTypeName();
+    declareSpecialization(typeName, code);
+    if (type.isConstWhenWriting() && !info.IsReader)
+      declareSpecialization("const " + typeName, code);
+  }
+  out << "\n";
+}
+
+static void emitBasicReaderWriterTemplate(ArrayRef<Record*> types,
+                                          raw_ostream &out,
+                                          const ReaderWriterInfo &info) {
+  // Emit the Basic{Reader,Writer}Base template.
+  out << "template <class Impl>\n"
+         "class Basic" << info.ClassSuffix << "Base {\n";
+  if (info.IsReader)
+    out << "  ASTContext &C;\n";
+  out << "protected:\n"
+         "  Basic" << info.ClassSuffix << "Base"
+                   << (info.IsReader ? "(ASTContext &ctx) : C(ctx)" : "()")
+                   << " {}\n"
+         "public:\n";
+  if (info.IsReader)
+    out << "  ASTContext &getASTContext() { return C; }\n";
+  out << "  Impl &asImpl() { return static_cast<Impl&>(*this); }\n";
+
+  auto enterReaderWriterMethod = [&](StringRef cxxTypeName,
+                                     StringRef abstractTypeName,
+                                     bool shouldPassByReference,
+                                     bool constWhenWriting) {
+    out << "  " << (info.IsReader ? cxxTypeName : "void")
+                << " " << info.MethodPrefix << abstractTypeName << "(";
+    if (!info.IsReader)
+      out       << (shouldPassByReference || constWhenWriting ? "const " : "")
+                << cxxTypeName
+                << (shouldPassByReference ? " &" : "") << " value";
+    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 : types) {
+    if (type.isEnum()) {
+      enterReaderWriterMethod(type.getCXXTypeName(),
+                              type.getAbstractTypeName(),
+                              /*pass by reference*/ false,
+                              /*const when writing*/ false);
+      if (info.IsReader)
+        out << "    return " << type.getCXXTypeName()
+                             << "(asImpl().readUInt32());\n";
+      else
+        out << "    asImpl().writeUInt32(uint32_t(value));\n";
+      out << "  }\n";
+    } else if (PropertyType superclass = type.getSuperclassType()) {
+      enterReaderWriterMethod(type.getCXXTypeName(),
+                              type.getAbstractTypeName(),
+                              /*pass by reference*/ false,
+                              /*const when writing*/ type.isConstWhenWriting());
+      if (info.IsReader)
+        out << "    return cast_or_null<" << type.getSubclassClassName()
+                                          << ">(asImpl().read"
+                                          << superclass.getAbstractTypeName()
+                                          << "());\n";
+      else
+        out << "    asImpl().write" << superclass.getAbstractTypeName()
+                                    << "(value);\n";
+      out << "  }\n";
+    } else {
+      // The other types can't be handled as trivially.
+    }
+  }
+  out << "};\n\n";
+}
+
+static void emitBasicReaderWriterFile(RecordKeeper &records, raw_ostream &out,
+                                      const ReaderWriterInfo &info) {
+  auto types = records.getAllDerivedDefinitions(PropertyTypeClassName);
+
+  emitDispatcherTemplate(types, out, info);
+  emitPackUnpackOptionalTemplate(types, out, info);
+  emitBasicReaderWriterTemplate(types, out, info);
+}
+
+/// Emit an .inc file that defines some helper classes for reading
+/// basic values.
+void clang::EmitClangBasicReader(RecordKeeper &records, raw_ostream &out) {
+  emitSourceFileHeader("Helper classes for BasicReaders", out);
+
+  // Use any property, we won't be using those properties.
+  auto info = ReaderWriterInfo::forReader<TypeNode>();
+  emitBasicReaderWriterFile(records, out, info);
+}
+
+/// Emit an .inc file that defines some helper classes for writing
+/// basic values.
+void clang::EmitClangBasicWriter(RecordKeeper &records, raw_ostream &out) {
+  emitSourceFileHeader("Helper classes for BasicWriters", out);
+
+  // Use any property, we won't be using those properties.
+  auto info = ReaderWriterInfo::forWriter<TypeNode>();
+  emitBasicReaderWriterFile(records, out, info);
+}
index c118a32..75e305f 100644 (file)
@@ -42,6 +42,8 @@ enum ActionType {
   GenClangAttrParsedAttrKinds,
   GenClangAttrTextNodeDump,
   GenClangAttrNodeTraverse,
+  GenClangBasicReader,
+  GenClangBasicWriter,
   GenClangDiagsDefs,
   GenClangDiagGroups,
   GenClangDiagsIndexName,
@@ -131,6 +133,10 @@ cl::opt<ActionType> Action(
                    "Generate Clang diagnostic groups"),
         clEnumValN(GenClangDiagsIndexName, "gen-clang-diags-index-name",
                    "Generate Clang diagnostic name index"),
+        clEnumValN(GenClangBasicReader, "gen-clang-basic-reader",
+                   "Generate Clang BasicReader classes"),
+        clEnumValN(GenClangBasicWriter, "gen-clang-basic-writer",
+                   "Generate Clang BasicWriter classes"),
         clEnumValN(GenClangCommentNodes, "gen-clang-comment-nodes",
                    "Generate Clang AST comment nodes"),
         clEnumValN(GenClangDeclNodes, "gen-clang-decl-nodes",
@@ -276,6 +282,12 @@ bool ClangTableGenMain(raw_ostream &OS, RecordKeeper &Records) {
   case GenClangTypeNodes:
     EmitClangTypeNodes(Records, OS);
     break;
+  case GenClangBasicReader:
+    EmitClangBasicReader(Records, OS);
+    break;
+  case GenClangBasicWriter:
+    EmitClangBasicWriter(Records, OS);
+    break;
   case GenClangOpcodes:
     EmitClangOpcodes(Records, OS);
     break;
index c3d1de8..7ac2e0e 100644 (file)
@@ -27,8 +27,11 @@ namespace clang {
 void EmitClangDeclContext(llvm::RecordKeeper &RK, llvm::raw_ostream &OS);
 void EmitClangASTNodes(llvm::RecordKeeper &RK, llvm::raw_ostream &OS,
                        const std::string &N, const std::string &S);
+void EmitClangBasicReader(llvm::RecordKeeper &Records, llvm::raw_ostream &OS);
+void EmitClangBasicWriter(llvm::RecordKeeper &Records, llvm::raw_ostream &OS);
 void EmitClangTypeNodes(llvm::RecordKeeper &Records, llvm::raw_ostream &OS);
-
+void EmitClangTypeReader(llvm::RecordKeeper &Records, llvm::raw_ostream &OS);
+void EmitClangTypeWriter(llvm::RecordKeeper &Records, llvm::raw_ostream &OS);
 void EmitClangAttrParserStringSwitches(llvm::RecordKeeper &Records,
                                        llvm::raw_ostream &OS);
 void EmitClangAttrSubjectMatchRulesParserStringSwitches(