Skip to content

[clang][AST] Add 'IgnoreTemplateParmDepth' to structural equivalence cache #115518

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Nov 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion clang/include/clang/AST/ASTImporter.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,8 @@ class TypeSourceInfo;
class ASTImporter {
friend class ASTNodeImporter;
public:
using NonEquivalentDeclSet = llvm::DenseSet<std::pair<Decl *, Decl *>>;
using NonEquivalentDeclSet =
llvm::DenseSet<std::tuple<Decl *, Decl *, int>>;
Comment on lines +65 to +66
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
using NonEquivalentDeclSet =
llvm::DenseSet<std::tuple<Decl *, Decl *, int>>;
using NonEquivalentDeclSet =
std::array<llvm::DenseSet<std::pair<Decl *, Decl *>>, 2>;

If you use two DenseSets and put them into an array, you can use them without significant code changes.

using ImportedCXXBaseSpecifierMap =
llvm::DenseMap<const CXXBaseSpecifier *, CXXBaseSpecifier *>;

Expand Down
19 changes: 12 additions & 7 deletions clang/include/clang/AST/ASTStructuralEquivalence.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<std::tuple<Decl *, Decl *, int>>;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
using NonEquivalentDeclSet = llvm::DenseSet<std::tuple<Decl *, Decl *, int>>;
using NonEquivalentDeclSet =
std::array<llvm::DenseSet<std::pair<Decl *, Decl *>>, 2>;

As above.


/// AST contexts for which we are checking structural equivalence.
ASTContext &FromCtx, &ToCtx;

Expand All @@ -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<std::pair<Decl *, Decl *>> &NonEquivalentDecls;
NonEquivalentDeclSet &NonEquivalentDecls;

StructuralEquivalenceKind EqKind;

Expand All @@ -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<std::pair<Decl *, Decl *>> &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),
Expand Down
6 changes: 4 additions & 2 deletions clang/lib/AST/ASTStructuralEquivalence.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)))
Comment on lines +2306 to +2307
Copy link
Contributor

@NagyDonat NagyDonat Nov 11, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (Context.NonEquivalentDecls.count(
std::make_tuple(D1, D2, Context.IgnoreTemplateParmDepth)))
if (Context.NonEquivalentDecls[Context.IgnoreTemplateParmDepth].count(P))

You can use a boolean value for indexing to select the right element in the array.

return false;

// Check if a check for these declarations is already pending.
Expand Down Expand Up @@ -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));
Comment on lines +2515 to +2516
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
NonEquivalentDecls.insert(
std::make_tuple(D1, D2, IgnoreTemplateParmDepth));
NonEquivalentDecls[IgnoreTemplateParmDepth].insert(P);


return true;
}
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/Sema/SemaType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9043,7 +9043,7 @@ bool Sema::RequireCompleteType(SourceLocation Loc, QualType T,
}

bool Sema::hasStructuralCompatLayout(Decl *D, Decl *Suggested) {
llvm::DenseSet<std::pair<Decl *, Decl *>> NonEquivalentDecls;
StructuralEquivalenceContext::NonEquivalentDeclSet NonEquivalentDecls;
if (!Suggested)
return false;

Expand Down
2 changes: 1 addition & 1 deletion clang/lib/Serialization/ASTReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9905,7 +9905,7 @@ void ASTReader::finishPendingActions() {
auto ExtensionsPair = PendingObjCExtensionIvarRedeclarations.back().first;
auto DuplicateIvars =
PendingObjCExtensionIvarRedeclarations.back().second;
llvm::DenseSet<std::pair<Decl *, Decl *>> NonEquivalentDecls;
StructuralEquivalenceContext::NonEquivalentDeclSet NonEquivalentDecls;
StructuralEquivalenceContext Ctx(
ExtensionsPair.first->getASTContext(),
ExtensionsPair.second->getASTContext(), NonEquivalentDecls,
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/Serialization/ASTReaderDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4447,7 +4447,7 @@ namespace {
ObjCCategoryDecl *&Existing = NameCategoryMap[Cat->getDeclName()];
if (Existing && Reader.getOwningModuleFile(Existing) !=
Reader.getOwningModuleFile(Cat)) {
llvm::DenseSet<std::pair<Decl *, Decl *>> NonEquivalentDecls;
StructuralEquivalenceContext::NonEquivalentDeclSet NonEquivalentDecls;
StructuralEquivalenceContext Ctx(
Cat->getASTContext(), Existing->getASTContext(),
NonEquivalentDecls, StructuralEquivalenceKind::Default,
Expand Down
88 changes: 81 additions & 7 deletions clang/unittests/AST/StructuralEquivalenceTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -134,8 +134,8 @@ struct StructuralEquivalenceTest : ::testing::Test {

bool testStructuralMatch(Decl *D0, Decl *D1,
bool IgnoreTemplateParmDepth = false) {
llvm::DenseSet<std::pair<Decl *, Decl *>> NonEquivalentDecls01;
llvm::DenseSet<std::pair<Decl *, Decl *>> NonEquivalentDecls10;
StructuralEquivalenceContext::NonEquivalentDeclSet NonEquivalentDecls01;
StructuralEquivalenceContext::NonEquivalentDeclSet NonEquivalentDecls10;
StructuralEquivalenceContext Ctx01(
D0->getASTContext(), D1->getASTContext(), NonEquivalentDecls01,
StructuralEquivalenceKind::Default, /*StrictTypeSpelling=*/false,
Expand All @@ -153,8 +153,8 @@ struct StructuralEquivalenceTest : ::testing::Test {
}

bool testStructuralMatch(StmtWithASTContext S0, StmtWithASTContext S1) {
llvm::DenseSet<std::pair<Decl *, Decl *>> NonEquivalentDecls01;
llvm::DenseSet<std::pair<Decl *, Decl *>> NonEquivalentDecls10;
StructuralEquivalenceContext::NonEquivalentDeclSet NonEquivalentDecls01;
StructuralEquivalenceContext::NonEquivalentDeclSet NonEquivalentDecls10;
StructuralEquivalenceContext Ctx01(
*S0.Context, *S1.Context, NonEquivalentDecls01,
StructuralEquivalenceKind::Default, false, false);
Expand Down Expand Up @@ -1792,7 +1792,7 @@ TEST_F(
EXPECT_FALSE(testStructuralMatch(t));
}
struct StructuralEquivalenceCacheTest : public StructuralEquivalenceTest {
llvm::DenseSet<std::pair<Decl *, Decl *>> NonEquivalentDecls;
StructuralEquivalenceContext::NonEquivalentDeclSet NonEquivalentDecls;

template <typename NodeType, typename MatcherType>
std::pair<NodeType *, NodeType *>
Expand All @@ -1804,8 +1804,10 @@ struct StructuralEquivalenceCacheTest : public StructuralEquivalenceTest {
}

template <typename NodeType>
bool isInNonEqCache(std::pair<NodeType *, NodeType *> D) {
return NonEquivalentDecls.count(D) > 0;
bool isInNonEqCache(std::pair<NodeType *, NodeType *> D,
bool IgnoreTemplateParmDepth = false) {
return NonEquivalentDecls.count(
std::make_tuple(D.first, D.second, IgnoreTemplateParmDepth)) > 0;
Comment on lines +1809 to +1810
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return NonEquivalentDecls.count(
std::make_tuple(D.first, D.second, IgnoreTemplateParmDepth)) > 0;
return NonEquivalentDecls[IgnoreTemplateParmDepth].count(D) > 0;

}
};

Expand Down Expand Up @@ -2015,6 +2017,78 @@ TEST_F(StructuralEquivalenceCacheTest, Cycle) {
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 {};

/// Fallback matcher to be used only when there is no specific matcher for a
Expand Down
Loading