diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -114,6 +114,8 @@ 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 --- a/clang/lib/AST/ASTImporter.cpp +++ b/clang/lib/AST/ASTImporter.cpp @@ -2857,6 +2857,10 @@ } else if (Importer.getToContext().getLangOpts().CPlusPlus) IDNS |= Decl::IDNS_Ordinary | Decl::IDNS_TagFriend; + bool IsDependentContext = DC != LexicalDC ? LexicalDC->isDependentContext() + : DC->isDependentContext(); + bool ShouldAddRedecl = !(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()) { @@ -2897,7 +2901,7 @@ if (!hasSameVisibilityContextAndLinkage(FoundRecord, D)) continue; - if (IsStructuralMatch(D, FoundRecord)) { + if (IsFriendTemplate || IsStructuralMatch(D, FoundRecord)) { RecordDecl *FoundDef = FoundRecord->getDefinition(); if (D->isThisDeclarationADefinition() && FoundDef) { // FIXME: Structural equivalence check should check for same @@ -2955,7 +2959,7 @@ return CDeclOrErr.takeError(); Numbering.ContextDecl = *CDeclOrErr; D2CXX->setLambdaNumbering(Numbering); - } else if (DCXX->isInjectedClassName()) { + } else if (DCXX->isInjectedClassName()) { // We have to be careful to do a similar dance to the one in // Sema::ActOnStartCXXMemberDeclarations const bool DelayTypeCreation = true; @@ -2967,10 +2971,11 @@ Importer.getToContext().getTypeDeclType( D2CXX, dyn_cast(DC)); } else { - if (GetImportedOrCreateDecl(D2CXX, D, Importer.getToContext(), - D->getTagKind(), DC, *BeginLocOrErr, Loc, - Name.getAsIdentifierInfo(), - cast_or_null(PrevDecl))) + if (GetImportedOrCreateDecl( + D2CXX, D, Importer.getToContext(), D->getTagKind(), DC, + *BeginLocOrErr, Loc, Name.getAsIdentifierInfo(), + ShouldAddRedecl ? cast_or_null(PrevDecl) + : nullptr)) return D2CXX; } @@ -5796,6 +5801,11 @@ if (ToD) return ToD; + bool IsFriendTemplate = D->getFriendObjectKind() != Decl::FOK_None; + bool IsDependentContext = DC != LexicalDC ? LexicalDC->isDependentContext() + : DC->isDependentContext(); + bool ShouldAddRedecl = !(IsFriendTemplate && IsDependentContext); + ClassTemplateDecl *FoundByLookup = nullptr; // We may already have a template of the same name; try to find and match it. @@ -5813,7 +5823,7 @@ if (!hasSameVisibilityContextAndLinkage(FoundTemplate, D)) continue; - if (IsStructuralMatch(D, FoundTemplate)) { + if (IsFriendTemplate || IsStructuralMatch(D, FoundTemplate)) { ClassTemplateDecl *TemplateWithDef = getTemplateDefinition(FoundTemplate); if (D->isThisDeclarationADefinition() && TemplateWithDef) @@ -5864,7 +5874,7 @@ addDeclToContexts(D, D2); updateLookupTableForTemplateParameters(**TemplateParamsOrErr); - if (FoundByLookup) { + if (ShouldAddRedecl && FoundByLookup) { auto *Recent = const_cast(FoundByLookup->getMostRecentDecl()); 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 @@ -3968,8 +3968,31 @@ EXPECT_EQ(ToDef->getPreviousDecl(), ToProto); } - -struct ImportFriendClasses : ASTImporterOptionSpecificTestBase {}; +struct ImportFriendClasses : ASTImporterOptionSpecificTestBase { + void testRecursiveFriendClassTemplate(Decl *FromTu) { + 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)); + + auto *FromFriend = + FirstDeclMatcher().match(FromD, friendDecl()); + auto *FromClass = + FirstDeclMatcher().match(FromD, classTemplateDecl()); + EXPECT_NE(FromFriend->getFriendDecl(), FromClass); + EXPECT_TRUE(FromFriend->getFriendDecl()->getPreviousDecl() == nullptr); + + auto *Class = + FirstDeclMatcher().match(ToD, classTemplateDecl()); + auto *Friend = FirstDeclMatcher().match(ToD, friendDecl()); + EXPECT_NE(Friend->getFriendDecl(), Class); + EXPECT_TRUE(Friend->getFriendDecl()->getPreviousDecl() == nullptr); + } +}; TEST_P(ImportFriendClasses, ImportOfFriendRecordDoesNotMergeDefinition) { Decl *FromTU = getTuDecl( @@ -4074,20 +4097,19 @@ )", 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) {