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 (!ShouldAddRedecl || 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; @@ -2970,7 +2974,9 @@ if (GetImportedOrCreateDecl(D2CXX, D, Importer.getToContext(), D->getTagKind(), DC, *BeginLocOrErr, Loc, Name.getAsIdentifierInfo(), - cast_or_null(PrevDecl))) + ShouldAddRedecl ? + cast_or_null(PrevDecl) : + nullptr)) return D2CXX; } @@ -5785,6 +5791,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. @@ -5802,7 +5813,7 @@ if (!hasSameVisibilityContextAndLinkage(FoundTemplate, D)) continue; - if (IsStructuralMatch(D, FoundTemplate)) { + if (!ShouldAddRedecl || IsStructuralMatch(D, FoundTemplate)) { ClassTemplateDecl *TemplateWithDef = getTemplateDefinition(FoundTemplate); if (D->isThisDeclarationADefinition() && TemplateWithDef) @@ -5853,7 +5864,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 @@ -3969,7 +3969,32 @@ } -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 +4099,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) {