diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp index 0540159f07e8a3..b762d6a4cd3800 100644 --- a/clang/lib/AST/ASTImporter.cpp +++ b/clang/lib/AST/ASTImporter.cpp @@ -5929,15 +5929,22 @@ 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; + // Should check if a declaration is friend in a dependent context. + // Such templates are not linked together in a declaration chain. + // The ASTImporter strategy is to map existing forward declarations to + // imported ones only if strictly necessary, otherwise import these as new + // forward declarations. In case of the "dependent friend" declarations, new + // declarations are created, but not linked in a declaration chain. + auto IsDependentFriend = [](ClassTemplateDecl *TD) { + return TD->getFriendObjectKind() != Decl::FOK_None && + TD->getLexicalDeclContext()->isDependentContext(); + }; + bool DependentFriend = IsDependentFriend(D); ClassTemplateDecl *FoundByLookup = nullptr; // We may already have a template of the same name; try to find and match it. - if (!DependentFriend && !DC->isFunctionOrMethod()) { + if (!DC->isFunctionOrMethod()) { SmallVector ConflictingDecls; auto FoundDecls = Importer.findDeclsInToCtx(DC, Name); for (auto *FoundDecl : FoundDecls) { @@ -5953,10 +5960,13 @@ ExpectedDecl ASTNodeImporter::VisitClassTemplateDecl(ClassTemplateDecl *D) { // FIXME: sufficient conditon for 'IgnoreTemplateParmDepth'? bool IgnoreTemplateParmDepth = - FoundTemplate->getFriendObjectKind() != Decl::FOK_None && - !D->specializations().empty(); + (FoundTemplate->getFriendObjectKind() != Decl::FOK_None) != + (D->getFriendObjectKind() != Decl::FOK_None); if (IsStructuralMatch(D, FoundTemplate, /*Complain=*/true, IgnoreTemplateParmDepth)) { + if (DependentFriend || IsDependentFriend(FoundTemplate)) + continue; + ClassTemplateDecl *TemplateWithDef = getTemplateDefinition(FoundTemplate); if (D->isThisDeclarationADefinition() && TemplateWithDef) diff --git a/clang/unittests/AST/ASTImporterTest.cpp b/clang/unittests/AST/ASTImporterTest.cpp index e4bd0d646cc9db..37cf14bdff6b33 100644 --- a/clang/unittests/AST/ASTImporterTest.cpp +++ b/clang/unittests/AST/ASTImporterTest.cpp @@ -4540,6 +4540,162 @@ TEST_P(ImportFriendClasses, ImportOfClassDefinitionAndFwdFriendShouldBeLinked) { EXPECT_EQ(ImportedFwd, ImportedDef->getPreviousDecl()); } +TEST_P(ImportFriendClasses, + ImportFriendTemplatesInDependentContext_DefToFriend) { + Decl *ToTU = getToTuDecl( + R"( + template + struct X { + template + friend struct Y; + }; + )", + Lang_CXX03); + auto *ToYFriend = FirstDeclMatcher().match( + ToTU, classTemplateDecl(hasName("Y"))); + Decl *FromTU = getTuDecl( + R"( + template + struct Y {}; + )", + Lang_CXX03, "input0.cc"); + auto *FromYDef = FirstDeclMatcher().match( + FromTU, classTemplateDecl(hasName("Y"))); + auto *ImportedYDef = Import(FromYDef, Lang_CXX03); + EXPECT_TRUE(ImportedYDef); + EXPECT_FALSE(ImportedYDef->getPreviousDecl()); + EXPECT_NE(ImportedYDef, ToYFriend); +} + +TEST_P(ImportFriendClasses, + ImportFriendTemplatesInDependentContext_DefToFriend_NE) { + getToTuDecl( + R"( + template + struct X { + template + friend struct Y; + }; + )", + Lang_CXX03); + Decl *FromTU = getTuDecl( + R"( + template + struct Y {}; + )", + Lang_CXX03, "input0.cc"); + auto *FromYDef = FirstDeclMatcher().match( + FromTU, classTemplateDecl(hasName("Y"))); + auto *ImportedYDef = Import(FromYDef, Lang_CXX03); + EXPECT_FALSE(ImportedYDef); +} + +TEST_P(ImportFriendClasses, + ImportFriendTemplatesInDependentContext_FriendToFriend) { + Decl *ToTU = getToTuDecl( + R"( + template + struct X { + template + friend struct Y; + }; + )", + Lang_CXX03); + auto *ToYFriend = FirstDeclMatcher().match( + ToTU, classTemplateDecl(hasName("Y"))); + Decl *FromTU = getTuDecl( + R"( + template + struct X { + template + friend struct Y; + }; + )", + Lang_CXX03, "input0.cc"); + auto *FromYFriend = FirstDeclMatcher().match( + FromTU, classTemplateDecl(hasName("Y"))); + auto *ImportedYFriend = Import(FromYFriend, Lang_CXX03); + EXPECT_TRUE(ImportedYFriend); + EXPECT_FALSE(ImportedYFriend->getPreviousDecl()); + EXPECT_NE(ImportedYFriend, ToYFriend); +} + +TEST_P(ImportFriendClasses, + ImportFriendTemplatesInDependentContext_FriendToFriend_NE) { + getToTuDecl( + R"( + template + struct X { + template + friend struct Y; + }; + )", + Lang_CXX03); + Decl *FromTU = getTuDecl( + R"( + template + struct X { + template + friend struct Y; + }; + )", + Lang_CXX03, "input0.cc"); + auto *FromYFriend = FirstDeclMatcher().match( + FromTU, classTemplateDecl(hasName("Y"))); + auto *ImportedYFriend = Import(FromYFriend, Lang_CXX03); + EXPECT_FALSE(ImportedYFriend); +} + +TEST_P(ImportFriendClasses, + ImportFriendTemplatesInDependentContext_FriendToDef) { + Decl *ToTU = getToTuDecl( + R"( + template + struct Y {}; + )", + Lang_CXX03); + auto *ToYDef = FirstDeclMatcher().match( + ToTU, classTemplateDecl(hasName("Y"))); + Decl *FromTU = getTuDecl( + R"( + template + struct X { + template + friend struct Y; + }; + )", + Lang_CXX03, "input0.cc"); + auto *FromYFriend = FirstDeclMatcher().match( + FromTU, classTemplateDecl(hasName("Y"))); + auto *ImportedYFriend = Import(FromYFriend, Lang_CXX03); + EXPECT_TRUE(ImportedYFriend); + EXPECT_FALSE(ImportedYFriend->getPreviousDecl()); + EXPECT_NE(ImportedYFriend, ToYDef); +} + +TEST_P(ImportFriendClasses, + ImportFriendTemplatesInDependentContext_FriendToDef_NE) { + getToTuDecl( + R"( + template + struct Y {}; + )", + Lang_CXX03); + Decl *FromTU = getTuDecl( + R"( + template + struct X { + template + friend struct Y; + }; + )", + Lang_CXX03, "input0.cc"); + auto *FromYFriend = FirstDeclMatcher().match( + FromTU, classTemplateDecl(hasName("Y"))); + auto *ImportedYFriend = Import(FromYFriend, Lang_CXX03); + EXPECT_FALSE(ImportedYFriend); +} + TEST_P(ImportFriendClasses, ImportOfRepeatedFriendType) { const char *Code = R"(