From: Richard Smith Date: Mon, 9 Jan 2017 23:54:33 +0000 (+0000) Subject: Check that template template arguments match template template parameters X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=b62544540682e5e9418ba65b69c081d6c029cd43;p=clang 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 --- 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 { typedef RecursiveASTVisitor 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 { 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(ND)) { - Depth = PD->getDepth(); - } else if (NonTypeTemplateParmDecl *PD = - dyn_cast(ND)) { - Depth = PD->getDepth(); - } else { - Depth = cast(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; struct Z4 { template T getAs(); }; - +template struct value { }; void template_exprs() { - f(array()); + f(value()); Z4().getAs(); } @@ -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(); // 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(); // 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(); + template_param_kinds_3(); // 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 { void foo() { f7(); } + +namespace CheckDependentNonTypeParamTypes { + template class> struct A {}; // expected-note {{previous}} + template struct B {}; // expected-note {{different type}} + A 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 typename T> struct TIntPtr {}; template typename T> struct TAuto {}; template typename T> struct TAutoPtr {}; + template typename T> struct TDecltypeAuto {}; template struct Auto; template struct AutoPtr; + template struct DecltypeAuto; template struct Int; template struct IntPtr; TInt ia; - TInt iap; // FIXME: ill-formed + TInt iap; // expected-error {{different template parameters}} + TInt ida; // FIXME expected-error {{different template parameters}} TInt ii; TInt iip; // expected-error {{different template parameters}} TIntPtr ipa; TIntPtr ipap; + TIntPtr ipda; // FIXME expected-error {{different template parameters}} TIntPtr ipi; // expected-error {{different template parameters}} TIntPtr ipip; TAuto aa; - TAuto aap; // FIXME: ill-formed - TAuto ai; // FIXME: ill-formed - TAuto aip; // FIXME: ill-formed + TAuto aap; // expected-error {{different template parameters}} + TAuto ai; // expected-error {{different template parameters}} + TAuto aip; // expected-error {{different template parameters}} TAutoPtr apa; TAutoPtr apap; - TAutoPtr api; // FIXME: ill-formed - TAutoPtr apip; // FIXME: ill-formed + TAutoPtr api; // expected-error {{different template parameters}} + TAutoPtr apip; // expected-error {{different template parameters}} + + TDecltypeAuto dada; + TDecltypeAuto dai; // expected-error {{different template parameters}} + TDecltypeAuto 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 ada; // expected-error {{different template parameters}} + TAutoPtr apda; // expected-error {{different template parameters}} + TDecltypeAuto daa; + TDecltypeAuto daa; // expected-error {{different template parameters}} int n; template struct SubstFailure;