diff --git a/clang/include/clang/AST/ASTStructuralEquivalence.h b/clang/include/clang/AST/ASTStructuralEquivalence.h --- a/clang/include/clang/AST/ASTStructuralEquivalence.h +++ b/clang/include/clang/AST/ASTStructuralEquivalence.h @@ -69,15 +69,19 @@ /// \c true if the last diagnostic came from ToCtx. bool LastDiagFromC2 = false; + /// Whether to ignore comparing the depth of NonTypeParmDecl + bool IgnoreDepth; + 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 IgnoreDepth = false) : FromCtx(FromCtx), ToCtx(ToCtx), NonEquivalentDecls(NonEquivalentDecls), EqKind(EqKind), StrictTypeSpelling(StrictTypeSpelling), - ErrorOnTagTypeMismatch(ErrorOnTagTypeMismatch), Complain(Complain) {} + ErrorOnTagTypeMismatch(ErrorOnTagTypeMismatch), Complain(Complain), + IgnoreDepth(IgnoreDepth) {} 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 --- a/clang/lib/AST/ASTImporter.cpp +++ b/clang/lib/AST/ASTImporter.cpp @@ -34,6 +34,7 @@ #include "clang/AST/LambdaCapture.h" #include "clang/AST/NestedNameSpecifier.h" #include "clang/AST/OperationKinds.h" +#include "clang/AST/ParentMapContext.h" #include "clang/AST/Stmt.h" #include "clang/AST/StmtCXX.h" #include "clang/AST/StmtObjC.h" @@ -506,7 +507,8 @@ 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 IgnoreDepth = true); ExpectedDecl VisitDecl(Decl *D); ExpectedDecl VisitImportDecl(ImportDecl *D); ExpectedDecl VisitEmptyDecl(EmptyDecl *D); @@ -2243,7 +2245,8 @@ : StructuralEquivalenceKind::Default; } -bool ASTNodeImporter::IsStructuralMatch(Decl *From, Decl *To, bool Complain) { +bool ASTNodeImporter::IsStructuralMatch(Decl *From, Decl *To, bool Complain, + bool IgnoreDepth) { // 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); @@ -2254,7 +2257,7 @@ StructuralEquivalenceContext Ctx( Importer.getFromContext(), Importer.getToContext(), Importer.getNonEquivalentDecls(), getStructuralEquivalenceKind(Importer), - false, Complain); + false, Complain, false, IgnoreDepth); return Ctx.IsEquivalent(From, To); } @@ -5822,7 +5825,11 @@ if (!hasSameVisibilityContextAndLinkage(FoundTemplate, D)) continue; - if (IsStructuralMatch(D, FoundTemplate)) { + // FIXME: sufficient conditon for 'IgnoreDepth'? + bool IgnoreDepth = + FoundTemplate->getFriendObjectKind() != Decl::FOK_None && + !D->specializations().empty(); + if (IsStructuralMatch(D, FoundTemplate, true, IgnoreDepth)) { ClassTemplateDecl *TemplateWithDef = getTemplateDefinition(FoundTemplate); if (D->isThisDeclarationADefinition() && TemplateWithDef) diff --git a/clang/lib/AST/ASTStructuralEquivalence.cpp b/clang/lib/AST/ASTStructuralEquivalence.cpp --- a/clang/lib/AST/ASTStructuralEquivalence.cpp +++ b/clang/lib/AST/ASTStructuralEquivalence.cpp @@ -1092,7 +1092,7 @@ case Type::TemplateTypeParm: { const auto *Parm1 = cast(T1); const auto *Parm2 = cast(T2); - if (Parm1->getDepth() != Parm2->getDepth()) + if (!Context.IgnoreDepth && 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 --- a/clang/unittests/AST/ASTImporterTest.cpp +++ b/clang/unittests/AST/ASTImporterTest.cpp @@ -4212,6 +4212,56 @@ 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(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(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(