faisalv updated this revision to Diff 51742.
faisalv marked 2 inline comments as done.
faisalv added a comment.

Updated the patch with the following:

- while checking dependent template template arguments at template definition 
time, we must accept the more general (pack) as compatible with the more 
specific (non-pack sequence), since the instantiation time check will preserve 
the compatibility between template parameter lists (for a subset of 
pack-expansions compatible with the non-pack sequences) mandated by the 
standard.
- added Richard's examples.

Thanks!


http://reviews.llvm.org/D18495

Files:
  include/clang/Sema/Sema.h
  lib/Sema/SemaTemplate.cpp
  test/CXX/temp/temp.arg/temp.arg.template/p3-0x.cpp
  test/CXX/temp/temp.param/p10-0x.cpp
  test/CXX/temp/temp.param/p12.cpp

Index: test/CXX/temp/temp.param/p12.cpp
===================================================================
--- test/CXX/temp/temp.param/p12.cpp
+++ test/CXX/temp/temp.param/p12.cpp
@@ -34,6 +34,6 @@
 // Check validity of default arguments
 template<template<class, int> class // expected-note{{previous template template parameter is here}}
            = Y1> // expected-error{{template template argument has different template parameters than its corresponding template template parameter}}
-  class C1 {};
+  class C1 {};  //expected-note{{template is declared here}}
 
-C1<> c1; // expected-note{{while checking a default template argument}}
+C1<> c1; // expected-error{{too few template arguments}}
Index: test/CXX/temp/temp.param/p10-0x.cpp
===================================================================
--- test/CXX/temp/temp.param/p10-0x.cpp
+++ test/CXX/temp/temp.param/p10-0x.cpp
@@ -8,7 +8,7 @@
 template<class T1 = int, class T2> using B2 = T1;
 
 template<template<class> class F, template<class> class G = Y1> using B2t = F<G<int>>;
-template<template<class> class F = Y2, template<class> class G> using B2t = F<G<int>>;
+template<template<class> class F = Y1, template<class> class G> using B2t = F<G<int>>;
 
 template<int N, int M = 5> using B2n = Y2<int, N + M>;
 template<int N = 5, int M> using B2n = Y2<int, N + M>;
Index: test/CXX/temp/temp.arg/temp.arg.template/p3-0x.cpp
===================================================================
--- test/CXX/temp/temp.arg/temp.arg.template/p3-0x.cpp
+++ test/CXX/temp/temp.arg/temp.arg.template/p3-0x.cpp
@@ -38,3 +38,116 @@
 X1<long, X1b> inst_x1b;
 X1<short, X1c> inst_x1c;
 X1<short, X1d> inst_x1d; // expected-error{{template template argument has different template parameters than its corresponding template template paramete}}
+
+
+namespace ns0 {
+template<class, class> using TA = int*;
+template<template<class, class> class TT = TA> struct X;
+
+}
+
+
+namespace ns1 {
+template<template<class, class> class TT> //expected-note{{too many template parameters}}
+struct X {
+  template<template<class> //expected-note{{previous template template parameter is here}}
+              class UU = TT>  //expected-error{{different template parameters}}
+  struct Y;
+};
+} // end ns1
+namespace ns2 {
+template<class T, template<T, class> class TT> 
+struct A {
+  template<class U, template<U, class...> class UU = TT> 
+  struct B;
+};
+} // end ns2
+namespace ns3 {
+template<class T, template<T, class...> class TT> struct A {
+  template<class U, template<U, class> class UU = T::template X> struct B { };
+};
+} // end ns3
+namespace ns4 {
+template<class T, template<T, class...> class TT> 
+struct A {
+  template<class U, template<U, class> 
+      class UU = TT> // OK
+  struct B;
+};
+template<int, class> struct Q;
+A<int, Q>::B<int> *b; // OK
+
+namespace ns4_0 {
+template<class T, template<T, class...> class TT> 
+struct A {
+  template<class U, template<U*, class> 
+      class UU = TT> // OK
+  struct B;
+};
+template<int*, class> struct Q;
+A<int*, Q>::B<int> *b;
+} // end ns4_0
+
+namespace ns4_1 {
+template<class T, template<template<T, class ...> class> class TT> 
+struct A {
+  template<class U, template<template<U*, class> class> 
+      class UU = TT> // OK
+  struct B;
+};
+
+} // end ns4_0
+
+} // end ns4
+
+namespace ns5 {
+template<class T, template<T, class...> class TT> 
+struct A {
+  template<class U, template<U, class...> 
+      class UU = TT> 
+  struct B;
+};
+
+
+} // end ns5
+
+namespace ns6 {
+
+template<typename T, template<T> class A>
+struct X {
+  template<template<int> class = A> struct Y {};
+};
+
+} // end ns6
+
+
+namespace ns7 {
+
+template<typename T, template<T> class A>
+struct X;
+
+template<typename T, template<T*> class A> struct X<T*, A> {
+  using type = char*;
+};
+template<char*> struct Q;
+X<char*, Q>::type *pc;
+
+} // end ns6
+
+namespace ns8 {
+// NOTE: This test for partial specializations is added here to remind us
+// that these cases are somewhat similar, yet not without differences.
+// Even though for default arguments at template defintion we allow 
+// the contravariant case (a pack on argument is compatible with a non-pack 
+// on the parameter) in the partial specialization setting, we do not allow it.
+template<typename T, template<class> class A> //expected-note{{declared here}}
+struct X {};
+
+template<typename T, template<class...> class A> //expected-note{{pack does not match}}
+struct X<T*, A> { //expected-error{{different template parameters than its corresponding template template parameter}}
+  using type = char*;
+};
+
+} // end ns6
+
+
Index: lib/Sema/SemaTemplate.cpp
===================================================================
--- lib/Sema/SemaTemplate.cpp
+++ lib/Sema/SemaTemplate.cpp
@@ -790,14 +790,6 @@
   }
 
   if (!Default.isInvalid()) {
-    // Check only that we have a template template argument. We don't want to
-    // try to check well-formedness now, because our template template parameter
-    // might have dependent types in its template parameters, which we wouldn't
-    // be able to match now.
-    //
-    // If none of the template template parameter's template arguments mention
-    // other template parameters, we could actually perform more checking here.
-    // However, it isn't worth doing.
     TemplateArgumentLoc DefaultArg = translateTemplateArgument(*this, Default);
     if (DefaultArg.getArgument().getAsTemplate().isNull()) {
       Diag(DefaultArg.getLocation(), diag::err_template_arg_not_valid_template)
@@ -810,7 +802,12 @@
                                         DefaultArg.getArgument().getAsTemplate(),
                                         UPPC_DefaultArgument))
       return Param;
-
+    // Check that the default template template argument is compatible with 
+    // the template template parameter, in as much as we can check at this
+    // time.
+    if (CheckTemplateArgument(Param, DefaultArg, /*ArgumentPackIndex*/ 0,
+                    /*IsCheckingDefaultArgumentAtTemplateDefinitionTime*/ true))
+      return Param;
     Param->setDefaultArgument(Context, DefaultArg);
   }
 
@@ -5324,9 +5321,10 @@
 ///
 /// This routine implements the semantics of C++ [temp.arg.template].
 /// It returns true if an error occurred, and false otherwise.
-bool Sema::CheckTemplateArgument(TemplateTemplateParmDecl *Param,
-                                 TemplateArgumentLoc &Arg,
-                                 unsigned ArgumentPackIndex) {
+bool Sema::CheckTemplateArgument(
+    TemplateTemplateParmDecl *Param, TemplateArgumentLoc &Arg,
+    const unsigned ArgumentPackIndex,
+    const bool IsCheckingDefaultArgumentAtTemplateDefinitionTime) {
   TemplateName Name = Arg.getArgument().getAsTemplateOrTemplatePattern();
   TemplateDecl *Template = Name.getAsTemplateDecl();
   if (!Template) {
@@ -5346,7 +5344,8 @@
   //
   // Note that we also allow template template parameters here, which
   // will happen when we are dealing with, e.g., class template
-  // partial specializations.
+  // partial specializations or default template template arguments that
+  // are template template parameters from enclosing class templates.
   if (!isa<ClassTemplateDecl>(Template) &&
       !isa<TemplateTemplateParmDecl>(Template) &&
       !isa<TypeAliasTemplateDecl>(Template)) {
@@ -5360,11 +5359,16 @@
   TemplateParameterList *Params = Param->getTemplateParameters();
   if (Param->isExpandedParameterPack())
     Params = Param->getExpansionTemplateParameters(ArgumentPackIndex);
-
+  TemplateParameterListEqualKind Kind =
+      IsCheckingDefaultArgumentAtTemplateDefinitionTime
+        ? (isa<TemplateTemplateParmDecl>(Template)
+           ? TPL_DependentTemplateTemplateArgumentMatchAtTemplateDefinition
+           : TPL_TemplateTemplateArgumentMatch)
+        : TPL_TemplateTemplateArgumentMatch;
   return !TemplateParameterListsAreEqual(Template->getTemplateParameters(),
                                          Params,
                                          true,
-                                         TPL_TemplateTemplateArgumentMatch,
+                                         Kind,
                                          Arg.getLocation());
 }
 
@@ -5572,11 +5576,19 @@
     return false;
   }
 
-  // Check that both are parameter packs are neither are parameter packs.
-  // However, if we are matching a template template argument to a
-  // template template parameter, the template template parameter can have
-  // a parameter pack where the template template argument does not.
+  // Check that both are parameter packs or neither are parameter packs.
+  // However, if we are matching a template template argument (A) to a 
+  // template template parameter (P):
+  //   - if 'A' is a default template template argument that is itself an
+  //   enclosing template template parameter then if either one has a pack,
+  //   do not check it at template definition time 
+  //   (i.e. TPL_DependentTemplateTemplateArgumentMatchAtTemplateDefinition)
+  //   - otherwise, 'P' can have a parameter pack where 'A' does not.
+
   if (Old->isTemplateParameterPack() != New->isTemplateParameterPack() &&
+      Kind !=
+          Sema::
+              TPL_DependentTemplateTemplateArgumentMatchAtTemplateDefinition &&
       !(Kind == Sema::TPL_TemplateTemplateArgumentMatch &&
         Old->isTemplateParameterPack())) {
     if (Complain) {
@@ -5608,7 +5620,7 @@
     // 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.
-    if (Kind == Sema::TPL_TemplateTemplateArgumentMatch &&
+    if (Kind >= Sema::TPL_TemplateTemplateArgumentMatch &&
         (OldNTTP->getType()->isDependentType() ||
          NewNTTP->getType()->isDependentType()))
       return true;
@@ -5704,7 +5716,7 @@
                                      bool Complain,
                                      TemplateParameterListEqualKind Kind,
                                      SourceLocation TemplateArgLoc) {
-  if (Old->size() != New->size() && Kind != TPL_TemplateTemplateArgumentMatch) {
+  if (Old->size() != New->size() && Kind < TPL_TemplateTemplateArgumentMatch) {
     if (Complain)
       DiagnoseTemplateParameterListArityMismatch(*this, New, Old, Kind,
                                                  TemplateArgLoc);
@@ -5723,7 +5735,7 @@
   for (TemplateParameterList::iterator OldParm = Old->begin(),
                                     OldParmEnd = Old->end();
        OldParm != OldParmEnd; ++OldParm) {
-    if (Kind != TPL_TemplateTemplateArgumentMatch ||
+    if (Kind < TPL_TemplateTemplateArgumentMatch ||
         !(*OldParm)->isTemplateParameterPack()) {
       if (NewParm == NewParmEnd) {
         if (Complain)
Index: include/clang/Sema/Sema.h
===================================================================
--- include/clang/Sema/Sema.h
+++ include/clang/Sema/Sema.h
@@ -5848,9 +5848,10 @@
                                    QualType InstantiatedParamType, Expr *Arg,
                                    TemplateArgument &Converted,
                                CheckTemplateArgumentKind CTAK = CTAK_Specified);
-  bool CheckTemplateArgument(TemplateTemplateParmDecl *Param,
-                             TemplateArgumentLoc &Arg,
-                             unsigned ArgumentPackIndex);
+  bool CheckTemplateArgument(
+      TemplateTemplateParmDecl *Param, TemplateArgumentLoc &Arg,
+      unsigned ArgumentPackIndex,
+      bool IsCheckingDefaultArgumentAtTemplateDefinitionTime = false);
 
   ExprResult
   BuildExpressionFromDeclTemplateArgument(const TemplateArgument &Arg,
@@ -5891,7 +5892,28 @@
     /// template<int Value> struct integer_c;
     /// X<integer_c> xic;
     /// \endcode
-    TPL_TemplateTemplateArgumentMatch
+    /// NOTE: Any enumerator that follows this one, implies it (i.e OK to use 
+    ///       '<' or '>' to check for implication)
+    TPL_TemplateTemplateArgumentMatch,
+
+    /// \brief We are matching the template parameter lists of a dependent 
+    /// default template template argument (i.e. 'TT' below) against the 
+    /// template parameter lists of a template template parameter.
+    ///
+    /// \code
+    /// template<class T, template<T, class...> class TT> struct X {
+    ///     template<class U, template<int,class> class UU = TT> struct Y;
+    /// }
+    /// template<int, class> struct Q;
+    /// X<Q>::Y<> y;  // OK
+    /// \endcode
+    ///
+    /// This enum MUST follow TPL_TemplateTemplateArgumentMatch since it implies
+    /// that.
+    TPL_DependentTemplateTemplateArgumentMatchAtTemplateDefinition
+
+    /// NOTE: Any enumerator added here, implies
+    /// TPL_TemplateTemplateArgumentMatch
   };
 
   bool TemplateParameterListsAreEqual(TemplateParameterList *New,
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to