Skip to content

Commit 7a1fdbb

Browse files
authored
[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.
1 parent 5a12881 commit 7a1fdbb

File tree

7 files changed

+102
-20
lines changed

7 files changed

+102
-20
lines changed

clang/include/clang/AST/ASTImporter.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,8 @@ class TypeSourceInfo;
6262
class ASTImporter {
6363
friend class ASTNodeImporter;
6464
public:
65-
using NonEquivalentDeclSet = llvm::DenseSet<std::pair<Decl *, Decl *>>;
65+
using NonEquivalentDeclSet =
66+
llvm::DenseSet<std::tuple<Decl *, Decl *, int>>;
6667
using ImportedCXXBaseSpecifierMap =
6768
llvm::DenseMap<const CXXBaseSpecifier *, CXXBaseSpecifier *>;
6869

clang/include/clang/AST/ASTStructuralEquivalence.h

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,10 @@ enum class StructuralEquivalenceKind {
3939
};
4040

4141
struct StructuralEquivalenceContext {
42+
/// Store declaration pairs already found to be non-equivalent.
43+
/// key: (from, to, IgnoreTemplateParmDepth)
44+
using NonEquivalentDeclSet = llvm::DenseSet<std::tuple<Decl *, Decl *, int>>;
45+
4246
/// AST contexts for which we are checking structural equivalence.
4347
ASTContext &FromCtx, &ToCtx;
4448

@@ -52,7 +56,7 @@ struct StructuralEquivalenceContext {
5256

5357
/// Declaration (from, to) pairs that are known not to be equivalent
5458
/// (which we have already complained about).
55-
llvm::DenseSet<std::pair<Decl *, Decl *>> &NonEquivalentDecls;
59+
NonEquivalentDeclSet &NonEquivalentDecls;
5660

5761
StructuralEquivalenceKind EqKind;
5862

@@ -72,12 +76,13 @@ struct StructuralEquivalenceContext {
7276
/// Whether to ignore comparing the depth of template param(TemplateTypeParm)
7377
bool IgnoreTemplateParmDepth;
7478

75-
StructuralEquivalenceContext(
76-
ASTContext &FromCtx, ASTContext &ToCtx,
77-
llvm::DenseSet<std::pair<Decl *, Decl *>> &NonEquivalentDecls,
78-
StructuralEquivalenceKind EqKind, bool StrictTypeSpelling = false,
79-
bool Complain = true, bool ErrorOnTagTypeMismatch = false,
80-
bool IgnoreTemplateParmDepth = false)
79+
StructuralEquivalenceContext(ASTContext &FromCtx, ASTContext &ToCtx,
80+
NonEquivalentDeclSet &NonEquivalentDecls,
81+
StructuralEquivalenceKind EqKind,
82+
bool StrictTypeSpelling = false,
83+
bool Complain = true,
84+
bool ErrorOnTagTypeMismatch = false,
85+
bool IgnoreTemplateParmDepth = false)
8186
: FromCtx(FromCtx), ToCtx(ToCtx), NonEquivalentDecls(NonEquivalentDecls),
8287
EqKind(EqKind), StrictTypeSpelling(StrictTypeSpelling),
8388
ErrorOnTagTypeMismatch(ErrorOnTagTypeMismatch), Complain(Complain),

clang/lib/AST/ASTStructuralEquivalence.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2303,7 +2303,8 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
23032303

23042304
// Check whether we already know that these two declarations are not
23052305
// structurally equivalent.
2306-
if (Context.NonEquivalentDecls.count(P))
2306+
if (Context.NonEquivalentDecls.count(
2307+
std::make_tuple(D1, D2, Context.IgnoreTemplateParmDepth)))
23072308
return false;
23082309

23092310
// Check if a check for these declarations is already pending.
@@ -2511,7 +2512,8 @@ bool StructuralEquivalenceContext::Finish() {
25112512
if (!Equivalent) {
25122513
// Note that these two declarations are not equivalent (and we already
25132514
// know about it).
2514-
NonEquivalentDecls.insert(P);
2515+
NonEquivalentDecls.insert(
2516+
std::make_tuple(D1, D2, IgnoreTemplateParmDepth));
25152517

25162518
return true;
25172519
}

clang/lib/Sema/SemaType.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9046,7 +9046,7 @@ bool Sema::RequireCompleteType(SourceLocation Loc, QualType T,
90469046
}
90479047

90489048
bool Sema::hasStructuralCompatLayout(Decl *D, Decl *Suggested) {
9049-
llvm::DenseSet<std::pair<Decl *, Decl *>> NonEquivalentDecls;
9049+
StructuralEquivalenceContext::NonEquivalentDeclSet NonEquivalentDecls;
90509050
if (!Suggested)
90519051
return false;
90529052

clang/lib/Serialization/ASTReader.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9907,7 +9907,7 @@ void ASTReader::finishPendingActions() {
99079907
auto ExtensionsPair = PendingObjCExtensionIvarRedeclarations.back().first;
99089908
auto DuplicateIvars =
99099909
PendingObjCExtensionIvarRedeclarations.back().second;
9910-
llvm::DenseSet<std::pair<Decl *, Decl *>> NonEquivalentDecls;
9910+
StructuralEquivalenceContext::NonEquivalentDeclSet NonEquivalentDecls;
99119911
StructuralEquivalenceContext Ctx(
99129912
ExtensionsPair.first->getASTContext(),
99139913
ExtensionsPair.second->getASTContext(), NonEquivalentDecls,

clang/lib/Serialization/ASTReaderDecl.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4447,7 +4447,7 @@ namespace {
44474447
ObjCCategoryDecl *&Existing = NameCategoryMap[Cat->getDeclName()];
44484448
if (Existing && Reader.getOwningModuleFile(Existing) !=
44494449
Reader.getOwningModuleFile(Cat)) {
4450-
llvm::DenseSet<std::pair<Decl *, Decl *>> NonEquivalentDecls;
4450+
StructuralEquivalenceContext::NonEquivalentDeclSet NonEquivalentDecls;
44514451
StructuralEquivalenceContext Ctx(
44524452
Cat->getASTContext(), Existing->getASTContext(),
44534453
NonEquivalentDecls, StructuralEquivalenceKind::Default,

clang/unittests/AST/StructuralEquivalenceTest.cpp

Lines changed: 81 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -134,8 +134,8 @@ struct StructuralEquivalenceTest : ::testing::Test {
134134

135135
bool testStructuralMatch(Decl *D0, Decl *D1,
136136
bool IgnoreTemplateParmDepth = false) {
137-
llvm::DenseSet<std::pair<Decl *, Decl *>> NonEquivalentDecls01;
138-
llvm::DenseSet<std::pair<Decl *, Decl *>> NonEquivalentDecls10;
137+
StructuralEquivalenceContext::NonEquivalentDeclSet NonEquivalentDecls01;
138+
StructuralEquivalenceContext::NonEquivalentDeclSet NonEquivalentDecls10;
139139
StructuralEquivalenceContext Ctx01(
140140
D0->getASTContext(), D1->getASTContext(), NonEquivalentDecls01,
141141
StructuralEquivalenceKind::Default, /*StrictTypeSpelling=*/false,
@@ -153,8 +153,8 @@ struct StructuralEquivalenceTest : ::testing::Test {
153153
}
154154

155155
bool testStructuralMatch(StmtWithASTContext S0, StmtWithASTContext S1) {
156-
llvm::DenseSet<std::pair<Decl *, Decl *>> NonEquivalentDecls01;
157-
llvm::DenseSet<std::pair<Decl *, Decl *>> NonEquivalentDecls10;
156+
StructuralEquivalenceContext::NonEquivalentDeclSet NonEquivalentDecls01;
157+
StructuralEquivalenceContext::NonEquivalentDeclSet NonEquivalentDecls10;
158158
StructuralEquivalenceContext Ctx01(
159159
*S0.Context, *S1.Context, NonEquivalentDecls01,
160160
StructuralEquivalenceKind::Default, false, false);
@@ -1792,7 +1792,7 @@ TEST_F(
17921792
EXPECT_FALSE(testStructuralMatch(t));
17931793
}
17941794
struct StructuralEquivalenceCacheTest : public StructuralEquivalenceTest {
1795-
llvm::DenseSet<std::pair<Decl *, Decl *>> NonEquivalentDecls;
1795+
StructuralEquivalenceContext::NonEquivalentDeclSet NonEquivalentDecls;
17961796

17971797
template <typename NodeType, typename MatcherType>
17981798
std::pair<NodeType *, NodeType *>
@@ -1804,8 +1804,10 @@ struct StructuralEquivalenceCacheTest : public StructuralEquivalenceTest {
18041804
}
18051805

18061806
template <typename NodeType>
1807-
bool isInNonEqCache(std::pair<NodeType *, NodeType *> D) {
1808-
return NonEquivalentDecls.count(D) > 0;
1807+
bool isInNonEqCache(std::pair<NodeType *, NodeType *> D,
1808+
bool IgnoreTemplateParmDepth = false) {
1809+
return NonEquivalentDecls.count(
1810+
std::make_tuple(D.first, D.second, IgnoreTemplateParmDepth)) > 0;
18091811
}
18101812
};
18111813

@@ -2015,6 +2017,78 @@ TEST_F(StructuralEquivalenceCacheTest, Cycle) {
20152017
findDeclPair<FunctionDecl>(TU, functionDecl(hasName("x")))));
20162018
}
20172019

2020+
TEST_F(StructuralEquivalenceCacheTest, TemplateParmDepth) {
2021+
// In 'friend struct Y' ClassTemplateDecl has the TU as parent context.
2022+
// This declaration has template depth 1 (it is already inside a template).
2023+
// It has not a previous declaration and is an "undeclared" friend.
2024+
//
2025+
// Second TU has a specialization of 'struct X'.
2026+
// In this case 'friend struct Y' has the ClassTemplateSpecializationDecl as
2027+
// parent. It has template depth 0 (it is in the specialization). It has the
2028+
// first 'struct Y' declaration as previous declaration and canonical
2029+
// declaration.
2030+
//
2031+
// When these two 'friend struct Y' are compared, only the template depth is
2032+
// different.
2033+
// FIXME: Structural equivalence checks the depth only in types, not in
2034+
// TemplateParmDecl. For this reason the second 'A1' argument is needed (as a
2035+
// type) in the template to make the check fail.
2036+
auto TU = makeTuDecls(
2037+
R"(
2038+
template <class A1, A1>
2039+
struct Y;
2040+
2041+
template <class A>
2042+
struct X {
2043+
template <class A1, A1>
2044+
friend struct Y;
2045+
};
2046+
)",
2047+
R"(
2048+
template <class A1, A1>
2049+
struct Y;
2050+
2051+
template <class A>
2052+
struct X {
2053+
template <class A1, A1>
2054+
friend struct Y;
2055+
};
2056+
2057+
X<int> x;
2058+
)",
2059+
Lang_CXX03);
2060+
2061+
auto *D0 = LastDeclMatcher<ClassTemplateDecl>().match(
2062+
get<0>(TU), classTemplateDecl(hasName("Y"), unless(isImplicit())));
2063+
auto *D1 = LastDeclMatcher<ClassTemplateDecl>().match(
2064+
get<1>(TU), classTemplateDecl(hasName("Y"), unless(isImplicit())));
2065+
ASSERT_EQ(D0->getTemplateDepth(), 1u);
2066+
ASSERT_EQ(D1->getTemplateDepth(), 0u);
2067+
2068+
StructuralEquivalenceContext Ctx_NoIgnoreTemplateParmDepth(
2069+
get<0>(TU)->getASTContext(), get<1>(TU)->getASTContext(),
2070+
NonEquivalentDecls, StructuralEquivalenceKind::Default, false, false,
2071+
false, false);
2072+
2073+
EXPECT_FALSE(Ctx_NoIgnoreTemplateParmDepth.IsEquivalent(D0, D1));
2074+
2075+
Decl *NonEqDecl0 =
2076+
D0->getCanonicalDecl()->getTemplateParameters()->getParam(1);
2077+
Decl *NonEqDecl1 =
2078+
D1->getCanonicalDecl()->getTemplateParameters()->getParam(1);
2079+
EXPECT_TRUE(isInNonEqCache(std::make_pair(NonEqDecl0, NonEqDecl1), false));
2080+
EXPECT_FALSE(isInNonEqCache(std::make_pair(NonEqDecl0, NonEqDecl1), true));
2081+
2082+
StructuralEquivalenceContext Ctx_IgnoreTemplateParmDepth(
2083+
get<0>(TU)->getASTContext(), get<1>(TU)->getASTContext(),
2084+
NonEquivalentDecls, StructuralEquivalenceKind::Default, false, false,
2085+
false, true);
2086+
2087+
EXPECT_TRUE(Ctx_IgnoreTemplateParmDepth.IsEquivalent(D0, D1));
2088+
2089+
EXPECT_FALSE(isInNonEqCache(std::make_pair(NonEqDecl0, NonEqDecl1), true));
2090+
}
2091+
20182092
struct StructuralEquivalenceStmtTest : StructuralEquivalenceTest {};
20192093

20202094
/// Fallback matcher to be used only when there is no specific matcher for a

0 commit comments

Comments
 (0)