Skip to content

Commit

Permalink
[ASTImporter] Fix inequivalence of ClassTemplateInstantiations
Browse files Browse the repository at this point in the history
Summary:
We falsely state inequivalence if the template parameter is a
qualified/nonquialified template in the first/second instantiation.
Also, different kinds of TemplateName should be equal if the template
decl (if available) is equal (even if the name kind is different).

Reviewers: a_sidorin, a.sidorin

Subscribers: rnkovacs, dkrupp, Szelethus, gamesh411, cfe-commits

Tags: #clang

Differential Revision: https://reviews.llvm.org/D64241

llvm-svn: 366818
  • Loading branch information
Gabor Marton committed Jul 23, 2019
1 parent 0e8359a commit 123f6ff
Show file tree
Hide file tree
Showing 2 changed files with 214 additions and 22 deletions.
44 changes: 22 additions & 22 deletions clang/lib/AST/ASTStructuralEquivalence.cpp
Expand Up @@ -235,12 +235,21 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
const TemplateName &N1,
const TemplateName &N2) {
if (N1.getKind() != N2.getKind())
TemplateDecl *TemplateDeclN1 = N1.getAsTemplateDecl();
TemplateDecl *TemplateDeclN2 = N2.getAsTemplateDecl();
if (TemplateDeclN1 && TemplateDeclN2) {
if (!IsStructurallyEquivalent(Context, TemplateDeclN1, TemplateDeclN2))
return false;
// If the kind is different we compare only the template decl.
if (N1.getKind() != N2.getKind())
return true;
} else if (TemplateDeclN1 || TemplateDeclN2)
return false;
else if (N1.getKind() != N2.getKind())
return false;

// Check for special case incompatibilities.
switch (N1.getKind()) {
case TemplateName::Template:
return IsStructurallyEquivalent(Context, N1.getAsTemplateDecl(),
N2.getAsTemplateDecl());

case TemplateName::OverloadedTemplate: {
OverloadedTemplateStorage *OS1 = N1.getAsOverloadedTemplate(),
Expand All @@ -259,14 +268,6 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
return TN1->getDeclName() == TN2->getDeclName();
}

case TemplateName::QualifiedTemplate: {
QualifiedTemplateName *QN1 = N1.getAsQualifiedTemplateName(),
*QN2 = N2.getAsQualifiedTemplateName();
return IsStructurallyEquivalent(Context, QN1->getDecl(), QN2->getDecl()) &&
IsStructurallyEquivalent(Context, QN1->getQualifier(),
QN2->getQualifier());
}

case TemplateName::DependentTemplate: {
DependentTemplateName *DN1 = N1.getAsDependentTemplateName(),
*DN2 = N2.getAsDependentTemplateName();
Expand All @@ -281,15 +282,6 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
return false;
}

case TemplateName::SubstTemplateTemplateParm: {
SubstTemplateTemplateParmStorage *TS1 = N1.getAsSubstTemplateTemplateParm(),
*TS2 = N2.getAsSubstTemplateTemplateParm();
return IsStructurallyEquivalent(Context, TS1->getParameter(),
TS2->getParameter()) &&
IsStructurallyEquivalent(Context, TS1->getReplacement(),
TS2->getReplacement());
}

case TemplateName::SubstTemplateTemplateParmPack: {
SubstTemplateTemplateParmPackStorage
*P1 = N1.getAsSubstTemplateTemplateParmPack(),
Expand All @@ -299,8 +291,16 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
IsStructurallyEquivalent(Context, P1->getParameterPack(),
P2->getParameterPack());
}

case TemplateName::Template:
case TemplateName::QualifiedTemplate:
case TemplateName::SubstTemplateTemplateParm:
// It is sufficient to check value of getAsTemplateDecl.
break;

}
return false;

return true;
}

/// Determine whether two template arguments are equivalent.
Expand Down
192 changes: 192 additions & 0 deletions clang/unittests/AST/StructuralEquivalenceTest.cpp
Expand Up @@ -944,6 +944,67 @@ TEST_F(StructuralEquivalenceTemplateTest, ExplicitBoolDifference) {
EXPECT_FALSE(testStructuralMatch(First, Second));
}

TEST_F(StructuralEquivalenceTemplateTest,
TemplateVsSubstTemplateTemplateParmInArgEq) {
auto t = makeDecls<ClassTemplateSpecializationDecl>(
R"(
template <typename P1> class Arg { };
template <template <typename PP1> class P1> class Primary { };
void f() {
// Make specialization with simple template.
Primary <Arg> A;
}
)",
R"(
template <typename P1> class Arg { };
template <template <typename PP1> class P1> class Primary { };
template <template <typename PP1> class P1> class Templ {
void f() {
// Make specialization with substituted template template param.
Primary <P1> A;
};
};
// Instantiate with substitution Arg into P1.
template class Templ <Arg>;
)",
Lang_CXX, classTemplateSpecializationDecl(hasName("Primary")));
EXPECT_TRUE(testStructuralMatch(t));
}

TEST_F(StructuralEquivalenceTemplateTest,
TemplateVsSubstTemplateTemplateParmInArgNotEq) {
auto t = makeDecls<ClassTemplateSpecializationDecl>(
R"(
template <typename P1> class Arg { };
template <template <typename PP1> class P1> class Primary { };
void f() {
// Make specialization with simple template.
Primary <Arg> A;
}
)",
R"(
// Arg is different from the other, this should cause non-equivalence.
template <typename P1> class Arg { int X; };
template <template <typename PP1> class P1> class Primary { };
template <template <typename PP1> class P1> class Templ {
void f() {
// Make specialization with substituted template template param.
Primary <P1> A;
};
};
// Instantiate with substitution Arg into P1.
template class Templ <Arg>;
)",
Lang_CXX, classTemplateSpecializationDecl(hasName("Primary")));
EXPECT_FALSE(testStructuralMatch(t));
}

struct StructuralEquivalenceDependentTemplateArgsTest
: StructuralEquivalenceTemplateTest {};

Expand Down Expand Up @@ -1082,5 +1143,136 @@ TEST_F(StructuralEquivalenceDependentTemplateArgsTest,
EXPECT_FALSE(testStructuralMatch(t));
}

TEST_F(
StructuralEquivalenceTemplateTest,
ClassTemplSpecWithQualifiedAndNonQualifiedTypeArgsShouldBeEqual) {
auto t = makeDecls<ClassTemplateSpecializationDecl>(
R"(
template <class T> struct Primary {};
namespace N {
struct Arg;
}
// Explicit instantiation with qualified name.
template struct Primary<N::Arg>;
)",
R"(
template <class T> struct Primary {};
namespace N {
struct Arg;
}
using namespace N;
// Explicit instantiation with UNqualified name.
template struct Primary<Arg>;
)",
Lang_CXX,
classTemplateSpecializationDecl(hasName("Primary")));
EXPECT_TRUE(testStructuralMatch(t));
}

TEST_F(
StructuralEquivalenceTemplateTest,
ClassTemplSpecWithInequivalentQualifiedAndNonQualifiedTypeArgs) {
auto t = makeDecls<ClassTemplateSpecializationDecl>(
R"(
template <class T> struct Primary {};
namespace N {
struct Arg { int a; };
}
// Explicit instantiation with qualified name.
template struct Primary<N::Arg>;
)",
R"(
template <class T> struct Primary {};
namespace N {
// This struct is not equivalent with the other in the prev TU.
struct Arg { double b; }; // -- Field mismatch.
}
using namespace N;
// Explicit instantiation with UNqualified name.
template struct Primary<Arg>;
)",
Lang_CXX,
classTemplateSpecializationDecl(hasName("Primary")));
EXPECT_FALSE(testStructuralMatch(t));
}

TEST_F(
StructuralEquivalenceTemplateTest,
ClassTemplSpecWithQualifiedAndNonQualifiedTemplArgsShouldBeEqual) {
auto t = makeDecls<ClassTemplateSpecializationDecl>(
R"(
template <template <class> class T> struct Primary {};
namespace N {
template <class T> struct Arg;
}
// Explicit instantiation with qualified name.
template struct Primary<N::Arg>;
)",
R"(
template <template <class> class T> struct Primary {};
namespace N {
template <class T> struct Arg;
}
using namespace N;
// Explicit instantiation with UNqualified name.
template struct Primary<Arg>;
)",
Lang_CXX,
classTemplateSpecializationDecl(hasName("Primary")));
EXPECT_TRUE(testStructuralMatch(t));
}

TEST_F(
StructuralEquivalenceTemplateTest,
ClassTemplSpecWithInequivalentQualifiedAndNonQualifiedTemplArgs) {
auto t = makeDecls<ClassTemplateSpecializationDecl>(
R"(
template <template <class> class T> struct Primary {};
namespace N {
template <class T> struct Arg { int a; };
}
// Explicit instantiation with qualified name.
template struct Primary<N::Arg>;
)",
R"(
template <template <class> class T> struct Primary {};
namespace N {
// This template is not equivalent with the other in the prev TU.
template <class T> struct Arg { double b; }; // -- Field mismatch.
}
using namespace N;
// Explicit instantiation with UNqualified name.
template struct Primary<Arg>;
)",
Lang_CXX,
classTemplateSpecializationDecl(hasName("Primary")));
EXPECT_FALSE(testStructuralMatch(t));
}

TEST_F(
StructuralEquivalenceTemplateTest,
ClassTemplSpecWithInequivalentShadowedTemplArg) {
auto t = makeDecls<ClassTemplateSpecializationDecl>(
R"(
template <template <class> class T> struct Primary {};
template <class T> struct Arg { int a; };
// Explicit instantiation with ::Arg
template struct Primary<Arg>;
)",
R"(
template <template <class> class T> struct Primary {};
template <class T> struct Arg { int a; };
namespace N {
// This template is not equivalent with the other in the global scope.
template <class T> struct Arg { double b; }; // -- Field mismatch.
// Explicit instantiation with N::Arg which shadows ::Arg
template struct Primary<Arg>;
}
)",
Lang_CXX,
classTemplateSpecializationDecl(hasName("Primary")));
EXPECT_FALSE(testStructuralMatch(t));
}

} // end namespace ast_matchers
} // end namespace clang

0 comments on commit 123f6ff

Please sign in to comment.