]> granicus.if.org Git - clang/commitdiff
Implement C++0x [temp.arg.template]p3, which allows slightly fuzzy
authorDouglas Gregor <dgregor@apple.com>
Thu, 13 Jan 2011 00:08:50 +0000 (00:08 +0000)
committerDouglas Gregor <dgregor@apple.com>
Thu, 13 Jan 2011 00:08:50 +0000 (00:08 +0000)
matching of variadic template template parameters to template
arguments. This paragraph was the subject of ISO C++ committee
document N2555: Extending Variadic Template Template Parameters.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@123348 91177308-0d34-0410-b5e6-96231b3b80d8

lib/Sema/SemaTemplate.cpp
test/CXX/temp/temp.arg/temp.arg.template/p3-0x.cpp [new file with mode: 0644]
test/CXX/temp/temp.decls/temp.variadic/metafunctions.cpp

index 8d0462daadeefd83891f412e95aa0d9c087bd0f3..08384ccef54f79f94a8a868fe1c7a684329e61eb 100644 (file)
@@ -3651,7 +3651,12 @@ static bool MatchTemplateParameterKind(Sema &S, NamedDecl *New, NamedDecl *Old,
   }
 
   // Check that both are parameter packs are neither are parameter packs. 
-  if (Old->isTemplateParameterPack() != New->isTemplateParameterPack()) {
+  // 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.
+  if (Old->isTemplateParameterPack() != New->isTemplateParameterPack() &&
+      !(Kind == Sema::TPL_TemplateTemplateArgumentMatch &&
+        Old->isTemplateParameterPack())) {
     if (Complain) {
       unsigned NextDiag = diag::err_template_parameter_pack_non_pack;
       if (TemplateArgLoc.isValid()) {
@@ -3726,6 +3731,28 @@ static bool MatchTemplateParameterKind(Sema &S, NamedDecl *New, NamedDecl *Old,
   return true;
 }
 
+/// \brief Diagnose a known arity mismatch when comparing template argument
+/// lists.
+static
+void DiagnoseTemplateParameterListArityMismatch(Sema &S, 
+                                                TemplateParameterList *New,
+                                                TemplateParameterList *Old,
+                                      Sema::TemplateParameterListEqualKind Kind,
+                                                SourceLocation TemplateArgLoc) {
+  unsigned NextDiag = diag::err_template_param_list_different_arity;
+  if (TemplateArgLoc.isValid()) {
+    S.Diag(TemplateArgLoc, diag::err_template_arg_template_params_mismatch);
+    NextDiag = diag::note_template_param_list_different_arity;
+  }
+  S.Diag(New->getTemplateLoc(), NextDiag)
+    << (New->size() > Old->size())
+    << (Kind != Sema::TPL_TemplateMatch)
+    << SourceRange(New->getTemplateLoc(), New->getRAngleLoc());
+  S.Diag(Old->getTemplateLoc(), diag::note_template_prev_declaration)
+    << (Kind != Sema::TPL_TemplateMatch)
+    << SourceRange(Old->getTemplateLoc(), Old->getRAngleLoc());
+}
+
 /// \brief Determine whether the given template parameter lists are
 /// equivalent.
 ///
@@ -3755,21 +3782,10 @@ Sema::TemplateParameterListsAreEqual(TemplateParameterList *New,
                                      bool Complain,
                                      TemplateParameterListEqualKind Kind,
                                      SourceLocation TemplateArgLoc) {
-  if (Old->size() != New->size()) {
-    if (Complain) {
-      unsigned NextDiag = diag::err_template_param_list_different_arity;
-      if (TemplateArgLoc.isValid()) {
-        Diag(TemplateArgLoc, diag::err_template_arg_template_params_mismatch);
-        NextDiag = diag::note_template_param_list_different_arity;
-      }
-      Diag(New->getTemplateLoc(), NextDiag)
-          << (New->size() > Old->size())
-          << (Kind != TPL_TemplateMatch)
-          << SourceRange(New->getTemplateLoc(), New->getRAngleLoc());
-      Diag(Old->getTemplateLoc(), diag::note_template_prev_declaration)
-        << (Kind != TPL_TemplateMatch)
-        << SourceRange(Old->getTemplateLoc(), Old->getRAngleLoc());
-    }
+  if (Old->size() != New->size() && Kind != TPL_TemplateTemplateArgumentMatch) {
+    if (Complain)
+      DiagnoseTemplateParameterListArityMismatch(*this, New, Old, Kind,
+                                                 TemplateArgLoc);
 
     return false;
   }
@@ -3779,15 +3795,52 @@ Sema::TemplateParameterListsAreEqual(TemplateParameterList *New,
   //   when each of the template parameters in the template-parameter-list of 
   //   the template-argument’s corresponding class template or template alias
   //   (call it A) matches the corresponding template parameter in the 
-  //   template-parameter-list of P.
+  //   template-parameter-list of P. [...]
+  TemplateParameterList::iterator NewParm = New->begin();
+  TemplateParameterList::iterator NewParmEnd = New->end();
   for (TemplateParameterList::iterator OldParm = Old->begin(),
-         OldParmEnd = Old->end(), NewParm = New->begin();
-       OldParm != OldParmEnd; ++OldParm, ++NewParm) {
-    if (!MatchTemplateParameterKind(*this, *NewParm, *OldParm, Complain,
-                                    Kind, TemplateArgLoc))
-      return false;    
+                                    OldParmEnd = Old->end();
+       OldParm != OldParmEnd; ++OldParm) {
+    if (!(*OldParm)->isTemplateParameterPack()) {
+      if (NewParm == NewParmEnd) {
+        if (Complain)
+          DiagnoseTemplateParameterListArityMismatch(*this, New, Old, Kind,
+                                                     TemplateArgLoc);
+        
+        return false;
+      }
+      
+      if (!MatchTemplateParameterKind(*this, *NewParm, *OldParm, Complain,
+                                      Kind, TemplateArgLoc))
+        return false;    
+      
+      ++NewParm;
+      continue;
+    }
+    
+    // C++0x [temp.arg.template]p3:
+    //   [...] When P’s template- parameter-list contains a template parameter
+    //   pack (14.5.3), the template parameter pack will match zero or more 
+    //   template parameters or template parameter packs in the 
+    //   template-parameter-list of A with the same type and form as the
+    //   template parameter pack in P (ignoring whether those template
+    //   parameters are template parameter packs).
+    for (; NewParm != NewParmEnd; ++NewParm) {
+      if (!MatchTemplateParameterKind(*this, *NewParm, *OldParm, Complain,
+                                      Kind, TemplateArgLoc))
+        return false;          
+    }
   }
-
+  
+  // Make sure we exhausted all of the arguments.
+  if (NewParm != NewParmEnd) {
+    if (Complain)
+      DiagnoseTemplateParameterListArityMismatch(*this, New, Old, Kind,
+                                                 TemplateArgLoc);
+      
+    return false;
+  }
+  
   return true;
 }
 
diff --git a/test/CXX/temp/temp.arg/temp.arg.template/p3-0x.cpp b/test/CXX/temp/temp.arg/temp.arg.template/p3-0x.cpp
new file mode 100644 (file)
index 0000000..794a050
--- /dev/null
@@ -0,0 +1,40 @@
+// RUN: %clang_cc1 -std=c++0x -fsyntax-only -verify %s
+
+template <class T> struct eval; // expected-note 3{{template is declared here}}
+
+template <template <class, class...> class TT, class T1, class... Rest> 
+struct eval<TT<T1, Rest...>> { };
+
+template <class T1> struct A; 
+template <class T1, class T2> struct B; 
+template <int N> struct C; 
+template <class T1, int N> struct D; 
+template <class T1, class T2, int N = 17> struct E;
+
+eval<A<int>> eA;
+eval<B<int, float>> eB;
+eval<C<17>> eC; // expected-error{{implicit instantiation of undefined template 'eval<C<17> >'}}
+eval<D<int, 17>> eD; // expected-error{{implicit instantiation of undefined template 'eval<D<int, 17> >'}}
+eval<E<int, float>> eE; // expected-error{{implicit instantiation of undefined template 'eval<E<int, float, 17> >}}
+
+template<template <int ...N> class TT> struct X0 { }; // expected-note{{previous non-type template parameter with type 'int' is here}}
+template<int I, int J, int ...Rest> struct X0a;
+template<int ...Rest> struct X0b;
+template<int I, long J> struct X0c; // expected-note{{template non-type parameter has a different type 'long' in template argument}}
+
+X0<X0a> inst_x0a;
+X0<X0b> inst_x0b;
+X0<X0c> inst_x0c; // expected-error{{template template argument has different template parameters than its corresponding template template parameter}}
+
+template<typename T, 
+         template <T ...N> class TT>  // expected-note{{previous non-type template parameter with type 'short' is here}}
+struct X1 { };
+template<int I, int J, int ...Rest> struct X1a;
+template<long I, long ...Rest> struct X1b;
+template<short I, short J> struct X1c;
+template<short I, long J> struct X1d; // expected-note{{template non-type parameter has a different type 'long' in template argument}}
+
+X1<int, X1a> inst_x1a;
+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}}
index f5453e08441bf559faeae6b9814e8fc37e70b052..d80182c1b657fcd74c6c22aaacaea89925fb951d 100644 (file)
@@ -14,6 +14,7 @@ struct is_same<T, T> {
 
 template<typename...> struct tuple { };
 template<int ...> struct int_tuple { };
+template<typename T, typename U> struct pair { };
 
 namespace Count {
   template<typename Head, typename ...Tail>
@@ -255,3 +256,19 @@ namespace FunctionTypes {
   int check3[Arity<int(float, double, long double...)>::value == 3? 1 : -1];
   Arity<int(float, double, long double, char)> check4; // expected-error{{implicit instantiation of undefined template 'FunctionTypes::Arity<int (float, double, long double, char)>'}}
 }
+
+namespace SuperReplace {
+  template<typename T>
+  struct replace_with_int {
+    typedef int type;
+  };
+  
+  template<template<typename ...> class TT, typename ...Types>
+  struct replace_with_int<TT<Types...>> {
+    typedef TT<typename replace_with_int<Types>::type...> type;
+  };
+  
+  int check0[is_same<replace_with_int<pair<tuple<float, double, short>,
+                                           pair<char, unsigned char>>>::type,
+                     pair<tuple<int, int, int>, pair<int, int>>>::value? 1 : -1];
+}