C++ PATCH for c++/82249, wrong mismatched pack length error

Message ID CADzB+2n=oW9GV1mXoDj5j3DdepeFLcQDNg84X7WfUJnXQBcwQQ@mail.gmail.com
State New
Headers show
Series
  • C++ PATCH for c++/82249, wrong mismatched pack length error
Related show

Commit Message

Jason Merrill Jan. 24, 2018, 2:04 p.m.
tsubst_pack_expansion already knows how to deal with partial
instantiation of a pack expansion, where we end up with arguments for
some packs but not others, but my recent lambda work made this come up
in a new situation: within a function, where we need to deal with
function parameter packs as well.  When we don't have arguments for a
template parameter pack we just don't have an argument pack, but for a
function parameter pack we instead end up with an argument pack
containing just an expansion of the parameter pack itself, so we need
to recognize that case and treat it like the usual unsubstituted case.
We then also need to handle the result of tsubst_expr in this case.

Tested x86_64-pc-linux-gnu, applying to trunk.
commit 329e2e39748f6f60630f247ecd75a6556f9f72e9
Author: Jason Merrill <jason@redhat.com>
Date:   Tue Jan 23 17:04:56 2018 -0500

            PR c++/82249 - wrong mismatched pack length error.
    
            * pt.c (extract_fnparm_pack, tsubst_pack_expansion): Handle
            unsubstituted function parameter pack.

Patch

diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index d39b54ed408..abfdbd96ae8 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -10961,7 +10961,12 @@  extract_fnparm_pack (tree tmpl_parm, tree *spec_p)
   parmvec = make_tree_vec (len);
   spec_parm = *spec_p;
   for (i = 0; i < len; i++, spec_parm = DECL_CHAIN (spec_parm))
-    TREE_VEC_ELT (parmvec, i) = spec_parm;
+    {
+      tree elt = spec_parm;
+      if (DECL_PACK_P (elt))
+	elt = make_pack_expansion (elt);
+      TREE_VEC_ELT (parmvec, i) = elt;
+    }
 
   /* Build the argument packs.  */
   SET_ARGUMENT_PACK_ARGS (argpack, parmvec);
@@ -11414,6 +11419,7 @@  tsubst_pack_expansion (tree t, tree args, tsubst_flags_t complain,
   tree pattern;
   tree pack, packs = NULL_TREE;
   bool unsubstituted_packs = false;
+  bool unsubstituted_fn_pack = false;
   int i, len = -1;
   tree result;
   hash_map<tree, tree> *saved_local_specializations = NULL;
@@ -11484,6 +11490,13 @@  tsubst_pack_expansion (tree t, tree args, tsubst_flags_t complain,
 	      else
 		arg_pack = make_fnparm_pack (arg_pack);
 	    }
+	  else if (argument_pack_element_is_expansion_p (arg_pack, 0))
+	    /* This argument pack isn't fully instantiated yet.  We set this
+	       flag rather than clear arg_pack because we do want to do the
+	       optimization below, and we don't want to substitute directly
+	       into the pattern (as that would expose a NONTYPE_ARGUMENT_PACK
+	       where it isn't expected).  */
+	    unsubstituted_fn_pack = true;
 	}
       else if (TREE_CODE (parm_pack) == FIELD_DECL)
 	arg_pack = tsubst_copy (parm_pack, args, complain, in_decl);
@@ -11521,7 +11534,8 @@  tsubst_pack_expansion (tree t, tree args, tsubst_flags_t complain,
 
           if (len < 0)
 	    len = my_len;
-          else if (len != my_len)
+          else if (len != my_len
+		   && !unsubstituted_fn_pack)
             {
 	      if (!(complain & tf_error))
 		/* Fail quietly.  */;
@@ -11574,7 +11588,8 @@  tsubst_pack_expansion (tree t, tree args, tsubst_flags_t complain,
 
   /* We cannot expand this expansion expression, because we don't have
      all of the argument packs we need.  */
-  if (use_pack_expansion_extra_args_p (packs, len, unsubstituted_packs))
+  if (use_pack_expansion_extra_args_p (packs, len, (unsubstituted_packs
+						    || unsubstituted_fn_pack)))
     {
       /* We got some full packs, but we can't substitute them in until we
 	 have values for all the packs.  So remember these until then.  */
diff --git a/gcc/testsuite/g++.dg/cpp1y/lambda-generic-variadic7.C b/gcc/testsuite/g++.dg/cpp1y/lambda-generic-variadic7.C
new file mode 100644
index 00000000000..5c5af1441c0
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1y/lambda-generic-variadic7.C
@@ -0,0 +1,19 @@ 
+// PR c++/82249
+// { dg-do compile { target c++14 } }
+
+template<class T, class U> T calc (T t, U u) { return t; }
+template <class... Ts> void sink(Ts...);
+
+template < typename ... Ds >
+void f(Ds ...) {
+  [](auto ... n){
+    sink (calc(n, Ds{}) ...);
+  }(Ds{} ...);
+}
+
+
+int main(){
+  f();      // Wrong error
+  f(0, 0);  // Wrong error
+  f(0);     // ICE
+}