[ASTImporter] Fix inequivalence of ClassTemplateInstantiations
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
This commit is contained in:
parent
0e8359aec1
commit
123f6ff299
|
@ -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(),
|
||||
|
@ -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();
|
||||
|
@ -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(),
|
||||
|
@ -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.
|
||||
|
|
|
@ -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 {};
|
||||
|
||||
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue