diff mbox

C++ PATCH for C++17 auto non-type template parameters

Message ID CADzB+2k76zpooAZdb6429Xh=r7xrecvgSfXo7D1xHODJXnSU7A@mail.gmail.com
State New
Headers show

Commit Message

Jason Merrill Nov. 9, 2016, 8 p.m. UTC
This was pretty straightforward; auto template parameters look more or
less the same as any other parameter with dependent type, so
substitution works without any changes, we just need to do auto
deduction in a couple of places.  The most involved bit was handling
deduction of a type parameter from the type of an array bound, which
only happens if deduction otherwise doesn't find a binding.  At first
I tried to work this into unify et al, but eventually decided to do it
in a (much simpler) separate function.

Tested x86_64-pc-linux-gnu, applying to trunk.
commit 04ade0b5013698fd1e458ef1425f7afd023feaf0
Author: Jason Merrill <jason@redhat.com>
Date:   Mon Nov 7 16:45:06 2016 -0800

            Implement P0127R2, Declaring non-type parameters with auto.
    
    gcc/cp/
            * cp-tree.h (enum auto_deduction_context): Add adc_unify.
            * decl.c (grokdeclarator): Allow 'auto' in C++17 template non-type
            parameter types.
            * pt.c (do_auto_deduction): Add outer_targs parameter.
            (convert_template_argument): Call do_auto_deduction.  If adc_unify,
            don't give up on dependent init.
            (unify): Likewise.  In C++17, walk into the type of a
            TEMPLATE_PARM_INDEX.
            (for_each_template_parm): Add any_fn parameter.
            (struct pair_fn_data): Likewise.
            (for_each_template_parm_r): Call it for any tree.  In C++17, walk
            into the type of a TEMPLATE_PARM_INDEX.
            (zero_r, array_deduction_r, try_array_deduction): New.
            (type_unification_real): Call try_array_deduction.
            (get_partial_spec_bindings): Likewise.
    gcc/c-family/
            * c-cppbuiltin.c (c_cpp_builtins): Define __cpp_template_auto.
diff mbox

Patch

diff --git a/gcc/c-family/c-cppbuiltin.c b/gcc/c-family/c-cppbuiltin.c
index 55dbf44..70eade1 100644
--- a/gcc/c-family/c-cppbuiltin.c
+++ b/gcc/c-family/c-cppbuiltin.c
@@ -942,6 +942,7 @@  c_cpp_builtins (cpp_reader *pfile)
 	  cpp_define (pfile, "__cpp_aggregate_bases=201603");
 	  cpp_define (pfile, "__cpp_deduction_guides=201606");
 	  cpp_define (pfile, "__cpp_noexcept_function_type=201510");
+	  cpp_define (pfile, "__cpp_template_auto=201606");
 	}
       if (flag_concepts)
 	cpp_define (pfile, "__cpp_concepts=201507");
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 20b52ad..9b5b5bc 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -5163,6 +5163,7 @@  enum auto_deduction_context
   adc_unspecified,   /* Not given */
   adc_variable_type, /* Variable initializer deduction */
   adc_return_type,   /* Return type deduction */
+  adc_unify,         /* Template argument deduction */
   adc_requirement    /* Argument dedution constraint */
 };
 
@@ -6088,7 +6089,8 @@  extern tree make_template_placeholder		(tree);
 extern tree do_auto_deduction                   (tree, tree, tree);
 extern tree do_auto_deduction                   (tree, tree, tree,
                                                  tsubst_flags_t,
-                                                 auto_deduction_context);
+                                                 auto_deduction_context,
+						 tree = NULL_TREE);
 extern tree type_uses_auto			(tree);
 extern tree type_uses_auto_or_concept		(tree);
 extern void append_type_to_template_for_access_check (tree, tree, tree,
diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c
index c0321f9..bd37faa 100644
--- a/gcc/cp/decl.c
+++ b/gcc/cp/decl.c
@@ -11135,7 +11135,8 @@  grokdeclarator (const cp_declarator *declarator,
       if (ctype || in_namespace)
 	error ("cannot use %<::%> in parameter declaration");
 
-      if (type_uses_auto (type))
+      if (type_uses_auto (type)
+	  && !(cxx_dialect >= cxx1z && template_parm_flag))
 	{
 	  if (cxx_dialect >= cxx14)
 	    error ("%<auto%> parameter not permitted in this context");
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index 3df71dd..64e566e 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -161,7 +161,7 @@  static tree convert_nontype_argument (tree, tree, tsubst_flags_t);
 static tree convert_template_argument (tree, tree, tree,
 				       tsubst_flags_t, int, tree);
 static tree for_each_template_parm (tree, tree_fn_t, void*,
-				    hash_set<tree> *, bool);
+				    hash_set<tree> *, bool, tree_fn_t = NULL);
 static tree expand_template_argument_pack (tree);
 static tree build_template_parm_index (int, int, int, tree, tree);
 static bool inline_needs_template_parms (tree, bool);
@@ -7299,6 +7299,13 @@  convert_template_argument (tree parm,
     {
       tree t = tsubst (TREE_TYPE (parm), args, complain, in_decl);
 
+      if (tree a = type_uses_auto (t))
+	{
+	  t = do_auto_deduction (t, arg, a, complain, adc_unspecified);
+	  if (t == error_mark_node)
+	    return error_mark_node;
+	}
+
       if (invalid_nontype_parm_type_p (t, complain))
 	return error_mark_node;
 
@@ -8789,6 +8796,7 @@  lookup_and_finish_template_variable (tree templ, tree targs,
 struct pair_fn_data
 {
   tree_fn_t fn;
+  tree_fn_t any_fn;
   void *data;
   /* True when we should also visit template parameters that occur in
      non-deduced contexts.  */
@@ -8811,11 +8819,15 @@  for_each_template_parm_r (tree *tp, int *walk_subtrees, void *d)
   do									\
     {									\
       result = for_each_template_parm (NODE, fn, data, pfd->visited,	\
-				       pfd->include_nondeduced_p);	\
+				       pfd->include_nondeduced_p,	\
+				       pfd->any_fn);			\
       if (result) goto out;						\
     }									\
   while (0)
 
+  if (pfd->any_fn && (*pfd->any_fn)(t, data))
+    return t;
+
   if (TYPE_P (t)
       && (pfd->include_nondeduced_p || TREE_CODE (t) != TYPENAME_TYPE))
     WALK_SUBTREE (TYPE_CONTEXT (t));
@@ -8880,7 +8892,8 @@  for_each_template_parm_r (tree *tp, int *walk_subtrees, void *d)
       if (pfd->include_nondeduced_p
 	  && for_each_template_parm (TYPE_VALUES_RAW (t), fn, data,
 				     pfd->visited, 
-				     pfd->include_nondeduced_p))
+				     pfd->include_nondeduced_p,
+				     pfd->any_fn))
 	return error_mark_node;
       break;
 
@@ -8911,6 +8924,12 @@  for_each_template_parm_r (tree *tp, int *walk_subtrees, void *d)
 	return t;
       else if (!fn)
 	return t;
+
+      /* In C++17 we can deduce a type argument from the type of a non-type
+	 argument.  */
+      if (cxx_dialect >= cxx1z
+	  && TREE_CODE (t) == TEMPLATE_PARM_INDEX)
+	WALK_SUBTREE (TREE_TYPE (t));
       break;
 
     case TEMPLATE_DECL:
@@ -8984,13 +9003,15 @@  for_each_template_parm_r (tree *tp, int *walk_subtrees, void *d)
 static tree
 for_each_template_parm (tree t, tree_fn_t fn, void* data,
 			hash_set<tree> *visited,
-			bool include_nondeduced_p)
+			bool include_nondeduced_p,
+			tree_fn_t any_fn)
 {
   struct pair_fn_data pfd;
   tree result;
 
   /* Set up.  */
   pfd.fn = fn;
+  pfd.any_fn = any_fn;
   pfd.data = data;
   pfd.include_nondeduced_p = include_nondeduced_p;
 
@@ -18559,6 +18580,53 @@  unify_one_argument (tree tparms, tree targs, tree parm, tree arg,
   return unify (tparms, targs, parm, arg, arg_strict, explain_p);
 }
 
+/* for_each_template_parm callback that always returns 0.  */
+
+static int
+zero_r (tree, void *)
+{
+  return 0;
+}
+
+/* for_each_template_parm any_fn callback to handle deduction of a template
+   type argument from the type of an array bound.  */
+
+static int
+array_deduction_r (tree t, void *data)
+{
+  tree_pair_p d = (tree_pair_p)data;
+  tree &tparms = d->purpose;
+  tree &targs = d->value;
+
+  if (TREE_CODE (t) == ARRAY_TYPE)
+    if (tree dom = TYPE_DOMAIN (t))
+      if (tree max = TYPE_MAX_VALUE (dom))
+	{
+	  if (TREE_CODE (max) == MINUS_EXPR)
+	    max = TREE_OPERAND (max, 0);
+	  if (TREE_CODE (max) == TEMPLATE_PARM_INDEX)
+	    unify (tparms, targs, TREE_TYPE (max), size_type_node,
+		   UNIFY_ALLOW_NONE, /*explain*/false);
+	}
+
+  /* Keep walking.  */
+  return 0;
+}
+
+/* Try to deduce any not-yet-deduced template type arguments from the type of
+   an array bound.  This is handled separately from unify because 14.8.2.5 says
+   "The type of a type parameter is only deduced from an array bound if it is
+   not otherwise deduced."  */
+
+static void
+try_array_deduction (tree tparms, tree targs, tree parm)
+{
+  tree_pair_s data = { tparms, targs };
+  hash_set<tree> visited;
+  for_each_template_parm (parm, zero_r, &data, &visited,
+			  /*nondeduced*/false, array_deduction_r);
+}
+
 /* Most parms like fn_type_unification.
 
    If SUBR is 1, we're being called recursively (to unify the
@@ -18688,6 +18756,7 @@  type_unification_real (tree tparms,
       tsubst_flags_t complain = (explain_p
 				 ? tf_warning_or_error
 				 : tf_none);
+      bool tried_array_deduction = (cxx_dialect < cxx1z);
 
       for (i = 0; i < ntparms; i++)
 	{
@@ -18706,6 +18775,15 @@  type_unification_real (tree tparms,
 	    continue;
 	  tparm = TREE_VALUE (tparm);
 
+	  if (TREE_CODE (tparm) == TYPE_DECL
+	      && !tried_array_deduction)
+	    {
+	      try_array_deduction (tparms, targs, xparms);
+	      tried_array_deduction = true;
+	      if (TREE_VEC_ELT (targs, i))
+		continue;
+	    }
+
 	  /* If this is an undeduced nontype parameter that depends on
 	     a type parameter, try another pass; its type may have been
 	     deduced from a later argument than the one from which
@@ -19378,8 +19456,8 @@  template_parm_level_and_index (tree parm, int* level, int* index)
 /* Unifies the remaining arguments in PACKED_ARGS with the pack
    expansion at the end of PACKED_PARMS. Returns 0 if the type
    deduction succeeds, 1 otherwise. STRICT is the same as in
-   unify. CALL_ARGS_P is true iff PACKED_ARGS is actually a function
-   call argument list. We'll need to adjust the arguments to make them
+   fn_type_unification. CALL_ARGS_P is true iff PACKED_ARGS is actually a
+   function call argument list. We'll need to adjust the arguments to make them
    types. SUBR tells us if this is from a recursive call to
    type_unification_real, or for comparing two template argument
    lists. */
@@ -19680,6 +19758,9 @@  unify (tree tparms, tree targs, tree parm, tree arg, int strict,
   tree targ;
   tree tparm;
   int strict_in = strict;
+  tsubst_flags_t complain = (explain_p
+			     ? tf_warning_or_error
+			     : tf_none);
 
   /* I don't think this will do the right thing with respect to types.
      But the only case I've seen it in so far has been array bounds, where
@@ -19897,9 +19978,7 @@  unify (tree tparms, tree targs, tree parm, tree arg, int strict,
 	    if (coerce_template_parms (parm_parms,
                                        full_argvec,
 				       TYPE_TI_TEMPLATE (parm),
-				       (explain_p
-					? tf_warning_or_error
-					: tf_none),
+				       complain,
 				       /*require_all_args=*/true,
 				       /*use_default_args=*/false)
 		== error_mark_node)
@@ -20046,6 +20125,18 @@  unify (tree tparms, tree targs, tree parm, tree arg, int strict,
 	  return x;
 	}
 
+      if (cxx_dialect >= cxx1z
+	  /* We deduce from array bounds in try_array_deduction.  */
+	  && !(strict & UNIFY_ALLOW_INTEGER)
+	  && uses_template_parms (TREE_TYPE (parm))
+	  && !type_uses_auto (TREE_TYPE (parm)))
+	{
+	  tree atype = TREE_TYPE (arg);
+	  RECUR_AND_CHECK_FAILURE (tparms, targs,
+				   TREE_TYPE (parm), atype,
+				   UNIFY_ALLOW_NONE, explain_p);
+	}
+
       /* [temp.deduct.type] If, in the declaration of a function template
 	 with a non-type template-parameter, the non-type
 	 template-parameter is used in an expression in the function
@@ -20055,6 +20146,13 @@  unify (tree tparms, tree targs, tree parm, tree arg, int strict,
 	 deduced from an array bound may be of any integral type.
 	 The non-type parameter might use already deduced type parameters.  */
       tparm = tsubst (TREE_TYPE (parm), targs, 0, NULL_TREE);
+      if (tree a = type_uses_auto (tparm))
+	{
+	  tparm = do_auto_deduction (tparm, arg, a, complain, adc_unify);
+	  if (tparm == error_mark_node)
+	    return 1;
+	}
+
       if (!TREE_TYPE (arg))
 	/* Template-parameter dependent expression.  Just accept it for now.
 	   It will later be processed in convert_template_argument.  */
@@ -21015,6 +21113,8 @@  get_partial_spec_bindings (tree tmpl, tree spec_tmpl, tree args)
   else
     deduced_args = innermost_deduced_args;
 
+  bool tried_array_deduction = (cxx_dialect < cxx1z);
+ again:
   if (unify (tparms, deduced_args,
 	     INNERMOST_TEMPLATE_ARGS (spec_args),
 	     INNERMOST_TEMPLATE_ARGS (args),
@@ -21023,7 +21123,17 @@  get_partial_spec_bindings (tree tmpl, tree spec_tmpl, tree args)
 
   for (i =  0; i < ntparms; ++i)
     if (! TREE_VEC_ELT (innermost_deduced_args, i))
-      return NULL_TREE;
+      {
+	if (!tried_array_deduction)
+	  {
+	    try_array_deduction (tparms, innermost_deduced_args,
+				 INNERMOST_TEMPLATE_ARGS (spec_args));
+	    tried_array_deduction = true;
+	    if (TREE_VEC_ELT (innermost_deduced_args, i))
+	      goto again;
+	  }
+	return NULL_TREE;
+      }
 
   tree tinst = build_tree_list (spec_tmpl, deduced_args);
   if (!push_tinst_level (tinst))
@@ -24607,14 +24717,16 @@  do_auto_deduction (tree type, tree init, tree auto_node)
 
 tree
 do_auto_deduction (tree type, tree init, tree auto_node,
-                   tsubst_flags_t complain, auto_deduction_context context)
+                   tsubst_flags_t complain, auto_deduction_context context,
+		   tree outer_targs)
 {
   tree targs;
 
   if (init == error_mark_node)
     return error_mark_node;
 
-  if (type_dependent_expression_p (init))
+  if (type_dependent_expression_p (init)
+      && context != adc_unify)
     /* Defining a subset of type-dependent expressions that we can deduce
        from ahead of time isn't worth the trouble.  */
     return type;
@@ -24733,6 +24845,7 @@  do_auto_deduction (tree type, tree init, tree auto_node,
                 switch (context)
                   {
                   case adc_unspecified:
+		  case adc_unify:
                     error("placeholder constraints not satisfied");
                     break;
                   case adc_variable_type:
@@ -24754,8 +24867,9 @@  do_auto_deduction (tree type, tree init, tree auto_node,
           }
       }
 
-  if (processing_template_decl)
-    targs = add_to_template_args (current_template_args (), targs);
+  if (processing_template_decl && context != adc_unify)
+    outer_targs = current_template_args ();
+  targs = add_to_template_args (outer_targs, targs);
   return tsubst (type, targs, complain, NULL_TREE);
 }
 
diff --git a/gcc/testsuite/g++.dg/cpp0x/auto9.C b/gcc/testsuite/g++.dg/cpp0x/auto9.C
index 9001f78..771ce0e 100644
--- a/gcc/testsuite/g++.dg/cpp0x/auto9.C
+++ b/gcc/testsuite/g++.dg/cpp0x/auto9.C
@@ -111,7 +111,7 @@  badthrow2 () throw (auto &)			// { dg-error "invalid use of|expected" }
 {
 }
 
-template <auto V = 4> struct G {};		// { dg-error "auto" }
+template <auto V = 4> struct G {};		// { dg-error "auto" "" { target { ! c++1z } } }
 
 template <typename T> struct H { H (); ~H (); };
 H<auto> h;					// { dg-error "invalid|initializer" }
diff --git a/gcc/testsuite/g++.dg/cpp1z/feat-cxx1z.C b/gcc/testsuite/g++.dg/cpp1z/feat-cxx1z.C
index f4658a9..adbc32c 100644
--- a/gcc/testsuite/g++.dg/cpp1z/feat-cxx1z.C
+++ b/gcc/testsuite/g++.dg/cpp1z/feat-cxx1z.C
@@ -368,6 +368,12 @@ 
 #  error "__cpp_aligned_new != 201606"
 #endif
 
+#ifndef __cpp_template_auto
+#  error "__cpp_template_auto"
+#elif __cpp_template_auto != 201606
+#  error "__cpp_template_auto != 201606"
+#endif
+
 #ifndef __cpp_inline_variables
 #  error "__cpp_inline_variables"
 #elif __cpp_inline_variables != 201606
diff --git a/gcc/testsuite/g++.dg/cpp1z/nontype-auto1.C b/gcc/testsuite/g++.dg/cpp1z/nontype-auto1.C
new file mode 100644
index 0000000..9d05074
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1z/nontype-auto1.C
@@ -0,0 +1,13 @@ 
+// Testcase from P0127R2
+// { dg-options -std=c++1z }
+
+template <long n> struct A { };
+
+template <class T> struct C;
+template <class T, T n> struct C<A<n>>
+{
+    using Q = T;
+};
+
+typedef long R;
+typedef C<A<2>>::Q R;  // OK; T was deduced to long from the template argument value in the type A<2>
diff --git a/gcc/testsuite/g++.dg/cpp1z/nontype-auto2.C b/gcc/testsuite/g++.dg/cpp1z/nontype-auto2.C
new file mode 100644
index 0000000..23dac8a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1z/nontype-auto2.C
@@ -0,0 +1,10 @@ 
+// Testcase from P0127R2
+// { dg-options -std=c++1z }
+
+template <typename T> struct S;
+template <typename T, T n> struct S<int[n]> {
+  using Q = T;
+};
+
+typedef S<int[42]>::Q V;
+typedef decltype(sizeof 0) V;  // OK; T was deduced to std::size_t from the type int[42]
diff --git a/gcc/testsuite/g++.dg/cpp1z/nontype-auto3.C b/gcc/testsuite/g++.dg/cpp1z/nontype-auto3.C
new file mode 100644
index 0000000..00b56b1
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1z/nontype-auto3.C
@@ -0,0 +1,15 @@ 
+// Testcase from P0127R2
+// { dg-options -std=c++1z }
+
+template<auto n> struct B { decltype(n) f = n; };
+B<5> b1;   // OK: template parameter type is int
+B<'a'> b2; // OK: template parameter type is char
+B<2.5> b3; // { dg-error "" } template parameter type cannot be double
+
+template <auto n> void f(B<n>) { }
+
+int main()
+{
+  f(B<42>());
+  f(B<'a'>());
+}
diff --git a/gcc/testsuite/g++.dg/cpp1z/nontype-auto4.C b/gcc/testsuite/g++.dg/cpp1z/nontype-auto4.C
new file mode 100644
index 0000000..80bbbed
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1z/nontype-auto4.C
@@ -0,0 +1,14 @@ 
+// { dg-options -std=c++1z }
+
+template <class T, T n> void f(T, int (&)[n]);
+template <class T, T n> void g(int (&)[n], T);
+template <class T, T n> void h(int (&)[n]);
+
+int main()
+{
+  const int i = 42;
+  int ar[i];
+  h(ar);
+  f(i, ar);
+  g(ar, i);
+}
diff --git a/gcc/testsuite/g++.dg/cpp1z/nontype-auto5.C b/gcc/testsuite/g++.dg/cpp1z/nontype-auto5.C
new file mode 100644
index 0000000..aa5ca7f0
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1z/nontype-auto5.C
@@ -0,0 +1,15 @@ 
+// { dg-options -std=c++1z }
+
+template <class T> struct A
+{
+  template <auto v>    struct Y;
+  template <auto* p>   struct Y<p> { using type1 = decltype (p); };
+  template <auto** pp> struct Y<pp> { using type2 = decltype (pp); };
+};
+
+int i;
+int *p;
+
+A<void>::Y<&i>::type1 t1;
+A<void>::Y<&p>::type2 t2;
+
diff --git a/gcc/testsuite/g++.dg/cpp1z/nontype-auto6.C b/gcc/testsuite/g++.dg/cpp1z/nontype-auto6.C
new file mode 100644
index 0000000..cbf1b46
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1z/nontype-auto6.C
@@ -0,0 +1,8 @@ 
+// { dg-do compile { target c++11 } }
+
+template <int N> struct A;
+template <typename T, T N> int foo(A<N> *) = delete;
+void foo(void *);
+void bar(A<0> *p) {
+  foo(p);			// { dg-error "" "" { target c++1z } }
+}
diff --git a/gcc/testsuite/g++.dg/template/partial5.C b/gcc/testsuite/g++.dg/template/partial5.C
index 979e4c6..2f400f7 100644
--- a/gcc/testsuite/g++.dg/template/partial5.C
+++ b/gcc/testsuite/g++.dg/template/partial5.C
@@ -14,7 +14,7 @@  template<typename T, typename T::foo V>
 struct Y { };
 
 template<typename T, typename U, U v>
-struct Y<T, v> { }; // { dg-error "not deducible|U" }
+struct Y<T, v> { }; // { dg-error "not deducible|U" "" { target { ! c++1z } } }
 
 
 template<typename T, T V>