summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRichard Smith <richard-llvm@metafoo.co.uk>2017-01-09 23:54:33 +0000
committerRichard Smith <richard-llvm@metafoo.co.uk>2017-01-09 23:54:33 +0000
commitb62544540682e5e9418ba65b69c081d6c029cd43 (patch)
tree244a95db294d36963eb57a217503e927bdba85e8
parente0078e5ad7b5e75cd2039496b5307698571cf56a (diff)
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
-rw-r--r--lib/Sema/SemaTemplate.cpp38
-rw-r--r--test/Index/index-templates.cpp6
-rw-r--r--test/Modules/cxx-templates.cpp10
-rw-r--r--test/SemaTemplate/temp_arg_template.cpp6
-rw-r--r--test/SemaTemplate/temp_arg_template_cxx1z.cpp29
5 files changed, 53 insertions, 36 deletions
diff --git a/lib/Sema/SemaTemplate.cpp b/lib/Sema/SemaTemplate.cpp
index 795e6025d9..fd086a4f7f 100644
--- a/lib/Sema/SemaTemplate.cpp
+++ b/lib/Sema/SemaTemplate.cpp
@@ -1653,6 +1653,7 @@ struct DependencyChecker : RecursiveASTVisitor<DependencyChecker> {
typedef RecursiveASTVisitor<DependencyChecker> super;
unsigned Depth;
+ bool FindLessThanDepth;
// Whether we're looking for a use of a template parameter that makes the
// overall construct type-dependent / a dependent type. This is strictly
@@ -1663,25 +1664,16 @@ struct DependencyChecker : RecursiveASTVisitor<DependencyChecker> {
bool Match;
SourceLocation MatchLoc;
- DependencyChecker(unsigned Depth, bool IgnoreNonTypeDependent)
- : Depth(Depth), IgnoreNonTypeDependent(IgnoreNonTypeDependent),
- Match(false) {}
+ DependencyChecker(unsigned Depth, bool IgnoreNonTypeDependent,
+ bool FindLessThanDepth = false)
+ : Depth(Depth), FindLessThanDepth(FindLessThanDepth),
+ IgnoreNonTypeDependent(IgnoreNonTypeDependent), Match(false) {}
DependencyChecker(TemplateParameterList *Params, bool IgnoreNonTypeDependent)
- : IgnoreNonTypeDependent(IgnoreNonTypeDependent), Match(false) {
- NamedDecl *ND = Params->getParam(0);
- if (TemplateTypeParmDecl *PD = dyn_cast<TemplateTypeParmDecl>(ND)) {
- Depth = PD->getDepth();
- } else if (NonTypeTemplateParmDecl *PD =
- dyn_cast<NonTypeTemplateParmDecl>(ND)) {
- Depth = PD->getDepth();
- } else {
- Depth = cast<TemplateTemplateParmDecl>(ND)->getDepth();
- }
- }
+ : DependencyChecker(Params->getDepth(), IgnoreNonTypeDependent) {}
bool Matches(unsigned ParmDepth, SourceLocation Loc = SourceLocation()) {
- if (ParmDepth >= Depth) {
+ if (FindLessThanDepth ^ (ParmDepth >= Depth)) {
Match = true;
MatchLoc = Loc;
return true;
@@ -5838,6 +5830,15 @@ Sema::BuildExpressionFromIntegralTemplateArgument(const TemplateArgument &Arg,
return E;
}
+static bool isDependentOnOuter(NonTypeTemplateParmDecl *NTTP) {
+ if (NTTP->getDepth() == 0 || !NTTP->getType()->isDependentType())
+ return false;
+ DependencyChecker Checker(NTTP->getDepth(), /*IgnoreNonTypeDependent*/ false,
+ /*FindLessThanDepth*/ true);
+ Checker.TraverseType(NTTP->getType());
+ return Checker.Match;
+}
+
/// \brief Match two template parameters within template parameter lists.
static bool MatchTemplateParameterKind(Sema &S, NamedDecl *New, NamedDecl *Old,
bool Complain,
@@ -5894,11 +5895,10 @@ static bool MatchTemplateParameterKind(Sema &S, NamedDecl *New, NamedDecl *Old,
// If we are matching a template template argument to a template
// template parameter and one of the non-type template parameter types
- // is dependent, then we must wait until template instantiation time
- // to actually compare the arguments.
+ // is dependent on an outer template's parameter, then we must wait until
+ // template instantiation time to actually compare the arguments.
if (Kind == Sema::TPL_TemplateTemplateArgumentMatch &&
- (OldNTTP->getType()->isDependentType() ||
- NewNTTP->getType()->isDependentType()))
+ (isDependentOnOuter(OldNTTP) || isDependentOnOuter(NewNTTP)))
return true;
if (!S.Context.hasSameType(OldNTTP->getType(), NewNTTP->getType())) {
diff --git a/test/Index/index-templates.cpp b/test/Index/index-templates.cpp
index 79b9c181ec..966cc4f5ea 100644
--- a/test/Index/index-templates.cpp
+++ b/test/Index/index-templates.cpp
@@ -49,9 +49,9 @@ template class vector<int*>;
struct Z4 {
template<typename T> T getAs();
};
-
+template<typename T, T> struct value { };
void template_exprs() {
- f<Unsigned, OneDimension, array>(array<Unsigned, OneDimension>());
+ f<Unsigned, OneDimension, value>(value<Unsigned, OneDimension>());
Z4().getAs<Unsigned>();
}
@@ -173,7 +173,7 @@ using alias = T;
// 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]
// CHECK-LOAD: index-templates.cpp:54:5: TypeRef=Unsigned:42:18 Extent=[54:5 - 54:13]
// CHECK-LOAD: index-templates.cpp:54:15: DeclRefExpr=OneDimension:35:16 Extent=[54:15 - 54:27]
-// CHECK-LOAD: index-templates.cpp:54:29: TemplateRef=array:37:8 Extent=[54:29 - 54:34]
+// CHECK-LOAD: index-templates.cpp:54:29: TemplateRef=value:52:32 Extent=[54:29 - 54:34]
// 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]
// CHECK-LOAD: index-templates.cpp:55:3: CallExpr=Z4:49:8 Extent=[55:3 - 55:7]
// CHECK-LOAD: index-templates.cpp:55:14: TypeRef=Unsigned:42:18 Extent=[55:14 - 55:22]
diff --git a/test/Modules/cxx-templates.cpp b/test/Modules/cxx-templates.cpp
index 401b770490..59e9136bd1 100644
--- a/test/Modules/cxx-templates.cpp
+++ b/test/Modules/cxx-templates.cpp
@@ -49,14 +49,8 @@ void g() {
// expected-note@Inputs/cxx-templates-a.h:11 {{candidate}}
// expected-note@Inputs/cxx-templates-b.h:11 {{candidate}}
- // FIXME: This should be valid, but we incorrectly match the template template
- // argument against both template template parameters.
- template_param_kinds_3<Tmpl_T_T_A>(); // expected-error {{ambiguous}}
- // expected-note@Inputs/cxx-templates-a.h:12 {{candidate}}
- // expected-note@Inputs/cxx-templates-b.h:12 {{candidate}}
- template_param_kinds_3<Tmpl_T_T_B>(); // expected-error {{ambiguous}}
- // expected-note@Inputs/cxx-templates-a.h:12 {{candidate}}
- // expected-note@Inputs/cxx-templates-b.h:12 {{candidate}}
+ template_param_kinds_3<Tmpl_T_T_A>();
+ template_param_kinds_3<Tmpl_T_T_B>();
// Trigger the instantiation of a template in 'a' that uses a type defined in
// 'common'. That type is not visible here.
diff --git a/test/SemaTemplate/temp_arg_template.cpp b/test/SemaTemplate/temp_arg_template.cpp
index 67cde53c92..b0df9149c6 100644
--- a/test/SemaTemplate/temp_arg_template.cpp
+++ b/test/SemaTemplate/temp_arg_template.cpp
@@ -100,3 +100,9 @@ struct S : public template_tuple<identity, identity> {
void foo() {
f7<identity>();
}
+
+namespace CheckDependentNonTypeParamTypes {
+ template<template<typename T, typename U, T v> class> struct A {}; // expected-note {{previous}}
+ template<typename T, typename U, U v> struct B {}; // expected-note {{different type}}
+ A<B> ab; // expected-error {{different template parameters}}
+}
diff --git a/test/SemaTemplate/temp_arg_template_cxx1z.cpp b/test/SemaTemplate/temp_arg_template_cxx1z.cpp
index b6b283b53c..aa517c3285 100644
--- a/test/SemaTemplate/temp_arg_template_cxx1z.cpp
+++ b/test/SemaTemplate/temp_arg_template_cxx1z.cpp
@@ -70,30 +70,47 @@ namespace Auto {
template<template<int*> typename T> struct TIntPtr {};
template<template<auto> typename T> struct TAuto {};
template<template<auto*> typename T> struct TAutoPtr {};
+ template<template<decltype(auto)> typename T> struct TDecltypeAuto {};
template<auto> struct Auto;
template<auto*> struct AutoPtr;
+ template<decltype(auto)> struct DecltypeAuto;
template<int> struct Int;
template<int*> struct IntPtr;
TInt<Auto> ia;
- TInt<AutoPtr> iap; // FIXME: ill-formed
+ TInt<AutoPtr> iap; // expected-error {{different template parameters}}
+ TInt<DecltypeAuto> ida; // FIXME expected-error {{different template parameters}}
TInt<Int> ii;
TInt<IntPtr> iip; // expected-error {{different template parameters}}
TIntPtr<Auto> ipa;
TIntPtr<AutoPtr> ipap;
+ TIntPtr<DecltypeAuto> ipda; // FIXME expected-error {{different template parameters}}
TIntPtr<Int> ipi; // expected-error {{different template parameters}}
TIntPtr<IntPtr> ipip;
TAuto<Auto> aa;
- TAuto<AutoPtr> aap; // FIXME: ill-formed
- TAuto<Int> ai; // FIXME: ill-formed
- TAuto<IntPtr> aip; // FIXME: ill-formed
+ TAuto<AutoPtr> aap; // expected-error {{different template parameters}}
+ TAuto<Int> ai; // expected-error {{different template parameters}}
+ TAuto<IntPtr> aip; // expected-error {{different template parameters}}
TAutoPtr<Auto> apa;
TAutoPtr<AutoPtr> apap;
- TAutoPtr<Int> api; // FIXME: ill-formed
- TAutoPtr<IntPtr> apip; // FIXME: ill-formed
+ TAutoPtr<Int> api; // expected-error {{different template parameters}}
+ TAutoPtr<IntPtr> apip; // expected-error {{different template parameters}}
+
+ TDecltypeAuto<DecltypeAuto> dada;
+ TDecltypeAuto<Int> dai; // expected-error {{different template parameters}}
+ TDecltypeAuto<IntPtr> daip; // expected-error {{different template parameters}}
+
+ // FIXME: It's completely unclear what should happen here. A case can be made
+ // that 'auto' is more specialized, because it's always a prvalue, whereas
+ // 'decltype(auto)' could have any value category. Under that interpretation,
+ // we get the following results entirely backwards:
+ TAuto<DecltypeAuto> ada; // expected-error {{different template parameters}}
+ TAutoPtr<DecltypeAuto> apda; // expected-error {{different template parameters}}
+ TDecltypeAuto<Auto> daa;
+ TDecltypeAuto<AutoPtr> daa; // expected-error {{different template parameters}}
int n;
template<auto A, decltype(A) B = &n> struct SubstFailure;