diff --git a/clang/include/clang/AST/ASTStructuralEquivalence.h b/clang/include/clang/AST/ASTStructuralEquivalence.h index 4a79e64fe5d51..029439c8e9a3a 100644 --- a/clang/include/clang/AST/ASTStructuralEquivalence.h +++ b/clang/include/clang/AST/ASTStructuralEquivalence.h @@ -69,15 +69,19 @@ struct StructuralEquivalenceContext { /// \c true if the last diagnostic came from ToCtx. bool LastDiagFromC2 = false; + /// Whether to ignore comparing the depth of template param(TemplateTypeParm) + bool IgnoreTemplateParmDepth; + StructuralEquivalenceContext( ASTContext &FromCtx, ASTContext &ToCtx, llvm::DenseSet> &NonEquivalentDecls, - StructuralEquivalenceKind EqKind, - bool StrictTypeSpelling = false, bool Complain = true, - bool ErrorOnTagTypeMismatch = false) + StructuralEquivalenceKind EqKind, bool StrictTypeSpelling = false, + bool Complain = true, bool ErrorOnTagTypeMismatch = false, + bool IgnoreTemplateParmDepth = false) : FromCtx(FromCtx), ToCtx(ToCtx), NonEquivalentDecls(NonEquivalentDecls), EqKind(EqKind), StrictTypeSpelling(StrictTypeSpelling), - ErrorOnTagTypeMismatch(ErrorOnTagTypeMismatch), Complain(Complain) {} + ErrorOnTagTypeMismatch(ErrorOnTagTypeMismatch), Complain(Complain), + IgnoreTemplateParmDepth(IgnoreTemplateParmDepth) {} DiagnosticBuilder Diag1(SourceLocation Loc, unsigned DiagID); DiagnosticBuilder Diag2(SourceLocation Loc, unsigned DiagID); diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp index 736aac8a646b7..5ef8b6a5c50ee 100644 --- a/clang/lib/AST/ASTImporter.cpp +++ b/clang/lib/AST/ASTImporter.cpp @@ -508,7 +508,8 @@ namespace clang { template bool hasSameVisibilityContextAndLinkage(T *Found, T *From); - bool IsStructuralMatch(Decl *From, Decl *To, bool Complain = true); + bool IsStructuralMatch(Decl *From, Decl *To, bool Complain = true, + bool IgnoreTemplateParmDepth = false); ExpectedDecl VisitDecl(Decl *D); ExpectedDecl VisitImportDecl(ImportDecl *D); ExpectedDecl VisitEmptyDecl(EmptyDecl *D); @@ -2269,7 +2270,8 @@ getStructuralEquivalenceKind(const ASTImporter &Importer) { : StructuralEquivalenceKind::Default; } -bool ASTNodeImporter::IsStructuralMatch(Decl *From, Decl *To, bool Complain) { +bool ASTNodeImporter::IsStructuralMatch(Decl *From, Decl *To, bool Complain, + bool IgnoreTemplateParmDepth) { // Eliminate a potential failure point where we attempt to re-import // something we're trying to import while completing ToRecord. Decl *ToOrigin = Importer.GetOriginalDecl(To); @@ -2280,7 +2282,8 @@ bool ASTNodeImporter::IsStructuralMatch(Decl *From, Decl *To, bool Complain) { StructuralEquivalenceContext Ctx( Importer.getFromContext(), Importer.getToContext(), Importer.getNonEquivalentDecls(), getStructuralEquivalenceKind(Importer), - false, Complain); + /*StrictTypeSpelling=*/false, Complain, /*ErrorOnTagTypeMismatch=*/false, + IgnoreTemplateParmDepth); return Ctx.IsEquivalent(From, To); } @@ -5848,7 +5851,12 @@ ExpectedDecl ASTNodeImporter::VisitClassTemplateDecl(ClassTemplateDecl *D) { if (!hasSameVisibilityContextAndLinkage(FoundTemplate, D)) continue; - if (IsStructuralMatch(D, FoundTemplate)) { + // FIXME: sufficient conditon for 'IgnoreTemplateParmDepth'? + bool IgnoreTemplateParmDepth = + FoundTemplate->getFriendObjectKind() != Decl::FOK_None && + !D->specializations().empty(); + if (IsStructuralMatch(D, FoundTemplate, /*Complain=*/true, + IgnoreTemplateParmDepth)) { ClassTemplateDecl *TemplateWithDef = getTemplateDefinition(FoundTemplate); if (D->isThisDeclarationADefinition() && TemplateWithDef) diff --git a/clang/lib/AST/ASTStructuralEquivalence.cpp b/clang/lib/AST/ASTStructuralEquivalence.cpp index f867b6bf84beb..b211e349a4129 100644 --- a/clang/lib/AST/ASTStructuralEquivalence.cpp +++ b/clang/lib/AST/ASTStructuralEquivalence.cpp @@ -1092,7 +1092,8 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, case Type::TemplateTypeParm: { const auto *Parm1 = cast(T1); const auto *Parm2 = cast(T2); - if (Parm1->getDepth() != Parm2->getDepth()) + if (!Context.IgnoreTemplateParmDepth && + Parm1->getDepth() != Parm2->getDepth()) return false; if (Parm1->getIndex() != Parm2->getIndex()) return false; diff --git a/clang/unittests/AST/ASTImporterTest.cpp b/clang/unittests/AST/ASTImporterTest.cpp index 057701dea2c51..ed3703d116a62 100644 --- a/clang/unittests/AST/ASTImporterTest.cpp +++ b/clang/unittests/AST/ASTImporterTest.cpp @@ -4246,6 +4246,58 @@ TEST_P(ImportFriendClasses, DeclsFromFriendsShouldBeInRedeclChains) { EXPECT_TRUE(Imported->getPreviousDecl()); } +TEST_P(ImportFriendClasses, SkipComparingFriendTemplateDepth) { + Decl *ToTU = getToTuDecl( + R"( + template + class A; + + template + class A { + public: + template + friend class A; + + A(T x) :x(x) {} + + private: + T x; + }; + )", + Lang_CXX11); + + auto *Fwd = FirstDeclMatcher().match( + ToTU, + classTemplateDecl(has(cxxRecordDecl(hasDefinition(), hasName("A"))))); + Decl *FromTU = getTuDecl( + R"( + template + class A; + + template + class A { + public: + template + friend class A; + + A(T x) : x(x) {} + + private: + T x; + }; + + A a1(0); + )", + Lang_CXX11, "input1.cc"); + auto *FromA = FirstDeclMatcher().match( + FromTU, + classTemplateDecl(has(cxxRecordDecl(hasDefinition(), hasName("A"))))); + auto *ToA = Import(FromA, Lang_CXX11); + EXPECT_TRUE(ToA); + EXPECT_EQ(Fwd->getTemplatedDecl()->getTypeForDecl(), + ToA->getTemplatedDecl()->getTypeForDecl()); +} + TEST_P(ImportFriendClasses, ImportOfClassTemplateDefinitionShouldConnectToFwdFriend) { Decl *ToTU = getToTuDecl( diff --git a/clang/unittests/AST/StructuralEquivalenceTest.cpp b/clang/unittests/AST/StructuralEquivalenceTest.cpp index 092d9cc319a2e..1001ce49b0673 100644 --- a/clang/unittests/AST/StructuralEquivalenceTest.cpp +++ b/clang/unittests/AST/StructuralEquivalenceTest.cpp @@ -1,5 +1,6 @@ #include "clang/AST/ASTContext.h" #include "clang/AST/ASTStructuralEquivalence.h" +#include "clang/AST/DeclTemplate.h" #include "clang/ASTMatchers/ASTMatchers.h" #include "clang/Frontend/ASTUnit.h" #include "clang/Testing/CommandLineArgs.h" @@ -130,15 +131,20 @@ struct StructuralEquivalenceTest : ::testing::Test { return makeStmts(Wrap(SrcCode0), Wrap(SrcCode1), Lang, AMatcher); } - bool testStructuralMatch(Decl *D0, Decl *D1) { + bool testStructuralMatch(Decl *D0, Decl *D1, + bool IgnoreTemplateParmDepth = false) { llvm::DenseSet> NonEquivalentDecls01; llvm::DenseSet> NonEquivalentDecls10; StructuralEquivalenceContext Ctx01( - D0->getASTContext(), D1->getASTContext(), - NonEquivalentDecls01, StructuralEquivalenceKind::Default, false, false); + D0->getASTContext(), D1->getASTContext(), NonEquivalentDecls01, + StructuralEquivalenceKind::Default, /*StrictTypeSpelling=*/false, + /*Complain=*/false, /*ErrorOnTagTypeMismatch=*/false, + IgnoreTemplateParmDepth); StructuralEquivalenceContext Ctx10( - D1->getASTContext(), D0->getASTContext(), - NonEquivalentDecls10, StructuralEquivalenceKind::Default, false, false); + D1->getASTContext(), D0->getASTContext(), NonEquivalentDecls10, + StructuralEquivalenceKind::Default, /*StrictTypeSpelling=*/false, + /*Complain=*/false, /*ErrorOnTagTypeMismatch=*/false, + IgnoreTemplateParmDepth); bool Eq01 = Ctx01.IsEquivalent(D0, D1); bool Eq10 = Ctx10.IsEquivalent(D1, D0); EXPECT_EQ(Eq01, Eq10); @@ -165,8 +171,9 @@ struct StructuralEquivalenceTest : ::testing::Test { return testStructuralMatch(get<0>(t), get<1>(t)); } - bool testStructuralMatch(std::tuple t) { - return testStructuralMatch(get<0>(t), get<1>(t)); + bool testStructuralMatch(std::tuple t, + bool IgnoreTemplateParmDepth = false) { + return testStructuralMatch(get<0>(t), get<1>(t), IgnoreTemplateParmDepth); } }; @@ -1689,6 +1696,40 @@ TEST_F( EXPECT_FALSE(testStructuralMatch(t)); } +TEST_F(StructuralEquivalenceTemplateTest, + IgnoreTemplateParmDepthAtTemplateTypeParmDecl) { + auto Decls = makeDecls( + R"( + template struct A; + )", + R"( + template struct S { + template friend struct A; + }; + )", + Lang_CXX03, classTemplateDecl(hasName("A")), + classTemplateDecl(hasName("A"))); + EXPECT_TRUE(testStructuralMatch(Decls)); + EXPECT_TRUE(testStructuralMatch(Decls, true)); +} + +TEST_F(StructuralEquivalenceTemplateTest, + IgnoreTemplateParmDepthAtNonTypeTemplateParmDecl) { + auto Decls = makeDecls( + R"( + template struct A; + )", + R"( + template struct S { + template friend struct A; + }; + )", + Lang_CXX03, classTemplateDecl(hasName("A")), + classTemplateDecl(hasName("A"))); + EXPECT_FALSE(testStructuralMatch(Decls)); + EXPECT_TRUE(testStructuralMatch(Decls, /*IgnoreTemplateParmDepth=*/true)); +} + TEST_F( StructuralEquivalenceTemplateTest, ClassTemplSpecWithInequivalentShadowedTemplArg) {