diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp --- a/clang/lib/AST/ASTImporter.cpp +++ b/clang/lib/AST/ASTImporter.cpp @@ -4079,22 +4079,34 @@ unsigned int IndexOfDecl; }; -template -static FriendCountAndPosition getFriendCountAndPosition( - const FriendDecl *FD, - llvm::function_ref GetCanTypeOrDecl) { +static bool IsEquivalentFriend(ASTImporter &Importer, FriendDecl *FD1, + FriendDecl *FD2) { + if ((!FD1->getFriendType()) != (!FD2->getFriendType())) + return false; + + if (const TypeSourceInfo *TSI = FD1->getFriendType()) + return Importer.IsStructurallyEquivalent( + TSI->getType(), FD2->getFriendType()->getType(), /*Complain=*/false); + + ASTImporter::NonEquivalentDeclSet NonEquivalentDecls; + StructuralEquivalenceContext Ctx( + FD1->getASTContext(), FD2->getASTContext(), NonEquivalentDecls, + StructuralEquivalenceKind::Default, + /* StrictTypeSpelling = */ false, /* Complain = */ false); + return Ctx.IsEquivalent(FD1, FD2); +} + +static FriendCountAndPosition getFriendCountAndPosition(ASTImporter &Importer, + FriendDecl *FD) { unsigned int FriendCount = 0; std::optional FriendPosition; const auto *RD = cast(FD->getLexicalDeclContext()); - T TypeOrDecl = GetCanTypeOrDecl(FD); - - for (const FriendDecl *FoundFriend : RD->friends()) { + for (FriendDecl *FoundFriend : RD->friends()) { if (FoundFriend == FD) { FriendPosition = FriendCount; ++FriendCount; - } else if (!FoundFriend->getFriendDecl() == !FD->getFriendDecl() && - GetCanTypeOrDecl(FoundFriend) == TypeOrDecl) { + } else if (IsEquivalentFriend(Importer, FD, FoundFriend)) { ++FriendCount; } } @@ -4104,21 +4116,6 @@ return {FriendCount, *FriendPosition}; } -static FriendCountAndPosition getFriendCountAndPosition(const FriendDecl *FD) { - if (FD->getFriendType()) - return getFriendCountAndPosition(FD, [](const FriendDecl *F) { - if (TypeSourceInfo *TSI = F->getFriendType()) - return TSI->getType().getCanonicalType(); - llvm_unreachable("Wrong friend object type."); - }); - else - return getFriendCountAndPosition(FD, [](const FriendDecl *F) { - if (Decl *D = F->getFriendDecl()) - return D->getCanonicalDecl(); - llvm_unreachable("Wrong friend object type."); - }); -} - ExpectedDecl ASTNodeImporter::VisitFriendDecl(FriendDecl *D) { // Import the major distinguishing characteristics of a declaration. DeclContext *DC, *LexicalDC; @@ -4129,26 +4126,13 @@ // FriendDecl is not a NamedDecl so we cannot use lookup. // We try to maintain order and count of redundant friend declarations. const auto *RD = cast(DC); - FriendDecl *ImportedFriend = RD->getFirstFriend(); SmallVector ImportedEquivalentFriends; - - while (ImportedFriend) { - bool Match = false; - if (D->getFriendDecl() && ImportedFriend->getFriendDecl()) { - Match = - IsStructuralMatch(D->getFriendDecl(), ImportedFriend->getFriendDecl(), - /*Complain=*/false); - } else if (D->getFriendType() && ImportedFriend->getFriendType()) { - Match = Importer.IsStructurallyEquivalent( - D->getFriendType()->getType(), - ImportedFriend->getFriendType()->getType(), /*Complain=*/false); - } - if (Match) + for (FriendDecl *ImportedFriend : RD->friends()) + if (IsEquivalentFriend(Importer, D, ImportedFriend)) ImportedEquivalentFriends.push_back(ImportedFriend); - ImportedFriend = ImportedFriend->getNextFriend(); - } - FriendCountAndPosition CountAndPosition = getFriendCountAndPosition(D); + FriendCountAndPosition CountAndPosition = + getFriendCountAndPosition(Importer, D); assert(ImportedEquivalentFriends.size() <= CountAndPosition.TotalCount && "Class with non-matching friends is imported, ODR check wrong?"); diff --git a/clang/unittests/AST/ASTImporterTest.cpp b/clang/unittests/AST/ASTImporterTest.cpp --- a/clang/unittests/AST/ASTImporterTest.cpp +++ b/clang/unittests/AST/ASTImporterTest.cpp @@ -4054,6 +4054,25 @@ ->lookup(ToRecordOfFriend->getDeclName()) .empty()); } + + void testRepeatedFriendImport(const char *Code) { + Decl *ToTu = getToTuDecl(Code, Lang_CXX03); + Decl *FromTu = getTuDecl(Code, Lang_CXX03, "from.cc"); + + auto *ToFriend1 = FirstDeclMatcher().match(ToTu, friendDecl()); + auto *ToFriend2 = LastDeclMatcher().match(ToTu, friendDecl()); + auto *FromFriend1 = + FirstDeclMatcher().match(FromTu, friendDecl()); + auto *FromFriend2 = + LastDeclMatcher().match(FromTu, friendDecl()); + + FriendDecl *ToImportedFriend1 = Import(FromFriend1, Lang_CXX03); + FriendDecl *ToImportedFriend2 = Import(FromFriend2, Lang_CXX03); + + EXPECT_NE(ToImportedFriend1, ToImportedFriend2); + EXPECT_EQ(ToFriend1, ToImportedFriend1); + EXPECT_EQ(ToFriend2, ToImportedFriend2); + } }; TEST_P(ImportFriendClasses, ImportOfFriendRecordDoesNotMergeDefinition) { @@ -4395,21 +4414,7 @@ friend class X; }; )"; - Decl *ToTu = getToTuDecl(Code, Lang_CXX03); - Decl *FromTu = getTuDecl(Code, Lang_CXX03, "from.cc"); - - auto *ToFriend1 = FirstDeclMatcher().match(ToTu, friendDecl()); - auto *ToFriend2 = LastDeclMatcher().match(ToTu, friendDecl()); - auto *FromFriend1 = - FirstDeclMatcher().match(FromTu, friendDecl()); - auto *FromFriend2 = LastDeclMatcher().match(FromTu, friendDecl()); - - FriendDecl *ToImportedFriend1 = Import(FromFriend1, Lang_CXX03); - FriendDecl *ToImportedFriend2 = Import(FromFriend2, Lang_CXX03); - - EXPECT_NE(ToImportedFriend1, ToImportedFriend2); - EXPECT_EQ(ToFriend1, ToImportedFriend1); - EXPECT_EQ(ToFriend2, ToImportedFriend2); + testRepeatedFriendImport(Code); } TEST_P(ImportFriendClasses, ImportOfRepeatedFriendDecl) { @@ -4420,21 +4425,31 @@ friend void f(); }; )"; - Decl *ToTu = getToTuDecl(Code, Lang_CXX03); - Decl *FromTu = getTuDecl(Code, Lang_CXX03, "from.cc"); - - auto *ToFriend1 = FirstDeclMatcher().match(ToTu, friendDecl()); - auto *ToFriend2 = LastDeclMatcher().match(ToTu, friendDecl()); - auto *FromFriend1 = - FirstDeclMatcher().match(FromTu, friendDecl()); - auto *FromFriend2 = LastDeclMatcher().match(FromTu, friendDecl()); + testRepeatedFriendImport(Code); +} - FriendDecl *ToImportedFriend1 = Import(FromFriend1, Lang_CXX03); - FriendDecl *ToImportedFriend2 = Import(FromFriend2, Lang_CXX03); +TEST_P(ImportFriendClasses, ImportOfRepeatedFriendFunctionTemplateDecl) { + const char *Code = + R"( + template + class Container { + template friend void m(); + template friend void m(); + }; + )"; + testRepeatedFriendImport(Code); +} - EXPECT_NE(ToImportedFriend1, ToImportedFriend2); - EXPECT_EQ(ToFriend1, ToImportedFriend1); - EXPECT_EQ(ToFriend2, ToImportedFriend2); +TEST_P(ImportFriendClasses, ImportOfRepeatedFriendClassTemplateDecl) { + const char *Code = + R"( + template + class Container { + template friend class X; + template friend class X; + }; + )"; + testRepeatedFriendImport(Code); } TEST_P(ASTImporterOptionSpecificTestBase, FriendFunInClassTemplate) {