Author: Balázs Kéri Date: 2024-01-11T11:18:11+01:00 New Revision: 4b0314d14f888cc1916556574ecaa35cc118ee00
URL: https://github.com/llvm/llvm-project/commit/4b0314d14f888cc1916556574ecaa35cc118ee00 DIFF: https://github.com/llvm/llvm-project/commit/4b0314d14f888cc1916556574ecaa35cc118ee00.diff LOG: [clang][ASTImporter] Improve import of friend class templates. (#74627) A friend template that is in a dependent context is not linked into declaration chains (for example with the definition of the befriended template). This condition was not correctly handled by `ASTImporter`. Added: Modified: clang/lib/AST/ASTImporter.cpp clang/unittests/AST/ASTImporterTest.cpp Removed: ################################################################################ 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<NamedDecl *, 4> 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<class T1> + struct X { + template<class T2> + friend struct Y; + }; + )", + Lang_CXX03); + auto *ToYFriend = FirstDeclMatcher<ClassTemplateDecl>().match( + ToTU, classTemplateDecl(hasName("Y"))); + Decl *FromTU = getTuDecl( + R"( + template<class T1> + struct Y {}; + )", + Lang_CXX03, "input0.cc"); + auto *FromYDef = FirstDeclMatcher<ClassTemplateDecl>().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<class T1> + struct X { + template<class T2> + friend struct Y; + }; + )", + Lang_CXX03); + Decl *FromTU = getTuDecl( + R"( + template<class T1, class T2> + struct Y {}; + )", + Lang_CXX03, "input0.cc"); + auto *FromYDef = FirstDeclMatcher<ClassTemplateDecl>().match( + FromTU, classTemplateDecl(hasName("Y"))); + auto *ImportedYDef = Import(FromYDef, Lang_CXX03); + EXPECT_FALSE(ImportedYDef); +} + +TEST_P(ImportFriendClasses, + ImportFriendTemplatesInDependentContext_FriendToFriend) { + Decl *ToTU = getToTuDecl( + R"( + template<class T1> + struct X { + template<class T2> + friend struct Y; + }; + )", + Lang_CXX03); + auto *ToYFriend = FirstDeclMatcher<ClassTemplateDecl>().match( + ToTU, classTemplateDecl(hasName("Y"))); + Decl *FromTU = getTuDecl( + R"( + template<class T1> + struct X { + template<class T2> + friend struct Y; + }; + )", + Lang_CXX03, "input0.cc"); + auto *FromYFriend = FirstDeclMatcher<ClassTemplateDecl>().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<class T1> + struct X { + template<class T2> + friend struct Y; + }; + )", + Lang_CXX03); + Decl *FromTU = getTuDecl( + R"( + template<class T1> + struct X { + template<class T2, class T3> + friend struct Y; + }; + )", + Lang_CXX03, "input0.cc"); + auto *FromYFriend = FirstDeclMatcher<ClassTemplateDecl>().match( + FromTU, classTemplateDecl(hasName("Y"))); + auto *ImportedYFriend = Import(FromYFriend, Lang_CXX03); + EXPECT_FALSE(ImportedYFriend); +} + +TEST_P(ImportFriendClasses, + ImportFriendTemplatesInDependentContext_FriendToDef) { + Decl *ToTU = getToTuDecl( + R"( + template<class T1> + struct Y {}; + )", + Lang_CXX03); + auto *ToYDef = FirstDeclMatcher<ClassTemplateDecl>().match( + ToTU, classTemplateDecl(hasName("Y"))); + Decl *FromTU = getTuDecl( + R"( + template<class T1> + struct X { + template<class T2> + friend struct Y; + }; + )", + Lang_CXX03, "input0.cc"); + auto *FromYFriend = FirstDeclMatcher<ClassTemplateDecl>().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<class T1> + struct Y {}; + )", + Lang_CXX03); + Decl *FromTU = getTuDecl( + R"( + template<class T1> + struct X { + template<class T2, class T3> + friend struct Y; + }; + )", + Lang_CXX03, "input0.cc"); + auto *FromYFriend = FirstDeclMatcher<ClassTemplateDecl>().match( + FromTU, classTemplateDecl(hasName("Y"))); + auto *ImportedYFriend = Import(FromYFriend, Lang_CXX03); + EXPECT_FALSE(ImportedYFriend); +} + TEST_P(ImportFriendClasses, ImportOfRepeatedFriendType) { const char *Code = R"( _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits