Add the ability to use property-based serialization for "cased" types.

This patch doesn't actually use this serialization for anything,
but follow-ups will move the current handling of various standard
types over to this.
This commit is contained in:
John McCall 2019-12-16 02:10:15 -05:00
parent 41d935f2c6
commit efd0dfbd70
12 changed files with 450 additions and 89 deletions

View File

@ -37,6 +37,34 @@ inline T *makePointerFromOptional(Optional<T *> value) {
// where TypeName is the name of a PropertyType node from PropertiesBase.td // where TypeName is the name of a PropertyType node from PropertiesBase.td
// and ValueType is the corresponding C++ type name. The read method may // and ValueType is the corresponding C++ type name. The read method may
// require one or more buffer arguments. // require one or more buffer arguments.
//
// In addition to the concrete type names, BasicReader is expected to
// implement these methods:
//
// template <class ValueType>
// Optional<ValueType> writeOptional();
//
// Reads an optional value from the current property.
//
// template <class ValueType>
// ArrayRef<ValueType> readArray(llvm::SmallVectorImpl<ValueType> &buffer);
//
// Reads an array of values from the current property.
//
// PropertyReader readObject();
//
// Reads an object from the current property; the returned property
// reader will be subjected to a sequence of property reads and then
// discarded before any other properties are reader from the "outer"
// property reader (which need not be the same type). The sub-reader
// will be used as if with the following code:
//
// {
// auto &&widget = W.find("widget").readObject();
// auto kind = widget.find("kind").readWidgetKind();
// auto declaration = widget.find("declaration").readDeclRef();
// return Widget(kind, declaration);
// }
// ReadDispatcher does type-based forwarding to one of the read methods // ReadDispatcher does type-based forwarding to one of the read methods
// on the BasicReader passed in: // on the BasicReader passed in:
@ -99,6 +127,10 @@ public:
return asImpl(); return asImpl();
} }
// Implement object reading by forwarding to this, collapsing the
// structure into a single data stream.
Impl &readObject() { return asImpl(); }
template <class T> template <class T>
llvm::ArrayRef<T> readArray(llvm::SmallVectorImpl<T> &buffer) { llvm::ArrayRef<T> readArray(llvm::SmallVectorImpl<T> &buffer) {
assert(buffer.empty()); assert(buffer.empty());

View File

@ -38,6 +38,33 @@ inline llvm::Optional<T*> makeOptionalFromPointer(T *value) {
// void write##TypeName(ValueType value); // void write##TypeName(ValueType value);
// where TypeName is the name of a PropertyType node from PropertiesBase.td // where TypeName is the name of a PropertyType node from PropertiesBase.td
// and ValueType is the corresponding C++ type name. // and ValueType is the corresponding C++ type name.
//
// In addition to the concrete property types, BasicWriter is expected
// to implement these methods:
//
// template <class ValueType>
// void writeOptional(Optional<ValueType> value);
//
// Writes an optional value as the current property.
//
// template <class ValueType>
// void writeArray(ArrayRef<ValueType> value);
//
// Writes an array of values as the current property.
//
// PropertyWriter writeObject();
//
// Writes an object as the current property; the returned property
// writer will be subjected to a sequence of property writes and then
// discarded before any other properties are written to the "outer"
// property writer (which need not be the same type). The sub-writer
// will be used as if with the following code:
//
// {
// auto &&widget = W.find("widget").writeObject();
// widget.find("kind").writeWidgetKind(...);
// widget.find("declaration").writeDeclRef(...);
// }
// WriteDispatcher is a template which does type-based forwarding to one // WriteDispatcher is a template which does type-based forwarding to one
// of the write methods of the BasicWriter passed in: // of the write methods of the BasicWriter passed in:
@ -95,6 +122,10 @@ public:
return asImpl(); return asImpl();
} }
// Implement object writing by forwarding to this, collapsing the
// structure into a single data stream.
Impl &writeObject() { return asImpl(); }
template <class T> template <class T>
void writeArray(llvm::ArrayRef<T> array) { void writeArray(llvm::ArrayRef<T> array) {
asImpl().writeUInt32(array.size()); asImpl().writeUInt32(array.size());

View File

@ -6,7 +6,7 @@
// //
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
class ASTNode; class HasProperties;
/// The type of the property. /// The type of the property.
class PropertyType<string typeName = ""> { class PropertyType<string typeName = ""> {
@ -151,7 +151,7 @@ class Optional<PropertyType element> : PropertyType {
/// A property of an AST node. /// A property of an AST node.
class Property<string name, PropertyType type> { class Property<string name, PropertyType type> {
ASTNode Class; HasProperties Class;
string Name = name; string Name = name;
PropertyType Type = type; PropertyType Type = type;
@ -162,7 +162,7 @@ class Property<string name, PropertyType type> {
/// A rule for creating objects of this type. /// A rule for creating objects of this type.
class Creator<code create> { class Creator<code create> {
ASTNode Class; HasProperties Class;
/// A function for creating values of this kind, expressed in terms of a /// 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 /// variable `ctx` of type `ASTContext &`. Must also refer to all of the
@ -172,8 +172,42 @@ class Creator<code create> {
/// A rule which overrides some of the normal rules. /// A rule which overrides some of the normal rules.
class Override { class Override {
ASTNode Class; HasProperties Class;
/// Properties from base classes that should be ignored. /// Properties from base classes that should be ignored.
list<string> IgnoredProperties = []; list<string> IgnoredProperties = [];
} }
/// A description of how to break a type into cases. Providing this and
/// an exhaustive list of the cases will cause AbstractBasic{Reader,Writer}
/// to be generated with a default implementation of how to read the
/// type.
///
/// Creator rules for the cases can additionally access a variable
/// `kind` of the KindType.
class PropertyTypeKind<PropertyType type,
PropertyType kindType,
string readCode> {
/// The type for which this describes cases.
PropertyType Type = type;
/// The type of this type's kind enum.
PropertyType KindType = kindType;
/// The property name to use for the kind.
string KindPropertyName = "kind";
/// An expression which reads the kind from a value, expressed in terms
/// of a variable `node`.
string Read = readCode;
}
/// One of the options for representing a particular type.
class PropertyTypeCase<PropertyType type, string name> : HasProperties {
/// The type of which this is a case.
PropertyType Type = type;
/// The name of the case (a value of the type's kind enum).
string Name = name;
}

View File

@ -0,0 +1,8 @@
#ifndef AST_NODE_TD
#define AST_NODE_TD
class HasProperties;
class ASTNode : HasProperties;
class AttrSubject;
#endif

View File

@ -1,4 +1,6 @@
class CommentNode<CommentNode base, bit abstract = 0> { include "clang/Basic/ASTNode.td"
class CommentNode<CommentNode base, bit abstract = 0> : ASTNode {
CommentNode Base = base; CommentNode Base = base;
bit Abstract = abstract; bit Abstract = abstract;
} }

View File

@ -1,5 +1,4 @@
class ASTNode; include "clang/Basic/ASTNode.td"
class AttrSubject;
class DeclNode<DeclNode base, string diagSpelling = "", bit abstract = 0> class DeclNode<DeclNode base, string diagSpelling = "", bit abstract = 0>
: ASTNode, AttrSubject { : ASTNode, AttrSubject {

View File

@ -1,5 +1,4 @@
class ASTNode; include "clang/Basic/ASTNode.td"
class AttrSubject;
class StmtNode<StmtNode base, bit abstract = 0> : ASTNode, AttrSubject { class StmtNode<StmtNode base, bit abstract = 0> : ASTNode, AttrSubject {
StmtNode Base = base; StmtNode Base = base;

View File

@ -1,4 +1,4 @@
class ASTNode; include "clang/Basic/ASTNode.td"
class TypeNode<TypeNode base, bit abstract = 0> : ASTNode { class TypeNode<TypeNode base, bit abstract = 0> : ASTNode {
TypeNode Base = base; TypeNode Base = base;

View File

@ -20,6 +20,16 @@ using namespace llvm;
using namespace clang; using namespace clang;
using namespace clang::tblgen; using namespace clang::tblgen;
llvm::StringRef clang::tblgen::HasProperties::getName() const {
if (auto node = getAs<ASTNode>()) {
return node.getName();
} else if (auto typeCase = getAs<TypeCase>()) {
return typeCase.getCaseName();
} else {
PrintFatalError(getLoc(), "unexpected node declaring properties");
}
}
static StringRef removeExpectedNodeNameSuffix(Record *node, StringRef suffix) { static StringRef removeExpectedNodeNameSuffix(Record *node, StringRef suffix) {
StringRef nodeName = node->getName(); StringRef nodeName = node->getName();
if (!nodeName.endswith(suffix)) { if (!nodeName.endswith(suffix)) {

View File

@ -14,7 +14,11 @@
// These are spellings in the tblgen files. // These are spellings in the tblgen files.
// Field names that are fortunately common across the hierarchies. #define HasPropertiesClassName "HasProperties"
// ASTNodes and their common fields. `Base` is actually defined
// in subclasses, but it's still common across the hierarchies.
#define ASTNodeClassName "ASTNode"
#define BaseFieldName "Base" #define BaseFieldName "Base"
#define AbstractFieldName "Abstract" #define AbstractFieldName "Abstract"
@ -35,6 +39,12 @@
#define NeverCanonicalUnlessDependentClassName "NeverCanonicalUnlessDependent" #define NeverCanonicalUnlessDependentClassName "NeverCanonicalUnlessDependent"
#define LeafTypeClassName "LeafType" #define LeafTypeClassName "LeafType"
// Cases of various non-ASTNode structured types like DeclarationName.
#define TypeKindClassName "PropertyTypeKind"
#define KindTypeFieldName "KindType"
#define KindPropertyNameFieldName "KindPropertyName"
#define TypeCaseClassName "PropertyTypeCase"
// Properties of AST nodes. // Properties of AST nodes.
#define PropertyClassName "Property" #define PropertyClassName "Property"
#define ClassFieldName "Class" #define ClassFieldName "Class"
@ -94,13 +104,54 @@ public:
bool isSubClassOf(llvm::StringRef className) const { bool isSubClassOf(llvm::StringRef className) const {
return get()->isSubClassOf(className); return get()->isSubClassOf(className);
} }
template <class NodeClass>
NodeClass getAs() const {
return (isSubClassOf(NodeClass::getTableGenNodeClassName())
? NodeClass(get()) : NodeClass());
}
friend bool operator<(WrappedRecord lhs, WrappedRecord rhs) {
assert(lhs && rhs && "sorting null nodes");
return lhs.get()->getName() < rhs.get()->getName();
}
friend bool operator>(WrappedRecord lhs, WrappedRecord rhs) {
return rhs < lhs;
}
friend bool operator<=(WrappedRecord lhs, WrappedRecord rhs) {
return !(rhs < lhs);
}
friend bool operator>=(WrappedRecord lhs, WrappedRecord rhs) {
return !(lhs < rhs);
}
friend bool operator==(WrappedRecord lhs, WrappedRecord rhs) {
// This should handle null nodes.
return lhs.getRecord() == rhs.getRecord();
}
friend bool operator!=(WrappedRecord lhs, WrappedRecord rhs) {
return !(lhs == rhs);
}
};
/// Anything in the AST that has properties.
class HasProperties : public WrappedRecord {
public:
static constexpr llvm::StringRef ClassName = HasPropertiesClassName;
HasProperties(llvm::Record *record = nullptr) : WrappedRecord(record) {}
llvm::StringRef getName() const;
static llvm::StringRef getTableGenNodeClassName() {
return HasPropertiesClassName;
}
}; };
/// An (optional) reference to a TableGen node representing a class /// An (optional) reference to a TableGen node representing a class
/// in one of Clang's AST hierarchies. /// in one of Clang's AST hierarchies.
class ASTNode : public WrappedRecord { class ASTNode : public HasProperties {
public: public:
ASTNode(llvm::Record *record = nullptr) : WrappedRecord(record) {} ASTNode(llvm::Record *record = nullptr) : HasProperties(record) {}
llvm::StringRef getName() const { llvm::StringRef getName() const {
return get()->getName(); return get()->getName();
@ -116,19 +167,9 @@ public:
return get()->getValueAsBit(AbstractFieldName); return get()->getValueAsBit(AbstractFieldName);
} }
friend bool operator<(ASTNode lhs, ASTNode rhs) { static llvm::StringRef getTableGenNodeClassName() {
assert(lhs && rhs && "sorting null nodes"); return ASTNodeClassName;
return lhs.getName() < rhs.getName();
} }
friend bool operator>(ASTNode lhs, ASTNode rhs) { return rhs < lhs; }
friend bool operator<=(ASTNode lhs, ASTNode rhs) { return !(rhs < lhs); }
friend bool operator>=(ASTNode lhs, ASTNode rhs) { return !(lhs < rhs); }
friend bool operator==(ASTNode lhs, ASTNode rhs) {
// This should handle null nodes.
return lhs.getRecord() == rhs.getRecord();
}
friend bool operator!=(ASTNode lhs, ASTNode rhs) { return !(lhs == rhs); }
}; };
class DeclNode : public ASTNode { class DeclNode : public ASTNode {
@ -275,6 +316,60 @@ public:
std::vector<llvm::Record*> getBufferElementTypes() const { std::vector<llvm::Record*> getBufferElementTypes() const {
return get()->getValueAsListOfDefs(BufferElementTypesFieldName); return get()->getValueAsListOfDefs(BufferElementTypesFieldName);
} }
static llvm::StringRef getTableGenNodeClassName() {
return PropertyTypeClassName;
}
};
/// A rule for returning the kind of a type.
class TypeKindRule : public WrappedRecord {
public:
TypeKindRule(llvm::Record *record = nullptr) : WrappedRecord(record) {}
/// Return the type to which this applies.
PropertyType getParentType() const {
return get()->getValueAsDef(TypeFieldName);
}
/// Return the type of the kind.
PropertyType getKindType() const {
return get()->getValueAsDef(KindTypeFieldName);
}
/// Return the name to use for the kind property.
llvm::StringRef getKindPropertyName() const {
return get()->getValueAsString(KindPropertyNameFieldName);
}
/// Return the code for reading the kind value.
llvm::StringRef getReadCode() const {
return get()->getValueAsString(ReadFieldName);
}
static llvm::StringRef getTableGenNodeClassName() {
return TypeKindClassName;
}
};
/// An implementation case of a property type.
class TypeCase : public HasProperties {
public:
TypeCase(llvm::Record *record = nullptr) : HasProperties(record) {}
/// Return the name of this case.
llvm::StringRef getCaseName() const {
return get()->getValueAsString(NameFieldName);
}
/// Return the type of which this is a case.
PropertyType getParentType() const {
return get()->getValueAsDef(TypeFieldName);
}
static llvm::StringRef getTableGenNodeClassName() {
return TypeCaseClassName;
}
}; };
/// A property of an AST node. /// A property of an AST node.
@ -301,6 +396,10 @@ public:
llvm::StringRef getReadCode() const { llvm::StringRef getReadCode() const {
return get()->getValueAsString(ReadFieldName); return get()->getValueAsString(ReadFieldName);
} }
static llvm::StringRef getTableGenNodeClassName() {
return PropertyClassName;
}
}; };
/// A rule for how to create an AST node from its properties. /// A rule for how to create an AST node from its properties.
@ -317,6 +416,10 @@ public:
llvm::StringRef getCreationCode() const { llvm::StringRef getCreationCode() const {
return get()->getValueAsString(CreateFieldName); return get()->getValueAsString(CreateFieldName);
} }
static llvm::StringRef getTableGenNodeClassName() {
return CreationRuleClassName;
}
}; };
/// A rule which overrides the standard rules for serializing an AST node. /// A rule which overrides the standard rules for serializing an AST node.
@ -336,6 +439,10 @@ public:
std::vector<llvm::StringRef> getIgnoredProperties() const { std::vector<llvm::StringRef> getIgnoredProperties() const {
return get()->getValueAsListOfStrings(IgnoredPropertiesFieldName); return get()->getValueAsListOfStrings(IgnoredPropertiesFieldName);
} }
static llvm::StringRef getTableGenNodeClassName() {
return OverrideRuleClassName;
}
}; };
/// A visitor for an AST node hierarchy. Note that `base` can be null for /// A visitor for an AST node hierarchy. Note that `base` can be null for

View File

@ -142,7 +142,7 @@ std::pair<ASTNode, ASTNode> ClangASTNodesEmitter::EmitNode(raw_ostream &OS,
} }
void ClangASTNodesEmitter::deriveChildTree() { void ClangASTNodesEmitter::deriveChildTree() {
assert(Root == nullptr && "already computed tree"); assert(!Root && "already computed tree");
// Emit statements // Emit statements
const std::vector<Record*> Stmts const std::vector<Record*> Stmts

View File

@ -80,11 +80,17 @@ struct NodeInfo {
OverrideRule Override = nullptr; OverrideRule Override = nullptr;
}; };
struct CasedTypeInfo {
TypeKindRule KindRule;
std::vector<TypeCase> Cases;
};
class ASTPropsEmitter { class ASTPropsEmitter {
raw_ostream &Out; raw_ostream &Out;
RecordKeeper &Records; RecordKeeper &Records;
std::map<ASTNode, NodeInfo> NodeInfos; std::map<HasProperties, NodeInfo> NodeInfos;
std::vector<PropertyType> AllPropertyTypes; std::vector<PropertyType> AllPropertyTypes;
std::map<PropertyType, CasedTypeInfo> CasedTypeInfos;
public: public:
ASTPropsEmitter(RecordKeeper &records, raw_ostream &out) ASTPropsEmitter(RecordKeeper &records, raw_ostream &out)
@ -125,6 +131,7 @@ public:
info.Override = overrideRule; info.Override = overrideRule;
} }
// Find all the concrete property types.
for (PropertyType type : for (PropertyType type :
records.getAllDerivedDefinitions(PropertyTypeClassName)) { records.getAllDerivedDefinitions(PropertyTypeClassName)) {
// Ignore generic specializations; they're generally not useful when // Ignore generic specializations; they're generally not useful when
@ -134,10 +141,29 @@ public:
AllPropertyTypes.push_back(type); 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(); Validator(*this).validate();
} }
void visitAllProperties(ASTNode derived, const NodeInfo &derivedInfo, void visitAllProperties(HasProperties derived, const NodeInfo &derivedInfo,
function_ref<void (Property)> visit) { function_ref<void (Property)> visit) {
std::set<StringRef> ignoredProperties; std::set<StringRef> ignoredProperties;
@ -147,60 +173,88 @@ public:
ignoredProperties.insert(list.begin(), list.end()); ignoredProperties.insert(list.begin(), list.end());
} }
for (ASTNode node = derived; node; node = node.getBase()) { visitAllNodesWithInfo(derived, derivedInfo,
auto it = NodeInfos.find(node); [&](HasProperties node, const NodeInfo &info) {
// Ignore intermediate nodes that don't add interesting properties.
if (it == NodeInfos.end()) continue;
auto &info = it->second;
for (Property prop : info.Properties) { for (Property prop : info.Properties) {
if (ignoredProperties.count(prop.getName())) if (ignoredProperties.count(prop.getName()))
continue; continue;
visit(prop); 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> template <class NodeClass>
void emitReaderClass() { void emitNodeReaderClass() {
auto info = ReaderWriterInfo::forReader<NodeClass>(); auto info = ReaderWriterInfo::forReader<NodeClass>();
emitReaderWriterClass<NodeClass>(info); emitNodeReaderWriterClass<NodeClass>(info);
} }
template <class NodeClass> template <class NodeClass>
void emitWriterClass() { void emitNodeWriterClass() {
auto info = ReaderWriterInfo::forWriter<NodeClass>(); auto info = ReaderWriterInfo::forWriter<NodeClass>();
emitReaderWriterClass<NodeClass>(info); emitNodeReaderWriterClass<NodeClass>(info);
} }
template <class NodeClass> template <class NodeClass>
void emitReaderWriterClass(const ReaderWriterInfo &info); void emitNodeReaderWriterClass(const ReaderWriterInfo &info);
template <class NodeClass> template <class NodeClass>
void emitNodeReaderWriterMethod(NodeClass node, void emitNodeReaderWriterMethod(NodeClass node,
const ReaderWriterInfo &info); const ReaderWriterInfo &info);
void emitReadOfProperty(Property property); void emitPropertiedReaderWriterBody(HasProperties node,
void emitWriteOfProperty(Property property); const ReaderWriterInfo &info);
void emitReadOfProperty(StringRef readerName, Property property);
void emitReadOfProperty(StringRef readerName, StringRef name,
PropertyType type);
void emitWriteOfProperty(StringRef writerName, Property property);
void emitWriteOfProperty(StringRef writerName, StringRef name,
PropertyType type, StringRef readCode);
void emitBasicReaderWriterFile(const ReaderWriterInfo &info); void emitBasicReaderWriterFile(const ReaderWriterInfo &info);
void emitDispatcherTemplate(const ReaderWriterInfo &info); void emitDispatcherTemplate(const ReaderWriterInfo &info);
void emitPackUnpackOptionalTemplate(const ReaderWriterInfo &info); void emitPackUnpackOptionalTemplate(const ReaderWriterInfo &info);
void emitBasicReaderWriterTemplate(const ReaderWriterInfo &info); void emitBasicReaderWriterTemplate(const ReaderWriterInfo &info);
void emitCasedReaderWriterMethodBody(PropertyType type,
const CasedTypeInfo &typeCases,
const ReaderWriterInfo &info);
private: private:
class Validator { class Validator {
const ASTPropsEmitter &Emitter; ASTPropsEmitter &Emitter;
std::set<ASTNode> ValidatedNodes; std::set<HasProperties> ValidatedNodes;
public: public:
Validator(const ASTPropsEmitter &emitter) : Emitter(emitter) {} Validator(ASTPropsEmitter &emitter) : Emitter(emitter) {}
void validate(); void validate();
private: private:
void validateNode(ASTNode node, const NodeInfo &nodeInfo); void validateNode(HasProperties node, const NodeInfo &nodeInfo);
void validateType(PropertyType type, WrappedRecord context); void validateType(PropertyType type, WrappedRecord context);
}; };
}; };
@ -217,21 +271,17 @@ void ASTPropsEmitter::Validator::validate() {
} }
} }
void ASTPropsEmitter::Validator::validateNode(ASTNode node, void ASTPropsEmitter::Validator::validateNode(HasProperties derivedNode,
const NodeInfo &nodeInfo) { const NodeInfo &derivedNodeInfo) {
if (!ValidatedNodes.insert(node).second) return; if (!ValidatedNodes.insert(derivedNode).second) return;
// A map from property name to property. // A map from property name to property.
std::map<StringRef, Property> allProperties; std::map<StringRef, Property> allProperties;
// Walk the hierarchy, ignoring nodes that don't declare anything Emitter.visitAllNodesWithInfo(derivedNode, derivedNodeInfo,
// interesting. [&](HasProperties node,
for (auto base = node; base; base = base.getBase()) { const NodeInfo &nodeInfo) {
auto it = Emitter.NodeInfos.find(base); for (Property property : nodeInfo.Properties) {
if (it == Emitter.NodeInfos.end()) continue;
auto &baseInfo = it->second;
for (Property property : baseInfo.Properties) {
validateType(property.getType(), property); validateType(property.getType(), property);
auto result = allProperties.insert( auto result = allProperties.insert(
@ -244,11 +294,11 @@ void ASTPropsEmitter::Validator::validateNode(ASTNode node,
Property existingProperty = result.first->second; Property existingProperty = result.first->second;
PrintError(existingProperty.getLoc(), PrintError(existingProperty.getLoc(),
"multiple properties named \"" + property.getName() "multiple properties named \"" + property.getName()
+ "\" in hierarchy of " + node.getName()); + "\" in hierarchy of " + derivedNode.getName());
PrintNote(property.getLoc(), "existing property"); PrintNote(property.getLoc(), "existing property");
} }
} }
} });
} }
void ASTPropsEmitter::Validator::validateType(PropertyType type, void ASTPropsEmitter::Validator::validateType(PropertyType type,
@ -284,7 +334,7 @@ void ASTPropsEmitter::Validator::validateType(PropertyType type,
/****************************************************************************/ /****************************************************************************/
template <class NodeClass> template <class NodeClass>
void ASTPropsEmitter::emitReaderWriterClass(const ReaderWriterInfo &info) { void ASTPropsEmitter::emitNodeReaderWriterClass(const ReaderWriterInfo &info) {
StringRef suffix = info.ClassSuffix; StringRef suffix = info.ClassSuffix;
StringRef var = info.HelperVariable; StringRef var = info.HelperVariable;
@ -350,6 +400,14 @@ void ASTPropsEmitter::emitNodeReaderWriterMethod(NodeClass node,
if (info.IsReader) if (info.IsReader)
Out << " auto &ctx = " << info.HelperVariable << ".getASTContext();\n"; 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. // Find the information for this node.
auto it = NodeInfos.find(node); auto it = NodeInfos.find(node);
if (it == NodeInfos.end()) if (it == NodeInfos.end())
@ -380,17 +438,14 @@ void ASTPropsEmitter::emitNodeReaderWriterMethod(NodeClass node,
// Emit code to read or write this property. // Emit code to read or write this property.
if (info.IsReader) if (info.IsReader)
emitReadOfProperty(prop); emitReadOfProperty(info.HelperVariable, prop);
else else
emitWriteOfProperty(prop); emitWriteOfProperty(info.HelperVariable, prop);
}); });
// Emit the final creation code. // Emit the final creation code.
if (info.IsReader) if (info.IsReader)
Out << " " << creationCode << "\n"; Out << " " << creationCode << "\n";
// Finish the method declaration.
Out << " }\n\n";
} }
static void emitBasicReaderWriterMethodSuffix(raw_ostream &out, static void emitBasicReaderWriterMethodSuffix(raw_ostream &out,
@ -422,10 +477,14 @@ static void emitBasicReaderWriterMethodSuffix(raw_ostream &out,
} }
/// Emit code to read the given property in a node-reader method. /// Emit code to read the given property in a node-reader method.
void ASTPropsEmitter::emitReadOfProperty(Property property) { void ASTPropsEmitter::emitReadOfProperty(StringRef readerName,
PropertyType type = property.getType(); Property property) {
auto name = property.getName(); emitReadOfProperty(readerName, property.getName(), property.getType());
}
void ASTPropsEmitter::emitReadOfProperty(StringRef readerName,
StringRef name,
PropertyType type) {
// Declare all the necessary buffers. // Declare all the necessary buffers.
auto bufferTypes = type.getBufferElementTypes(); auto bufferTypes = type.getBufferElementTypes();
for (size_t i = 0, e = bufferTypes.size(); i != e; ++i) { for (size_t i = 0, e = bufferTypes.size(); i != e; ++i) {
@ -440,7 +499,7 @@ void ASTPropsEmitter::emitReadOfProperty(Property property) {
// that in the creation rule. // that in the creation rule.
Out << " "; Out << " ";
type.emitCXXValueTypeName(true, Out); type.emitCXXValueTypeName(true, Out);
Out << " " << name << " = R.find(\"" << name << "\")." Out << " " << name << " = " << readerName << ".find(\"" << name << "\")."
<< (type.isGenericSpecialization() ? "template " : "") << "read"; << (type.isGenericSpecialization() ? "template " : "") << "read";
emitBasicReaderWriterMethodSuffix(Out, type, /*for read*/ true); emitBasicReaderWriterMethodSuffix(Out, type, /*for read*/ true);
Out << "("; Out << "(";
@ -451,13 +510,21 @@ void ASTPropsEmitter::emitReadOfProperty(Property property) {
} }
/// Emit code to write the given property in a node-writer method. /// Emit code to write the given property in a node-writer method.
void ASTPropsEmitter::emitWriteOfProperty(Property property) { void ASTPropsEmitter::emitWriteOfProperty(StringRef writerName,
Property property) {
emitWriteOfProperty(writerName, property.getName(), property.getType(),
property.getReadCode());
}
void ASTPropsEmitter::emitWriteOfProperty(StringRef writerName,
StringRef name,
PropertyType type,
StringRef readCode) {
// Focus down to the property: // Focus down to the property:
// W.find("prop").write##ValueType(value); // W.find("prop").write##ValueType(value);
Out << " W.find(\"" << property.getName() << "\").write"; Out << " " << writerName << ".find(\"" << name << "\").write";
emitBasicReaderWriterMethodSuffix(Out, property.getType(), emitBasicReaderWriterMethodSuffix(Out, type, /*for read*/ false);
/*for read*/ false); Out << "(" << readCode << ");\n";
Out << "(" << property.getReadCode() << ");\n";
} }
/// Emit an .inc file that defines the AbstractFooReader class /// Emit an .inc file that defines the AbstractFooReader class
@ -467,7 +534,7 @@ static void emitASTReader(RecordKeeper &records, raw_ostream &out,
StringRef description) { StringRef description) {
emitSourceFileHeader(description, out); emitSourceFileHeader(description, out);
ASTPropsEmitter(records, out).emitReaderClass<NodeClass>(); ASTPropsEmitter(records, out).emitNodeReaderClass<NodeClass>();
} }
void clang::EmitClangTypeReader(RecordKeeper &records, raw_ostream &out) { void clang::EmitClangTypeReader(RecordKeeper &records, raw_ostream &out) {
@ -481,7 +548,7 @@ static void emitASTWriter(RecordKeeper &records, raw_ostream &out,
StringRef description) { StringRef description) {
emitSourceFileHeader(description, out); emitSourceFileHeader(description, out);
ASTPropsEmitter(records, out).emitWriterClass<NodeClass>(); ASTPropsEmitter(records, out).emitNodeWriterClass<NodeClass>();
} }
void clang::EmitClangTypeWriter(RecordKeeper &records, raw_ostream &out) { void clang::EmitClangTypeWriter(RecordKeeper &records, raw_ostream &out) {
@ -596,35 +663,49 @@ ASTPropsEmitter::emitBasicReaderWriterTemplate(const ReaderWriterInfo &info) {
auto enterReaderWriterMethod = [&](StringRef cxxTypeName, auto enterReaderWriterMethod = [&](StringRef cxxTypeName,
StringRef abstractTypeName, StringRef abstractTypeName,
bool shouldPassByReference, bool shouldPassByReference,
bool constWhenWriting) { bool constWhenWriting,
StringRef paramName) {
Out << " " << (info.IsReader ? cxxTypeName : "void") Out << " " << (info.IsReader ? cxxTypeName : "void")
<< " " << info.MethodPrefix << abstractTypeName << "("; << " " << info.MethodPrefix << abstractTypeName << "(";
if (!info.IsReader) if (!info.IsReader)
Out << (shouldPassByReference || constWhenWriting ? "const " : "") Out << (shouldPassByReference || constWhenWriting ? "const " : "")
<< cxxTypeName << cxxTypeName
<< (shouldPassByReference ? " &" : "") << " value"; << (shouldPassByReference ? " &" : "") << " " << paramName;
Out << ") {\n"; Out << ") {\n";
}; };
// Emit {read,write}ValueType methods for all the enum and subclass types // Emit {read,write}ValueType methods for all the enum and subclass types
// that default to using the integer/base-class implementations. // that default to using the integer/base-class implementations.
for (PropertyType type : AllPropertyTypes) { for (PropertyType type : AllPropertyTypes) {
if (type.isEnum()) { auto enterMethod = [&](StringRef paramName) {
enterReaderWriterMethod(type.getCXXTypeName(), enterReaderWriterMethod(type.getCXXTypeName(),
type.getAbstractTypeName(), type.getAbstractTypeName(),
/*pass by reference*/ false, type.shouldPassByReference(),
/*const when writing*/ false); 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) if (info.IsReader)
Out << " return " << type.getCXXTypeName() Out << " return " << type.getCXXTypeName()
<< "(asImpl().readUInt32());\n"; << "(asImpl().readUInt32());\n";
else else
Out << " asImpl().writeUInt32(uint32_t(value));\n"; Out << " asImpl().writeUInt32(uint32_t(value));\n";
Out << " }\n"; exitMethod();
} else if (PropertyType superclass = type.getSuperclassType()) { } else if (PropertyType superclass = type.getSuperclassType()) {
enterReaderWriterMethod(type.getCXXTypeName(), enterMethod("value");
type.getAbstractTypeName(),
/*pass by reference*/ false,
/*const when writing*/ type.isConstWhenWriting());
if (info.IsReader) if (info.IsReader)
Out << " return cast_or_null<" << type.getSubclassClassName() Out << " return cast_or_null<" << type.getSubclassClassName()
<< ">(asImpl().read" << ">(asImpl().read"
@ -633,7 +714,8 @@ ASTPropsEmitter::emitBasicReaderWriterTemplate(const ReaderWriterInfo &info) {
else else
Out << " asImpl().write" << superclass.getAbstractTypeName() Out << " asImpl().write" << superclass.getAbstractTypeName()
<< "(value);\n"; << "(value);\n";
Out << " }\n"; exitMethod();
} else { } else {
// The other types can't be handled as trivially. // The other types can't be handled as trivially.
} }
@ -641,9 +723,66 @@ ASTPropsEmitter::emitBasicReaderWriterTemplate(const ReaderWriterInfo &info) {
Out << "};\n\n"; Out << "};\n\n";
} }
void ASTPropsEmitter::emitBasicReaderWriterFile(const ReaderWriterInfo &info) { void ASTPropsEmitter::emitCasedReaderWriterMethodBody(PropertyType type,
auto types = Records.getAllDerivedDefinitions(PropertyTypeClassName); const CasedTypeInfo &typeCases,
const ReaderWriterInfo &info) {
if (typeCases.Cases.empty()) {
assert(typeCases.KindRule);
PrintFatalError(typeCases.KindRule.getLoc(),
"no cases found for \"" + type.getCXXTypeName() + "\"");
}
if (!typeCases.KindRule) {
assert(!typeCases.Cases.empty());
PrintFatalError(typeCases.Cases.front().getLoc(),
"no kind rule for \"" + type.getCXXTypeName() + "\"");
}
auto var = info.HelperVariable;
std::string subvar = ("sub" + var).str();
// Bind `ctx` for readers.
if (info.IsReader)
Out << " auto &ctx = asImpl().getASTContext();\n";
// Start an object.
Out << " auto &&" << subvar << " = asImpl()."
<< info.MethodPrefix << "Object();\n";
// Read/write the kind property;
TypeKindRule kindRule = typeCases.KindRule;
StringRef kindProperty = kindRule.getKindPropertyName();
PropertyType kindType = kindRule.getKindType();
if (info.IsReader) {
emitReadOfProperty(subvar, kindProperty, kindType);
} else {
// Read the kind into a local variable.
Out << " ";
kindType.emitCXXValueTypeName(/*for read*/ false, Out);
Out << " " << kindProperty << " = " << kindRule.getReadCode() << ";\n";
emitWriteOfProperty(subvar, kindProperty, kindType, kindProperty);
}
// Prepare a ReaderWriterInfo with a helper variable that will use
// the sub-reader/writer.
ReaderWriterInfo subInfo = info;
subInfo.HelperVariable = subvar;
// Switch on the kind.
Out << " switch (" << kindProperty << ") {\n";
for (TypeCase typeCase : typeCases.Cases) {
Out << " case " << type.getCXXTypeName() << "::"
<< typeCase.getCaseName() << ": {\n";
emitPropertiedReaderWriterBody(typeCase, subInfo);
if (!info.IsReader)
Out << " return;\n";
Out << " }\n\n";
}
Out << " }\n"
" llvm_unreachable(\"bad " << kindType.getCXXTypeName()
<< "\");\n";
}
void ASTPropsEmitter::emitBasicReaderWriterFile(const ReaderWriterInfo &info) {
emitDispatcherTemplate(info); emitDispatcherTemplate(info);
emitPackUnpackOptionalTemplate(info); emitPackUnpackOptionalTemplate(info);
emitBasicReaderWriterTemplate(info); emitBasicReaderWriterTemplate(info);