[clang] [Clang][ASTImporter] Fix cycle in importing template specialization on auto type with typename (PR #162514)
via cfe-commits
cfe-commits at lists.llvm.org
Tue Oct 14 07:48:10 PDT 2025
https://github.com/ganenkokb-yandex updated https://github.com/llvm/llvm-project/pull/162514
>From 63e609960bc477fa516dac9b707a767e3719cbb3 Mon Sep 17 00:00:00 2001
From: Konstantin Ganenko <ganenkokb at yandex-team.ru>
Date: Tue, 7 Oct 2025 14:36:49 +0300
Subject: [PATCH 1/8] Fix cycle in importing template specialization on auto
type
---
clang/include/clang/AST/ASTImporter.h | 1 +
clang/lib/AST/ASTImporter.cpp | 9 ++++-
clang/unittests/AST/ASTImporterTest.cpp | 50 +++++++++++++++++++++++++
3 files changed, 59 insertions(+), 1 deletion(-)
diff --git a/clang/include/clang/AST/ASTImporter.h b/clang/include/clang/AST/ASTImporter.h
index 4a0ca45b785a9..eea4ccccb1600 100644
--- a/clang/include/clang/AST/ASTImporter.h
+++ b/clang/include/clang/AST/ASTImporter.h
@@ -254,6 +254,7 @@ class TypeSourceInfo;
/// Declaration (from, to) pairs that are known not to be equivalent
/// (which we have already complained about).
NonEquivalentDeclSet NonEquivalentDecls;
+ llvm::DenseSet<const Decl *> DeclTypeCycles;
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 f43fa8c90ad3b..0dc2e1c3b4f8b 100644
--- a/clang/lib/AST/ASTImporter.cpp
+++ b/clang/lib/AST/ASTImporter.cpp
@@ -4035,7 +4035,8 @@ 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)) {
+ if (hasReturnTypeDeclaredInside(D) ||
+ Importer.DeclTypeCycles.find(D) != Importer.DeclTypeCycles.end()) {
FromReturnTy = Importer.getFromContext().VoidTy;
UsedDifferentProtoType = true;
}
@@ -4058,7 +4059,13 @@ ExpectedDecl ASTNodeImporter::VisitFunctionDecl(FunctionDecl *D) {
}
Error Err = Error::success();
+ if (!UsedDifferentProtoType) {
+ Importer.DeclTypeCycles.insert(D);
+ }
auto T = importChecked(Err, FromTy);
+ if (!UsedDifferentProtoType) {
+ Importer.DeclTypeCycles.erase(D);
+ }
auto TInfo = importChecked(Err, FromTSI);
auto ToInnerLocStart = importChecked(Err, D->getInnerLocStart());
auto ToEndLoc = importChecked(Err, D->getEndLoc());
diff --git a/clang/unittests/AST/ASTImporterTest.cpp b/clang/unittests/AST/ASTImporterTest.cpp
index e7160bcf2e0c2..5f7fcdf817ea0 100644
--- a/clang/unittests/AST/ASTImporterTest.cpp
+++ b/clang/unittests/AST/ASTImporterTest.cpp
@@ -3204,6 +3204,56 @@ TEST_P(ImportExpr, UnresolvedMemberExpr) {
compoundStmt(has(callExpr(has(unresolvedMemberExpr())))))))));
}
+TEST_P(ImportExpr, 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"(
>From 3e2323025a1e64265adf587c2191d244506640dd Mon Sep 17 00:00:00 2001
From: Konstantin Ganenko <ganenkokb at yandex-team.ru>
Date: Mon, 13 Oct 2025 15:07:32 +0300
Subject: [PATCH 2/8] clang format + code review
---
clang/lib/AST/ASTImporter.cpp | 8 +++-----
clang/unittests/AST/ASTImporterTest.cpp | 3 ++-
2 files changed, 5 insertions(+), 6 deletions(-)
diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp
index 0dc2e1c3b4f8b..051a633cb99fa 100644
--- a/clang/lib/AST/ASTImporter.cpp
+++ b/clang/lib/AST/ASTImporter.cpp
@@ -4036,7 +4036,7 @@ ExpectedDecl ASTNodeImporter::VisitFunctionDecl(FunctionDecl *D) {
// To avoid an infinite recursion when importing, create the FunctionDecl
// with a simplified return type.
if (hasReturnTypeDeclaredInside(D) ||
- Importer.DeclTypeCycles.find(D) != Importer.DeclTypeCycles.end()) {
+ Importer.DeclTypeCycles.find(D) != Importer.DeclTypeCycles.end()) {
FromReturnTy = Importer.getFromContext().VoidTy;
UsedDifferentProtoType = true;
}
@@ -4059,13 +4059,11 @@ ExpectedDecl ASTNodeImporter::VisitFunctionDecl(FunctionDecl *D) {
}
Error Err = Error::success();
- if (!UsedDifferentProtoType) {
+ if (!UsedDifferentProtoType)
Importer.DeclTypeCycles.insert(D);
- }
auto T = importChecked(Err, FromTy);
- if (!UsedDifferentProtoType) {
+ if (!UsedDifferentProtoType)
Importer.DeclTypeCycles.erase(D);
- }
auto TInfo = importChecked(Err, FromTSI);
auto ToInnerLocStart = importChecked(Err, D->getInnerLocStart());
auto ToEndLoc = importChecked(Err, D->getEndLoc());
diff --git a/clang/unittests/AST/ASTImporterTest.cpp b/clang/unittests/AST/ASTImporterTest.cpp
index 5f7fcdf817ea0..ab63ddeb5e94b 100644
--- a/clang/unittests/AST/ASTImporterTest.cpp
+++ b/clang/unittests/AST/ASTImporterTest.cpp
@@ -3249,7 +3249,8 @@ TEST_P(ImportExpr, CycleInAutoTemplateSpec) {
}
)";
// This test reproduces the StrCatT recursion pattern with concepts and span
- // that may cause infinite recursion during AST import due to circular dependencies
+ // that may cause infinite recursion during AST import due to circular
+ // dependencies
testImport(Code, Lang_CXX20, "", Lang_CXX20, Verifier,
functionTemplateDecl(hasName("declToImport")));
}
>From 416fbca5730acb09c1d242458d415773f75f61d7 Mon Sep 17 00:00:00 2001
From: Konstantin Ganenko <ganenkokb at yandex-team.ru>
Date: Mon, 13 Oct 2025 16:24:09 +0300
Subject: [PATCH 3/8] Code review
Add comments for new member.
Make RAII approach for cycles monitoring.
---
clang/include/clang/AST/ASTImporter.h | 13 ++++++-
clang/lib/AST/ASTImporter.cpp | 52 +++++++++++++++++++++++----
2 files changed, 58 insertions(+), 7 deletions(-)
diff --git a/clang/include/clang/AST/ASTImporter.h b/clang/include/clang/AST/ASTImporter.h
index eea4ccccb1600..d5ad014bd9877 100644
--- a/clang/include/clang/AST/ASTImporter.h
+++ b/clang/include/clang/AST/ASTImporter.h
@@ -190,6 +190,8 @@ class TypeSourceInfo;
llvm::SmallDenseMap<Decl *, int, 32> Aux;
};
+ class FunctionReturnTypeDeclCycleDetector;
+
private:
std::shared_ptr<ASTImporterSharedState> SharedState = nullptr;
@@ -254,7 +256,16 @@ class TypeSourceInfo;
/// Declaration (from, to) pairs that are known not to be equivalent
/// (which we have already complained about).
NonEquivalentDeclSet NonEquivalentDecls;
- llvm::DenseSet<const Decl *> DeclTypeCycles;
+ // When template function return type is auto and return type is declared as
+ // typename from template params, there could be cycles in function
+ // importing when function decaration is still the need for return type
+ // declaration import. We have code path for nested types inside function
+ // (see hasReturnTypeDeclaredInside): assuming return type as VoidTy and
+ // calculate it later under UsedDifferentProtoType boolean. This class is
+ // reuse of this approach and make logic lazy - detect cycle - calculate
+ // return type later on.
+ std::unique_ptr<FunctionReturnTypeDeclCycleDetector>
+ FunctionReturnTypeCycleDetector;
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 051a633cb99fa..bcf8c19fb9391 100644
--- a/clang/lib/AST/ASTImporter.cpp
+++ b/clang/lib/AST/ASTImporter.cpp
@@ -1287,6 +1287,44 @@ bool ASTNodeImporter::hasSameVisibilityContextAndLinkage(TypedefNameDecl *Found,
using namespace clang;
+class ASTImporter::FunctionReturnTypeDeclCycleDetector {
+public:
+ class ScopedReturnTypeImport {
+ public:
+ // Do not track cycles on D == nullptr.
+ ScopedReturnTypeImport(FunctionReturnTypeDeclCycleDetector &owner,
+ const Decl *D)
+ : CycleDetector(owner), D(D) {
+ if (D)
+ CycleDetector.FunctionReturnTypeDeclCycles.insert(D);
+ }
+ ~ScopedReturnTypeImport() {
+ if (D)
+ CycleDetector.FunctionReturnTypeDeclCycles.erase(D);
+ }
+ ScopedReturnTypeImport(const ScopedReturnTypeImport &) = delete;
+ ScopedReturnTypeImport &operator=(const ScopedReturnTypeImport &) = delete;
+
+ private:
+ FunctionReturnTypeDeclCycleDetector &CycleDetector;
+ const Decl *D;
+ };
+
+ ScopedReturnTypeImport DetectImportCycles(const Decl *D) {
+ if (!IsCycle(D))
+ return ScopedReturnTypeImport(*this, D);
+ return ScopedReturnTypeImport(*this, nullptr);
+ }
+
+ bool IsCycle(const Decl *D) const {
+ return FunctionReturnTypeDeclCycles.find(D) !=
+ FunctionReturnTypeDeclCycles.end();
+ }
+
+private:
+ llvm::DenseSet<const Decl *> FunctionReturnTypeDeclCycles;
+};
+
ExpectedType ASTNodeImporter::VisitType(const Type *T) {
Importer.FromDiag(SourceLocation(), diag::err_unsupported_ast_node)
<< T->getTypeClassName();
@@ -4035,8 +4073,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.
+ // Reuse this approach for auto return types declared as typenames from
+ // template pamams, tracked in FunctionReturnTypeCycleDetector.
if (hasReturnTypeDeclaredInside(D) ||
- Importer.DeclTypeCycles.find(D) != Importer.DeclTypeCycles.end()) {
+ Importer.FunctionReturnTypeCycleDetector->IsCycle(D)) {
FromReturnTy = Importer.getFromContext().VoidTy;
UsedDifferentProtoType = true;
}
@@ -4059,11 +4099,9 @@ ExpectedDecl ASTNodeImporter::VisitFunctionDecl(FunctionDecl *D) {
}
Error Err = Error::success();
- if (!UsedDifferentProtoType)
- Importer.DeclTypeCycles.insert(D);
+ auto ScopedReturnTypeDeclCycleDetector =
+ Importer.FunctionReturnTypeCycleDetector->DetectImportCycles(D);
auto T = importChecked(Err, FromTy);
- if (!UsedDifferentProtoType)
- Importer.DeclTypeCycles.erase(D);
auto TInfo = importChecked(Err, FromTSI);
auto ToInnerLocStart = importChecked(Err, D->getInnerLocStart());
auto ToEndLoc = importChecked(Err, D->getEndLoc());
@@ -9299,7 +9337,9 @@ ASTImporter::ASTImporter(ASTContext &ToContext, FileManager &ToFileManager,
std::shared_ptr<ASTImporterSharedState> SharedState)
: SharedState(SharedState), ToContext(ToContext), FromContext(FromContext),
ToFileManager(ToFileManager), FromFileManager(FromFileManager),
- Minimal(MinimalImport), ODRHandling(ODRHandlingType::Conservative) {
+ Minimal(MinimalImport), ODRHandling(ODRHandlingType::Conservative),
+ FunctionReturnTypeCycleDetector(
+ std::make_unique<FunctionReturnTypeDeclCycleDetector>()) {
// Create a default state without the lookup table: LLDB case.
if (!SharedState) {
>From 087f9828ee9ec27cf8640acc74af49929fe10b05 Mon Sep 17 00:00:00 2001
From: Konstantin Ganenko <ganenkokb at yandex-team.ru>
Date: Mon, 13 Oct 2025 16:28:38 +0300
Subject: [PATCH 4/8] Code review
Use FunctionDecl straight forward.
---
clang/lib/AST/ASTImporter.cpp | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp
index bcf8c19fb9391..d348bfd097eda 100644
--- a/clang/lib/AST/ASTImporter.cpp
+++ b/clang/lib/AST/ASTImporter.cpp
@@ -1293,7 +1293,7 @@ class ASTImporter::FunctionReturnTypeDeclCycleDetector {
public:
// Do not track cycles on D == nullptr.
ScopedReturnTypeImport(FunctionReturnTypeDeclCycleDetector &owner,
- const Decl *D)
+ const FunctionDecl *D)
: CycleDetector(owner), D(D) {
if (D)
CycleDetector.FunctionReturnTypeDeclCycles.insert(D);
@@ -1307,22 +1307,22 @@ class ASTImporter::FunctionReturnTypeDeclCycleDetector {
private:
FunctionReturnTypeDeclCycleDetector &CycleDetector;
- const Decl *D;
+ const FunctionDecl *D;
};
- ScopedReturnTypeImport DetectImportCycles(const Decl *D) {
+ ScopedReturnTypeImport DetectImportCycles(const FunctionDecl *D) {
if (!IsCycle(D))
return ScopedReturnTypeImport(*this, D);
return ScopedReturnTypeImport(*this, nullptr);
}
- bool IsCycle(const Decl *D) const {
+ bool IsCycle(const FunctionDecl *D) const {
return FunctionReturnTypeDeclCycles.find(D) !=
FunctionReturnTypeDeclCycles.end();
}
private:
- llvm::DenseSet<const Decl *> FunctionReturnTypeDeclCycles;
+ llvm::DenseSet<const FunctionDecl *> FunctionReturnTypeDeclCycles;
};
ExpectedType ASTNodeImporter::VisitType(const Type *T) {
>From 6d9a3a1d60a3e421e136422d9c3e12e52d92ac45 Mon Sep 17 00:00:00 2001
From: Konstantin Ganenko <ganenkokb at yandex-team.ru>
Date: Mon, 13 Oct 2025 19:27:21 +0300
Subject: [PATCH 5/8] Code review
Fix naming and comment
---
clang/include/clang/AST/ASTImporter.h | 13 +++++--------
clang/lib/AST/ASTImporter.cpp | 26 ++++++++++++-------------
clang/unittests/AST/ASTImporterTest.cpp | 2 +-
3 files changed, 19 insertions(+), 22 deletions(-)
diff --git a/clang/include/clang/AST/ASTImporter.h b/clang/include/clang/AST/ASTImporter.h
index d5ad014bd9877..3169b0074cc8d 100644
--- a/clang/include/clang/AST/ASTImporter.h
+++ b/clang/include/clang/AST/ASTImporter.h
@@ -256,14 +256,11 @@ class TypeSourceInfo;
/// Declaration (from, to) pairs that are known not to be equivalent
/// (which we have already complained about).
NonEquivalentDeclSet NonEquivalentDecls;
- // When template function return type is auto and return type is declared as
- // typename from template params, there could be cycles in function
- // importing when function decaration is still the need for return type
- // declaration import. We have code path for nested types inside function
- // (see hasReturnTypeDeclaredInside): assuming return type as VoidTy and
- // calculate it later under UsedDifferentProtoType boolean. This class is
- // reuse of this approach and make logic lazy - detect cycle - calculate
- // return type later on.
+ /// 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.
std::unique_ptr<FunctionReturnTypeDeclCycleDetector>
FunctionReturnTypeCycleDetector;
diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp
index d348bfd097eda..819c6bfc970e9 100644
--- a/clang/lib/AST/ASTImporter.cpp
+++ b/clang/lib/AST/ASTImporter.cpp
@@ -1289,34 +1289,34 @@ using namespace clang;
class ASTImporter::FunctionReturnTypeDeclCycleDetector {
public:
- class ScopedReturnTypeImport {
+ class DeclCycleMapInserter {
public:
// Do not track cycles on D == nullptr.
- ScopedReturnTypeImport(FunctionReturnTypeDeclCycleDetector &owner,
- const FunctionDecl *D)
+ DeclCycleMapInserter(FunctionReturnTypeDeclCycleDetector &owner,
+ const FunctionDecl *D)
: CycleDetector(owner), D(D) {
if (D)
CycleDetector.FunctionReturnTypeDeclCycles.insert(D);
}
- ~ScopedReturnTypeImport() {
+ ~DeclCycleMapInserter() {
if (D)
CycleDetector.FunctionReturnTypeDeclCycles.erase(D);
}
- ScopedReturnTypeImport(const ScopedReturnTypeImport &) = delete;
- ScopedReturnTypeImport &operator=(const ScopedReturnTypeImport &) = delete;
+ DeclCycleMapInserter(const DeclCycleMapInserter &) = delete;
+ DeclCycleMapInserter &operator=(const DeclCycleMapInserter &) = delete;
private:
FunctionReturnTypeDeclCycleDetector &CycleDetector;
const FunctionDecl *D;
};
- ScopedReturnTypeImport DetectImportCycles(const FunctionDecl *D) {
- if (!IsCycle(D))
- return ScopedReturnTypeImport(*this, D);
- return ScopedReturnTypeImport(*this, nullptr);
+ DeclCycleMapInserter detectImportCycle(const FunctionDecl *D) {
+ if (!isCycle(D))
+ return DeclCycleMapInserter(*this, D);
+ return DeclCycleMapInserter(*this, nullptr);
}
- bool IsCycle(const FunctionDecl *D) const {
+ bool isCycle(const FunctionDecl *D) const {
return FunctionReturnTypeDeclCycles.find(D) !=
FunctionReturnTypeDeclCycles.end();
}
@@ -4076,7 +4076,7 @@ ExpectedDecl ASTNodeImporter::VisitFunctionDecl(FunctionDecl *D) {
// Reuse this approach for auto return types declared as typenames from
// template pamams, tracked in FunctionReturnTypeCycleDetector.
if (hasReturnTypeDeclaredInside(D) ||
- Importer.FunctionReturnTypeCycleDetector->IsCycle(D)) {
+ Importer.FunctionReturnTypeCycleDetector->isCycle(D)) {
FromReturnTy = Importer.getFromContext().VoidTy;
UsedDifferentProtoType = true;
}
@@ -4100,7 +4100,7 @@ ExpectedDecl ASTNodeImporter::VisitFunctionDecl(FunctionDecl *D) {
Error Err = Error::success();
auto ScopedReturnTypeDeclCycleDetector =
- Importer.FunctionReturnTypeCycleDetector->DetectImportCycles(D);
+ Importer.FunctionReturnTypeCycleDetector->detectImportCycle(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 ab63ddeb5e94b..bbfa591d3efa1 100644
--- a/clang/unittests/AST/ASTImporterTest.cpp
+++ b/clang/unittests/AST/ASTImporterTest.cpp
@@ -3204,7 +3204,7 @@ TEST_P(ImportExpr, UnresolvedMemberExpr) {
compoundStmt(has(callExpr(has(unresolvedMemberExpr())))))))));
}
-TEST_P(ImportExpr, CycleInAutoTemplateSpec) {
+TEST_P(ImportDecl, CycleInAutoTemplateSpec) {
MatchVerifier<Decl> Verifier;
const char *Code = R"(
template <class _CharT>
>From d57089e25bcbe65c38c6774bb7a05085ab00d700 Mon Sep 17 00:00:00 2001
From: Konstantin Ganenko <ganenkokb at yandex-team.ru>
Date: Tue, 14 Oct 2025 12:57:44 +0300
Subject: [PATCH 6/8] Remove hasReturnTypeDeclaredInside
Now FunctionReturnTypeCycleDetector works out with nested
typenames as well.
---
clang/lib/AST/ASTImporter.cpp | 38 ++++-------------------------------
1 file changed, 4 insertions(+), 34 deletions(-)
diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp
index 819c6bfc970e9..c1e0781f0c023 100644
--- a/clang/lib/AST/ASTImporter.cpp
+++ b/clang/lib/AST/ASTImporter.cpp
@@ -744,13 +744,8 @@ namespace clang {
Error ImportOverriddenMethods(CXXMethodDecl *ToMethod,
CXXMethodDecl *FromMethod);
- Expected<FunctionDecl *> FindFunctionTemplateSpecialization(
- FunctionDecl *FromFD);
-
- // Returns true if the given function has a placeholder return type and
- // that type is declared inside the body of the function.
- // E.g. auto f() { struct X{}; return X(); }
- bool hasReturnTypeDeclaredInside(FunctionDecl *D);
+ Expected<FunctionDecl *>
+ FindFunctionTemplateSpecialization(FunctionDecl *FromFD);
};
template <typename InContainerTy>
@@ -3902,30 +3897,6 @@ class IsTypeDeclaredInsideVisitor
};
} // namespace
-/// This function checks if the given function has a return type that contains
-/// a reference (in any way) to a declaration inside the same function.
-bool ASTNodeImporter::hasReturnTypeDeclaredInside(FunctionDecl *D) {
- QualType FromTy = D->getType();
- const auto *FromFPT = FromTy->getAs<FunctionProtoType>();
- assert(FromFPT && "Must be called on FunctionProtoType");
-
- auto IsCXX11Lambda = [&]() {
- if (Importer.FromContext.getLangOpts().CPlusPlus14) // C++14 or later
- return false;
-
- return isLambdaMethod(D);
- };
-
- QualType RetT = FromFPT->getReturnType();
- if (isa<AutoType>(RetT.getTypePtr()) || IsCXX11Lambda()) {
- FunctionDecl *Def = D->getDefinition();
- IsTypeDeclaredInsideVisitor Visitor(Def ? Def : D);
- return Visitor.CheckType(RetT);
- }
-
- return false;
-}
-
ExplicitSpecifier
ASTNodeImporter::importExplicitSpecifier(Error &Err, ExplicitSpecifier ESpec) {
Expr *ExplicitExpr = ESpec.getExpr();
@@ -4074,9 +4045,8 @@ ExpectedDecl ASTNodeImporter::VisitFunctionDecl(FunctionDecl *D) {
// To avoid an infinite recursion when importing, create the FunctionDecl
// with a simplified return type.
// Reuse this approach for auto return types declared as typenames from
- // template pamams, tracked in FunctionReturnTypeCycleDetector.
- if (hasReturnTypeDeclaredInside(D) ||
- Importer.FunctionReturnTypeCycleDetector->isCycle(D)) {
+ // template params, tracked in FunctionReturnTypeCycleDetector.
+ if (Importer.FunctionReturnTypeCycleDetector->isCycle(D)) {
FromReturnTy = Importer.getFromContext().VoidTy;
UsedDifferentProtoType = true;
}
>From 959ff8861faa9ba57e5f19adaa61932ff1eaa564 Mon Sep 17 00:00:00 2001
From: Konstantin Ganenko <ganenkokb at yandex-team.ru>
Date: Tue, 14 Oct 2025 17:45:11 +0300
Subject: [PATCH 7/8] Revert "Remove hasReturnTypeDeclaredInside"
This reverts commit d57089e25bcbe65c38c6774bb7a05085ab00d700.
---
clang/lib/AST/ASTImporter.cpp | 38 +++++++++++++++++++++++++++++++----
1 file changed, 34 insertions(+), 4 deletions(-)
diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp
index c1e0781f0c023..819c6bfc970e9 100644
--- a/clang/lib/AST/ASTImporter.cpp
+++ b/clang/lib/AST/ASTImporter.cpp
@@ -744,8 +744,13 @@ namespace clang {
Error ImportOverriddenMethods(CXXMethodDecl *ToMethod,
CXXMethodDecl *FromMethod);
- Expected<FunctionDecl *>
- FindFunctionTemplateSpecialization(FunctionDecl *FromFD);
+ Expected<FunctionDecl *> FindFunctionTemplateSpecialization(
+ FunctionDecl *FromFD);
+
+ // Returns true if the given function has a placeholder return type and
+ // that type is declared inside the body of the function.
+ // E.g. auto f() { struct X{}; return X(); }
+ bool hasReturnTypeDeclaredInside(FunctionDecl *D);
};
template <typename InContainerTy>
@@ -3897,6 +3902,30 @@ class IsTypeDeclaredInsideVisitor
};
} // namespace
+/// This function checks if the given function has a return type that contains
+/// a reference (in any way) to a declaration inside the same function.
+bool ASTNodeImporter::hasReturnTypeDeclaredInside(FunctionDecl *D) {
+ QualType FromTy = D->getType();
+ const auto *FromFPT = FromTy->getAs<FunctionProtoType>();
+ assert(FromFPT && "Must be called on FunctionProtoType");
+
+ auto IsCXX11Lambda = [&]() {
+ if (Importer.FromContext.getLangOpts().CPlusPlus14) // C++14 or later
+ return false;
+
+ return isLambdaMethod(D);
+ };
+
+ QualType RetT = FromFPT->getReturnType();
+ if (isa<AutoType>(RetT.getTypePtr()) || IsCXX11Lambda()) {
+ FunctionDecl *Def = D->getDefinition();
+ IsTypeDeclaredInsideVisitor Visitor(Def ? Def : D);
+ return Visitor.CheckType(RetT);
+ }
+
+ return false;
+}
+
ExplicitSpecifier
ASTNodeImporter::importExplicitSpecifier(Error &Err, ExplicitSpecifier ESpec) {
Expr *ExplicitExpr = ESpec.getExpr();
@@ -4045,8 +4074,9 @@ ExpectedDecl ASTNodeImporter::VisitFunctionDecl(FunctionDecl *D) {
// To avoid an infinite recursion when importing, create the FunctionDecl
// with a simplified return type.
// Reuse this approach for auto return types declared as typenames from
- // template params, tracked in FunctionReturnTypeCycleDetector.
- if (Importer.FunctionReturnTypeCycleDetector->isCycle(D)) {
+ // template pamams, tracked in FunctionReturnTypeCycleDetector.
+ if (hasReturnTypeDeclaredInside(D) ||
+ Importer.FunctionReturnTypeCycleDetector->isCycle(D)) {
FromReturnTy = Importer.getFromContext().VoidTy;
UsedDifferentProtoType = true;
}
>From 786def129f10cc9d3e0c06bc1314bc368973030b Mon Sep 17 00:00:00 2001
From: Konstantin Ganenko <ganenkokb at yandex-team.ru>
Date: Tue, 14 Oct 2025 17:47:49 +0300
Subject: [PATCH 8/8] Fix misspelling
---
clang/lib/AST/ASTImporter.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp
index 819c6bfc970e9..a3d13037d530a 100644
--- a/clang/lib/AST/ASTImporter.cpp
+++ b/clang/lib/AST/ASTImporter.cpp
@@ -4074,7 +4074,7 @@ ExpectedDecl ASTNodeImporter::VisitFunctionDecl(FunctionDecl *D) {
// To avoid an infinite recursion when importing, create the FunctionDecl
// with a simplified return type.
// Reuse this approach for auto return types declared as typenames from
- // template pamams, tracked in FunctionReturnTypeCycleDetector.
+ // template params, tracked in FunctionReturnTypeCycleDetector.
if (hasReturnTypeDeclaredInside(D) ||
Importer.FunctionReturnTypeCycleDetector->isCycle(D)) {
FromReturnTy = Importer.getFromContext().VoidTy;
More information about the cfe-commits
mailing list