r291512 - Check that template template arguments match template template parameters

Richard Smith via cfe-commits cfe-commits at lists.llvm.org
Mon Jan 9 15:54:33 PST 2017


Author: rsmith
Date: Mon Jan  9 17:54:33 2017
New Revision: 291512

URL: http://llvm.org/viewvc/llvm-project?rev=291512&view=rev
Log:
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.

Modified:
    cfe/trunk/lib/Sema/SemaTemplate.cpp
    cfe/trunk/test/Index/index-templates.cpp
    cfe/trunk/test/Modules/cxx-templates.cpp
    cfe/trunk/test/SemaTemplate/temp_arg_template.cpp
    cfe/trunk/test/SemaTemplate/temp_arg_template_cxx1z.cpp

Modified: cfe/trunk/lib/Sema/SemaTemplate.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaTemplate.cpp?rev=291512&r1=291511&r2=291512&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaTemplate.cpp (original)
+++ cfe/trunk/lib/Sema/SemaTemplate.cpp Mon Jan  9 17:54:33 2017
@@ -1653,6 +1653,7 @@ struct DependencyChecker : RecursiveASTV
   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 : RecursiveASTV
   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::BuildExpressionFromIntegralTemplat
   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(S
 
     // 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())) {

Modified: cfe/trunk/test/Index/index-templates.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Index/index-templates.cpp?rev=291512&r1=291511&r2=291512&view=diff
==============================================================================
--- cfe/trunk/test/Index/index-templates.cpp (original)
+++ cfe/trunk/test/Index/index-templates.cpp Mon Jan  9 17:54:33 2017
@@ -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]

Modified: cfe/trunk/test/Modules/cxx-templates.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Modules/cxx-templates.cpp?rev=291512&r1=291511&r2=291512&view=diff
==============================================================================
--- cfe/trunk/test/Modules/cxx-templates.cpp (original)
+++ cfe/trunk/test/Modules/cxx-templates.cpp Mon Jan  9 17:54:33 2017
@@ -49,14 +49,8 @@ void g() {
   // expected-note at Inputs/cxx-templates-a.h:11 {{candidate}}
   // expected-note at 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 at Inputs/cxx-templates-a.h:12 {{candidate}}
-  // expected-note at Inputs/cxx-templates-b.h:12 {{candidate}}
-  template_param_kinds_3<Tmpl_T_T_B>(); // expected-error {{ambiguous}}
-  // expected-note at Inputs/cxx-templates-a.h:12 {{candidate}}
-  // expected-note at 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.

Modified: cfe/trunk/test/SemaTemplate/temp_arg_template.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaTemplate/temp_arg_template.cpp?rev=291512&r1=291511&r2=291512&view=diff
==============================================================================
--- cfe/trunk/test/SemaTemplate/temp_arg_template.cpp (original)
+++ cfe/trunk/test/SemaTemplate/temp_arg_template.cpp Mon Jan  9 17:54:33 2017
@@ -100,3 +100,9 @@ struct S : public template_tuple<identit
 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}}
+}

Modified: cfe/trunk/test/SemaTemplate/temp_arg_template_cxx1z.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaTemplate/temp_arg_template_cxx1z.cpp?rev=291512&r1=291511&r2=291512&view=diff
==============================================================================
--- cfe/trunk/test/SemaTemplate/temp_arg_template_cxx1z.cpp (original)
+++ cfe/trunk/test/SemaTemplate/temp_arg_template_cxx1z.cpp Mon Jan  9 17:54:33 2017
@@ -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;




More information about the cfe-commits mailing list