diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index a6b70042cc50f..a536a9fe2363c 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -131,6 +131,8 @@ Bug Fixes to C++ Support Bug Fixes to AST Handling ^^^^^^^^^^^^^^^^^^^^^^^^^ +- Fixed an import failure of recursive friend class template. + `Issue 64169 `_ Miscellaneous Bug Fixes ^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp index eb8e28ccd3e62..1ad7067f8ca9c 100644 --- a/clang/lib/AST/ASTImporter.cpp +++ b/clang/lib/AST/ASTImporter.cpp @@ -2857,9 +2857,13 @@ ExpectedDecl ASTNodeImporter::VisitRecordDecl(RecordDecl *D) { } else if (Importer.getToContext().getLangOpts().CPlusPlus) IDNS |= Decl::IDNS_Ordinary | Decl::IDNS_TagFriend; + bool IsDependentContext = DC != LexicalDC ? LexicalDC->isDependentContext() + : DC->isDependentContext(); + bool DependentFriend = IsFriendTemplate && IsDependentContext; + // We may already have a record of the same name; try to find and match it. RecordDecl *PrevDecl = nullptr; - if (!DC->isFunctionOrMethod() && !D->isLambda()) { + if (!DependentFriend && !DC->isFunctionOrMethod() && !D->isLambda()) { SmallVector ConflictingDecls; auto FoundDecls = Importer.findDeclsInToCtx(DC, SearchName); @@ -5796,10 +5800,15 @@ 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; + ClassTemplateDecl *FoundByLookup = nullptr; // We may already have a template of the same name; try to find and match it. - if (!DC->isFunctionOrMethod()) { + if (!DependentFriend && !DC->isFunctionOrMethod()) { SmallVector ConflictingDecls; auto FoundDecls = Importer.findDeclsInToCtx(DC, Name); for (auto *FoundDecl : FoundDecls) { diff --git a/clang/unittests/AST/ASTImporterTest.cpp b/clang/unittests/AST/ASTImporterTest.cpp index c6ae269393040..544550f05fd74 100644 --- a/clang/unittests/AST/ASTImporterTest.cpp +++ b/clang/unittests/AST/ASTImporterTest.cpp @@ -3968,8 +3968,58 @@ TEST_P(ImportClasses, ImportNestedPrototypeThenDefinition) { EXPECT_EQ(ToDef->getPreviousDecl(), ToProto); } - -struct ImportFriendClasses : ASTImporterOptionSpecificTestBase {}; +struct ImportFriendClasses : ASTImporterOptionSpecificTestBase { + void testRecursiveFriendClassTemplate(Decl *FromTu) { + auto *FromD = FirstDeclMatcher().match( + FromTu, classTemplateDecl()); + + auto Pattern = classTemplateDecl( + has(cxxRecordDecl(has(friendDecl(has(classTemplateDecl())))))); + ASSERT_TRUE(MatchVerifier{}.match(FromD, Pattern)); + + auto *FromFriend = + FirstDeclMatcher().match(FromD, friendDecl()); + auto *FromRecordOfFriend = + cast(FromFriend->getFriendDecl()) + ->getTemplatedDecl(); + EXPECT_NE(FromRecordOfFriend, FromD->getTemplatedDecl()); + EXPECT_TRUE(FromRecordOfFriend->getPreviousDecl() == nullptr); + + auto *FromDC = FromRecordOfFriend->getDeclContext(); + auto *FromLexicalDC = FromRecordOfFriend->getLexicalDeclContext(); + ASSERT_EQ(FromDC, cast(FromTu)); + ASSERT_EQ(FromLexicalDC, cast(FromD->getTemplatedDecl())); + + ASSERT_FALSE(FromDC->containsDecl(FromRecordOfFriend)); + ASSERT_FALSE(FromLexicalDC->containsDecl(FromRecordOfFriend)); + ASSERT_FALSE(cast(FromRecordOfFriend) + ->getLookupParent() + ->lookup(FromRecordOfFriend->getDeclName()) + .empty()); + + auto *ToD = Import(FromD, Lang_CXX03); + EXPECT_TRUE(MatchVerifier{}.match(ToD, Pattern)); + + auto *ToFriend = FirstDeclMatcher().match(ToD, friendDecl()); + auto *ToRecordOfFriend = + cast(ToFriend->getFriendDecl())->getTemplatedDecl(); + + EXPECT_NE(ToRecordOfFriend, ToD->getTemplatedDecl()); + EXPECT_TRUE(ToRecordOfFriend->getPreviousDecl() == nullptr); + + auto *ToDC = ToRecordOfFriend->getDeclContext(); + auto *ToLexicalDC = ToRecordOfFriend->getLexicalDeclContext(); + ASSERT_EQ(ToDC, cast(ToD->getTranslationUnitDecl())); + ASSERT_EQ(ToLexicalDC, cast(ToD->getTemplatedDecl())); + + ASSERT_FALSE(ToDC->containsDecl(ToRecordOfFriend)); + ASSERT_FALSE(ToLexicalDC->containsDecl(ToRecordOfFriend)); + ASSERT_FALSE(cast(ToRecordOfFriend) + ->getLookupParent() + ->lookup(ToRecordOfFriend->getDeclName()) + .empty()); + } +}; TEST_P(ImportFriendClasses, ImportOfFriendRecordDoesNotMergeDefinition) { Decl *FromTU = getTuDecl( @@ -4074,20 +4124,19 @@ TEST_P(ImportFriendClasses, ImportOfRecursiveFriendClassTemplate) { )", Lang_CXX03, "input.cc"); - auto *FromD = - FirstDeclMatcher().match(FromTu, classTemplateDecl()); - auto *ToD = Import(FromD, Lang_CXX03); - - auto Pattern = classTemplateDecl( - has(cxxRecordDecl(has(friendDecl(has(classTemplateDecl())))))); - ASSERT_TRUE(MatchVerifier{}.match(FromD, Pattern)); - EXPECT_TRUE(MatchVerifier{}.match(ToD, Pattern)); + testRecursiveFriendClassTemplate(FromTu); +} - auto *Class = - FirstDeclMatcher().match(ToD, classTemplateDecl()); - auto *Friend = FirstDeclMatcher().match(ToD, friendDecl()); - EXPECT_NE(Friend->getFriendDecl(), Class); - EXPECT_EQ(Friend->getFriendDecl()->getPreviousDecl(), Class); +TEST_P(ImportFriendClasses, + ImportOfRecursiveFriendClassTemplateWithNonTypeParm) { + Decl *FromTu = getTuDecl( + R"( + template class declToImport { + template friend class declToImport; + }; + )", + Lang_CXX03, "input.cc"); + testRecursiveFriendClassTemplate(FromTu); } TEST_P(ImportFriendClasses, ProperPrevDeclForClassTemplateDecls) {