#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;
StringRef HierarchyName;
StringRef ClassSuffix;
StringRef MethodPrefix;
StringRef HelperVariable;
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) {
for (Property property :
records.getAllDerivedDefinitions(PropertyClassName)) {
HasProperties node = property.getClass();
NodeInfos[node].Properties.push_back(property);
}
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;
}
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;
}
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;
}
for (PropertyType type :
records.getAllDerivedDefinitions(PropertyTypeClassName)) {
if (type.isGenericSpecialization()) continue;
AllPropertyTypes.push_back(type);
}
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;
}
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());
}
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);
if (ASTNode base = derivedNode.getAs<ASTNode>()) {
for (base = base.getBase(); base; base = base.getBase()) {
auto it = NodeInfos.find(base);
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);
};
};
}
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;
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));
if (!result.second) {
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");
}
}
template <class NodeClass>
void ASTPropsEmitter::emitNodeReaderWriterClass(const ReaderWriterInfo &info) {
StringRef suffix = info.ClassSuffix;
StringRef var = info.HelperVariable;
Out << "template <class Property" << suffix << ">\n"
"class Abstract" << info.HierarchyName << suffix << " {\n"
"public:\n"
" Property" << suffix << " &" << var << ";\n\n";
Out << " Abstract" << info.HierarchyName << suffix
<< "(Property" << suffix << " &" << var << ") : "
<< var << "(" << var << ") {}\n\n";
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";
visitASTNodeHierarchy<NodeClass>(Records,
[&](NodeClass node, NodeClass base) {
if (node.isAbstract()) return;
emitNodeReaderWriterMethod(node, info);
});
Out << "};\n\n";
}
template <class NodeClass>
void ASTPropsEmitter::emitNodeReaderWriterMethod(NodeClass node,
const ReaderWriterInfo &info) {
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);
Out << " }\n\n";
}
void ASTPropsEmitter::emitPropertiedReaderWriterBody(HasProperties node,
const ReaderWriterInfo &info) {
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) {
if (!nodeInfo.Creator)
PrintFatalError(node.getLoc(),
"no " CreationRuleClassName " for \""
+ node.getName() + "\"");
creationCode = nodeInfo.Creator.getCreationCode();
}
if (!info.IsReader && nodeInfo.ReadHelper) {
Out << " " << nodeInfo.ReadHelper.getHelperCode() << "\n";
}
visitAllProperties(node, nodeInfo, [&](Property prop) {
if (info.IsReader && !creationCode.contains(prop.getName()))
PrintFatalError(nodeInfo.Creator.getLoc(),
"creation code for " + node.getName()
+ " doesn't refer to property \""
+ prop.getName() + "\"");
if (info.IsReader)
emitReadOfProperty(info.HelperVariable, prop);
else
emitWriteOfProperty(info.HelperVariable, prop);
});
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";
if (isForRead) {
out << "<";
eltType.emitCXXValueTypeName(isForRead, out);
out << ">";
}
} else if (auto valueType = type.getOptionalElementType()) {
out << "Optional";
if (isForRead) {
out << "<";
valueType.emitCXXValueTypeName(isForRead, out);
out << ">";
}
} else {
PrintFatalError(type.getLoc(), "unexpected generic property type");
}
}
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) {
auto bufferTypes = type.getBufferElementTypes();
for (size_t i = 0, e = bufferTypes.size(); i != e; ++i) {
Out << " llvm::SmallVector<";
PropertyType(bufferTypes[i]).emitCXXValueTypeName( true, Out);
Out << ", 8> " << name << "_buffer_" << i << ";\n";
}
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, 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";
}
}
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";
}
Out << " ";
type.emitCXXValueTypeName(false, Out);
Out << " " << name << " = (" << readCode << ");\n"
" " << writerName << ".find(\"" << name << "\").write";
emitBasicReaderWriterMethodSuffix(Out, type, false);
Out << "(" << name << ");\n";
if (!condition.empty()) {
Out << " }\n";
}
}
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");
}
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");
}
void
ASTPropsEmitter::emitDispatcherTemplate(const ReaderWriterInfo &info) {
StringRef dispatcherPrefix = (info.IsReader ? "Read" : "Write");
Out << "template <class ValueType>\n"
"struct " << dispatcherPrefix << "Dispatcher;\n";
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";
};
for (PropertyType type : AllPropertyTypes) {
declareSpecialization("<>",
type.getCXXTypeName(),
type.getAbstractTypeName());
if (!info.IsReader && type.isConstWhenWriting()) {
declareSpecialization("<>",
"const " + type.getCXXTypeName(),
type.getAbstractTypeName());
}
}
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");
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) {
Out << "template <class Impl>\n"
"class Basic" << info.ClassSuffix << "Base {\n";
Out << " ASTContext &C;\n";
Out << "protected:\n"
" Basic"
<< info.ClassSuffix << "Base" << ("(ASTContext &ctx) : C(ctx)")
<< " {}\n"
"public:\n";
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";
};
for (PropertyType type : AllPropertyTypes) {
auto enterMethod = [&](StringRef paramName) {
enterReaderWriterMethod(type.getCXXTypeName(),
type.getAbstractTypeName(),
type.shouldPassByReference(),
type.isConstWhenWriting(),
paramName);
};
auto exitMethod = [&] {
Out << " }\n";
};
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 {
}
}
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();
if (info.IsReader)
Out << " auto &ctx = asImpl().getASTContext();\n";
Out << " auto &&" << subvar << " = asImpl()."
<< info.MethodPrefix << "Object();\n";
TypeKindRule kindRule = typeCases.KindRule;
StringRef kindProperty = kindRule.getKindPropertyName();
PropertyType kindType = kindRule.getKindType();
if (info.IsReader) {
emitReadOfProperty(subvar, kindProperty, kindType);
} else {
emitWriteOfProperty(subvar, kindProperty, kindType,
kindRule.getReadCode());
}
ReaderWriterInfo subInfo = info;
subInfo.HelperVariable = subvar;
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);
}
void clang::EmitClangBasicReader(RecordKeeper &records, raw_ostream &out) {
emitSourceFileHeader("Helper classes for BasicReaders", out);
auto info = ReaderWriterInfo::forReader<TypeNode>();
ASTPropsEmitter(records, out).emitBasicReaderWriterFile(info);
}
void clang::EmitClangBasicWriter(RecordKeeper &records, raw_ostream &out) {
emitSourceFileHeader("Helper classes for BasicWriters", out);
auto info = ReaderWriterInfo::forWriter<TypeNode>();
ASTPropsEmitter(records, out).emitBasicReaderWriterFile(info);
}