868 lines
		
	
	
		
			30 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			868 lines
		
	
	
		
			30 KiB
		
	
	
	
		
			C++
		
	
	
	
| //=== 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);
 | |
| }
 |