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
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);