diff options
Diffstat (limited to 'clang/utils/TableGen/ClangASTPropertiesEmitter.cpp')
-rw-r--r-- | clang/utils/TableGen/ClangASTPropertiesEmitter.cpp | 867 |
1 files changed, 867 insertions, 0 deletions
diff --git a/clang/utils/TableGen/ClangASTPropertiesEmitter.cpp b/clang/utils/TableGen/ClangASTPropertiesEmitter.cpp new file mode 100644 index 0000000000000..256ca42e9aab9 --- /dev/null +++ b/clang/utils/TableGen/ClangASTPropertiesEmitter.cpp @@ -0,0 +1,867 @@ +//=== 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; + ReadHelperRule ReadHelper = nullptr; +}; + +struct CasedTypeInfo { + TypeKindRule KindRule; + std::vector<TypeCase> Cases; +}; + +class ASTPropsEmitter { + raw_ostream &Out; + RecordKeeper &Records; + std::map<HasProperties, NodeInfo> NodeInfos; + std::vector<PropertyType> AllPropertyTypes; + std::map<PropertyType, CasedTypeInfo> CasedTypeInfos; + +public: + ASTPropsEmitter(RecordKeeper &records, raw_ostream &out) + : Out(out), Records(records) { + + // Find all the properties. + for (Property property : + records.getAllDerivedDefinitions(PropertyClassName)) { + HasProperties node = property.getClass(); + NodeInfos[node].Properties.push_back(property); + } + + // Find all the creation rules. + for (CreationRule creationRule : + records.getAllDerivedDefinitions(CreationRuleClassName)) { + HasProperties 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)) { + HasProperties node = overrideRule.getClass(); + + auto &info = NodeInfos[node]; + if (info.Override) { + PrintFatalError(overrideRule.getLoc(), + "multiple override rules for \"" + node.getName() + + "\""); + } + info.Override = overrideRule; + } + + // Find all the write helper rules. + for (ReadHelperRule helperRule : + records.getAllDerivedDefinitions(ReadHelperRuleClassName)) { + HasProperties node = helperRule.getClass(); + + auto &info = NodeInfos[node]; + if (info.ReadHelper) { + PrintFatalError(helperRule.getLoc(), + "multiple write helper rules for \"" + node.getName() + + "\""); + } + info.ReadHelper = helperRule; + } + + // Find all the concrete property types. + for (PropertyType type : + records.getAllDerivedDefinitions(PropertyTypeClassName)) { + // Ignore generic specializations; they're generally not useful when + // emitting basic emitters etc. + if (type.isGenericSpecialization()) continue; + + 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(HasProperties 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()); + } + + // TODO: we should sort the properties in various ways + // - put arrays at the end to enable abbreviations + // - put conditional properties after properties used in the condition + + 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 emitNodeReaderClass() { + auto info = ReaderWriterInfo::forReader<NodeClass>(); + emitNodeReaderWriterClass<NodeClass>(info); + } + + template <class NodeClass> + void emitNodeWriterClass() { + auto info = ReaderWriterInfo::forWriter<NodeClass>(); + emitNodeReaderWriterClass<NodeClass>(info); + } + + template <class NodeClass> + void emitNodeReaderWriterClass(const ReaderWriterInfo &info); + + template <class NodeClass> + void emitNodeReaderWriterMethod(NodeClass node, + const ReaderWriterInfo &info); + + void emitPropertiedReaderWriterBody(HasProperties node, + const ReaderWriterInfo &info); + + void emitReadOfProperty(StringRef readerName, Property property); + void emitReadOfProperty(StringRef readerName, StringRef name, + PropertyType type, StringRef condition = ""); + + void emitWriteOfProperty(StringRef writerName, Property property); + void emitWriteOfProperty(StringRef writerName, StringRef name, + PropertyType type, StringRef readCode, + StringRef condition = ""); + + 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 { + ASTPropsEmitter &Emitter; + std::set<HasProperties> ValidatedNodes; + + public: + Validator(ASTPropsEmitter &emitter) : Emitter(emitter) {} + void validate(); + + private: + void validateNode(HasProperties 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(HasProperties derivedNode, + const NodeInfo &derivedNodeInfo) { + if (!ValidatedNodes.insert(derivedNode).second) return; + + // A map from property name to property. + std::map<StringRef, Property> allProperties; + + Emitter.visitAllNodesWithInfo(derivedNode, derivedNodeInfo, + [&](HasProperties node, + const NodeInfo &nodeInfo) { + for (Property property : nodeInfo.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 " + derivedNode.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::emitNodeReaderWriterClass(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"; + + 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()) + 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 the ReadHelper code, if present. + if (!info.IsReader && nodeInfo.ReadHelper) { + Out << " " << nodeInfo.ReadHelper.getHelperCode() << "\n"; + } + + // 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(info.HelperVariable, prop); + else + emitWriteOfProperty(info.HelperVariable, prop); + }); + + // Emit the final creation code. + if (info.IsReader) + Out << " " << creationCode << "\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(StringRef readerName, + Property property) { + emitReadOfProperty(readerName, property.getName(), property.getType(), + property.getCondition()); +} + +void ASTPropsEmitter::emitReadOfProperty(StringRef readerName, + StringRef name, + PropertyType type, + StringRef condition) { + // 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 << " "; + if (!condition.empty()) Out << "llvm::Optional<"; + type.emitCXXValueTypeName(true, Out); + if (!condition.empty()) Out << ">"; + Out << " " << name; + + if (condition.empty()) { + Out << " = "; + } else { + Out << ";\n" + " if (" << condition << ") {\n" + " " << name << ".emplace("; + } + + Out << readerName << ".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 << ")"; + + if (condition.empty()) { + Out << ";\n"; + } else { + Out << ");\n" + " }\n"; + } +} + +/// Emit code to write the given property in a node-writer method. +void ASTPropsEmitter::emitWriteOfProperty(StringRef writerName, + Property property) { + emitWriteOfProperty(writerName, property.getName(), property.getType(), + property.getReadCode(), property.getCondition()); +} + +void ASTPropsEmitter::emitWriteOfProperty(StringRef writerName, + StringRef name, + PropertyType type, + StringRef readCode, + StringRef condition) { + if (!condition.empty()) { + Out << " if (" << condition << ") {\n"; + } + + // Focus down to the property: + // T prop = <READ>; + // W.find("prop").write##ValueType(prop); + Out << " "; + type.emitCXXValueTypeName(false, Out); + Out << " " << name << " = (" << readCode << ");\n" + " " << writerName << ".find(\"" << name << "\").write"; + emitBasicReaderWriterMethodSuffix(Out, type, /*for read*/ false); + Out << "(" << name << ");\n"; + + if (!condition.empty()) { + Out << " }\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).emitNodeReaderClass<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).emitNodeWriterClass<NodeClass>(); +} + +void clang::EmitClangTypeWriter(RecordKeeper &records, raw_ostream &out) { + emitASTWriter<TypeNode>(records, out, "A CRTP writer for Clang Type nodes"); +} + +/****************************************************************************/ +/*************************** BASIC READER/WRITERS ***************************/ +/****************************************************************************/ + +void +ASTPropsEmitter::emitDispatcherTemplate(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 : AllPropertyTypes) { + 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"; +} + +void +ASTPropsEmitter::emitPackUnpackOptionalTemplate(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 : AllPropertyTypes) { + 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"; +} + +void +ASTPropsEmitter::emitBasicReaderWriterTemplate(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, + StringRef paramName) { + Out << " " << (info.IsReader ? cxxTypeName : "void") + << " " << info.MethodPrefix << abstractTypeName << "("; + if (!info.IsReader) + Out << (shouldPassByReference || constWhenWriting ? "const " : "") + << cxxTypeName + << (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) { + auto enterMethod = [&](StringRef paramName) { + enterReaderWriterMethod(type.getCXXTypeName(), + type.getAbstractTypeName(), + 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 asImpl().template readEnum<" + << type.getCXXTypeName() << ">();\n"; + else + Out << " asImpl().writeEnum(value);\n"; + exitMethod(); + + } else if (PropertyType superclass = type.getSuperclassType()) { + enterMethod("value"); + if (info.IsReader) + Out << " return cast_or_null<" << type.getSubclassClassName() + << ">(asImpl().read" + << superclass.getAbstractTypeName() + << "());\n"; + else + Out << " asImpl().write" << superclass.getAbstractTypeName() + << "(value);\n"; + exitMethod(); + + } else { + // The other types can't be handled as trivially. + } + } + Out << "};\n\n"; +} + +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 { + // Write the property. Note that this will implicitly read the + // kind into a local variable with the right name. + emitWriteOfProperty(subvar, kindProperty, kindType, + kindRule.getReadCode()); + } + + // 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); +} + +/// 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>(); + ASTPropsEmitter(records, out).emitBasicReaderWriterFile(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>(); + ASTPropsEmitter(records, out).emitBasicReaderWriterFile(info); +} |