Clang generator: Remember "seen" types when collecting related types

This change includes a small refactoring to remember which types the generator
has already seen during header generations. Otherwise we may loop indefinitely
on certain complex types. One such type is `std::FILE` in Clang's libc++.

PiperOrigin-RevId: 335589238
Change-Id: I5bbe03b6c7fc89c743163f5534075d7912ed4e58
This commit is contained in:
Christian Blichmann 2020-10-06 01:04:11 -07:00 committed by Copybara-Service
parent b74cf8839b
commit afa232cc17
4 changed files with 47 additions and 32 deletions

View File

@ -59,9 +59,9 @@ bool GeneratorASTVisitor::VisitFunctionDecl(clang::FunctionDecl* decl) {
options_.function_names.count(ToStringView(decl->getName())) > 0)) { options_.function_names.count(ToStringView(decl->getName())) > 0)) {
functions_.push_back(decl); functions_.push_back(decl);
CollectRelatedTypes(decl->getDeclaredReturnType(), &types_); collector_.CollectRelatedTypes(decl->getDeclaredReturnType());
for (const clang::ParmVarDecl* param : decl->parameters()) { for (const clang::ParmVarDecl* param : decl->parameters()) {
CollectRelatedTypes(param->getType(), &types_); collector_.CollectRelatedTypes(param->getType());
} }
} }
return true; return true;
@ -75,7 +75,7 @@ void GeneratorASTConsumer::HandleTranslationUnit(clang::ASTContext& context) {
"AST traversal exited early"); "AST traversal exited early");
} }
for (clang::QualType qual : visitor_.types_) { for (clang::QualType qual : visitor_.collector_.collected()) {
emitter_.CollectType(qual); emitter_.CollectType(qual);
} }
for (clang::FunctionDecl* func : visitor_.functions_) { for (clang::FunctionDecl* func : visitor_.functions_) {

View File

@ -65,8 +65,9 @@ class GeneratorASTVisitor
private: private:
friend class GeneratorASTConsumer; friend class GeneratorASTConsumer;
TypeCollector collector_;
std::vector<clang::FunctionDecl*> functions_; std::vector<clang::FunctionDecl*> functions_;
QualTypeSet types_;
const GeneratorOptions& options_; const GeneratorOptions& options_;
}; };

View File

@ -30,10 +30,15 @@ bool IsFunctionReferenceType(clang::QualType qual) {
} // namespace } // namespace
void CollectRelatedTypes(clang::QualType qual, QualTypeSet* types) { void TypeCollector::CollectRelatedTypes(clang::QualType qual) {
if (seen_.contains(qual)) {
return;
}
seen_.insert(qual);
if (const auto* typedef_type = qual->getAs<clang::TypedefType>()) { if (const auto* typedef_type = qual->getAs<clang::TypedefType>()) {
CollectRelatedTypes(typedef_type->getDecl()->getUnderlyingType(), types); CollectRelatedTypes(typedef_type->getDecl()->getUnderlyingType());
types->insert(qual); collected_.insert(qual);
return; return;
} }
@ -43,26 +48,26 @@ void CollectRelatedTypes(clang::QualType qual, QualTypeSet* types) {
->getAs<clang::FunctionProtoType>()) { ->getAs<clang::FunctionProtoType>()) {
// Note: Do not add the function type itself, as this will always be a // Note: Do not add the function type itself, as this will always be a
// pointer argument. We only need to collect all its related types. // pointer argument. We only need to collect all its related types.
CollectRelatedTypes(function_type->getReturnType(), types); CollectRelatedTypes(function_type->getReturnType());
for (const clang::QualType& param : function_type->getParamTypes()) { for (const clang::QualType& param : function_type->getParamTypes()) {
CollectRelatedTypes(param, types); CollectRelatedTypes(param);
} }
return; return;
} }
} }
if (IsPointerOrReference(qual)) { if (IsPointerOrReference(qual)) {
clang::QualType pointee = qual->getPointeeType(); clang::QualType pointee = qual;
while (IsPointerOrReference(pointee)) { do {
pointee = pointee->getPointeeType(); pointee = pointee->getPointeeType();
} } while (IsPointerOrReference(pointee));
CollectRelatedTypes(pointee, types); CollectRelatedTypes(pointee);
return; return;
} }
// C array with specified constant size (i.e. int a[42])? // C array with specified constant size (i.e. int a[42])?
if (const clang::ArrayType* array_type = qual->getAsArrayTypeUnsafe()) { if (const clang::ArrayType* array_type = qual->getAsArrayTypeUnsafe()) {
CollectRelatedTypes(array_type->getElementType(), types); CollectRelatedTypes(array_type->getElementType());
return; return;
} }
@ -71,19 +76,19 @@ void CollectRelatedTypes(clang::QualType qual, QualTypeSet* types) {
// Collect the underlying integer type of enum classes as well, as it may // Collect the underlying integer type of enum classes as well, as it may
// be a typedef. // be a typedef.
if (const clang::EnumDecl* decl = enum_type->getDecl(); decl->isFixed()) { if (const clang::EnumDecl* decl = enum_type->getDecl(); decl->isFixed()) {
CollectRelatedTypes(decl->getIntegerType(), types); CollectRelatedTypes(decl->getIntegerType());
} }
} }
types->insert(qual); collected_.insert(qual);
return; return;
} }
if (const auto* record_type = qual->getAs<clang::RecordType>()) { if (const auto* record_type = qual->getAs<clang::RecordType>()) {
const clang::RecordDecl* decl = record_type->getDecl(); const clang::RecordDecl* decl = record_type->getDecl();
for (const clang::FieldDecl* field : decl->fields()) { for (const clang::FieldDecl* field : decl->fields()) {
CollectRelatedTypes(field->getType(), types); CollectRelatedTypes(field->getType());
} }
types->insert(qual); collected_.insert(qual);
return; return;
} }
} }

View File

@ -41,20 +41,29 @@ inline bool IsPointerOrReference(clang::QualType qual) {
qual->isLValueReferenceType() || qual->isRValueReferenceType(); qual->isLValueReferenceType() || qual->isRValueReferenceType();
} }
// Computes the transitive closure of all types that a type depends on. Those class TypeCollector {
// are types that need to be declared before a declaration of the type denoted public:
// by the qual parameter is valid. For example, given // Computes the transitive closure of all types that a type depends on. Those
// struct SubStruct { bool truth_value; }; // are types that need to be declared before a declaration of the type denoted
// struct AggregateStruct { // by the qual parameter is valid. For example, given
// int int_member; // struct SubStruct { bool truth_value; };
// SubStruct struct_member; // struct AggregateStruct {
// }; // int int_member;
// // SubStruct struct_member;
// calling this function on the type "AggregateStruct" yields these types: // };
// int //
// SubStruct // calling this function on the type "AggregateStruct" yields these types:
// bool // int
void CollectRelatedTypes(clang::QualType qual, QualTypeSet* types); // SubStruct
// bool
void CollectRelatedTypes(clang::QualType qual);
QualTypeSet& collected() { return collected_; }
private:
QualTypeSet collected_;
QualTypeSet seen_;
};
// Maps a qualified type to a fully qualified SAPI-compatible type name. This // Maps a qualified type to a fully qualified SAPI-compatible type name. This
// is used for the generated code that invokes the actual function call RPC. // is used for the generated code that invokes the actual function call RPC.