diff mbox

[RFC] Make generic functions work with 'auto' return types (PR64969)

Message ID 54E79213.7060809@gigawatt.nl
State New
Headers show

Commit Message

Harald van Dijk Feb. 20, 2015, 7:59 p.m. UTC
Hello all,

As noted in the bug, and a couple of times on StackOverflow, generic 
functions and 'auto' return types do not play nice together. This 
happens because the parser does not expect a level of template 
parameters to be added to a function after it has already parsed 'auto' 
in a return type. To make it work requires either patching up existing 
references to 'auto' after that additional level of template parameters 
gets added, or change the representation of 'auto' to something that 
does not depend on the level of template parameters. The latter seemed 
easiest: a fixed level of 0 (otherwise unused) works well.

The added test used to fail, passes with this patch, and there's no 
other change in test results (--enable-languages=c,c++).

Does this approach look correct?

Does the patch itself look correct?

Is fixing this appropriate for GCC 5.0? It's not a regression, it fails 
on 4.9 (the first version that added support for generic functions) too. 
I'm happy to wait until after 5.0 is released.

Note: I don't have any copyright assignment, and getting that may be 
troublesome right now. However, a copyright disclaimer for this change 
should not be a problem. Who should I contact about that?

Cheers,
Harald van Dijk
diff mbox

Patch


    gcc/cp/
    
        PR 64969
        * cp-tree.h (tsubst_flags): Add tf_auto.
        * pt.c (make_auto_1): Encode auto with a level of 0.
        (tsubst): Change auto to its proper level. Ignore unless tf_auto.
        (tsubst_pack_expansion): Add sanity check.
        (reduce_template_parm_level): Ignore auto.
        (listify_autos): Pass tf_auto.
        (do_auto_deduction): Likewise.
        (splice_late_return_type): Likewise.
    
    gcc/testsuite/
    
        PR 64969
        * g++.dg/cpp1y/pr64969.C: New test.

diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 65219f1..575cb4d 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -4427,6 +4427,7 @@  enum tsubst_flags {
 				    for calls in decltype (5.2.2/11).  */
   tf_partial = 1 << 8,		 /* Doing initial explicit argument
 				    substitution in fn_type_unification.  */
+  tf_auto = 1 << 9,		 /* Substitute auto.  */
   /* Convenient substitution flags combinations.  */
   tf_warning_or_error = tf_warning | tf_error
 };
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index 9a00d0d..0221d48 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -3699,8 +3699,10 @@  reduce_template_parm_level (tree index, tree type, int levels, tree args,
       DECL_ARTIFICIAL (decl) = 1;
       SET_DECL_TEMPLATE_PARM_P (decl);
 
-      t = build_template_parm_index (TEMPLATE_PARM_IDX (index),
-				     TEMPLATE_PARM_LEVEL (index) - levels,
+      int level = TEMPLATE_PARM_LEVEL (index);
+      if (level)
+	level -= levels;
+      t = build_template_parm_index (TEMPLATE_PARM_IDX (index), level,
 				     TEMPLATE_PARM_ORIG_LEVEL (index),
 				     decl, type);
       TEMPLATE_PARM_DESCENDANTS (index) = t;
@@ -10003,6 +10005,9 @@  tsubst_pack_expansion (tree t, tree args, tsubst_flags_t complain,
 	  int idx;
           template_parm_level_and_index (parm_pack, &level, &idx);
 
+          /* A pack can never be 'auto'.  */
+          gcc_assert (level);
+
           if (level <= levels)
             arg_pack = TMPL_ARG (args, level, idx);
         }
@@ -11968,8 +11973,16 @@  tsubst (tree t, tree args, tsubst_flags_t complain, tree in_decl)
 	gcc_assert (TREE_VEC_LENGTH (args) > 0);
 	template_parm_level_and_index (t, &level, &idx); 
 
+	/* A level of zero represents 'auto'.  If complain & tf_auto,
+	   then the template arguments will contain a deduced or
+	   specified type for auto at one level deeper than the current
+	   template parameters.  The value of processing_template_decl
+	   is not meaningful here in other cases.  */
+	if (complain & tf_auto && !level)
+	  level = processing_template_decl + 1;
+
 	levels = TMPL_ARGS_DEPTH (args);
-	if (level <= levels)
+	if (level && level <= levels)
 	  {
 	    arg = TMPL_ARG (args, level, idx);
 
@@ -22095,8 +22108,7 @@  make_args_non_dependent (vec<tree, va_gc> *args)
 }
 
 /* Returns a type which represents 'auto' or 'decltype(auto)'.  We use a
-   TEMPLATE_TYPE_PARM with a level one deeper than the actual template
-   parms.  */
+   TEMPLATE_TYPE_PARM with a level of 0.  */
 
 static tree
 make_auto_1 (tree name)
@@ -22106,8 +22118,7 @@  make_auto_1 (tree name)
 			       TYPE_DECL, name, au);
   TYPE_STUB_DECL (au) = TYPE_NAME (au);
   TEMPLATE_TYPE_PARM_INDEX (au) = build_template_parm_index
-    (0, processing_template_decl + 1, processing_template_decl + 1,
-     TYPE_NAME (au), NULL_TREE);
+    (0, 0, 0, TYPE_NAME (au), NULL_TREE);
   TYPE_CANONICAL (au) = canonical_type_parameter (au);
   DECL_ARTIFICIAL (TYPE_NAME (au)) = 1;
   SET_DECL_TEMPLATE_PARM_P (TYPE_NAME (au));
@@ -22157,7 +22168,7 @@  listify_autos (tree type, tree auto_node)
   TREE_VEC_ELT (argvec, 0) = init_auto;
   if (processing_template_decl)
     argvec = add_to_template_args (current_template_args (), argvec);
-  return tsubst (type, argvec, tf_warning_or_error, NULL_TREE);
+  return tsubst (type, argvec, tf_warning_or_error | tf_auto, NULL_TREE);
 }
 
 /* Replace occurrences of 'auto' in TYPE with the appropriate type deduced
@@ -22264,7 +22275,7 @@  do_auto_deduction (tree type, tree init, tree auto_node)
 
   if (processing_template_decl)
     targs = add_to_template_args (current_template_args (), targs);
-  return tsubst (type, targs, tf_warning_or_error, NULL_TREE);
+  return tsubst (type, targs, tf_warning_or_error | tf_auto, NULL_TREE);
 }
 
 /* Substitutes LATE_RETURN_TYPE for 'auto' in TYPE and returns the
@@ -22286,7 +22297,7 @@  splice_late_return_type (tree type, tree late_return_type)
       (make_tree_vec (processing_template_parmlist), argvec);
   if (current_template_parms)
     argvec = add_to_template_args (current_template_args (), argvec);
-  return tsubst (type, argvec, tf_warning_or_error, NULL_TREE);
+  return tsubst (type, argvec, tf_warning_or_error | tf_auto, NULL_TREE);
 }
 
 /* Returns true iff TYPE is a TEMPLATE_TYPE_PARM representing 'auto' or
diff --git a/gcc/testsuite/g++.dg/cpp1y/pr64969.C b/gcc/testsuite/g++.dg/cpp1y/pr64969.C
new file mode 100644
index 0000000..05848b9
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1y/pr64969.C
@@ -0,0 +1,23 @@ 
+// { dg-do compile { target c++14 } }
+// { dg-options "" }
+
+int f();
+
+auto t1(auto f)
+{ return f(); }
+
+decltype(auto) t2(auto f)
+{ return f(); }
+
+template <typename T>
+auto t3(auto f)
+{ return f(); }
+
+template <typename T>
+decltype(auto) t4(auto f)
+{ return f(); }
+
+int r1 = t1(f);
+int r2 = t2(f);
+int r3 = t3<void>(f) | t3<void, int()>(f);
+int r4 = t4<void>(f) | t4<void, int()>(f);