Skip to content

[clang][ASTImporter] Improve import of friend class templates. #74627

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Jan 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 17 additions & 7 deletions clang/lib/AST/ASTImporter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5919,15 +5919,22 @@ ExpectedDecl ASTNodeImporter::VisitClassTemplateDecl(ClassTemplateDecl *D) {
if (ToD)
return ToD;

bool IsFriendTemplate = D->getFriendObjectKind() != Decl::FOK_None;
bool IsDependentContext = DC != LexicalDC ? LexicalDC->isDependentContext()
: DC->isDependentContext();
bool DependentFriend = IsFriendTemplate && 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) {
return TD->getFriendObjectKind() != Decl::FOK_None &&
TD->getLexicalDeclContext()->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) {
Expand All @@ -5943,10 +5950,13 @@ ExpectedDecl ASTNodeImporter::VisitClassTemplateDecl(ClassTemplateDecl *D) {

// FIXME: sufficient conditon for 'IgnoreTemplateParmDepth'?
bool IgnoreTemplateParmDepth =
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change of this condition was needed to make a failing test ImportOfRecursiveFriendClassTemplateWithNonTypeParm to pass.

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)
Expand Down
156 changes: 156 additions & 0 deletions clang/unittests/AST/ASTImporterTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4528,6 +4528,162 @@ 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) {
getToTuDecl(
R"(
template<class T1>
struct X {
template<class T2>
friend struct Y;
};
)",
Lang_CXX03);
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) {
getToTuDecl(
R"(
template<class T1>
struct X {
template<class T2>
friend struct Y;
};
)",
Lang_CXX03);
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) {
getToTuDecl(
R"(
template<class T1>
struct Y {};
)",
Lang_CXX03);
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"(
Expand Down