Author: martong Date: Tue Jul 23 08:46:38 2019 New Revision: 366818 URL: http://llvm.org/viewvc/llvm-project?rev=366818&view=rev Log: [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 Modified: cfe/trunk/lib/AST/ASTStructuralEquivalence.cpp cfe/trunk/unittests/AST/StructuralEquivalenceTest.cpp Modified: cfe/trunk/lib/AST/ASTStructuralEquivalence.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/ASTStructuralEquivalence.cpp?rev=366818&r1=366817&r2=366818&view=diff ============================================================================== --- cfe/trunk/lib/AST/ASTStructuralEquivalence.cpp (original) +++ cfe/trunk/lib/AST/ASTStructuralEquivalence.cpp Tue Jul 23 08:46:38 2019 @@ -235,12 +235,21 @@ static bool IsStructurallyEquivalent(Str 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(Str 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(Str 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(Str 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. Modified: cfe/trunk/unittests/AST/StructuralEquivalenceTest.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/unittests/AST/StructuralEquivalenceTest.cpp?rev=366818&r1=366817&r2=366818&view=diff ============================================================================== --- cfe/trunk/unittests/AST/StructuralEquivalenceTest.cpp (original) +++ cfe/trunk/unittests/AST/StructuralEquivalenceTest.cpp Tue Jul 23 08:46:38 2019 @@ -944,6 +944,67 @@ TEST_F(StructuralEquivalenceTemplateTest 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(StructuralEquivalenceDependentTem 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 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits