[clang] [clang][ASTImporter] Improve import of friend class templates. (PR #74627)
via cfe-commits
cfe-commits at lists.llvm.org
Wed Dec 6 09:15:42 PST 2023
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang
Author: Balázs Kéri (balazske)
<details>
<summary>Changes</summary>
A friend template that is in a dependent context is not linked into declaration chains (for example with the definition of the befriended template). This condition was not correctly handled by `ASTImporter`.
---
Full diff: https://github.com/llvm/llvm-project/pull/74627.diff
2 Files Affected:
- (modified) clang/lib/AST/ASTImporter.cpp (+20-6)
- (modified) clang/unittests/AST/ASTImporterTest.cpp (+162)
``````````diff
diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp
index f1f335118f37a..a243bf65cca27 100644
--- a/clang/lib/AST/ASTImporter.cpp
+++ b/clang/lib/AST/ASTImporter.cpp
@@ -5919,15 +5919,26 @@ ExpectedDecl ASTNodeImporter::VisitClassTemplateDecl(ClassTemplateDecl *D) {
if (ToD)
return ToD;
- bool IsFriendTemplate = D->getFriendObjectKind() != Decl::FOK_None;
- bool IsDependentContext = DC != LexicalDC ? LexicalDC->isDependentContext()
+ // Should check if a declaration is friend in a dependent context.
+ // Such templates are not linked together in a declaration chain.
+ // The ASTImporter strategy is to map existing forward declarations to
+ // imported ones only if strictly necessary, otherwise import these as new
+ // forward declarations. In case of the "dependent friend" declarations, new
+ // declarations are created, but not linked in a declaration chain.
+ auto IsDependentFriend = [](ClassTemplateDecl *TD) {
+ bool IsFriendTemplate = TD->getFriendObjectKind() != Decl::FOK_None;
+ DeclContext *DC = TD->getDeclContext();
+ DeclContext *LexicalDC = TD->getLexicalDeclContext();
+ bool IsDependentContext = DC != LexicalDC ? LexicalDC->isDependentContext()
: DC->isDependentContext();
- bool DependentFriend = IsFriendTemplate && IsDependentContext;
+ return IsFriendTemplate && IsDependentContext;
+ };
+ bool DependentFriend = IsDependentFriend(D);
ClassTemplateDecl *FoundByLookup = nullptr;
// We may already have a template of the same name; try to find and match it.
- if (!DependentFriend && !DC->isFunctionOrMethod()) {
+ if (!DC->isFunctionOrMethod()) {
SmallVector<NamedDecl *, 4> ConflictingDecls;
auto FoundDecls = Importer.findDeclsInToCtx(DC, Name);
for (auto *FoundDecl : FoundDecls) {
@@ -5943,10 +5954,13 @@ ExpectedDecl ASTNodeImporter::VisitClassTemplateDecl(ClassTemplateDecl *D) {
// FIXME: sufficient conditon for 'IgnoreTemplateParmDepth'?
bool IgnoreTemplateParmDepth =
- FoundTemplate->getFriendObjectKind() != Decl::FOK_None &&
- !D->specializations().empty();
+ (FoundTemplate->getFriendObjectKind() != Decl::FOK_None) !=
+ (D->getFriendObjectKind() != Decl::FOK_None);
if (IsStructuralMatch(D, FoundTemplate, /*Complain=*/true,
IgnoreTemplateParmDepth)) {
+ if (DependentFriend || IsDependentFriend(FoundTemplate))
+ continue;
+
ClassTemplateDecl *TemplateWithDef =
getTemplateDefinition(FoundTemplate);
if (D->isThisDeclarationADefinition() && TemplateWithDef)
diff --git a/clang/unittests/AST/ASTImporterTest.cpp b/clang/unittests/AST/ASTImporterTest.cpp
index 4dd7510bf8ddf..f9ee73626c948 100644
--- a/clang/unittests/AST/ASTImporterTest.cpp
+++ b/clang/unittests/AST/ASTImporterTest.cpp
@@ -4528,6 +4528,168 @@ TEST_P(ImportFriendClasses, ImportOfClassDefinitionAndFwdFriendShouldBeLinked) {
EXPECT_EQ(ImportedFwd, ImportedDef->getPreviousDecl());
}
+TEST_P(ImportFriendClasses,
+ ImportFriendTemplatesInDependentContext_DefToFriend) {
+ Decl *ToTU = getToTuDecl(
+ R"(
+ template<class T1>
+ struct X {
+ template<class T2>
+ friend struct Y;
+ };
+ )",
+ Lang_CXX03);
+ auto *ToYFriend = FirstDeclMatcher<ClassTemplateDecl>().match(
+ ToTU, classTemplateDecl(hasName("Y")));
+ Decl *FromTU = getTuDecl(
+ R"(
+ template<class T1>
+ struct Y {};
+ )",
+ Lang_CXX03, "input0.cc");
+ auto *FromYDef = FirstDeclMatcher<ClassTemplateDecl>().match(
+ FromTU, classTemplateDecl(hasName("Y")));
+ auto *ImportedYDef = Import(FromYDef, Lang_CXX03);
+ EXPECT_TRUE(ImportedYDef);
+ EXPECT_FALSE(ImportedYDef->getPreviousDecl());
+ EXPECT_NE(ImportedYDef, ToYFriend);
+}
+
+TEST_P(ImportFriendClasses,
+ ImportFriendTemplatesInDependentContext_DefToFriend_NE) {
+ Decl *ToTU = getToTuDecl(
+ R"(
+ template<class T1>
+ struct X {
+ template<class T2>
+ friend struct Y;
+ };
+ )",
+ Lang_CXX03);
+ auto *ToYFriend = FirstDeclMatcher<ClassTemplateDecl>().match(
+ ToTU, classTemplateDecl(hasName("Y")));
+ Decl *FromTU = getTuDecl(
+ R"(
+ template<class T1, class T2>
+ struct Y {};
+ )",
+ Lang_CXX03, "input0.cc");
+ auto *FromYDef = FirstDeclMatcher<ClassTemplateDecl>().match(
+ FromTU, classTemplateDecl(hasName("Y")));
+ auto *ImportedYDef = Import(FromYDef, Lang_CXX03);
+ EXPECT_FALSE(ImportedYDef);
+}
+
+TEST_P(ImportFriendClasses,
+ ImportFriendTemplatesInDependentContext_FriendToFriend) {
+ Decl *ToTU = getToTuDecl(
+ R"(
+ template<class T1>
+ struct X {
+ template<class T2>
+ friend struct Y;
+ };
+ )",
+ Lang_CXX03);
+ auto *ToYFriend = FirstDeclMatcher<ClassTemplateDecl>().match(
+ ToTU, classTemplateDecl(hasName("Y")));
+ Decl *FromTU = getTuDecl(
+ R"(
+ template<class T1>
+ struct X {
+ template<class T2>
+ friend struct Y;
+ };
+ )",
+ Lang_CXX03, "input0.cc");
+ auto *FromYFriend = FirstDeclMatcher<ClassTemplateDecl>().match(
+ FromTU, classTemplateDecl(hasName("Y")));
+ auto *ImportedYFriend = Import(FromYFriend, Lang_CXX03);
+ EXPECT_TRUE(ImportedYFriend);
+ EXPECT_FALSE(ImportedYFriend->getPreviousDecl());
+ EXPECT_NE(ImportedYFriend, ToYFriend);
+}
+
+TEST_P(ImportFriendClasses,
+ ImportFriendTemplatesInDependentContext_FriendToFriend_NE) {
+ Decl *ToTU = getToTuDecl(
+ R"(
+ template<class T1>
+ struct X {
+ template<class T2>
+ friend struct Y;
+ };
+ )",
+ Lang_CXX03);
+ auto *ToYFriend = FirstDeclMatcher<ClassTemplateDecl>().match(
+ ToTU, classTemplateDecl(hasName("Y")));
+ Decl *FromTU = getTuDecl(
+ R"(
+ template<class T1>
+ struct X {
+ template<class T2, class T3>
+ friend struct Y;
+ };
+ )",
+ Lang_CXX03, "input0.cc");
+ auto *FromYFriend = FirstDeclMatcher<ClassTemplateDecl>().match(
+ FromTU, classTemplateDecl(hasName("Y")));
+ auto *ImportedYFriend = Import(FromYFriend, Lang_CXX03);
+ EXPECT_FALSE(ImportedYFriend);
+}
+
+TEST_P(ImportFriendClasses,
+ ImportFriendTemplatesInDependentContext_FriendToDef) {
+ Decl *ToTU = getToTuDecl(
+ R"(
+ template<class T1>
+ struct Y {};
+ )",
+ Lang_CXX03);
+ auto *ToYDef = FirstDeclMatcher<ClassTemplateDecl>().match(
+ ToTU, classTemplateDecl(hasName("Y")));
+ Decl *FromTU = getTuDecl(
+ R"(
+ template<class T1>
+ struct X {
+ template<class T2>
+ friend struct Y;
+ };
+ )",
+ Lang_CXX03, "input0.cc");
+ auto *FromYFriend = FirstDeclMatcher<ClassTemplateDecl>().match(
+ FromTU, classTemplateDecl(hasName("Y")));
+ auto *ImportedYFriend = Import(FromYFriend, Lang_CXX03);
+ EXPECT_TRUE(ImportedYFriend);
+ EXPECT_FALSE(ImportedYFriend->getPreviousDecl());
+ EXPECT_NE(ImportedYFriend, ToYDef);
+}
+
+TEST_P(ImportFriendClasses,
+ ImportFriendTemplatesInDependentContext_FriendToDef_NE) {
+ Decl *ToTU = getToTuDecl(
+ R"(
+ template<class T1>
+ struct Y {};
+ )",
+ Lang_CXX03);
+ auto *ToYDef = FirstDeclMatcher<ClassTemplateDecl>().match(
+ ToTU, classTemplateDecl(hasName("Y")));
+ Decl *FromTU = getTuDecl(
+ R"(
+ template<class T1>
+ struct X {
+ template<class T2, class T3>
+ friend struct Y;
+ };
+ )",
+ Lang_CXX03, "input0.cc");
+ auto *FromYFriend = FirstDeclMatcher<ClassTemplateDecl>().match(
+ FromTU, classTemplateDecl(hasName("Y")));
+ auto *ImportedYFriend = Import(FromYFriend, Lang_CXX03);
+ EXPECT_FALSE(ImportedYFriend);
+}
+
TEST_P(ImportFriendClasses, ImportOfRepeatedFriendType) {
const char *Code =
R"(
``````````
</details>
https://github.com/llvm/llvm-project/pull/74627
More information about the cfe-commits
mailing list