diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp index 35aba41f0052a..1f4ae74f3c365 100644 --- a/clang/lib/AST/ASTImporter.cpp +++ b/clang/lib/AST/ASTImporter.cpp @@ -6095,8 +6095,7 @@ ExpectedDecl ASTNodeImporter::VisitClassTemplateDecl(ClassTemplateDecl *D) { Decl::IDNS_TagFriend)) continue; - Decl *Found = FoundDecl; - auto *FoundTemplate = dyn_cast(Found); + auto *FoundTemplate = dyn_cast(FoundDecl); if (FoundTemplate) { if (!hasSameVisibilityContextAndLinkage(FoundTemplate, D)) continue; @@ -6120,6 +6119,19 @@ ExpectedDecl ASTNodeImporter::VisitClassTemplateDecl(ClassTemplateDecl *D) { // see ASTTests test ImportExistingFriendClassTemplateDef. continue; } + // When importing a friend, it is possible that multiple declarations + // with same name can co-exist in specific cases (if a template contains + // a friend template and has a specialization). For this case the + // declarations should match, except that the "template depth" is + // different. No linking of previous declaration is needed in this case. + // FIXME: This condition may need refinement. + if (D->getFriendObjectKind() != Decl::FOK_None && + FoundTemplate->getFriendObjectKind() != Decl::FOK_None && + D->getFriendObjectKind() != FoundTemplate->getFriendObjectKind() && + IsStructuralMatch(D, FoundTemplate, /*Complain=*/false, + /*IgnoreTemplateParmDepth=*/true)) + continue; + ConflictingDecls.push_back(FoundDecl); } } diff --git a/clang/unittests/AST/ASTImporterTest.cpp b/clang/unittests/AST/ASTImporterTest.cpp index bf7313f882e45..abf07d094e62c 100644 --- a/clang/unittests/AST/ASTImporterTest.cpp +++ b/clang/unittests/AST/ASTImporterTest.cpp @@ -10181,6 +10181,111 @@ TEST_P(ImportTemplateParmDeclDefaultValue, FromD, FromDInherited); } +TEST_P(ASTImporterOptionSpecificTestBase, + ExistingUndeclaredImportDeclaredFriend) { + Decl *ToTU = getToTuDecl( + R"( + template + struct foo; + + template + struct X { + template + friend struct foo; + }; + )", + Lang_CXX11); + Decl *FromTU = getTuDecl( + R"( + template + struct foo; + + template + struct X { + template + friend struct foo; + }; + + X x; + )", + Lang_CXX11); + + auto *ToFr1 = FirstDeclMatcher().match(ToTU, friendDecl()); + auto *ToFrD1 = ToFr1->getFriendDecl(); + + auto *FromFr1 = FirstDeclMatcher().match(FromTU, friendDecl()); + auto *FromFr2 = LastDeclMatcher().match(FromTU, friendDecl()); + + auto *FromFrD1 = FromFr1->getFriendDecl(); + auto *FromFrD2 = FromFr2->getFriendDecl(); + + auto *Ctx1 = cast(FromFrD1->getDeclContext()); + auto *Ctx2 = cast(FromFrD2->getDeclContext()); + + ASSERT_EQ(Ctx1, Ctx2); + ASSERT_EQ(ToFrD1->getTemplateDepth(), 1u); + ASSERT_EQ(FromFrD2->getTemplateDepth(), 0u); + ASSERT_EQ(ToFrD1->getFriendObjectKind(), Decl::FOK_Undeclared); + ASSERT_EQ(FromFrD2->getFriendObjectKind(), Decl::FOK_Declared); + + auto *ToFr2Imp = Import(FromFr2, Lang_CXX11); + + EXPECT_TRUE(ToFr2Imp); +} + +TEST_P(ASTImporterOptionSpecificTestBase, + ExistingDeclaredImportUndeclaredFriend) { + Decl *ToTU = getToTuDecl( + R"( + template + struct foo; + + template + struct X { + template + friend struct foo; + }; + + X x; + )", + Lang_CXX11); + Decl *FromTU = getTuDecl( + R"( + template + struct foo; + + template + struct X { + template + friend struct foo; + }; + )", + Lang_CXX11); + + auto *ToFr1 = FirstDeclMatcher().match(ToTU, friendDecl()); + auto *ToFr2 = LastDeclMatcher().match(ToTU, friendDecl()); + + auto *ToFrD1 = ToFr1->getFriendDecl(); + auto *ToFrD2 = ToFr2->getFriendDecl(); + + auto *FromFr1 = FirstDeclMatcher().match(FromTU, friendDecl()); + auto *FromFrD1 = FromFr1->getFriendDecl(); + + auto *Ctx1 = cast(ToFrD1->getDeclContext()); + auto *Ctx2 = cast(ToFrD2->getDeclContext()); + + ASSERT_EQ(Ctx1, Ctx2); + ASSERT_EQ(FromFrD1->getTemplateDepth(), 1u); + ASSERT_EQ(ToFrD2->getTemplateDepth(), 0u); + ASSERT_EQ(FromFrD1->getFriendObjectKind(), Decl::FOK_Undeclared); + ASSERT_EQ(ToFrD2->getFriendObjectKind(), Decl::FOK_Declared); + + auto *ToFr1Imp = Import(FromFr1, Lang_CXX11); + + EXPECT_TRUE(ToFr1Imp); + EXPECT_EQ(ToFr1Imp, ToFr1); +} + INSTANTIATE_TEST_SUITE_P(ParameterizedTests, ASTImporterLookupTableTest, DefaultTestValuesForRunOptions);