[clang][AST] Add 'IgnoreTemplateParmDepth' to structural equivalence cache (#115518)
Structural equivalence check uses a cache to store already found non-equivalent values. This cache can be reused for calls (ASTImporter does this). Value of "IgnoreTemplateParmDepth" can have an effect on the structural equivalence therefore it is wrong to reuse the same cache for checks with different values of 'IgnoreTemplateParmDepth'. The current change adds the 'IgnoreTemplateParmDepth' to the cache key to fix the problem.
This commit is contained in:
parent
5a12881514
commit
7a1fdbb9c0
@ -62,7 +62,8 @@ class TypeSourceInfo;
|
|||||||
class ASTImporter {
|
class ASTImporter {
|
||||||
friend class ASTNodeImporter;
|
friend class ASTNodeImporter;
|
||||||
public:
|
public:
|
||||||
using NonEquivalentDeclSet = llvm::DenseSet<std::pair<Decl *, Decl *>>;
|
using NonEquivalentDeclSet =
|
||||||
|
llvm::DenseSet<std::tuple<Decl *, Decl *, int>>;
|
||||||
using ImportedCXXBaseSpecifierMap =
|
using ImportedCXXBaseSpecifierMap =
|
||||||
llvm::DenseMap<const CXXBaseSpecifier *, CXXBaseSpecifier *>;
|
llvm::DenseMap<const CXXBaseSpecifier *, CXXBaseSpecifier *>;
|
||||||
|
|
||||||
|
@ -39,6 +39,10 @@ enum class StructuralEquivalenceKind {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct StructuralEquivalenceContext {
|
struct StructuralEquivalenceContext {
|
||||||
|
/// Store declaration pairs already found to be non-equivalent.
|
||||||
|
/// key: (from, to, IgnoreTemplateParmDepth)
|
||||||
|
using NonEquivalentDeclSet = llvm::DenseSet<std::tuple<Decl *, Decl *, int>>;
|
||||||
|
|
||||||
/// AST contexts for which we are checking structural equivalence.
|
/// AST contexts for which we are checking structural equivalence.
|
||||||
ASTContext &FromCtx, &ToCtx;
|
ASTContext &FromCtx, &ToCtx;
|
||||||
|
|
||||||
@ -52,7 +56,7 @@ struct StructuralEquivalenceContext {
|
|||||||
|
|
||||||
/// Declaration (from, to) pairs that are known not to be equivalent
|
/// Declaration (from, to) pairs that are known not to be equivalent
|
||||||
/// (which we have already complained about).
|
/// (which we have already complained about).
|
||||||
llvm::DenseSet<std::pair<Decl *, Decl *>> &NonEquivalentDecls;
|
NonEquivalentDeclSet &NonEquivalentDecls;
|
||||||
|
|
||||||
StructuralEquivalenceKind EqKind;
|
StructuralEquivalenceKind EqKind;
|
||||||
|
|
||||||
@ -72,12 +76,13 @@ struct StructuralEquivalenceContext {
|
|||||||
/// Whether to ignore comparing the depth of template param(TemplateTypeParm)
|
/// Whether to ignore comparing the depth of template param(TemplateTypeParm)
|
||||||
bool IgnoreTemplateParmDepth;
|
bool IgnoreTemplateParmDepth;
|
||||||
|
|
||||||
StructuralEquivalenceContext(
|
StructuralEquivalenceContext(ASTContext &FromCtx, ASTContext &ToCtx,
|
||||||
ASTContext &FromCtx, ASTContext &ToCtx,
|
NonEquivalentDeclSet &NonEquivalentDecls,
|
||||||
llvm::DenseSet<std::pair<Decl *, Decl *>> &NonEquivalentDecls,
|
StructuralEquivalenceKind EqKind,
|
||||||
StructuralEquivalenceKind EqKind, bool StrictTypeSpelling = false,
|
bool StrictTypeSpelling = false,
|
||||||
bool Complain = true, bool ErrorOnTagTypeMismatch = false,
|
bool Complain = true,
|
||||||
bool IgnoreTemplateParmDepth = false)
|
bool ErrorOnTagTypeMismatch = false,
|
||||||
|
bool IgnoreTemplateParmDepth = false)
|
||||||
: FromCtx(FromCtx), ToCtx(ToCtx), NonEquivalentDecls(NonEquivalentDecls),
|
: FromCtx(FromCtx), ToCtx(ToCtx), NonEquivalentDecls(NonEquivalentDecls),
|
||||||
EqKind(EqKind), StrictTypeSpelling(StrictTypeSpelling),
|
EqKind(EqKind), StrictTypeSpelling(StrictTypeSpelling),
|
||||||
ErrorOnTagTypeMismatch(ErrorOnTagTypeMismatch), Complain(Complain),
|
ErrorOnTagTypeMismatch(ErrorOnTagTypeMismatch), Complain(Complain),
|
||||||
|
@ -2303,7 +2303,8 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
|
|||||||
|
|
||||||
// Check whether we already know that these two declarations are not
|
// Check whether we already know that these two declarations are not
|
||||||
// structurally equivalent.
|
// structurally equivalent.
|
||||||
if (Context.NonEquivalentDecls.count(P))
|
if (Context.NonEquivalentDecls.count(
|
||||||
|
std::make_tuple(D1, D2, Context.IgnoreTemplateParmDepth)))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Check if a check for these declarations is already pending.
|
// Check if a check for these declarations is already pending.
|
||||||
@ -2511,7 +2512,8 @@ bool StructuralEquivalenceContext::Finish() {
|
|||||||
if (!Equivalent) {
|
if (!Equivalent) {
|
||||||
// Note that these two declarations are not equivalent (and we already
|
// Note that these two declarations are not equivalent (and we already
|
||||||
// know about it).
|
// know about it).
|
||||||
NonEquivalentDecls.insert(P);
|
NonEquivalentDecls.insert(
|
||||||
|
std::make_tuple(D1, D2, IgnoreTemplateParmDepth));
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -9046,7 +9046,7 @@ bool Sema::RequireCompleteType(SourceLocation Loc, QualType T,
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool Sema::hasStructuralCompatLayout(Decl *D, Decl *Suggested) {
|
bool Sema::hasStructuralCompatLayout(Decl *D, Decl *Suggested) {
|
||||||
llvm::DenseSet<std::pair<Decl *, Decl *>> NonEquivalentDecls;
|
StructuralEquivalenceContext::NonEquivalentDeclSet NonEquivalentDecls;
|
||||||
if (!Suggested)
|
if (!Suggested)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
@ -9907,7 +9907,7 @@ void ASTReader::finishPendingActions() {
|
|||||||
auto ExtensionsPair = PendingObjCExtensionIvarRedeclarations.back().first;
|
auto ExtensionsPair = PendingObjCExtensionIvarRedeclarations.back().first;
|
||||||
auto DuplicateIvars =
|
auto DuplicateIvars =
|
||||||
PendingObjCExtensionIvarRedeclarations.back().second;
|
PendingObjCExtensionIvarRedeclarations.back().second;
|
||||||
llvm::DenseSet<std::pair<Decl *, Decl *>> NonEquivalentDecls;
|
StructuralEquivalenceContext::NonEquivalentDeclSet NonEquivalentDecls;
|
||||||
StructuralEquivalenceContext Ctx(
|
StructuralEquivalenceContext Ctx(
|
||||||
ExtensionsPair.first->getASTContext(),
|
ExtensionsPair.first->getASTContext(),
|
||||||
ExtensionsPair.second->getASTContext(), NonEquivalentDecls,
|
ExtensionsPair.second->getASTContext(), NonEquivalentDecls,
|
||||||
|
@ -4447,7 +4447,7 @@ namespace {
|
|||||||
ObjCCategoryDecl *&Existing = NameCategoryMap[Cat->getDeclName()];
|
ObjCCategoryDecl *&Existing = NameCategoryMap[Cat->getDeclName()];
|
||||||
if (Existing && Reader.getOwningModuleFile(Existing) !=
|
if (Existing && Reader.getOwningModuleFile(Existing) !=
|
||||||
Reader.getOwningModuleFile(Cat)) {
|
Reader.getOwningModuleFile(Cat)) {
|
||||||
llvm::DenseSet<std::pair<Decl *, Decl *>> NonEquivalentDecls;
|
StructuralEquivalenceContext::NonEquivalentDeclSet NonEquivalentDecls;
|
||||||
StructuralEquivalenceContext Ctx(
|
StructuralEquivalenceContext Ctx(
|
||||||
Cat->getASTContext(), Existing->getASTContext(),
|
Cat->getASTContext(), Existing->getASTContext(),
|
||||||
NonEquivalentDecls, StructuralEquivalenceKind::Default,
|
NonEquivalentDecls, StructuralEquivalenceKind::Default,
|
||||||
|
@ -134,8 +134,8 @@ struct StructuralEquivalenceTest : ::testing::Test {
|
|||||||
|
|
||||||
bool testStructuralMatch(Decl *D0, Decl *D1,
|
bool testStructuralMatch(Decl *D0, Decl *D1,
|
||||||
bool IgnoreTemplateParmDepth = false) {
|
bool IgnoreTemplateParmDepth = false) {
|
||||||
llvm::DenseSet<std::pair<Decl *, Decl *>> NonEquivalentDecls01;
|
StructuralEquivalenceContext::NonEquivalentDeclSet NonEquivalentDecls01;
|
||||||
llvm::DenseSet<std::pair<Decl *, Decl *>> NonEquivalentDecls10;
|
StructuralEquivalenceContext::NonEquivalentDeclSet NonEquivalentDecls10;
|
||||||
StructuralEquivalenceContext Ctx01(
|
StructuralEquivalenceContext Ctx01(
|
||||||
D0->getASTContext(), D1->getASTContext(), NonEquivalentDecls01,
|
D0->getASTContext(), D1->getASTContext(), NonEquivalentDecls01,
|
||||||
StructuralEquivalenceKind::Default, /*StrictTypeSpelling=*/false,
|
StructuralEquivalenceKind::Default, /*StrictTypeSpelling=*/false,
|
||||||
@ -153,8 +153,8 @@ struct StructuralEquivalenceTest : ::testing::Test {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool testStructuralMatch(StmtWithASTContext S0, StmtWithASTContext S1) {
|
bool testStructuralMatch(StmtWithASTContext S0, StmtWithASTContext S1) {
|
||||||
llvm::DenseSet<std::pair<Decl *, Decl *>> NonEquivalentDecls01;
|
StructuralEquivalenceContext::NonEquivalentDeclSet NonEquivalentDecls01;
|
||||||
llvm::DenseSet<std::pair<Decl *, Decl *>> NonEquivalentDecls10;
|
StructuralEquivalenceContext::NonEquivalentDeclSet NonEquivalentDecls10;
|
||||||
StructuralEquivalenceContext Ctx01(
|
StructuralEquivalenceContext Ctx01(
|
||||||
*S0.Context, *S1.Context, NonEquivalentDecls01,
|
*S0.Context, *S1.Context, NonEquivalentDecls01,
|
||||||
StructuralEquivalenceKind::Default, false, false);
|
StructuralEquivalenceKind::Default, false, false);
|
||||||
@ -1792,7 +1792,7 @@ TEST_F(
|
|||||||
EXPECT_FALSE(testStructuralMatch(t));
|
EXPECT_FALSE(testStructuralMatch(t));
|
||||||
}
|
}
|
||||||
struct StructuralEquivalenceCacheTest : public StructuralEquivalenceTest {
|
struct StructuralEquivalenceCacheTest : public StructuralEquivalenceTest {
|
||||||
llvm::DenseSet<std::pair<Decl *, Decl *>> NonEquivalentDecls;
|
StructuralEquivalenceContext::NonEquivalentDeclSet NonEquivalentDecls;
|
||||||
|
|
||||||
template <typename NodeType, typename MatcherType>
|
template <typename NodeType, typename MatcherType>
|
||||||
std::pair<NodeType *, NodeType *>
|
std::pair<NodeType *, NodeType *>
|
||||||
@ -1804,8 +1804,10 @@ struct StructuralEquivalenceCacheTest : public StructuralEquivalenceTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename NodeType>
|
template <typename NodeType>
|
||||||
bool isInNonEqCache(std::pair<NodeType *, NodeType *> D) {
|
bool isInNonEqCache(std::pair<NodeType *, NodeType *> D,
|
||||||
return NonEquivalentDecls.count(D) > 0;
|
bool IgnoreTemplateParmDepth = false) {
|
||||||
|
return NonEquivalentDecls.count(
|
||||||
|
std::make_tuple(D.first, D.second, IgnoreTemplateParmDepth)) > 0;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -2015,6 +2017,78 @@ TEST_F(StructuralEquivalenceCacheTest, Cycle) {
|
|||||||
findDeclPair<FunctionDecl>(TU, functionDecl(hasName("x")))));
|
findDeclPair<FunctionDecl>(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 <class A1, A1>
|
||||||
|
struct Y;
|
||||||
|
|
||||||
|
template <class A>
|
||||||
|
struct X {
|
||||||
|
template <class A1, A1>
|
||||||
|
friend struct Y;
|
||||||
|
};
|
||||||
|
)",
|
||||||
|
R"(
|
||||||
|
template <class A1, A1>
|
||||||
|
struct Y;
|
||||||
|
|
||||||
|
template <class A>
|
||||||
|
struct X {
|
||||||
|
template <class A1, A1>
|
||||||
|
friend struct Y;
|
||||||
|
};
|
||||||
|
|
||||||
|
X<int> x;
|
||||||
|
)",
|
||||||
|
Lang_CXX03);
|
||||||
|
|
||||||
|
auto *D0 = LastDeclMatcher<ClassTemplateDecl>().match(
|
||||||
|
get<0>(TU), classTemplateDecl(hasName("Y"), unless(isImplicit())));
|
||||||
|
auto *D1 = LastDeclMatcher<ClassTemplateDecl>().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 {};
|
struct StructuralEquivalenceStmtTest : StructuralEquivalenceTest {};
|
||||||
|
|
||||||
/// Fallback matcher to be used only when there is no specific matcher for a
|
/// Fallback matcher to be used only when there is no specific matcher for a
|
||||||
|
Loading…
x
Reference in New Issue
Block a user