Patchwork [C++,1/3] Refactor implicit function template implementation and fix 58534, 58536, 58548, 58549 and 58637.

login
register
mail settings
Submitter Adam Butcher
Date Oct. 31, 2013, 9:47 a.m.
Message ID <1383212824-5493-2-git-send-email-adam@jessamine.co.uk>
Download mbox | patch
Permalink /patch/287961/
State New
Headers show

Comments

Adam Butcher - Oct. 31, 2013, 9:47 a.m.
gcc/
	* tree.c (grow_tree_vec_stat): New function ...
	* tree.h (grow_tree_vec_stat) (grow_tree_vec): ... and its declaration
	and macro front-end.

gcc/cp/
	PR c++/58534
	PR c++/58536
	PR c++/58548
	PR c++/58549
	PR c++/58637
	* parser.h (struct cp_parser): New members implicit_template_parms,
	implicit_template_scope and auto_is_implicit_function_template_parm_p.
	* parser.c (add_implicit_template_parms): Refactor as ...
	(synthesize_implicit_template_parm): ... this to append a new template
	type parm to the current template parameter list (introducing a new list
	if necessary).
	(cp_parser_new): Initialize new cp_parser members.
	(cp_parser_parameter_declaration_clause): Consider auto as implicit
	template parm when parsing a parameter declaration (unless paring an
	explicit specialization).
	(cp_parser_parameter_declaration_list): Remove local
	implicit_template_parms counter and reset cp_parser implicit template
	state when complete.
	(cp_parser_lambda_expression): Reset implicit template cp_parser members
	whilst generating lambda class.
	(cp_parser_function_definition_after_declarator): Reset implicit
	template cp_parser members whilst parsing function definition.
	(make_generic_type_name): Respell '<autoN>' as 'auto:N' which works
	better with template diagnostics.
	(cp_parser_simple_type_specifier): Synthesize implicit template parm on
	parsing 'auto' if auto_is_implicit_function_template_parm_p and provide
	diagnostics ...
	* decl.c (grokdeclarator): ... that were previously done here.

gcc/testsuite/g++.dg/
	* cpp1y/pr58534.C: New testcase.
	* cpp1y/pr58536.C: New testcase.
	* cpp1y/pr58548.C: New testcase.
	* cpp1y/pr58549.C: New testcase.
	* cpp1y/pr58637.C: New testcase.
---
 gcc/cp/decl.c                        |  30 +---
 gcc/cp/parser.c                      | 278 +++++++++++++++++++++++------------
 gcc/cp/parser.h                      |  19 +++
 gcc/testsuite/g++.dg/cpp1y/pr58534.C |   9 ++
 gcc/testsuite/g++.dg/cpp1y/pr58536.C |  12 ++
 gcc/testsuite/g++.dg/cpp1y/pr58548.C |  10 ++
 gcc/testsuite/g++.dg/cpp1y/pr58549.C |  10 ++
 gcc/testsuite/g++.dg/cpp1y/pr58637.C |   7 +
 gcc/tree.c                           |  22 +++
 gcc/tree.h                           |   5 +
 10 files changed, 281 insertions(+), 121 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/cpp1y/pr58534.C
 create mode 100644 gcc/testsuite/g++.dg/cpp1y/pr58536.C
 create mode 100644 gcc/testsuite/g++.dg/cpp1y/pr58548.C
 create mode 100644 gcc/testsuite/g++.dg/cpp1y/pr58549.C
 create mode 100644 gcc/testsuite/g++.dg/cpp1y/pr58637.C
Jason Merrill - Nov. 8, 2013, 6:50 p.m.
On 10/31/2013 05:47 AM, Adam Butcher wrote:
> +         become_template = true;

> +         push_deferring_access_checks (dk_deferred);

Why is this call here?  I don't see anything in the rest of the function 
that would trigger an access check, or a matching pop.

> +      /* Create a distinct parameter pack type from the current parm and add it
> +        to the replacement args to tsubst below into the generic function
> +        parameter.  */
> +
> +      tree t = copy_type (TREE_TYPE (TREE_VALUE (TREE_VEC_ELT (current, i))));
> +      TYPE_STUB_DECL (t) = TYPE_NAME (t) = TEMPLATE_TYPE_DECL (t);

Is this changing anything?  I'm not sure if we need to copy the decl or 
if we can just reuse it, but either way we need to set the type of 
TEMPLATE_TYPE_DECL (t) to t.

> +      SET_TYPE_STRUCTURAL_EQUALITY (t);

Why not

       TYPE_CANONICAL (t) = canonical_type_parameter (t);

?

Jason
Adam Butcher - Nov. 9, 2013, 1:21 p.m.
On 2013-11-08 18:50, Jason Merrill wrote:
> On 10/31/2013 05:47 AM, Adam Butcher wrote:
>> +         become_template = true;
>>
>> +         push_deferring_access_checks (dk_deferred);
>
> Why is this call here?  I don't see anything in the rest of the
> function that would trigger an access check, or a matching pop.
>
This is only in the 'fully implicit function template' case; and the 
matching pop is in finish_fully_implicit_template.  It was in the 
original impl which was trying to do all the things that beginning and 
ending an explicit template parameter list did.  Maybe this is 
unnecessary.  I'll see if anything breaks if I remove it.

>> +      /* Create a distinct parameter pack type from the current 
>> parm and add it
>> +        to the replacement args to tsubst below into the generic 
>> function
>> +        parameter.  */
>> +
>> +      tree t = copy_type (TREE_TYPE (TREE_VALUE (TREE_VEC_ELT 
>> (current, i))));
>> +      TYPE_STUB_DECL (t) = TYPE_NAME (t) = TEMPLATE_TYPE_DECL (t);
>
> Is this changing anything?  I'm not sure if we need to copy the decl
> or if we can just reuse it, but either way we need to set the type of
> TEMPLATE_TYPE_DECL (t) to t.
>
I think it (or at least one of the assignments) is necessary.  It is 
derived from some similar code in tsubst.  I'll have another look into 
it and add TREE_TYPE (TEMPLATE_TYPE_DECL (t)) = t also.  What problem 
will be caused by not setting the latter; is it a consistency issue?  I 
have not seen any probs in testing so far.

>> +      SET_TYPE_STRUCTURAL_EQUALITY (t);
>
> Why not
>
>       TYPE_CANONICAL (t) = canonical_type_parameter (t);
>
> ?
>
Only for the sake of not exposing this (currently static) function from 
pt.c (or moving the pack convert function into pt.c).  My original impl 
did extern canonical_type_parameter and use that and I think it worked 
as expected.  Would that be preferred?

Cheers,
Adam
Adam Butcher - Nov. 9, 2013, 7:55 p.m.
On 2013-11-09 13:21, Adam Butcher wrote:
> On 2013-11-08 18:50, Jason Merrill wrote:
>> On 10/31/2013 05:47 AM, Adam Butcher wrote:
>>> +         become_template = true;
>>>
>>> +         push_deferring_access_checks (dk_deferred);
>>
>> Why is this call here?  I don't see anything in the rest of the
>> function that would trigger an access check, or a matching pop.
>>
> This is only in the 'fully implicit function template' case; and the
> matching pop is in finish_fully_implicit_template.  It was in the
> original impl which was trying to do all the things that beginning 
> and
> ending an explicit template parameter list did.  Maybe this is
> unnecessary.  I'll see if anything breaks if I remove it.
>
>>> +      /* Create a distinct parameter pack type from the current 
>>> parm and add it
>>> +        to the replacement args to tsubst below into the generic 
>>> function
>>> +        parameter.  */
>>> +
>>> +      tree t = copy_type (TREE_TYPE (TREE_VALUE (TREE_VEC_ELT 
>>> (current, i))));
>>> +      TYPE_STUB_DECL (t) = TYPE_NAME (t) = TEMPLATE_TYPE_DECL (t);
>>
>> Is this changing anything?  I'm not sure if we need to copy the decl
>> or if we can just reuse it, but either way we need to set the type 
>> of
>> TEMPLATE_TYPE_DECL (t) to t.
>>
> I think it (or at least one of the assignments) is necessary.  It is
> derived from some similar code in tsubst.  I'll have another look 
> into
> it and add TREE_TYPE (TEMPLATE_TYPE_DECL (t)) = t also.  What problem
> will be caused by not setting the latter; is it a consistency issue?
> I have not seen any probs in testing so far.
>
>>> +      SET_TYPE_STRUCTURAL_EQUALITY (t);
>>
>> Why not
>>
>>       TYPE_CANONICAL (t) = canonical_type_parameter (t);
>>
>> ?
>>
> Only for the sake of not exposing this (currently static) function
> from pt.c (or moving the pack convert function into pt.c).  My
> original impl did extern canonical_type_parameter and use that and I
> think it worked as expected.  Would that be preferred?
>
The following update appears to work.

Cheers,
Adam
Jason Merrill - Nov. 10, 2013, 6:10 a.m.
On 11/09/2013 08:21 AM, Adam Butcher wrote:
>>> +      TYPE_STUB_DECL (t) = TYPE_NAME (t) = TEMPLATE_TYPE_DECL (t);
>>
>> Is this changing anything?  I'm not sure if we need to copy the decl
>> or if we can just reuse it, but either way we need to set the type of
>> TEMPLATE_TYPE_DECL (t) to t.
>>
> I think it (or at least one of the assignments) is necessary.  It is
> derived from some similar code in tsubst.

The code in tsubst is updating the type because the 
TEMPLATE_TYPE_PARM_INDEX has changed; in this case, it hasn't, so 
neither has the TEMPLATE_TYPE_DECL.  But see below...

> I'll have another look into
> it and add TREE_TYPE (TEMPLATE_TYPE_DECL (t)) = t also.  What problem
> will be caused by not setting the latter; is it a consistency issue?  I
> have not seen any probs in testing so far.

Just that without this assignment anything wanting to get from the 
TYPE_DECL to its type will get the non-pack, but we want it to get the pack.

Hmm, actually I think messing with the non-pack's decl is dangerous, and 
we should come up with a new decl for the pack instead.  I think you can 
use reduce_template_parm_level with a "levels" argument of 0 to build a 
new decl and parm index.

And if we're doing that, setting TYPE_STUB_DECL and TYPE_NAME is indeed 
necessary.

> Only for the sake of not exposing this (currently static) function from
> pt.c (or moving the pack convert function into pt.c).  My original impl
> did extern canonical_type_parameter and use that and I think it worked
> as expected.  Would that be preferred?

Yes.  Please declare it in cp-tree.h rather than within the function.

Jason
Adam Butcher - Nov. 10, 2013, 10:38 a.m.
On 2013-11-10 6:10, Jason Merrill wrote:
>
> Hmm, actually I think messing with the non-pack's decl is dangerous,
> and we should come up with a new decl for the pack instead.  I think
> you can use reduce_template_parm_level with a "levels" argument of 0
> to build a new decl and parm index.
>
I actually did this in one of my experiments and it worked fine.
Although I also had to update the template parm list with new decl:

   TREE_VALUE (TREE_VEC_ELT (current, i)) = TREE_CHAIN (t);

At that point I had the convert function in pt.c an operating directly
on current_template_parms (also having direct access to the two statics
required; reduce_template_parm_level and canonical_type_parameter).

I can't think of a case where we'd want to make this substitution in
anything but the current_template_parms so maybe moving the convert
function back into to pt.c and removing the 'current' parm from it
might be best?

> And if we're doing that, setting TYPE_STUB_DECL and TYPE_NAME is
> indeed necessary.
>
>> Only for the sake of not exposing this (currently static) function 
>> from
>> pt.c (or moving the pack convert function into pt.c).  My original 
>> impl
>> did extern canonical_type_parameter and use that and I think it 
>> worked
>> as expected.  Would that be preferred?
>
> Yes.  Please declare it in cp-tree.h rather than within the function.
>
Will do.  Unless, as suggested above, we go for moving
convert_generic_types_to_packs into pt.c in which case exposing these
internals won't be necessary.

Cheers,
Adam
Adam Butcher - Nov. 10, 2013, 1:10 p.m.
On 2013-11-10 10:38, Adam Butcher wrote:
> On 2013-11-10 6:10, Jason Merrill wrote:
>>
>> Hmm, actually I think messing with the non-pack's decl is dangerous,
>> and we should come up with a new decl for the pack instead.  I think
>> you can use reduce_template_parm_level with a "levels" argument of 0
>> to build a new decl and parm index.
>>
> I actually did this in one of my experiments and it worked fine.
> Although I also had to update the template parm list with new decl:
>
>   TREE_VALUE (TREE_VEC_ELT (current, i)) = TREE_CHAIN (t);
>
> At that point I had the convert function in pt.c an operating 
> directly
> on current_template_parms (also having direct access to the two 
> statics
> required; reduce_template_parm_level and canonical_type_parameter).
>
> I can't think of a case where we'd want to make this substitution in
> anything but the current_template_parms so maybe moving the convert
> function back into to pt.c and removing the 'current' parm from it
> might be best?
>
>> And if we're doing that, setting TYPE_STUB_DECL and TYPE_NAME is
>> indeed necessary.
>>
>>> Only for the sake of not exposing this (currently static) function 
>>> from
>>> pt.c (or moving the pack convert function into pt.c).  My original 
>>> impl
>>> did extern canonical_type_parameter and use that and I think it 
>>> worked
>>> as expected.  Would that be preferred?
>>
>> Yes.  Please declare it in cp-tree.h rather than within the 
>> function.
>>
> Will do.  Unless, as suggested above, we go for moving
> convert_generic_types_to_packs into pt.c in which case exposing these
> internals won't be necessary.
>

With the convert function in pt.c, PATCH 2/3 now looks as follows:
Jason Merrill - Nov. 10, 2013, 6:49 p.m.
On 11/10/2013 08:10 AM, Adam Butcher wrote:
> With the convert function in pt.c, PATCH 2/3 now looks as follows:

I like this direction.

> +      /* Build up a tree vec of empty tree vecs up to the inner substitution
> +	 args built above.  */

I think we want to copy the enclosing args; see existing uses of 
add_outermost_template_args.

Jason
Adam Butcher - Nov. 10, 2013, 7:39 p.m.
On 2013-11-10 18:49, Jason Merrill wrote:
> On 11/10/2013 08:10 AM, Adam Butcher wrote:
>>
>> +      /* Build up a tree vec of empty tree vecs up to the inner 
>> substitution
>> +	 args built above.  */
>
> I think we want to copy the enclosing args; see existing uses of
> add_outermost_template_args.
>
OK.  I had that originally but I was concerned that it was wasteful 
which is why I ended up using nullptrs for non-subst'd types in the 
inner level and empty tree vecs for the prior levels.  It seemed to work 
as expected; I assumed that tsubst simply doesn't do anything with a 
null tree substitution (i.e. it is an identity op).  There will be an 
additional, seemingly unnecessary, cost of copying these and the loop 
above will presumably be required to set the non-subst'd types rather 
than leave them as nullptr also.

Since we are tsubsting the declaration here and we only want to adjust 
the template parameter types themselves at their declaration is this 
really necessary?  I've no problem with implementing this if it truly is 
necessary but I don't want to add unnecessary cycles if not.

One other thing, by 'copy' I take it you mean copy the tree vecs of the 
enclosing levels only, not also the types within them.  And I also 
assume that I'll need to set the currently unset types in the inner 
level also?

Cheers
Adam
Jason Merrill - Nov. 10, 2013, 11:21 p.m.
On 11/10/2013 02:39 PM, Adam Butcher wrote:
> I assumed that tsubst simply doesn't do anything with a
> null tree substitution (i.e. it is an identity op).

Substituting NULL_TREE for a template parameter gives a template 
parameter with a reduced level; this happens during partial instantiation.

> Since we are tsubsting the declaration here and we only want to adjust
> the template parameter types themselves at their declaration is this
> really necessary?  I've no problem with implementing this if it truly is
> necessary but I don't want to add unnecessary cycles if not.

The difference between setting copying a pointer to a vec versus setting 
it to null seems completely negligible to me.

I think it is necessary in case the function parameter type involves 
template parameters from the enclosing context as well as implicit 
template parameters.

> One other thing, by 'copy' I take it you mean copy the tree vecs of the
> enclosing levels only, not also the types within them.

Yes, using add_outermost_template_args.

> And I also assume that I'll need to set the currently unset types in the inner
> level also?

Can you have explicit template parameters at the same level as the 
implicit ones?  If so, then their places in the vec will need to be set 
appropriately in case they are used in the function parameter type.

Jason
Adam Butcher - Nov. 11, 2013, 7:44 p.m.
On 2013-11-10 23:21, Jason Merrill wrote:
> On 11/10/2013 02:39 PM, Adam Butcher wrote:
>> I assumed that tsubst simply doesn't do anything with a
>> null tree substitution (i.e. it is an identity op).
>
> Substituting NULL_TREE for a template parameter gives a template
> parameter with a reduced level; this happens during partial
> instantiation.
>
Ah, OK.

>> Since we are tsubsting the declaration here and we only want to 
>> adjust
>> the template parameter types themselves at their declaration is this
>> really necessary?  I've no problem with implementing this if it 
>> truly is
>> necessary but I don't want to add unnecessary cycles if not.
>
> The difference between setting copying a pointer to a vec versus
> setting it to null seems completely negligible to me.
>
Sure.  I was more concerned with allocating vecs for each outer list 
and copying the parms over versus allocating a 'null' placeholder list 
for each level which we're not concerned about.  No worries though, I've 
proceeded with your suggestion below.

> I think it is necessary in case the function parameter type involves
> template parameters from the enclosing context as well as implicit
> template parameters.
>
>> One other thing, by 'copy' I take it you mean copy the tree vecs of 
>> the
>> enclosing levels only, not also the types within them.
>
> Yes, using add_outermost_template_args.
>
I have done this (diff -w -b follows).  The route to this seems even 
more costly though; current_template_parms needs to first be converted 
to a vec by current_template_args() then the last nesting level is 
thrown away and replaced with the one we're working on.  It's all 
working but I'm wondering whether a custom loop here would be better.

>> And I also assume that I'll need to set the currently unset types in 
>> the inner
>> level also?
>
> Can you have explicit template parameters at the same level as the
> implicit ones?
>
Yes.

> If so, then their places in the vec will need to be
> set appropriately in case they are used in the function parameter
> type.
>
OK.  I had testcases that appeared to pass OK with the previous 
NULL_TREE tsubst but maybe I wasn't checking something (perhaps a 
combination of an explicit template parm type and an auto in a single 
parm).

End result is that everything looks OK though so we might be able to 
get the C++14 lambda support marked as "in 4.9" soon hopefully.

Cheers,
Adam
Jason Merrill - Nov. 12, 2013, 1:57 a.m.
On 11/11/2013 02:44 PM, Adam Butcher wrote:
>> Yes, using add_outermost_template_args.
>>
> I have done this (diff -w -b follows).  The route to this seems even
> more costly though; current_template_parms needs to first be converted
> to a vec by current_template_args() then the last nesting level is
> thrown away and replaced with the one we're working on.

True.  It looks like you could avoid the wasted conversion of the 
innermost level by doing

template_parms_to_args (TREE_CHAIN (current_template_parms))

rather than

current_template_args.

The patch is OK with or without that change.

Jason

Patch

diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c
index 09c1daa..786814c 100644
--- a/gcc/cp/decl.c
+++ b/gcc/cp/decl.c
@@ -10375,33 +10375,11 @@  grokdeclarator (const cp_declarator *declarator,
 
       if (type_uses_auto (type))
 	{
-	  if (template_parm_flag)
-	    {
-	      error ("template parameter declared %<auto%>");
-	      type = error_mark_node;
-	    }
-	  else if (decl_context == CATCHPARM)
-	    {
-	      error ("catch parameter declared %<auto%>");
-	      type = error_mark_node;
-	    }
-	  else if (current_class_type && LAMBDA_TYPE_P (current_class_type))
-	    {
-	      if (cxx_dialect < cxx1y)
-		pedwarn (location_of (type), 0,
-			 "use of %<auto%> in lambda parameter declaration "
-			 "only available with "
-			 "-std=c++1y or -std=gnu++1y");
-	    }
-	  else if (cxx_dialect < cxx1y)
-	    pedwarn (location_of (type), 0,
-		     "use of %<auto%> in parameter declaration "
-		     "only available with "
-		     "-std=c++1y or -std=gnu++1y");
+	  if (cxx_dialect >= cxx1y)
+	    error ("%<auto%> parameter not permitted in this context");
 	  else
-	    pedwarn (location_of (type), OPT_Wpedantic,
-		     "ISO C++ forbids use of %<auto%> in parameter "
-		     "declaration");
+	    error ("parameter declared %<auto%>");
+	  type = error_mark_node;
 	}
 
       /* A parameter declared as an array of T is really a pointer to T.
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index bbc8e75..b699ac4 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -2107,8 +2107,8 @@  static bool cp_parser_ctor_initializer_opt_and_function_body
 static tree cp_parser_late_parsing_omp_declare_simd
   (cp_parser *, tree);
 
-static tree add_implicit_template_parms
-  (cp_parser *, size_t, tree);
+static tree synthesize_implicit_template_parm
+  (cp_parser *);
 static tree finish_fully_implicit_template
   (cp_parser *, tree);
 
@@ -3443,7 +3443,10 @@  cp_parser_new (void)
   parser->num_template_parameter_lists = 0;
 
   /* Not declaring an implicit function template.  */
+  parser->auto_is_implicit_function_template_parm_p = false;
   parser->fully_implicit_function_template_p = false;
+  parser->implicit_template_parms = 0;
+  parser->implicit_template_scope = 0;
 
   return parser;
 }
@@ -8617,12 +8620,17 @@  cp_parser_lambda_expression (cp_parser* parser)
         = parser->num_template_parameter_lists;
     unsigned char in_statement = parser->in_statement;
     bool in_switch_statement_p = parser->in_switch_statement_p;
-    bool fully_implicit_function_template_p = parser->fully_implicit_function_template_p;
+    bool fully_implicit_function_template_p
+        = parser->fully_implicit_function_template_p;
+    tree implicit_template_parms = parser->implicit_template_parms;
+    cp_binding_level* implicit_template_scope = parser->implicit_template_scope;
 
     parser->num_template_parameter_lists = 0;
     parser->in_statement = 0;
     parser->in_switch_statement_p = false;
     parser->fully_implicit_function_template_p = false;
+    parser->implicit_template_parms = 0;
+    parser->implicit_template_scope = 0;
 
     /* By virtue of defining a local class, a lambda expression has access to
        the private variables of enclosing classes.  */
@@ -8646,7 +8654,10 @@  cp_parser_lambda_expression (cp_parser* parser)
     parser->num_template_parameter_lists = saved_num_template_parameter_lists;
     parser->in_statement = in_statement;
     parser->in_switch_statement_p = in_switch_statement_p;
-    parser->fully_implicit_function_template_p = fully_implicit_function_template_p;
+    parser->fully_implicit_function_template_p
+	= fully_implicit_function_template_p;
+    parser->implicit_template_parms = implicit_template_parms;
+    parser->implicit_template_scope = implicit_template_scope;
   }
 
   pop_deferring_access_checks ();
@@ -14053,7 +14064,7 @@  cp_parser_explicit_specialization (cp_parser* parser)
     cp_parser_single_declaration (parser,
 				  /*checks=*/NULL,
 				  /*member_p=*/false,
-                                  /*explicit_specialization_p=*/true,
+				  /*explicit_specialization_p=*/true,
 				  /*friend_p=*/NULL);
   /* We're done with the specialization.  */
   end_specialization ();
@@ -14355,10 +14366,33 @@  cp_parser_simple_type_specifier (cp_parser* parser,
     case RID_VOID:
       type = void_type_node;
       break;
-      
+
     case RID_AUTO:
       maybe_warn_cpp0x (CPP0X_AUTO);
-      type = make_auto ();
+      if (parser->auto_is_implicit_function_template_parm_p)
+	{
+	  type = synthesize_implicit_template_parm (parser);
+
+	  if (current_class_type && LAMBDA_TYPE_P (current_class_type))
+	    {
+	      if (cxx_dialect < cxx1y)
+		pedwarn (location_of (type), 0,
+			 "use of %<auto%> in lambda parameter declaration "
+			 "only available with "
+			 "-std=c++1y or -std=gnu++1y");
+	    }
+	  else if (cxx_dialect < cxx1y)
+	    pedwarn (location_of (type), 0,
+		     "use of %<auto%> in parameter declaration "
+		     "only available with "
+		     "-std=c++1y or -std=gnu++1y");
+	  else
+	    pedwarn (location_of (type), OPT_Wpedantic,
+		     "ISO C++ forbids use of %<auto%> in parameter "
+		     "declaration");
+	}
+      else
+	type = make_auto ();
       break;
 
     case RID_DECLTYPE:
@@ -17937,6 +17971,20 @@  cp_parser_parameter_declaration_clause (cp_parser* parser)
   bool ellipsis_p;
   bool is_error;
 
+  struct cleanup {
+    cp_parser* parser;
+    int auto_is_implicit_function_template_parm_p;
+    ~cleanup() {
+      parser->auto_is_implicit_function_template_parm_p
+	= auto_is_implicit_function_template_parm_p;
+    }
+  } cleanup = { parser, parser->auto_is_implicit_function_template_parm_p };
+
+  (void) cleanup;
+
+  if (!processing_specialization)
+    parser->auto_is_implicit_function_template_parm_p = true;
+
   /* Peek at the next token.  */
   token = cp_lexer_peek_token (parser->lexer);
   /* Check for trivial parameter-declaration-clauses.  */
@@ -18024,7 +18072,6 @@  cp_parser_parameter_declaration_list (cp_parser* parser, bool *is_error)
   tree *tail = &parameters; 
   bool saved_in_unbraced_linkage_specification_p;
   int index = 0;
-  int implicit_template_parms = 0;
 
   /* Assume all will go well.  */
   *is_error = false;
@@ -18052,18 +18099,11 @@  cp_parser_parameter_declaration_list (cp_parser* parser, bool *is_error)
       deprecated_state = DEPRECATED_SUPPRESS;
 
       if (parameter)
-	{
-	  decl = grokdeclarator (parameter->declarator,
-				 &parameter->decl_specifiers,
-				 PARM,
-				 parameter->default_argument != NULL_TREE,
-				 &parameter->decl_specifiers.attributes);
-
-	  if (TREE_TYPE (decl) != error_mark_node
-	      && parameter->decl_specifiers.type
-	      && is_auto_or_concept (parameter->decl_specifiers.type))
-	      ++implicit_template_parms;
-	}
+	decl = grokdeclarator (parameter->declarator,
+			       &parameter->decl_specifiers,
+			       PARM,
+			       parameter->default_argument != NULL_TREE,
+			       &parameter->decl_specifiers.attributes);
 
       deprecated_state = DEPRECATED_NORMAL;
 
@@ -18151,10 +18191,12 @@  cp_parser_parameter_declaration_list (cp_parser* parser, bool *is_error)
   parser->in_unbraced_linkage_specification_p
     = saved_in_unbraced_linkage_specification_p;
 
-  if (parameters != error_mark_node && implicit_template_parms)
-    parameters = add_implicit_template_parms (parser,
-					      implicit_template_parms,
-					      parameters);
+  if (cp_binding_level *its = parser->implicit_template_scope)
+    if (current_binding_level->level_chain == its)
+      {
+	parser->implicit_template_parms = 0;
+	parser->implicit_template_scope = 0;
+      }
 
   return parameters;
 }
@@ -22406,6 +22448,15 @@  cp_parser_function_definition_after_declarator (cp_parser* parser,
   bool saved_in_function_body;
   unsigned saved_num_template_parameter_lists;
   cp_token *token;
+  bool fully_implicit_function_template_p
+    = parser->fully_implicit_function_template_p;
+  parser->fully_implicit_function_template_p = false;
+  tree implicit_template_parms
+    = parser->implicit_template_parms;
+  parser->implicit_template_parms = 0;
+  cp_binding_level* implicit_template_scope
+    = parser->implicit_template_scope;
+  parser->implicit_template_scope = 0;
 
   saved_in_function_body = parser->in_function_body;
   parser->in_function_body = true;
@@ -22478,6 +22529,13 @@  cp_parser_function_definition_after_declarator (cp_parser* parser,
     = saved_num_template_parameter_lists;
   parser->in_function_body = saved_in_function_body;
 
+  parser->fully_implicit_function_template_p
+    = fully_implicit_function_template_p;
+  parser->implicit_template_parms
+    = implicit_template_parms;
+  parser->implicit_template_scope
+    = implicit_template_scope;
+
   if (parser->fully_implicit_function_template_p)
     finish_fully_implicit_template (parser, /*member_decl_opt=*/0);
 
@@ -31011,7 +31069,7 @@  static tree
 make_generic_type_name ()
 {
   char buf[32];
-  sprintf (buf, "<auto%d>", ++generic_parm_count);
+  sprintf (buf, "auto:%d", ++generic_parm_count);
   return get_identifier (buf);
 }
 
@@ -31025,110 +31083,140 @@  tree_type_is_auto_or_concept (const_tree t)
   return TREE_TYPE (t) && is_auto_or_concept (TREE_TYPE (t));
 }
 
-/* Add EXPECT_COUNT implicit template parameters gleaned from the generic
-   type parameters in PARAMETERS to the CURRENT_TEMPLATE_PARMS (creating a new
-   template parameter list if necessary).  Returns PARAMETERS suitably rewritten
-   to reference the newly created types or ERROR_MARK_NODE on failure.  */
+/* Add an implicit template type parameter to the CURRENT_TEMPLATE_PARMS
+   (creating a new template parameter list if necessary).  Returns the newly
+   created template type parm.  */
 
 tree
-add_implicit_template_parms (cp_parser *parser, size_t expect_count,
-			     tree parameters)
+synthesize_implicit_template_parm  (cp_parser *parser)
 {
   gcc_assert (current_binding_level->kind == sk_function_parms);
 
-  cp_binding_level *fn_parms_scope = current_binding_level;
-
-  bool become_template =
-    fn_parms_scope->level_chain->kind != sk_template_parms;
-
-  size_t synth_count = 0;
+  /* We are either continuing a function template that already contains implicit
+     template parameters, creating a new fully-implicit function template, or
+     extending an existing explicit function template with implicit template
+     parameters.  */
 
-  /* Roll back a scope level and either introduce a new template parameter list
-     or update an existing one.  The function scope is added back after template
-     parameter synthesis below.  */
-  current_binding_level = fn_parms_scope->level_chain;
+  cp_binding_level *const entry_scope = current_binding_level;
 
-  /* TPARMS tracks the function's template parameter list.  This is either a new
-     chain in the case of a fully implicit function template or an extension of
-     the function's explicitly specified template parameter list.  */
-  tree tparms = NULL_TREE;
+  bool become_template = false;
+  cp_binding_level *parent_scope = 0;
 
-  if (become_template)
+  if (parser->implicit_template_scope)
     {
-      push_deferring_access_checks (dk_deferred);
-      begin_template_parm_list ();
+      gcc_assert (parser->implicit_template_parms);
 
-      parser->fully_implicit_function_template_p = true;
-      ++parser->num_template_parameter_lists;
+      current_binding_level = parser->implicit_template_scope;
     }
   else
     {
-      /* Roll back the innermost template parameter list such that it may be
-	 extended in the loop below as if it were being explicitly declared.  */
-
-      gcc_assert (current_template_parms);
+      /* Roll back to the existing template parameter scope (in the case of
+	 extending an explicit function template) or introduce a new template
+	 parameter scope ahead of the function parameter scope (or class scope
+	 in the case of out-of-line member definitions).  The function scope is
+	 added back after template parameter synthesis below.  */
 
-      /* Pop the innermost template parms into TPARMS.  */
-      tree inner_vec = INNERMOST_TEMPLATE_PARMS (current_template_parms);
-      current_template_parms = TREE_CHAIN (current_template_parms);
+      cp_binding_level *scope = entry_scope;
 
-      size_t inner_vec_len = TREE_VEC_LENGTH (inner_vec);
-      if (inner_vec_len != 0)
+      while (scope->kind == sk_function_parms)
 	{
-	  tree t = tparms = TREE_VEC_ELT (inner_vec, 0);
-	  for (size_t n = 1; n < inner_vec_len; ++n)
-	    t = TREE_CHAIN (t) = TREE_VEC_ELT (inner_vec, n);
+	  parent_scope = scope;
+	  scope = scope->level_chain;
 	}
+      if (current_class_type && !LAMBDA_TYPE_P (current_class_type)
+	  && parser->num_classes_being_defined == 0)
+	while (scope->kind == sk_class)
+	  {
+	    parent_scope = scope;
+	    scope = scope->level_chain;
+	  }
 
-      ++processing_template_parmlist;
-    }
+      current_binding_level = scope;
 
-  for (tree p = parameters; p && synth_count < expect_count; p = TREE_CHAIN (p))
-    {
-      tree generic_type_ptr
-	= find_type_usage (TREE_VALUE (p), tree_type_is_auto_or_concept);
-
-      if (!generic_type_ptr)
-	continue;
+      if (scope->kind != sk_template_parms)
+	{
+	  /* Introduce a new template parameter list for implicit template
+	     parameters.  */
 
-      ++synth_count;
+	  become_template = true;
 
-      tree synth_id = make_generic_type_name ();
-      tree synth_tmpl_parm = finish_template_type_parm (class_type_node,
-							synth_id);
-      tparms = process_template_parm (tparms, DECL_SOURCE_LOCATION (TREE_VALUE
-								    (p)),
-				      build_tree_list (NULL_TREE,
-						       synth_tmpl_parm),
-				      /*non_type=*/false,
-				      /*param_pack=*/false);
+	  push_deferring_access_checks (dk_deferred);
 
-      /* Rewrite the type of P to be the template_parm added above (getdecls is
-         used to retrieve it since it is the most recent declaration in this
-         scope).  Qualifiers need to be preserved also.  */
+	  parser->implicit_template_scope
+	      = begin_scope (sk_template_parms, NULL);
 
-      tree& cur_type = TREE_TYPE (generic_type_ptr);
-      tree new_type = TREE_TYPE (getdecls ());
+	  ++processing_template_decl;
 
-      if (TYPE_QUALS (cur_type))
-	cur_type = cp_build_qualified_type (new_type, TYPE_QUALS (cur_type));
+	  parser->fully_implicit_function_template_p = true;
+	  ++parser->num_template_parameter_lists;
+	}
       else
-	cur_type = new_type;
+	{
+	  /* Synthesize implicit template parameters at the end of the explicit
+	     template parameter list.  */
+
+	  gcc_assert (current_template_parms);
+
+	  parser->implicit_template_scope = scope;
+
+	  tree v = INNERMOST_TEMPLATE_PARMS (current_template_parms);
+	  parser->implicit_template_parms
+	    = TREE_VEC_ELT (v, TREE_VEC_LENGTH (v) - 1);
+	}
     }
 
-  gcc_assert (synth_count == expect_count);
+  /* Synthesize a new template parameter and track the current template
+     parameter chain with implicit_template_parms.  */
 
-  push_binding_level (fn_parms_scope);
+  tree synth_id = make_generic_type_name ();
+  tree synth_tmpl_parm = finish_template_type_parm (class_type_node,
+						    synth_id);
+  tree new_parm
+    = process_template_parm (parser->implicit_template_parms,
+			     input_location,
+			     build_tree_list (NULL_TREE, synth_tmpl_parm),
+			     /*non_type=*/false,
+			     /*param_pack=*/false);
 
-  end_template_parm_list (tparms);
 
-  return parameters;
+  if (parser->implicit_template_parms)
+    parser->implicit_template_parms
+      = TREE_CHAIN (parser->implicit_template_parms);
+  else
+    parser->implicit_template_parms = new_parm;
+
+  tree new_type = TREE_TYPE (getdecls ());
+
+  /* If creating a fully implicit function template, start the new implicit
+     template parameter list with this synthesized type, otherwise grow the
+     current template parameter list.  */
+
+  if (become_template)
+    {
+      parent_scope->level_chain = current_binding_level;
+
+      tree new_parms = make_tree_vec (1);
+      TREE_VEC_ELT (new_parms, 0) = parser->implicit_template_parms;
+      current_template_parms = tree_cons (size_int (processing_template_decl),
+					  new_parms, current_template_parms);
+    }
+  else
+    {
+      tree& new_parms = INNERMOST_TEMPLATE_PARMS (current_template_parms);
+      int new_parm_idx = TREE_VEC_LENGTH (new_parms);
+      new_parms = grow_tree_vec_stat (new_parms, new_parm_idx + 1);
+      TREE_VEC_ELT (new_parms, new_parm_idx) = parser->implicit_template_parms;
+    }
+
+  current_binding_level = entry_scope;
+
+  return new_type;
 }
 
 /* Finish the declaration of a fully implicit function template.  Such a
    template has no explicit template parameter list so has not been through the
-   normal template head and tail processing.  add_implicit_template_parms tries
-   to do the head; this tries to do the tail.  MEMBER_DECL_OPT should be
+   normal template head and tail processing.  synthesize_implicit_template_parm
+   tries to do the head; this tries to do the tail.  MEMBER_DECL_OPT should be
    provided if the declaration is a class member such that its template
    declaration can be completed.  If MEMBER_DECL_OPT is provided the finished
    form is returned.  Otherwise NULL_TREE is returned. */
diff --git a/gcc/cp/parser.h b/gcc/cp/parser.h
index 75f327b..1024024 100644
--- a/gcc/cp/parser.h
+++ b/gcc/cp/parser.h
@@ -360,11 +360,30 @@  typedef struct GTY(()) cp_parser {
      data structure with everything needed for parsing the clauses.  */
   cp_omp_declare_simd_data * GTY((skip)) omp_declare_simd;
 
+  /* Nonzero if parsing a parameter list where 'auto' should trigger an implicit
+     template parameter.  */
+  bool auto_is_implicit_function_template_parm_p;
+
   /* TRUE if the function being declared was made a template due to its
      parameter list containing generic type specifiers (`auto' or concept
      identifiers) rather than an explicit template parameter list.  */
   bool fully_implicit_function_template_p;
 
+  /* Tracks the function's template parameter list when declaring a function
+     using generic type parameters.  This is either a new chain in the case of a
+     fully implicit function template or an extension of the function's existing
+     template parameter list.  This is tracked to optimize calls subsequent
+     calls to synthesize_implicit_template_parm during
+     cp_parser_parameter_declaration.  */
+  tree implicit_template_parms;
+
+  /* The scope into which an implicit template parameter list has been
+     introduced or an existing template parameter list is being extended with
+     implicit template paramaters.  In most cases this is the sk_function_parms
+     scope containing the use of a generic type.  In the case of an out-of-line
+     member definition using a generic type, it is the sk_class scope.  */
+  cp_binding_level* implicit_template_scope;
+
 } cp_parser;
 
 /* In parser.c  */
diff --git a/gcc/testsuite/g++.dg/cpp1y/pr58534.C b/gcc/testsuite/g++.dg/cpp1y/pr58534.C
new file mode 100644
index 0000000..4aa4f43
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1y/pr58534.C
@@ -0,0 +1,9 @@ 
+// { dg-do compile }
+// { dg-options "-std=gnu++1y" }
+
+// PR c++/58534
+
+template<typename> void foo(const auto&) {}
+
+template<typename, typename...T> void foo(const auto&, T...) {}
+
diff --git a/gcc/testsuite/g++.dg/cpp1y/pr58536.C b/gcc/testsuite/g++.dg/cpp1y/pr58536.C
new file mode 100644
index 0000000..8050c19
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1y/pr58536.C
@@ -0,0 +1,12 @@ 
+// { dg-do compile }
+// { dg-options "-std=gnu++1y" }
+
+// PR c++/58536
+
+struct A
+{
+  A(auto);
+};
+
+A::A(auto) {}
+
diff --git a/gcc/testsuite/g++.dg/cpp1y/pr58548.C b/gcc/testsuite/g++.dg/cpp1y/pr58548.C
new file mode 100644
index 0000000..0ac2e1c
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1y/pr58548.C
@@ -0,0 +1,10 @@ 
+// { dg-do compile }
+// { dg-options "-std=gnu++1y" }
+
+// PR c++/58548
+
+void foo(auto)
+{
+  struct A { int i; };
+}
+
diff --git a/gcc/testsuite/g++.dg/cpp1y/pr58549.C b/gcc/testsuite/g++.dg/cpp1y/pr58549.C
new file mode 100644
index 0000000..b71bac9
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1y/pr58549.C
@@ -0,0 +1,10 @@ 
+// { dg-do compile }
+// { dg-options "-std=gnu++1y" }
+
+// PR c++/58549
+
+void foo(auto)
+{
+  void bar();
+}
+
diff --git a/gcc/testsuite/g++.dg/cpp1y/pr58637.C b/gcc/testsuite/g++.dg/cpp1y/pr58637.C
new file mode 100644
index 0000000..46200ff
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1y/pr58637.C
@@ -0,0 +1,7 @@ 
+// { dg-do compile }
+// { dg-options "-std=gnu++1y" }
+
+// PR c++/58637
+
+template<> void foo(auto); // { dg-error "auto|not a template" }
+
diff --git a/gcc/tree.c b/gcc/tree.c
index 332751a..28cbba8 100644
--- a/gcc/tree.c
+++ b/gcc/tree.c
@@ -1940,6 +1940,28 @@  make_tree_vec_stat (int len MEM_STAT_DECL)
 
   return t;
 }
+
+/* Grow a TREE_VEC node to new length LEN.  */
+
+tree
+grow_tree_vec_stat (tree v, int len MEM_STAT_DECL)
+{
+  gcc_assert (TREE_CODE (v) == TREE_VEC);
+
+  int oldlen = TREE_VEC_LENGTH (v);
+  gcc_assert (len > oldlen);
+
+  int oldlength = (oldlen - 1) * sizeof (tree) + sizeof (struct tree_vec);
+  int length = (len - 1) * sizeof (tree) + sizeof (struct tree_vec);
+
+  record_node_allocation_statistics (TREE_VEC, length - oldlength);
+
+  v = (tree) ggc_realloc_stat (v, length PASS_MEM_STAT);
+
+  TREE_VEC_LENGTH (v) = len;
+
+  return v;
+}
 
 /* Return 1 if EXPR is the integer constant zero or a complex constant
    of zero.  */
diff --git a/gcc/tree.h b/gcc/tree.h
index 920ad07..8ca89d9 100644
--- a/gcc/tree.h
+++ b/gcc/tree.h
@@ -3412,6 +3412,11 @@  extern tree make_tree_binfo_stat (unsigned MEM_STAT_DECL);
 extern tree make_tree_vec_stat (int MEM_STAT_DECL);
 #define make_tree_vec(t) make_tree_vec_stat (t MEM_STAT_INFO)
 
+/* Grow a TREE_VEC.  */
+
+extern tree grow_tree_vec_stat (tree v, int MEM_STAT_DECL);
+#define grow_tree_vec(v, t) grow_tree_vec_stat (v, t MEM_STAT_INFO)
+
 /* Return the (unique) IDENTIFIER_NODE node for a given name.
    The name is supplied as a char *.  */