Skip to content
This repository was archived by the owner on Nov 1, 2021. It is now read-only.

Commit b625445

Browse files
committed
Check that template template arguments match template template parameters
properly even when a non-type template parameter has a dependent type. Previously, if a non-type template parameter was dependent, but not dependent on an outer level of template parameter, we would not match the type of the parameter. Under [temp.arg.template], we are supposed to check that the types are equivalent, which means checking for syntactic equivalence in the dependent case. This also fixes some accepts-invalids when passing templates with auto-typed non-type template parameters as template template arguments. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@291512 91177308-0d34-0410-b5e6-96231b3b80d8
1 parent e0078e5 commit b625445

File tree

5 files changed

+53
-36
lines changed

5 files changed

+53
-36
lines changed

lib/Sema/SemaTemplate.cpp

+19-19
Original file line numberDiff line numberDiff line change
@@ -1653,6 +1653,7 @@ struct DependencyChecker : RecursiveASTVisitor<DependencyChecker> {
16531653
typedef RecursiveASTVisitor<DependencyChecker> super;
16541654

16551655
unsigned Depth;
1656+
bool FindLessThanDepth;
16561657

16571658
// Whether we're looking for a use of a template parameter that makes the
16581659
// overall construct type-dependent / a dependent type. This is strictly
@@ -1663,25 +1664,16 @@ struct DependencyChecker : RecursiveASTVisitor<DependencyChecker> {
16631664
bool Match;
16641665
SourceLocation MatchLoc;
16651666

1666-
DependencyChecker(unsigned Depth, bool IgnoreNonTypeDependent)
1667-
: Depth(Depth), IgnoreNonTypeDependent(IgnoreNonTypeDependent),
1668-
Match(false) {}
1667+
DependencyChecker(unsigned Depth, bool IgnoreNonTypeDependent,
1668+
bool FindLessThanDepth = false)
1669+
: Depth(Depth), FindLessThanDepth(FindLessThanDepth),
1670+
IgnoreNonTypeDependent(IgnoreNonTypeDependent), Match(false) {}
16691671

16701672
DependencyChecker(TemplateParameterList *Params, bool IgnoreNonTypeDependent)
1671-
: IgnoreNonTypeDependent(IgnoreNonTypeDependent), Match(false) {
1672-
NamedDecl *ND = Params->getParam(0);
1673-
if (TemplateTypeParmDecl *PD = dyn_cast<TemplateTypeParmDecl>(ND)) {
1674-
Depth = PD->getDepth();
1675-
} else if (NonTypeTemplateParmDecl *PD =
1676-
dyn_cast<NonTypeTemplateParmDecl>(ND)) {
1677-
Depth = PD->getDepth();
1678-
} else {
1679-
Depth = cast<TemplateTemplateParmDecl>(ND)->getDepth();
1680-
}
1681-
}
1673+
: DependencyChecker(Params->getDepth(), IgnoreNonTypeDependent) {}
16821674

16831675
bool Matches(unsigned ParmDepth, SourceLocation Loc = SourceLocation()) {
1684-
if (ParmDepth >= Depth) {
1676+
if (FindLessThanDepth ^ (ParmDepth >= Depth)) {
16851677
Match = true;
16861678
MatchLoc = Loc;
16871679
return true;
@@ -5838,6 +5830,15 @@ Sema::BuildExpressionFromIntegralTemplateArgument(const TemplateArgument &Arg,
58385830
return E;
58395831
}
58405832

5833+
static bool isDependentOnOuter(NonTypeTemplateParmDecl *NTTP) {
5834+
if (NTTP->getDepth() == 0 || !NTTP->getType()->isDependentType())
5835+
return false;
5836+
DependencyChecker Checker(NTTP->getDepth(), /*IgnoreNonTypeDependent*/ false,
5837+
/*FindLessThanDepth*/ true);
5838+
Checker.TraverseType(NTTP->getType());
5839+
return Checker.Match;
5840+
}
5841+
58415842
/// \brief Match two template parameters within template parameter lists.
58425843
static bool MatchTemplateParameterKind(Sema &S, NamedDecl *New, NamedDecl *Old,
58435844
bool Complain,
@@ -5894,11 +5895,10 @@ static bool MatchTemplateParameterKind(Sema &S, NamedDecl *New, NamedDecl *Old,
58945895

58955896
// If we are matching a template template argument to a template
58965897
// template parameter and one of the non-type template parameter types
5897-
// is dependent, then we must wait until template instantiation time
5898-
// to actually compare the arguments.
5898+
// is dependent on an outer template's parameter, then we must wait until
5899+
// template instantiation time to actually compare the arguments.
58995900
if (Kind == Sema::TPL_TemplateTemplateArgumentMatch &&
5900-
(OldNTTP->getType()->isDependentType() ||
5901-
NewNTTP->getType()->isDependentType()))
5901+
(isDependentOnOuter(OldNTTP) || isDependentOnOuter(NewNTTP)))
59025902
return true;
59035903

59045904
if (!S.Context.hasSameType(OldNTTP->getType(), NewNTTP->getType())) {

test/Index/index-templates.cpp

+3-3
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,9 @@ template class vector<int*>;
4949
struct Z4 {
5050
template<typename T> T getAs();
5151
};
52-
52+
template<typename T, T> struct value { };
5353
void template_exprs() {
54-
f<Unsigned, OneDimension, array>(array<Unsigned, OneDimension>());
54+
f<Unsigned, OneDimension, value>(value<Unsigned, OneDimension>());
5555
Z4().getAs<Unsigned>();
5656
}
5757

@@ -173,7 +173,7 @@ using alias = T;
173173
// CHECK-LOAD: index-templates.cpp:54:3: DeclRefExpr=f:4:6 RefName=[54:3 - 54:4] RefName=[54:4 - 54:35] Extent=[54:3 - 54:35]
174174
// CHECK-LOAD: index-templates.cpp:54:5: TypeRef=Unsigned:42:18 Extent=[54:5 - 54:13]
175175
// CHECK-LOAD: index-templates.cpp:54:15: DeclRefExpr=OneDimension:35:16 Extent=[54:15 - 54:27]
176-
// CHECK-LOAD: index-templates.cpp:54:29: TemplateRef=array:37:8 Extent=[54:29 - 54:34]
176+
// CHECK-LOAD: index-templates.cpp:54:29: TemplateRef=value:52:32 Extent=[54:29 - 54:34]
177177
// CHECK-LOAD: index-templates.cpp:55:8: MemberRefExpr=getAs:50:26 SingleRefName=[55:8 - 55:13] RefName=[55:8 - 55:13] Extent=[55:3 - 55:23]
178178
// CHECK-LOAD: index-templates.cpp:55:3: CallExpr=Z4:49:8 Extent=[55:3 - 55:7]
179179
// CHECK-LOAD: index-templates.cpp:55:14: TypeRef=Unsigned:42:18 Extent=[55:14 - 55:22]

test/Modules/cxx-templates.cpp

+2-8
Original file line numberDiff line numberDiff line change
@@ -49,14 +49,8 @@ void g() {
4949
// expected-note@Inputs/cxx-templates-a.h:11 {{candidate}}
5050
// expected-note@Inputs/cxx-templates-b.h:11 {{candidate}}
5151

52-
// FIXME: This should be valid, but we incorrectly match the template template
53-
// argument against both template template parameters.
54-
template_param_kinds_3<Tmpl_T_T_A>(); // expected-error {{ambiguous}}
55-
// expected-note@Inputs/cxx-templates-a.h:12 {{candidate}}
56-
// expected-note@Inputs/cxx-templates-b.h:12 {{candidate}}
57-
template_param_kinds_3<Tmpl_T_T_B>(); // expected-error {{ambiguous}}
58-
// expected-note@Inputs/cxx-templates-a.h:12 {{candidate}}
59-
// expected-note@Inputs/cxx-templates-b.h:12 {{candidate}}
52+
template_param_kinds_3<Tmpl_T_T_A>();
53+
template_param_kinds_3<Tmpl_T_T_B>();
6054

6155
// Trigger the instantiation of a template in 'a' that uses a type defined in
6256
// 'common'. That type is not visible here.

test/SemaTemplate/temp_arg_template.cpp

+6
Original file line numberDiff line numberDiff line change
@@ -100,3 +100,9 @@ struct S : public template_tuple<identity, identity> {
100100
void foo() {
101101
f7<identity>();
102102
}
103+
104+
namespace CheckDependentNonTypeParamTypes {
105+
template<template<typename T, typename U, T v> class> struct A {}; // expected-note {{previous}}
106+
template<typename T, typename U, U v> struct B {}; // expected-note {{different type}}
107+
A<B> ab; // expected-error {{different template parameters}}
108+
}

test/SemaTemplate/temp_arg_template_cxx1z.cpp

+23-6
Original file line numberDiff line numberDiff line change
@@ -70,30 +70,47 @@ namespace Auto {
7070
template<template<int*> typename T> struct TIntPtr {};
7171
template<template<auto> typename T> struct TAuto {};
7272
template<template<auto*> typename T> struct TAutoPtr {};
73+
template<template<decltype(auto)> typename T> struct TDecltypeAuto {};
7374
template<auto> struct Auto;
7475
template<auto*> struct AutoPtr;
76+
template<decltype(auto)> struct DecltypeAuto;
7577
template<int> struct Int;
7678
template<int*> struct IntPtr;
7779

7880
TInt<Auto> ia;
79-
TInt<AutoPtr> iap; // FIXME: ill-formed
81+
TInt<AutoPtr> iap; // expected-error {{different template parameters}}
82+
TInt<DecltypeAuto> ida; // FIXME expected-error {{different template parameters}}
8083
TInt<Int> ii;
8184
TInt<IntPtr> iip; // expected-error {{different template parameters}}
8285

8386
TIntPtr<Auto> ipa;
8487
TIntPtr<AutoPtr> ipap;
88+
TIntPtr<DecltypeAuto> ipda; // FIXME expected-error {{different template parameters}}
8589
TIntPtr<Int> ipi; // expected-error {{different template parameters}}
8690
TIntPtr<IntPtr> ipip;
8791

8892
TAuto<Auto> aa;
89-
TAuto<AutoPtr> aap; // FIXME: ill-formed
90-
TAuto<Int> ai; // FIXME: ill-formed
91-
TAuto<IntPtr> aip; // FIXME: ill-formed
93+
TAuto<AutoPtr> aap; // expected-error {{different template parameters}}
94+
TAuto<Int> ai; // expected-error {{different template parameters}}
95+
TAuto<IntPtr> aip; // expected-error {{different template parameters}}
9296

9397
TAutoPtr<Auto> apa;
9498
TAutoPtr<AutoPtr> apap;
95-
TAutoPtr<Int> api; // FIXME: ill-formed
96-
TAutoPtr<IntPtr> apip; // FIXME: ill-formed
99+
TAutoPtr<Int> api; // expected-error {{different template parameters}}
100+
TAutoPtr<IntPtr> apip; // expected-error {{different template parameters}}
101+
102+
TDecltypeAuto<DecltypeAuto> dada;
103+
TDecltypeAuto<Int> dai; // expected-error {{different template parameters}}
104+
TDecltypeAuto<IntPtr> daip; // expected-error {{different template parameters}}
105+
106+
// FIXME: It's completely unclear what should happen here. A case can be made
107+
// that 'auto' is more specialized, because it's always a prvalue, whereas
108+
// 'decltype(auto)' could have any value category. Under that interpretation,
109+
// we get the following results entirely backwards:
110+
TAuto<DecltypeAuto> ada; // expected-error {{different template parameters}}
111+
TAutoPtr<DecltypeAuto> apda; // expected-error {{different template parameters}}
112+
TDecltypeAuto<Auto> daa;
113+
TDecltypeAuto<AutoPtr> daa; // expected-error {{different template parameters}}
97114

98115
int n;
99116
template<auto A, decltype(A) B = &n> struct SubstFailure;

0 commit comments

Comments
 (0)