diff --git a/clang/include/clang/AST/ASTImporter.h b/clang/include/clang/AST/ASTImporter.h index 088a2bd0fdd4..8c3fa842ab8b 100644 --- a/clang/include/clang/AST/ASTImporter.h +++ b/clang/include/clang/AST/ASTImporter.h @@ -62,7 +62,8 @@ class TypeSourceInfo; class ASTImporter { friend class ASTNodeImporter; public: - using NonEquivalentDeclSet = llvm::DenseSet>; + using NonEquivalentDeclSet = + llvm::DenseSet>; using ImportedCXXBaseSpecifierMap = llvm::DenseMap; diff --git a/clang/include/clang/AST/ASTStructuralEquivalence.h b/clang/include/clang/AST/ASTStructuralEquivalence.h index 029439c8e9a3..67aa0023c25d 100644 --- a/clang/include/clang/AST/ASTStructuralEquivalence.h +++ b/clang/include/clang/AST/ASTStructuralEquivalence.h @@ -39,6 +39,10 @@ enum class StructuralEquivalenceKind { }; struct StructuralEquivalenceContext { + /// Store declaration pairs already found to be non-equivalent. + /// key: (from, to, IgnoreTemplateParmDepth) + using NonEquivalentDeclSet = llvm::DenseSet>; + /// AST contexts for which we are checking structural equivalence. ASTContext &FromCtx, &ToCtx; @@ -52,7 +56,7 @@ struct StructuralEquivalenceContext { /// Declaration (from, to) pairs that are known not to be equivalent /// (which we have already complained about). - llvm::DenseSet> &NonEquivalentDecls; + NonEquivalentDeclSet &NonEquivalentDecls; StructuralEquivalenceKind EqKind; @@ -72,12 +76,13 @@ struct StructuralEquivalenceContext { /// 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, - bool IgnoreTemplateParmDepth = false) + StructuralEquivalenceContext(ASTContext &FromCtx, ASTContext &ToCtx, + NonEquivalentDeclSet &NonEquivalentDecls, + 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), diff --git a/clang/lib/AST/ASTStructuralEquivalence.cpp b/clang/lib/AST/ASTStructuralEquivalence.cpp index 120ddc0f26c0..bf2f42932f25 100644 --- a/clang/lib/AST/ASTStructuralEquivalence.cpp +++ b/clang/lib/AST/ASTStructuralEquivalence.cpp @@ -2303,7 +2303,8 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, // Check whether we already know that these two declarations are not // structurally equivalent. - if (Context.NonEquivalentDecls.count(P)) + if (Context.NonEquivalentDecls.count( + std::make_tuple(D1, D2, Context.IgnoreTemplateParmDepth))) return false; // Check if a check for these declarations is already pending. @@ -2511,7 +2512,8 @@ bool StructuralEquivalenceContext::Finish() { if (!Equivalent) { // Note that these two declarations are not equivalent (and we already // know about it). - NonEquivalentDecls.insert(P); + NonEquivalentDecls.insert( + std::make_tuple(D1, D2, IgnoreTemplateParmDepth)); return true; } diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp index 515b9f689a24..55caa8bdb403 100644 --- a/clang/lib/Sema/SemaType.cpp +++ b/clang/lib/Sema/SemaType.cpp @@ -9046,7 +9046,7 @@ bool Sema::RequireCompleteType(SourceLocation Loc, QualType T, } bool Sema::hasStructuralCompatLayout(Decl *D, Decl *Suggested) { - llvm::DenseSet> NonEquivalentDecls; + StructuralEquivalenceContext::NonEquivalentDeclSet NonEquivalentDecls; if (!Suggested) return false; diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp index cf987df0e215..873d12390699 100644 --- a/clang/lib/Serialization/ASTReader.cpp +++ b/clang/lib/Serialization/ASTReader.cpp @@ -9907,7 +9907,7 @@ void ASTReader::finishPendingActions() { auto ExtensionsPair = PendingObjCExtensionIvarRedeclarations.back().first; auto DuplicateIvars = PendingObjCExtensionIvarRedeclarations.back().second; - llvm::DenseSet> NonEquivalentDecls; + StructuralEquivalenceContext::NonEquivalentDeclSet NonEquivalentDecls; StructuralEquivalenceContext Ctx( ExtensionsPair.first->getASTContext(), ExtensionsPair.second->getASTContext(), NonEquivalentDecls, diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp index 33fcddbbdb2f..6ece3ba7af9f 100644 --- a/clang/lib/Serialization/ASTReaderDecl.cpp +++ b/clang/lib/Serialization/ASTReaderDecl.cpp @@ -4447,7 +4447,7 @@ namespace { ObjCCategoryDecl *&Existing = NameCategoryMap[Cat->getDeclName()]; if (Existing && Reader.getOwningModuleFile(Existing) != Reader.getOwningModuleFile(Cat)) { - llvm::DenseSet> NonEquivalentDecls; + StructuralEquivalenceContext::NonEquivalentDeclSet NonEquivalentDecls; StructuralEquivalenceContext Ctx( Cat->getASTContext(), Existing->getASTContext(), NonEquivalentDecls, StructuralEquivalenceKind::Default, diff --git a/clang/unittests/AST/StructuralEquivalenceTest.cpp b/clang/unittests/AST/StructuralEquivalenceTest.cpp index e994086c99d0..7cf52df9b14d 100644 --- a/clang/unittests/AST/StructuralEquivalenceTest.cpp +++ b/clang/unittests/AST/StructuralEquivalenceTest.cpp @@ -134,8 +134,8 @@ struct StructuralEquivalenceTest : ::testing::Test { bool testStructuralMatch(Decl *D0, Decl *D1, bool IgnoreTemplateParmDepth = false) { - llvm::DenseSet> NonEquivalentDecls01; - llvm::DenseSet> NonEquivalentDecls10; + StructuralEquivalenceContext::NonEquivalentDeclSet NonEquivalentDecls01; + StructuralEquivalenceContext::NonEquivalentDeclSet NonEquivalentDecls10; StructuralEquivalenceContext Ctx01( D0->getASTContext(), D1->getASTContext(), NonEquivalentDecls01, StructuralEquivalenceKind::Default, /*StrictTypeSpelling=*/false, @@ -153,8 +153,8 @@ struct StructuralEquivalenceTest : ::testing::Test { } bool testStructuralMatch(StmtWithASTContext S0, StmtWithASTContext S1) { - llvm::DenseSet> NonEquivalentDecls01; - llvm::DenseSet> NonEquivalentDecls10; + StructuralEquivalenceContext::NonEquivalentDeclSet NonEquivalentDecls01; + StructuralEquivalenceContext::NonEquivalentDeclSet NonEquivalentDecls10; StructuralEquivalenceContext Ctx01( *S0.Context, *S1.Context, NonEquivalentDecls01, StructuralEquivalenceKind::Default, false, false); @@ -1792,7 +1792,7 @@ TEST_F( EXPECT_FALSE(testStructuralMatch(t)); } struct StructuralEquivalenceCacheTest : public StructuralEquivalenceTest { - llvm::DenseSet> NonEquivalentDecls; + StructuralEquivalenceContext::NonEquivalentDeclSet NonEquivalentDecls; template std::pair @@ -1804,8 +1804,10 @@ struct StructuralEquivalenceCacheTest : public StructuralEquivalenceTest { } template - bool isInNonEqCache(std::pair D) { - return NonEquivalentDecls.count(D) > 0; + bool isInNonEqCache(std::pair D, + bool IgnoreTemplateParmDepth = false) { + return NonEquivalentDecls.count( + std::make_tuple(D.first, D.second, IgnoreTemplateParmDepth)) > 0; } }; @@ -2015,6 +2017,78 @@ TEST_F(StructuralEquivalenceCacheTest, Cycle) { findDeclPair(TU, functionDecl(hasName("x"))))); } +TEST_F(StructuralEquivalenceCacheTest, TemplateParmDepth) { + // In 'friend struct Y' ClassTemplateDecl has the TU as parent context. + // This declaration has template depth 1 (it is already inside a template). + // It has not a previous declaration and is an "undeclared" friend. + // + // Second TU has a specialization of 'struct X'. + // In this case 'friend struct Y' has the ClassTemplateSpecializationDecl as + // parent. It has template depth 0 (it is in the specialization). It has the + // first 'struct Y' declaration as previous declaration and canonical + // declaration. + // + // When these two 'friend struct Y' are compared, only the template depth is + // different. + // FIXME: Structural equivalence checks the depth only in types, not in + // TemplateParmDecl. For this reason the second 'A1' argument is needed (as a + // type) in the template to make the check fail. + auto TU = makeTuDecls( + R"( + template + struct Y; + + template + struct X { + template + friend struct Y; + }; + )", + R"( + template + struct Y; + + template + struct X { + template + friend struct Y; + }; + + X x; + )", + Lang_CXX03); + + auto *D0 = LastDeclMatcher().match( + get<0>(TU), classTemplateDecl(hasName("Y"), unless(isImplicit()))); + auto *D1 = LastDeclMatcher().match( + get<1>(TU), classTemplateDecl(hasName("Y"), unless(isImplicit()))); + ASSERT_EQ(D0->getTemplateDepth(), 1u); + ASSERT_EQ(D1->getTemplateDepth(), 0u); + + StructuralEquivalenceContext Ctx_NoIgnoreTemplateParmDepth( + get<0>(TU)->getASTContext(), get<1>(TU)->getASTContext(), + NonEquivalentDecls, StructuralEquivalenceKind::Default, false, false, + false, false); + + EXPECT_FALSE(Ctx_NoIgnoreTemplateParmDepth.IsEquivalent(D0, D1)); + + Decl *NonEqDecl0 = + D0->getCanonicalDecl()->getTemplateParameters()->getParam(1); + Decl *NonEqDecl1 = + D1->getCanonicalDecl()->getTemplateParameters()->getParam(1); + EXPECT_TRUE(isInNonEqCache(std::make_pair(NonEqDecl0, NonEqDecl1), false)); + EXPECT_FALSE(isInNonEqCache(std::make_pair(NonEqDecl0, NonEqDecl1), true)); + + StructuralEquivalenceContext Ctx_IgnoreTemplateParmDepth( + get<0>(TU)->getASTContext(), get<1>(TU)->getASTContext(), + NonEquivalentDecls, StructuralEquivalenceKind::Default, false, false, + false, true); + + EXPECT_TRUE(Ctx_IgnoreTemplateParmDepth.IsEquivalent(D0, D1)); + + EXPECT_FALSE(isInNonEqCache(std::make_pair(NonEqDecl0, NonEqDecl1), true)); +} + struct StructuralEquivalenceStmtTest : StructuralEquivalenceTest {}; /// Fallback matcher to be used only when there is no specific matcher for a