[cfe-commits] r149685 - in /cfe/trunk: lib/Sema/SemaTemplate.cpp test/CXX/temp/temp.decls/temp.variadic/fixed-expansion.cpp

Douglas Gregor dgregor at apple.com
Thu Feb 2 23:34:46 PST 2012


Author: dgregor
Date: Fri Feb  3 01:34:46 2012
New Revision: 149685

URL: http://llvm.org/viewvc/llvm-project?rev=149685&view=rev
Log:
Implement support for a pack expansion into a fixed-length
template. Such pack expansions can easily fail at template
instantiation time, if the expanded parameter packs are of the wrong
length. Fixes <rdar://problem/10040867>, PR9021, and the example that
came up today at Going Native.

Added:
    cfe/trunk/test/CXX/temp/temp.decls/temp.variadic/fixed-expansion.cpp   (with props)
Modified:
    cfe/trunk/lib/Sema/SemaTemplate.cpp

Modified: cfe/trunk/lib/Sema/SemaTemplate.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaTemplate.cpp?rev=149685&r1=149684&r2=149685&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaTemplate.cpp (original)
+++ cfe/trunk/lib/Sema/SemaTemplate.cpp Fri Feb  3 01:34:46 2012
@@ -1887,7 +1887,6 @@
   }
 }
 
-
 QualType Sema::CheckTemplateIdType(TemplateName Name,
                                    SourceLocation TemplateLoc,
                                    TemplateArgumentListInfo &TemplateArgs) {
@@ -1923,9 +1922,6 @@
                                 false, Converted))
     return QualType();
 
-  assert((Converted.size() == Template->getTemplateParameters()->size()) &&
-         "Converted template argument list is too short!");
-
   QualType CanonType;
 
   bool InstantiationDependent = false;
@@ -2866,6 +2862,29 @@
   return false;
 }
 
+/// \brief Diagnose an arity mismatch in the 
+static bool diagnoseArityMismatch(Sema &S, TemplateDecl *Template,
+                                  SourceLocation TemplateLoc,
+                                  TemplateArgumentListInfo &TemplateArgs) {
+  TemplateParameterList *Params = Template->getTemplateParameters();
+  unsigned NumParams = Params->size();
+  unsigned NumArgs = TemplateArgs.size();
+
+  SourceRange Range;
+  if (NumArgs > NumParams)
+    Range = SourceRange(TemplateArgs[NumParams].getLocation(), 
+                        TemplateArgs.getRAngleLoc());
+  S.Diag(TemplateLoc, diag::err_template_arg_list_different_arity)
+    << (NumArgs > NumParams)
+    << (isa<ClassTemplateDecl>(Template)? 0 :
+        isa<FunctionTemplateDecl>(Template)? 1 :
+        isa<TemplateTemplateParmDecl>(Template)? 2 : 3)
+    << Template << Range;
+  S.Diag(Template->getLocation(), diag::note_template_decl_here)
+    << Params->getSourceRange();
+  return true;
+}
+
 /// \brief Check that the given template argument list is well-formed
 /// for specializing the given template.
 bool Sema::CheckTemplateArgumentList(TemplateDecl *Template,
@@ -2883,26 +2902,6 @@
   bool HasParameterPack =
     NumParams > 0 && Params->getParam(NumParams - 1)->isTemplateParameterPack();
 
-  if ((NumArgs > NumParams && !HasParameterPack) ||
-      (NumArgs < Params->getMinRequiredArguments() &&
-       !PartialTemplateArgs)) {
-    // FIXME: point at either the first arg beyond what we can handle,
-    // or the '>', depending on whether we have too many or too few
-    // arguments.
-    SourceRange Range;
-    if (NumArgs > NumParams)
-      Range = SourceRange(TemplateArgs[NumParams].getLocation(), RAngleLoc);
-    Diag(TemplateLoc, diag::err_template_arg_list_different_arity)
-      << (NumArgs > NumParams)
-      << (isa<ClassTemplateDecl>(Template)? 0 :
-          isa<FunctionTemplateDecl>(Template)? 1 :
-          isa<TemplateTemplateParmDecl>(Template)? 2 : 3)
-      << Template << Range;
-    Diag(Template->getLocation(), diag::note_template_decl_here)
-      << Params->getSourceRange();
-    Invalid = true;
-  }
-
   // C++ [temp.arg]p1:
   //   [...] The type and form of each template-argument specified in
   //   a template-id shall match the type and form specified for the
@@ -2914,10 +2913,12 @@
                                ParamEnd = Params->end();
   unsigned ArgIdx = 0;
   LocalInstantiationScope InstScope(*this, true);
+  bool SawPackExpansion = false;
   while (Param != ParamEnd) {
     if (ArgIdx < NumArgs) {
       // If we have an expanded parameter pack, make sure we don't have too
       // many arguments.
+      // FIXME: This really should fall out from the normal arity checking.
       if (NonTypeTemplateParmDecl *NTTP
                                 = dyn_cast<NonTypeTemplateParmDecl>(*Param)) {
         if (NTTP->isExpandedParameterPack() &&
@@ -2951,6 +2952,15 @@
         // Move to the next template parameter.
         ++Param;
       }
+      
+      // If this template argument is a pack expansion, record that fact
+      // and break out; we can't actually check any more.
+      if (TemplateArgs[ArgIdx].getArgument().isPackExpansion()) {
+        SawPackExpansion = true;
+        ++ArgIdx;
+        break;
+      }
+      
       ++ArgIdx;
       continue;
     }
@@ -2970,7 +2980,7 @@
     if ((*Param)->isTemplateParameterPack())
       break;
     
-    // We have a default template argument that we will use.
+    // Check whether we have a default argument.
     TemplateArgumentLoc Arg;
 
     // Retrieve the default template argument from the template
@@ -2979,10 +2989,9 @@
     // (when the template parameter was part of a nested template) into
     // the default argument.
     if (TemplateTypeParmDecl *TTP = dyn_cast<TemplateTypeParmDecl>(*Param)) {
-      if (!TTP->hasDefaultArgument()) {
-        assert(Invalid && "Missing default argument");
-        break;
-      }
+      if (!TTP->hasDefaultArgument())
+        return diagnoseArityMismatch(*this, Template, TemplateLoc, 
+                                     TemplateArgs);
 
       TypeSourceInfo *ArgType = SubstDefaultTemplateArgument(*this,
                                                              Template,
@@ -2997,10 +3006,9 @@
                                 ArgType);
     } else if (NonTypeTemplateParmDecl *NTTP
                  = dyn_cast<NonTypeTemplateParmDecl>(*Param)) {
-      if (!NTTP->hasDefaultArgument()) {
-        assert(Invalid && "Missing default argument");
-        break;
-      }
+      if (!NTTP->hasDefaultArgument())
+        return diagnoseArityMismatch(*this, Template, TemplateLoc, 
+                                     TemplateArgs);
 
       ExprResult E = SubstDefaultTemplateArgument(*this, Template,
                                                               TemplateLoc,
@@ -3016,10 +3024,9 @@
       TemplateTemplateParmDecl *TempParm
         = cast<TemplateTemplateParmDecl>(*Param);
 
-      if (!TempParm->hasDefaultArgument()) {
-        assert(Invalid && "Missing default argument");
-        break;
-      }
+      if (!TempParm->hasDefaultArgument())
+        return diagnoseArityMismatch(*this, Template, TemplateLoc, 
+                                     TemplateArgs);
 
       NestedNameSpecifierLoc QualifierLoc;
       TemplateName Name = SubstDefaultTemplateArgument(*this, Template,
@@ -3057,11 +3064,65 @@
     ++ArgIdx;
   }
 
+  // If we saw a pack expansion, then directly convert the remaining arguments,
+  // because we don't know what parameters they'll match up with.
+  if (SawPackExpansion) {
+    bool AddToArgumentPack
+      = Param != ParamEnd && (*Param)->isTemplateParameterPack();
+    while (ArgIdx < NumArgs) {
+      if (AddToArgumentPack)
+        ArgumentPack.push_back(TemplateArgs[ArgIdx].getArgument());
+      else
+        Converted.push_back(TemplateArgs[ArgIdx].getArgument());
+      ++ArgIdx;
+    }
+
+    // Push the argument pack onto the list of converted arguments.
+    if (AddToArgumentPack) {
+      if (ArgumentPack.empty())
+        Converted.push_back(TemplateArgument(0, 0));
+      else {
+        Converted.push_back(
+          TemplateArgument::CreatePackCopy(Context,
+                                           ArgumentPack.data(),
+                                           ArgumentPack.size()));
+        ArgumentPack.clear();
+      }      
+    }
+
+    return Invalid;
+  }
+
+  // If we have any leftover arguments, then there were too many arguments.
+  // Complain and fail.
+  if (ArgIdx < NumArgs)
+    return diagnoseArityMismatch(*this, Template, TemplateLoc, TemplateArgs);
+  
+  // If we have an expanded parameter pack, make sure we don't have too
+  // many arguments.
+  // FIXME: This really should fall out from the normal arity checking.
+  if (Param != ParamEnd) {
+    if (NonTypeTemplateParmDecl *NTTP
+          = dyn_cast<NonTypeTemplateParmDecl>(*Param)) {
+      if (NTTP->isExpandedParameterPack() &&
+          ArgumentPack.size() < NTTP->getNumExpansionTypes()) {
+        Diag(TemplateLoc, diag::err_template_arg_list_different_arity)
+          << false
+          << (isa<ClassTemplateDecl>(Template)? 0 :
+              isa<FunctionTemplateDecl>(Template)? 1 :
+              isa<TemplateTemplateParmDecl>(Template)? 2 : 3)
+          << Template;
+        Diag(Template->getLocation(), diag::note_template_decl_here)
+          << Params->getSourceRange();
+        return true;
+      }
+    }
+  }
+  
   // Form argument packs for each of the parameter packs remaining.
   while (Param != ParamEnd) {
     // If we're checking a partial list of template arguments, don't fill
     // in arguments for non-template parameter packs.
-
     if ((*Param)->isTemplateParameterPack()) {
       if (!HasParameterPack)
         return true;
@@ -3073,7 +3134,8 @@
                                                          ArgumentPack.size()));
         ArgumentPack.clear();
       }
-    }
+    } else if (!PartialTemplateArgs)
+      return diagnoseArityMismatch(*this, Template, TemplateLoc, TemplateArgs);
 
     ++Param;
   }
@@ -4961,9 +5023,6 @@
                                 TemplateArgs, false, Converted))
     return true;
 
-  assert((Converted.size() == ClassTemplate->getTemplateParameters()->size()) &&
-         "Converted template argument list is too short!");
-
   // Find the class template (partial) specialization declaration that
   // corresponds to these arguments.
   if (isPartialSpecialization) {
@@ -5955,9 +6014,6 @@
                                 TemplateArgs, false, Converted))
     return true;
 
-  assert((Converted.size() == ClassTemplate->getTemplateParameters()->size()) &&
-         "Converted template argument list is too short!");
-
   // Find the class template specialization declaration that
   // corresponds to these arguments.
   void *InsertPos = 0;

Added: cfe/trunk/test/CXX/temp/temp.decls/temp.variadic/fixed-expansion.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/temp/temp.decls/temp.variadic/fixed-expansion.cpp?rev=149685&view=auto
==============================================================================
--- cfe/trunk/test/CXX/temp/temp.decls/temp.variadic/fixed-expansion.cpp (added)
+++ cfe/trunk/test/CXX/temp/temp.decls/temp.variadic/fixed-expansion.cpp Fri Feb  3 01:34:46 2012
@@ -0,0 +1,120 @@
+// RUN: %clang_cc1 -fsyntax-only -std=c++11 -verify %s
+
+template<typename T, typename U> struct pair { };
+template<typename ...Types> struct tuple { };
+
+template<typename T, typename U>
+struct is_same {
+  static const bool value = false;
+};
+
+template<typename T>
+struct is_same<T, T> {
+  static const bool value = true;
+};
+
+namespace ExpandIntoFixed {
+  template<typename T, 
+           typename U, 
+           typename V = pair<T, U>, 
+           typename W = V*> 
+  class X0 { };
+
+  template<typename ...Ts>
+  class X1 {
+  public:
+    typedef X0<Ts...> type;
+  };
+
+  static_assert(is_same<X1<int, int>::type, 
+                        X0<int, int, pair<int, int>, pair<int, int>*>>::value,
+                "fails with two default arguments");
+
+  static_assert(is_same<X1<int, int, float>::type, 
+                        X0<int, int, float, float*>>::value,
+                "fails with one default argument");
+
+  static_assert(is_same<X1<int, int, float, double>::type, 
+                        X0<int, int, float, double>>::value,
+                "fails with no default arguments");
+}
+
+namespace ExpandIntoFixedShifted {
+  template<typename T, 
+           typename U, 
+           typename V = pair<T, U>, 
+           typename W = V*> 
+  class X0 { };
+
+  template<typename ...Ts>
+  class X1 {
+  public:
+    typedef X0<char, Ts...> type;
+  };
+
+  static_assert(is_same<X1<int>::type, 
+                        X0<char, int, pair<char, int>, pair<char, int>*>>::value,
+                "fails with two default arguments");
+
+  static_assert(is_same<X1<int, float>::type, 
+                        X0<char, int, float, float*>>::value,
+                "fails with one default argument");
+
+  static_assert(is_same<X1<int, float, double>::type, 
+                        X0<char, int, float, double>>::value,
+                "fails with no default arguments");
+}
+
+namespace Deduction {
+  template <typename X, typename Y = double> struct Foo {};
+  template <typename ...Args> tuple<Args...> &foo(Foo<Args...>);
+
+  void call_foo(Foo<int, float> foo_if, Foo<int> foo_i) {
+    tuple<int, float> &t1 = foo(foo_if);
+    tuple<int, double> &t2 = foo(foo_i);
+  }
+}
+
+namespace PR9021a {
+  template<typename, typename> 
+  struct A { };
+
+  template<typename ...T>
+  struct B { 
+    A<T...> a1;
+  };
+
+  void test() {
+    B<int, int> c;
+  }
+}
+
+namespace PR9021b {
+  template<class, class>
+  struct t2
+  {
+    
+  };
+  
+  template<template<class...> class M>
+  struct m
+  {
+    template<class... B>
+    using inner = M<B...>;
+  };
+
+  m<t2> sta2;
+}
+
+namespace PartialSpecialization {
+  template<typename T, typename U, typename V = U>
+  struct X0; // expected-note{{template is declared here}}
+
+  template<typename ...Ts>
+  struct X0<Ts...> {
+  };
+
+  X0<int> x0i; // expected-error{{too few template arguments for class template 'X0'}}
+  X0<int, float> x0if;
+  X0<int, float, double> x0ifd;
+}

Propchange: cfe/trunk/test/CXX/temp/temp.decls/temp.variadic/fixed-expansion.cpp
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: cfe/trunk/test/CXX/temp/temp.decls/temp.variadic/fixed-expansion.cpp
------------------------------------------------------------------------------
    svn:keywords = Id

Propchange: cfe/trunk/test/CXX/temp/temp.decls/temp.variadic/fixed-expansion.cpp
------------------------------------------------------------------------------
    svn:mime-type = text/plain





More information about the cfe-commits mailing list