Skip to content

Commit 645ad75

Browse files
ganenkokb-yandexPriyanshu3820
authored andcommitted
[Clang][ASTImporter] Fix cycle in importing template specialization on auto type with typename (llvm#162514)
ASTImporter on importing template specialization with auto return type faces cycle when return type is not nested one, but typename from template arguments and other template. There is code, that prevents cycle to auto return types when nested type declared. Solved case differs somehow from nested types, but have same solution with UsedDifferentProtoType - with delayed return type determining.
1 parent 2c924bf commit 645ad75

File tree

3 files changed

+93
-1
lines changed

3 files changed

+93
-1
lines changed

clang/include/clang/AST/ASTImporter.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,16 @@ class TypeSourceInfo;
190190
llvm::SmallDenseMap<Decl *, int, 32> Aux;
191191
};
192192

193+
class FunctionDeclImportCycleDetector {
194+
public:
195+
auto makeScopedCycleDetection(const FunctionDecl *D);
196+
197+
bool isCycle(const FunctionDecl *D) const;
198+
199+
private:
200+
llvm::DenseSet<const FunctionDecl *> FunctionDeclsWithImportInProgress;
201+
};
202+
193203
private:
194204
std::shared_ptr<ASTImporterSharedState> SharedState = nullptr;
195205

@@ -254,6 +264,12 @@ class TypeSourceInfo;
254264
/// Declaration (from, to) pairs that are known not to be equivalent
255265
/// (which we have already complained about).
256266
NonEquivalentDeclSet NonEquivalentDecls;
267+
/// A FunctionDecl can have properties that have a reference to the
268+
/// function itself and are imported before the function is created. This
269+
/// can come for example from auto return type or when template parameters
270+
/// are used in the return type or parameters. This member is used to detect
271+
/// cyclic import of FunctionDecl objects to avoid infinite recursion.
272+
FunctionDeclImportCycleDetector FindFunctionDeclImportCycle;
257273

258274
using FoundDeclsTy = SmallVector<NamedDecl *, 2>;
259275
FoundDeclsTy findDeclsInToCtx(DeclContext *DC, DeclarationName Name);

clang/lib/AST/ASTImporter.cpp

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1291,6 +1291,26 @@ bool ASTNodeImporter::hasSameVisibilityContextAndLinkage(TypedefNameDecl *Found,
12911291

12921292
using namespace clang;
12931293

1294+
auto ASTImporter::FunctionDeclImportCycleDetector::makeScopedCycleDetection(
1295+
const FunctionDecl *D) {
1296+
const FunctionDecl *LambdaD = nullptr;
1297+
if (!isCycle(D) && D) {
1298+
FunctionDeclsWithImportInProgress.insert(D);
1299+
LambdaD = D;
1300+
}
1301+
return llvm::make_scope_exit([this, LambdaD]() {
1302+
if (LambdaD) {
1303+
FunctionDeclsWithImportInProgress.erase(LambdaD);
1304+
}
1305+
});
1306+
}
1307+
1308+
bool ASTImporter::FunctionDeclImportCycleDetector::isCycle(
1309+
const FunctionDecl *D) const {
1310+
return FunctionDeclsWithImportInProgress.find(D) !=
1311+
FunctionDeclsWithImportInProgress.end();
1312+
}
1313+
12941314
ExpectedType ASTNodeImporter::VisitType(const Type *T) {
12951315
Importer.FromDiag(SourceLocation(), diag::err_unsupported_ast_node)
12961316
<< T->getTypeClassName();
@@ -4038,7 +4058,10 @@ ExpectedDecl ASTNodeImporter::VisitFunctionDecl(FunctionDecl *D) {
40384058
// E.g.: auto foo() { struct X{}; return X(); }
40394059
// To avoid an infinite recursion when importing, create the FunctionDecl
40404060
// with a simplified return type.
4041-
if (hasReturnTypeDeclaredInside(D)) {
4061+
// Reuse this approach for auto return types declared as typenames from
4062+
// template params, tracked in FindFunctionDeclImportCycle.
4063+
if (hasReturnTypeDeclaredInside(D) ||
4064+
Importer.FindFunctionDeclImportCycle.isCycle(D)) {
40424065
FromReturnTy = Importer.getFromContext().VoidTy;
40434066
UsedDifferentProtoType = true;
40444067
}
@@ -4061,6 +4084,8 @@ ExpectedDecl ASTNodeImporter::VisitFunctionDecl(FunctionDecl *D) {
40614084
}
40624085

40634086
Error Err = Error::success();
4087+
auto ScopedReturnTypeDeclCycleDetector =
4088+
Importer.FindFunctionDeclImportCycle.makeScopedCycleDetection(D);
40644089
auto T = importChecked(Err, FromTy);
40654090
auto TInfo = importChecked(Err, FromTSI);
40664091
auto ToInnerLocStart = importChecked(Err, D->getInnerLocStart());

clang/unittests/AST/ASTImporterTest.cpp

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3204,6 +3204,57 @@ TEST_P(ImportExpr, UnresolvedMemberExpr) {
32043204
compoundStmt(has(callExpr(has(unresolvedMemberExpr())))))))));
32053205
}
32063206

3207+
TEST_P(ImportDecl, CycleInAutoTemplateSpec) {
3208+
MatchVerifier<Decl> Verifier;
3209+
const char *Code = R"(
3210+
template <class _CharT>
3211+
struct basic_string {
3212+
using value_type = _CharT;
3213+
};
3214+
3215+
template<typename T>
3216+
struct basic_string_view {
3217+
using value_type = T;
3218+
};
3219+
3220+
using string_view = basic_string_view<char>;
3221+
using string = basic_string<char>;
3222+
3223+
template<typename T>
3224+
struct span {
3225+
};
3226+
3227+
template <typename StringT>
3228+
auto StrCatT(span<const StringT> pieces) {
3229+
basic_string<typename StringT::value_type> result;
3230+
return result;
3231+
}
3232+
3233+
string StrCat(span<const string_view> pieces) {
3234+
return StrCatT(pieces);
3235+
}
3236+
3237+
string StrCat(span<const string> pieces) {
3238+
return StrCatT(pieces);
3239+
}
3240+
3241+
template <typename T>
3242+
auto declToImport(T pieces) {
3243+
return StrCat(pieces);
3244+
}
3245+
3246+
void test() {
3247+
span<const string> pieces;
3248+
auto result = declToImport(pieces);
3249+
}
3250+
)";
3251+
// This test reproduces the StrCatT recursion pattern with concepts and span
3252+
// that may cause infinite recursion during AST import due to circular
3253+
// dependencies
3254+
testImport(Code, Lang_CXX20, "", Lang_CXX20, Verifier,
3255+
functionTemplateDecl(hasName("declToImport")));
3256+
}
3257+
32073258
TEST_P(ImportExpr, ConceptNoRequirement) {
32083259
MatchVerifier<Decl> Verifier;
32093260
const char *Code = R"(

0 commit comments

Comments
 (0)