Author: ganenkokb-yandex Date: 2025-11-24T11:47:29+03:00 New Revision: c15a6cc00b1a0e3a47d99172b839ec45c72168ae
URL: https://github.com/llvm/llvm-project/commit/c15a6cc00b1a0e3a47d99172b839ec45c72168ae DIFF: https://github.com/llvm/llvm-project/commit/c15a6cc00b1a0e3a47d99172b839ec45c72168ae.diff LOG: [Clang][ASTImporter] Fix cycle in importing template specialization on auto type with typename (#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. Added: Modified: clang/include/clang/AST/ASTImporter.h clang/lib/AST/ASTImporter.cpp clang/unittests/AST/ASTImporterTest.cpp Removed: ################################################################################ diff --git a/clang/include/clang/AST/ASTImporter.h b/clang/include/clang/AST/ASTImporter.h index 4a0ca45b785a9..39d1429639ed1 100644 --- a/clang/include/clang/AST/ASTImporter.h +++ b/clang/include/clang/AST/ASTImporter.h @@ -190,6 +190,16 @@ class TypeSourceInfo; llvm::SmallDenseMap<Decl *, int, 32> Aux; }; + class FunctionDeclImportCycleDetector { + public: + auto makeScopedCycleDetection(const FunctionDecl *D); + + bool isCycle(const FunctionDecl *D) const; + + private: + llvm::DenseSet<const FunctionDecl *> FunctionDeclsWithImportInProgress; + }; + private: std::shared_ptr<ASTImporterSharedState> SharedState = nullptr; @@ -254,6 +264,12 @@ class TypeSourceInfo; /// Declaration (from, to) pairs that are known not to be equivalent /// (which we have already complained about). NonEquivalentDeclSet NonEquivalentDecls; + /// A FunctionDecl can have properties that have a reference to the + /// function itself and are imported before the function is created. This + /// can come for example from auto return type or when template parameters + /// are used in the return type or parameters. This member is used to detect + /// cyclic import of FunctionDecl objects to avoid infinite recursion. + FunctionDeclImportCycleDetector FindFunctionDeclImportCycle; using FoundDeclsTy = SmallVector<NamedDecl *, 2>; FoundDeclsTy findDeclsInToCtx(DeclContext *DC, DeclarationName Name); diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp index 735f3157b694e..c1441744c8578 100644 --- a/clang/lib/AST/ASTImporter.cpp +++ b/clang/lib/AST/ASTImporter.cpp @@ -1291,6 +1291,26 @@ bool ASTNodeImporter::hasSameVisibilityContextAndLinkage(TypedefNameDecl *Found, using namespace clang; +auto ASTImporter::FunctionDeclImportCycleDetector::makeScopedCycleDetection( + const FunctionDecl *D) { + const FunctionDecl *LambdaD = nullptr; + if (!isCycle(D) && D) { + FunctionDeclsWithImportInProgress.insert(D); + LambdaD = D; + } + return llvm::make_scope_exit([this, LambdaD]() { + if (LambdaD) { + FunctionDeclsWithImportInProgress.erase(LambdaD); + } + }); +} + +bool ASTImporter::FunctionDeclImportCycleDetector::isCycle( + const FunctionDecl *D) const { + return FunctionDeclsWithImportInProgress.find(D) != + FunctionDeclsWithImportInProgress.end(); +} + ExpectedType ASTNodeImporter::VisitType(const Type *T) { Importer.FromDiag(SourceLocation(), diag::err_unsupported_ast_node) << T->getTypeClassName(); @@ -4038,7 +4058,10 @@ ExpectedDecl ASTNodeImporter::VisitFunctionDecl(FunctionDecl *D) { // E.g.: auto foo() { struct X{}; return X(); } // To avoid an infinite recursion when importing, create the FunctionDecl // with a simplified return type. - if (hasReturnTypeDeclaredInside(D)) { + // Reuse this approach for auto return types declared as typenames from + // template params, tracked in FindFunctionDeclImportCycle. + if (hasReturnTypeDeclaredInside(D) || + Importer.FindFunctionDeclImportCycle.isCycle(D)) { FromReturnTy = Importer.getFromContext().VoidTy; UsedDifferentProtoType = true; } @@ -4061,6 +4084,8 @@ ExpectedDecl ASTNodeImporter::VisitFunctionDecl(FunctionDecl *D) { } Error Err = Error::success(); + auto ScopedReturnTypeDeclCycleDetector = + Importer.FindFunctionDeclImportCycle.makeScopedCycleDetection(D); auto T = importChecked(Err, FromTy); auto TInfo = importChecked(Err, FromTSI); auto ToInnerLocStart = importChecked(Err, D->getInnerLocStart()); diff --git a/clang/unittests/AST/ASTImporterTest.cpp b/clang/unittests/AST/ASTImporterTest.cpp index 3cab4c600b1b1..164790606ea0b 100644 --- a/clang/unittests/AST/ASTImporterTest.cpp +++ b/clang/unittests/AST/ASTImporterTest.cpp @@ -3204,6 +3204,57 @@ TEST_P(ImportExpr, UnresolvedMemberExpr) { compoundStmt(has(callExpr(has(unresolvedMemberExpr()))))))))); } +TEST_P(ImportDecl, CycleInAutoTemplateSpec) { + MatchVerifier<Decl> Verifier; + const char *Code = R"( + template <class _CharT> + struct basic_string { + using value_type = _CharT; + }; + + template<typename T> + struct basic_string_view { + using value_type = T; + }; + + using string_view = basic_string_view<char>; + using string = basic_string<char>; + + template<typename T> + struct span { + }; + + template <typename StringT> + auto StrCatT(span<const StringT> pieces) { + basic_string<typename StringT::value_type> result; + return result; + } + + string StrCat(span<const string_view> pieces) { + return StrCatT(pieces); + } + + string StrCat(span<const string> pieces) { + return StrCatT(pieces); + } + + template <typename T> + auto declToImport(T pieces) { + return StrCat(pieces); + } + + void test() { + span<const string> pieces; + auto result = declToImport(pieces); + } +)"; + // This test reproduces the StrCatT recursion pattern with concepts and span + // that may cause infinite recursion during AST import due to circular + // dependencies + testImport(Code, Lang_CXX20, "", Lang_CXX20, Verifier, + functionTemplateDecl(hasName("declToImport"))); +} + TEST_P(ImportExpr, ConceptNoRequirement) { MatchVerifier<Decl> Verifier; const char *Code = R"( _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
