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