diff mbox series

[v2] c++: Fix constrained auto deduction templ parms resolution [PR114915, PR115030]

Message ID 20240812150452.1302484-1-sska1377@gmail.com
State New
Headers show
Series [v2] c++: Fix constrained auto deduction templ parms resolution [PR114915, PR115030] | expand

Commit Message

Seyed Sajad Kahani Aug. 12, 2024, 3:04 p.m. UTC
When deducing auto for `adc_return_type`, `adc_variable_type`, and
`adc_decomp_type` contexts (at the usage time), we try to resolve the outermost
template arguments to be used for satisfaction. This is done by one of the
following, depending on the scope:

1. Checking the `DECL_TEMPLATE_INFO` of the current function scope and
extracting `DECL_TI_ARGS` from it for function scope deductions (pt.cc:31236).
2. Checking the `DECL_TEMPLATE_INFO` of the declaration (alongside with other
conditions) for non-function scope variable declaration deductions
(decl.cc:8527).

Note that `DECL_TI_ARGS` for partial and explicit specializations will yield the
arguments with respect to the most_general_template, which is the primary
template. This can lead to rejection of valid code or acceptance of invalid code
(PR115030) in a partial specialization context. For an explicitly specialized
case, due to the mismatch between the desired depth and the actual depth of
args, it can lead to ICEs (PR114915) where we intend to fill the missing levels
with dummy levels (pt.cc:31260), while the missing levels are negative.

This patch resolves PR114915 and PR115030 by replacing the logic of extracting
args for the declaration in those two places with `outer_template_args`.
`outer_template_args` is an existing function that was used in limited contexts to
do so. Now, it is extended to handle partial and explicit specializations and
lambda functions as well. A few inevitable changes are also made to the
signature of some functions, relaxing `const_tree` to `tree`.

	PR c++/114915
	PR c++/115030

gcc/cp/ChangeLog:

	* constraint.cc (maybe_substitute_reqs_for): Relax the argument type to
	be compatible with outer_template_args.
	* cp-tree.h (outer_template_args): Relax the argument type and add an
	optional argument.
	(maybe_substitute_reqs_for): Relax the argument type to be compatible
	with outer_template_args.
	* decl.cc (cp_finish_decl): Replace the logic of extracting args with
	outer_template_args.
	* pt.cc (outer_template_args): Handle partial and explicit
	specializations and lambda functions.
	(do_auto_deduction): Replace the logic of extracting args with
	outer_template_args.

gcc/testsuite/ChangeLog:

	* g++.dg/cpp2a/concepts-placeholder14.C: New test.
	* g++.dg/cpp2a/concepts-placeholder15.C: New test.
	* g++.dg/cpp2a/concepts-placeholder16.C: New test.
	* g++.dg/cpp2a/concepts-placeholder17.C: New test.
---
 gcc/cp/constraint.cc                          |  2 +-
 gcc/cp/cp-tree.h                              |  4 +-
 gcc/cp/decl.cc                                |  2 +-
 gcc/cp/pt.cc                                  | 84 +++++++++++++------
 .../g++.dg/cpp2a/concepts-placeholder14.C     | 19 +++++
 .../g++.dg/cpp2a/concepts-placeholder15.C     | 26 ++++++
 .../g++.dg/cpp2a/concepts-placeholder16.C     | 33 ++++++++
 .../g++.dg/cpp2a/concepts-placeholder17.C     | 20 +++++
 8 files changed, 161 insertions(+), 29 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-placeholder14.C
 create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-placeholder15.C
 create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-placeholder16.C
 create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-placeholder17.C

Comments

Seyed Sajad Kahani Sept. 4, 2024, 9:27 p.m. UTC | #1
I'm gently pinging about the patch I submitted:

https://gcc.gnu.org/pipermail/gcc-patches/2024-August/660177.html

This patch was created in response to Jason's comments here:

https://gcc.gnu.org/pipermail/gcc-patches/2024-July/657739.html

I appreciate your time and consideration.

Thank you.
Patrick Palka Sept. 12, 2024, 5:32 p.m. UTC | #2
On Mon, 12 Aug 2024, Seyed Sajad Kahani wrote:

> When deducing auto for `adc_return_type`, `adc_variable_type`, and
> `adc_decomp_type` contexts (at the usage time), we try to resolve the outermost
> template arguments to be used for satisfaction. This is done by one of the
> following, depending on the scope:
> 
> 1. Checking the `DECL_TEMPLATE_INFO` of the current function scope and
> extracting `DECL_TI_ARGS` from it for function scope deductions (pt.cc:31236).
> 2. Checking the `DECL_TEMPLATE_INFO` of the declaration (alongside with other
> conditions) for non-function scope variable declaration deductions
> (decl.cc:8527).
> 
> Note that `DECL_TI_ARGS` for partial and explicit specializations will yield the
> arguments with respect to the most_general_template, which is the primary
> template. This can lead to rejection of valid code or acceptance of invalid code
> (PR115030) in a partial specialization context. For an explicitly specialized
> case, due to the mismatch between the desired depth and the actual depth of
> args, it can lead to ICEs (PR114915) where we intend to fill the missing levels
> with dummy levels (pt.cc:31260), while the missing levels are negative.
> 
> This patch resolves PR114915 and PR115030 by replacing the logic of extracting
> args for the declaration in those two places with `outer_template_args`.
> `outer_template_args` is an existing function that was used in limited contexts to
> do so. Now, it is extended to handle partial and explicit specializations and
> lambda functions as well. A few inevitable changes are also made to the
> signature of some functions, relaxing `const_tree` to `tree`.

Thanks for working on this and for the patch ping!

> 
> 	PR c++/114915
> 	PR c++/115030
> 
> gcc/cp/ChangeLog:
> 
> 	* constraint.cc (maybe_substitute_reqs_for): Relax the argument type to
> 	be compatible with outer_template_args.
> 	* cp-tree.h (outer_template_args): Relax the argument type and add an
> 	optional argument.
> 	(maybe_substitute_reqs_for): Relax the argument type to be compatible
> 	with outer_template_args.
> 	* decl.cc (cp_finish_decl): Replace the logic of extracting args with
> 	outer_template_args.
> 	* pt.cc (outer_template_args): Handle partial and explicit
> 	specializations and lambda functions.

Jason, I'm not sure if you had in mind extending outer_template_args
with a flag like you have, or creating a new function?

> 	(do_auto_deduction): Replace the logic of extracting args with
> 	outer_template_args.
> 
> gcc/testsuite/ChangeLog:
> 
> 	* g++.dg/cpp2a/concepts-placeholder14.C: New test.
> 	* g++.dg/cpp2a/concepts-placeholder15.C: New test.
> 	* g++.dg/cpp2a/concepts-placeholder16.C: New test.
> 	* g++.dg/cpp2a/concepts-placeholder17.C: New test.
> ---
>  gcc/cp/constraint.cc                          |  2 +-
>  gcc/cp/cp-tree.h                              |  4 +-
>  gcc/cp/decl.cc                                |  2 +-
>  gcc/cp/pt.cc                                  | 84 +++++++++++++------
>  .../g++.dg/cpp2a/concepts-placeholder14.C     | 19 +++++
>  .../g++.dg/cpp2a/concepts-placeholder15.C     | 26 ++++++
>  .../g++.dg/cpp2a/concepts-placeholder16.C     | 33 ++++++++
>  .../g++.dg/cpp2a/concepts-placeholder17.C     | 20 +++++
>  8 files changed, 161 insertions(+), 29 deletions(-)
>  create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-placeholder14.C
>  create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-placeholder15.C
>  create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-placeholder16.C
>  create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-placeholder17.C
> 
> diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
> index ebf4255e5..a1c3962c4 100644
> --- a/gcc/cp/constraint.cc
> +++ b/gcc/cp/constraint.cc
> @@ -1332,7 +1332,7 @@ remove_constraints (tree t)
>     for declaration matching.  */
>  
>  tree
> -maybe_substitute_reqs_for (tree reqs, const_tree decl)
> +maybe_substitute_reqs_for (tree reqs, tree decl)
>  {
>    if (reqs == NULL_TREE)
>      return NULL_TREE;
> diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
> index 9a8c86591..2d6733f57 100644
> --- a/gcc/cp/cp-tree.h
> +++ b/gcc/cp/cp-tree.h
> @@ -7215,7 +7215,7 @@ extern tree maybe_set_retval_sentinel		(void);
>  extern tree template_parms_to_args		(tree);
>  extern tree template_parms_level_to_args	(tree);
>  extern tree generic_targs_for			(tree);
> -extern tree outer_template_args			(const_tree);
> +extern tree outer_template_args			(tree, bool = true);
>  
>  /* in expr.cc */
>  extern tree cplus_expand_constant		(tree);
> @@ -8560,7 +8560,7 @@ extern void remove_constraints                  (tree);
>  extern tree current_template_constraints	(void);
>  extern tree associate_classtype_constraints     (tree);
>  extern tree build_constraints                   (tree, tree);
> -extern tree maybe_substitute_reqs_for		(tree, const_tree);
> +extern tree maybe_substitute_reqs_for		(tree, tree);
>  extern tree get_trailing_function_requirements	(tree);
>  extern tree get_shorthand_constraints           (tree);
>  
> diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
> index a139b293e..3a9344a32 100644
> --- a/gcc/cp/decl.cc
> +++ b/gcc/cp/decl.cc
> @@ -8560,7 +8560,7 @@ cp_finish_decl (tree decl, tree init, bool init_const_expr_p,
>  	/* The outer template arguments might be needed for satisfaction.
>  	   (For function scope variables, do_auto_deduction will obtain the
>  	   outer template arguments from current_function_decl.)  */
> -	outer_targs = DECL_TI_ARGS (decl);
> +        outer_targs = outer_template_args (decl, false);
>        type = TREE_TYPE (decl) = do_auto_deduction (type, d_init, auto_node,
>  						   tf_warning_or_error, adc,
>  						   outer_targs, flags);
> diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
> index 32640f8e9..b446884c8 100644
> --- a/gcc/cp/pt.cc
> +++ b/gcc/cp/pt.cc
> @@ -4998,23 +4998,70 @@ generic_targs_for (tree tmpl)
>  /* Return the template arguments corresponding to the template parameters of
>     DECL's enclosing scope.  When DECL is a member of a partial specialization,
>     this returns the arguments for the partial specialization as opposed to those
> -   for the primary template, which is the main difference between this function
> -   and simply using e.g. the TYPE_TI_ARGS of DECL's DECL_CONTEXT.  */
> -
> +   for the primary template, and for a full specialization, it returns null.
> +   STRIP_CURRENT specifies whether it should include currently declared 
> +   templates or not.  */
>  tree
> -outer_template_args (const_tree decl)
> +outer_template_args (tree decl, bool strip_current /* = true */) 
>  {
>    if (TREE_CODE (decl) == TEMPLATE_DECL)
>      decl = DECL_TEMPLATE_RESULT (decl);
>    tree ti = get_template_info (decl);
> +  tree args = NULL_TREE;
> +
>    if (!ti)
> -    return NULL_TREE;
> -  tree args = TI_ARGS (ti);
> -  if (!PRIMARY_TEMPLATE_P (TI_TEMPLATE (ti)))
> -    return args;
> -  if (TMPL_ARGS_DEPTH (args) == 1)
> -    return NULL_TREE;
> -  return strip_innermost_template_args (args, 1);
> +    {
> +      if (DECL_FUNCTION_SCOPE_P (decl))
> +	args = outer_template_args (DECL_CONTEXT (decl), false);
> +    }
> +  else
> +    {
> +      if (PRIMARY_TEMPLATE_P (TI_TEMPLATE (ti)) && TI_PARTIAL_INFO (ti))
> +	ti = TI_PARTIAL_INFO (ti);

More of a style issue, but I think this if could be moved into ...

> +
> +      /* For an explicitly specialized declaration. */
> +      if (DECL_LANG_SPECIFIC (decl) && DECL_TEMPLATE_SPECIALIZATION (decl))
> +	args = NULL_TREE;
> +      else
> +	{

... this branch which is the only user?  And then we can flatten this
nested if into an else if chain.

I'll defer to Jason review of the logic since he has more of a vision of
what it should look like.

> +	  args = TI_ARGS (ti);
> +
> +	  int actual_depth = TMPL_ARGS_DEPTH (args);
> +	  /* Finding explicitly specialized scopes */
> +	  for (tree tmpl = TI_TEMPLATE(ti);
> +	       DECL_LANG_SPECIFIC (tmpl) && DECL_TEMPLATE_INFO (tmpl);
> +	       tmpl = DECL_TI_TEMPLATE (tmpl))
> +	    {
> +	      if (DECL_TEMPLATE_SPECIALIZATION (tmpl))
> +		{
> +		  actual_depth = TMPL_PARMS_DEPTH (DECL_TEMPLATE_PARMS (tmpl));
> +		  break;
> +		}
> +	      if (TREE_CODE (DECL_TI_TEMPLATE (tmpl)) != TEMPLATE_DECL)
> +		break;
> +	    }
> +
> +	  /* If any of outer scopes are explicitly specialized. */
> +	  if (TMPL_ARGS_DEPTH(args) > actual_depth)
> +	    args = get_innermost_template_args (args, actual_depth);
> +
> +	  if (strip_current && PRIMARY_TEMPLATE_P (TI_TEMPLATE (ti)))
> +	    {
> +	      if (TMPL_ARGS_DEPTH (args) == 1)
> +		return NULL_TREE;
> +	      return strip_innermost_template_args (args, 1);
> +	    }
> +	}
> +    }
> +
> +  /* Special treatment of lambda functions. */
> +  if (LAMBDA_FUNCTION_P (decl) && !strip_current)
> +    {
> +	tree regen_targs = lambda_regenerating_args (decl);
> +	args =  add_to_template_args (regen_targs, args);
> +    }
> +
> +  return args;
>  }
>  
>  /* Update the declared TYPE by doing any lookups which were thought to be
> @@ -31233,20 +31280,7 @@ do_auto_deduction (tree type, tree init, tree auto_node,
>  	  || context == adc_variable_type
>  	  || context == adc_decomp_type)
>  	if (tree fn = current_function_decl)
> -	  if (DECL_TEMPLATE_INFO (fn) || LAMBDA_FUNCTION_P (fn))
> -	    {
> -	      outer_targs = DECL_TEMPLATE_INFO (fn)
> -		? DECL_TI_ARGS (fn) : NULL_TREE;
> -	      if (LAMBDA_FUNCTION_P (fn))
> -		{
> -		  /* As in satisfy_declaration_constraints.  */
> -		  tree regen_args = lambda_regenerating_args (fn);
> -		  if (outer_targs)
> -		    outer_targs = add_to_template_args (regen_args, outer_targs);
> -		  else
> -		    outer_targs = regen_args;
> -		}
> -	    }
> +	  outer_targs = outer_template_args(fn, false);
>  
>        tree full_targs = outer_targs;
>        if (context == adc_unify && tmpl)
> diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-placeholder14.C b/gcc/testsuite/g++.dg/cpp2a/concepts-placeholder14.C
> new file mode 100644
> index 000000000..fcdbd7608
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-placeholder14.C
> @@ -0,0 +1,19 @@
> +// PR c++/114915
> +// { dg-do compile { target c++20 } }
> +
> +template<typename T>
> +concept C = __is_same(T, int);
> +
> +template<typename T>
> +void f() {
> +}
> +
> +template<>
> +void f<int>() {
> +  C auto x = 1;
> +}
> +
> +int main() {
> +  f<int>();
> +  return 0;
> +}
> diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-placeholder15.C b/gcc/testsuite/g++.dg/cpp2a/concepts-placeholder15.C
> new file mode 100644
> index 000000000..b507e4165
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-placeholder15.C
> @@ -0,0 +1,26 @@
> +// PR c++/114915
> +// { dg-do compile { target c++20 } }
> +
> +template<typename T, typename U>
> +concept C = __is_same(T, U);
> +
> +template<typename T>
> +int x = 0;
> +
> +template<>
> +C<double> auto x<double> = 1.0;
> +
> +template <typename T>
> +struct S {};
> +
> +template<typename T>
> +int y = 0;
> +
> +template<typename T>
> +C<char> auto y<S<T>> = 'c';
> +
> +int main() {
> +  if (y<S<int>> != 'c')
> +      return 1;
> +  return 0;
> +}
> diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-placeholder16.C b/gcc/testsuite/g++.dg/cpp2a/concepts-placeholder16.C
> new file mode 100644
> index 000000000..f808ef1b6
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-placeholder16.C
> @@ -0,0 +1,33 @@
> +// PR c++/114915
> +// { dg-do compile { target c++20 } }
> +
> +template<typename T, typename U>
> +concept C = __is_same(T, U);
> +
> +template<typename T>
> +struct A
> +{ 
> +    template<typename U>
> +    void f() {
> +    }
> +};
> + 
> +template<>
> +template<>
> +void A<int>::f<int>() {
> +  C<int> auto x = 1;
> +}
> +
> +template<>
> +template<typename U>
> +void A<bool>::f() {
> +  C<int> auto x = 1;
> +}
> +
> +int main() {
> +  A<bool> a;
> +  a.f<char>();
> +  A<int> b;
> +  b.f<int>();
> +  return 0;
> +}
> diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-placeholder17.C b/gcc/testsuite/g++.dg/cpp2a/concepts-placeholder17.C
> new file mode 100644
> index 000000000..a8bd48e39
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-placeholder17.C
> @@ -0,0 +1,20 @@
> +// PR c++/115030
> +// { dg-do compile { target c++20 } }
> +
> +template<typename T, typename U>
> +concept C = __is_same(T, U);
> +
> +template <typename T>
> +struct s {
> +};
> +
> +template <typename T>
> +char v = 'a';
> +
> +template<typename T>
> +C<T> auto v<s<T>> = 'c';
> +
> +int main() {
> +  v<s<char>> = 'b';
> +  return 0;
> +}
> -- 
> 2.46.0
> 
>
Jason Merrill Sept. 15, 2024, 12:58 p.m. UTC | #3
On 9/12/24 1:32 PM, Patrick Palka wrote:
> On Mon, 12 Aug 2024, Seyed Sajad Kahani wrote:
> 
>> When deducing auto for `adc_return_type`, `adc_variable_type`, and
>> `adc_decomp_type` contexts (at the usage time), we try to resolve the outermost
>> template arguments to be used for satisfaction. This is done by one of the
>> following, depending on the scope:
>>
>> 1. Checking the `DECL_TEMPLATE_INFO` of the current function scope and
>> extracting `DECL_TI_ARGS` from it for function scope deductions (pt.cc:31236).
>> 2. Checking the `DECL_TEMPLATE_INFO` of the declaration (alongside with other
>> conditions) for non-function scope variable declaration deductions
>> (decl.cc:8527).
>>
>> Note that `DECL_TI_ARGS` for partial and explicit specializations will yield the
>> arguments with respect to the most_general_template, which is the primary
>> template. This can lead to rejection of valid code or acceptance of invalid code
>> (PR115030) in a partial specialization context. For an explicitly specialized
>> case, due to the mismatch between the desired depth and the actual depth of
>> args, it can lead to ICEs (PR114915) where we intend to fill the missing levels
>> with dummy levels (pt.cc:31260), while the missing levels are negative.
>>
>> This patch resolves PR114915 and PR115030 by replacing the logic of extracting
>> args for the declaration in those two places with `outer_template_args`.
>> `outer_template_args` is an existing function that was used in limited contexts to
>> do so. Now, it is extended to handle partial and explicit specializations and
>> lambda functions as well. A few inevitable changes are also made to the
>> signature of some functions, relaxing `const_tree` to `tree`.
> 
> Thanks for working on this and for the patch ping!

Yes, absolutely!

>> 	PR c++/114915
>> 	PR c++/115030
>>
>> gcc/cp/ChangeLog:
>>
>> 	* constraint.cc (maybe_substitute_reqs_for): Relax the argument type to
>> 	be compatible with outer_template_args.
>> 	* cp-tree.h (outer_template_args): Relax the argument type and add an
>> 	optional argument.
>> 	(maybe_substitute_reqs_for): Relax the argument type to be compatible
>> 	with outer_template_args.
>> 	* decl.cc (cp_finish_decl): Replace the logic of extracting args with
>> 	outer_template_args.
>> 	* pt.cc (outer_template_args): Handle partial and explicit
>> 	specializations and lambda functions.
> 
> Jason, I'm not sure if you had in mind extending outer_template_args
> with a flag like you have, or creating a new function?

Indeed, I'd prefer for the two uses to use different names rather than 
add a flag.  Maybe targs_for_substitution, with outer_template_args 
becoming a wrapper that just calls strip_innermost_template_args.

>> 	(do_auto_deduction): Replace the logic of extracting args with
>> 	outer_template_args.
>>
>> gcc/testsuite/ChangeLog:
>>
>> 	* g++.dg/cpp2a/concepts-placeholder14.C: New test.
>> 	* g++.dg/cpp2a/concepts-placeholder15.C: New test.
>> 	* g++.dg/cpp2a/concepts-placeholder16.C: New test.
>> 	* g++.dg/cpp2a/concepts-placeholder17.C: New test.
>> ---
>>   gcc/cp/constraint.cc                          |  2 +-
>>   gcc/cp/cp-tree.h                              |  4 +-
>>   gcc/cp/decl.cc                                |  2 +-
>>   gcc/cp/pt.cc                                  | 84 +++++++++++++------
>>   .../g++.dg/cpp2a/concepts-placeholder14.C     | 19 +++++
>>   .../g++.dg/cpp2a/concepts-placeholder15.C     | 26 ++++++
>>   .../g++.dg/cpp2a/concepts-placeholder16.C     | 33 ++++++++
>>   .../g++.dg/cpp2a/concepts-placeholder17.C     | 20 +++++
>>   8 files changed, 161 insertions(+), 29 deletions(-)
>>   create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-placeholder14.C
>>   create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-placeholder15.C
>>   create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-placeholder16.C
>>   create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-placeholder17.C
>>
>> diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
>> index ebf4255e5..a1c3962c4 100644
>> --- a/gcc/cp/constraint.cc
>> +++ b/gcc/cp/constraint.cc
>> @@ -1332,7 +1332,7 @@ remove_constraints (tree t)
>>      for declaration matching.  */
>>   
>>   tree
>> -maybe_substitute_reqs_for (tree reqs, const_tree decl)
>> +maybe_substitute_reqs_for (tree reqs, tree decl)
>>   {
>>     if (reqs == NULL_TREE)
>>       return NULL_TREE;
>> diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
>> index 9a8c86591..2d6733f57 100644
>> --- a/gcc/cp/cp-tree.h
>> +++ b/gcc/cp/cp-tree.h
>> @@ -7215,7 +7215,7 @@ extern tree maybe_set_retval_sentinel		(void);
>>   extern tree template_parms_to_args		(tree);
>>   extern tree template_parms_level_to_args	(tree);
>>   extern tree generic_targs_for			(tree);
>> -extern tree outer_template_args			(const_tree);
>> +extern tree outer_template_args			(tree, bool = true);
>>   
>>   /* in expr.cc */
>>   extern tree cplus_expand_constant		(tree);
>> @@ -8560,7 +8560,7 @@ extern void remove_constraints                  (tree);
>>   extern tree current_template_constraints	(void);
>>   extern tree associate_classtype_constraints     (tree);
>>   extern tree build_constraints                   (tree, tree);
>> -extern tree maybe_substitute_reqs_for		(tree, const_tree);
>> +extern tree maybe_substitute_reqs_for		(tree, tree);
>>   extern tree get_trailing_function_requirements	(tree);
>>   extern tree get_shorthand_constraints           (tree);
>>   
>> diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
>> index a139b293e..3a9344a32 100644
>> --- a/gcc/cp/decl.cc
>> +++ b/gcc/cp/decl.cc
>> @@ -8560,7 +8560,7 @@ cp_finish_decl (tree decl, tree init, bool init_const_expr_p,
>>   	/* The outer template arguments might be needed for satisfaction.
>>   	   (For function scope variables, do_auto_deduction will obtain the
>>   	   outer template arguments from current_function_decl.)  */
>> -	outer_targs = DECL_TI_ARGS (decl);
>> +        outer_targs = outer_template_args (decl, false);
>>         type = TREE_TYPE (decl) = do_auto_deduction (type, d_init, auto_node,
>>   						   tf_warning_or_error, adc,
>>   						   outer_targs, flags);
>> diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
>> index 32640f8e9..b446884c8 100644
>> --- a/gcc/cp/pt.cc
>> +++ b/gcc/cp/pt.cc
>> @@ -4998,23 +4998,70 @@ generic_targs_for (tree tmpl)
>>   /* Return the template arguments corresponding to the template parameters of
>>      DECL's enclosing scope.  When DECL is a member of a partial specialization,
>>      this returns the arguments for the partial specialization as opposed to those
>> -   for the primary template, which is the main difference between this function
>> -   and simply using e.g. the TYPE_TI_ARGS of DECL's DECL_CONTEXT.  */
>> -
>> +   for the primary template, and for a full specialization, it returns null.
>> +   STRIP_CURRENT specifies whether it should include currently declared
>> +   templates or not.  */
>>   tree
>> -outer_template_args (const_tree decl)
>> +outer_template_args (tree decl, bool strip_current /* = true */)
>>   {
>>     if (TREE_CODE (decl) == TEMPLATE_DECL)
>>       decl = DECL_TEMPLATE_RESULT (decl);
>>     tree ti = get_template_info (decl);
>> +  tree args = NULL_TREE;
>> +
>>     if (!ti)
>> -    return NULL_TREE;
>> -  tree args = TI_ARGS (ti);
>> -  if (!PRIMARY_TEMPLATE_P (TI_TEMPLATE (ti)))
>> -    return args;
>> -  if (TMPL_ARGS_DEPTH (args) == 1)
>> -    return NULL_TREE;
>> -  return strip_innermost_template_args (args, 1);
>> +    {
>> +      if (DECL_FUNCTION_SCOPE_P (decl))
>> +	args = outer_template_args (DECL_CONTEXT (decl), false);
>> +    }
>> +  else
>> +    {
>> +      if (PRIMARY_TEMPLATE_P (TI_TEMPLATE (ti)) && TI_PARTIAL_INFO (ti))
>> +	ti = TI_PARTIAL_INFO (ti);
> 
> More of a style issue, but I think this if could be moved into ...
> 
>> +
>> +      /* For an explicitly specialized declaration. */
>> +      if (DECL_LANG_SPECIFIC (decl) && DECL_TEMPLATE_SPECIALIZATION (decl))
>> +	args = NULL_TREE;
>> +      else
>> +	{
> 
> ... this branch which is the only user?  And then we can flatten this
> nested if into an else if chain.
> 
> I'll defer to Jason review of the logic since he has more of a vision of
> what it should look like.
> 
>> +	  args = TI_ARGS (ti);
>> +
>> +	  int actual_depth = TMPL_ARGS_DEPTH (args);
>> +	  /* Finding explicitly specialized scopes */

Missing . at end of comment.

>> +	  for (tree tmpl = TI_TEMPLATE(ti);

Missing space before (

>> +	       DECL_LANG_SPECIFIC (tmpl) && DECL_TEMPLATE_INFO (tmpl);
>> +	       tmpl = DECL_TI_TEMPLATE (tmpl))
>> +	    {
>> +	      if (DECL_TEMPLATE_SPECIALIZATION (tmpl))
>> +		{
>> +		  actual_depth = TMPL_PARMS_DEPTH (DECL_TEMPLATE_PARMS (tmpl));
>> +		  break;
>> +		}
>> +	      if (TREE_CODE (DECL_TI_TEMPLATE (tmpl)) != TEMPLATE_DECL)
>> +		break;
>> +	    }

Can you use template_for_substitution or most_general_template instead 
of writing another version of that loop?

>> +	  /* If any of outer scopes are explicitly specialized. */

Need two spaces after .

>> +	  if (TMPL_ARGS_DEPTH(args) > actual_depth)

Missing space.

>> +	    args = get_innermost_template_args (args, actual_depth);
>> +
>> +	  if (strip_current && PRIMARY_TEMPLATE_P (TI_TEMPLATE (ti)))
>> +	    {
>> +	      if (TMPL_ARGS_DEPTH (args) == 1)
>> +		return NULL_TREE;
>> +	      return strip_innermost_template_args (args, 1);
>> +	    }
>> +	}
>> +    }
>> +
>> +  /* Special treatment of lambda functions. */
>> +  if (LAMBDA_FUNCTION_P (decl) && !strip_current)

Perhaps the !strip_current check should be a checking_assert rather than 
a condition?

>> +    {
>> +	tree regen_targs = lambda_regenerating_args (decl);
>> +	args =  add_to_template_args (regen_targs, args);
>> +    }
>> +
>> +  return args;
>>   }
>>   
>>   /* Update the declared TYPE by doing any lookups which were thought to be
>> @@ -31233,20 +31280,7 @@ do_auto_deduction (tree type, tree init, tree auto_node,
>>   	  || context == adc_variable_type
>>   	  || context == adc_decomp_type)
>>   	if (tree fn = current_function_decl)
>> -	  if (DECL_TEMPLATE_INFO (fn) || LAMBDA_FUNCTION_P (fn))
>> -	    {
>> -	      outer_targs = DECL_TEMPLATE_INFO (fn)
>> -		? DECL_TI_ARGS (fn) : NULL_TREE;
>> -	      if (LAMBDA_FUNCTION_P (fn))
>> -		{
>> -		  /* As in satisfy_declaration_constraints.  */
>> -		  tree regen_args = lambda_regenerating_args (fn);
>> -		  if (outer_targs)
>> -		    outer_targs = add_to_template_args (regen_args, outer_targs);
>> -		  else
>> -		    outer_targs = regen_args;
>> -		}
>> -	    }
>> +	  outer_targs = outer_template_args(fn, false);
>>   
>>         tree full_targs = outer_targs;
>>         if (context == adc_unify && tmpl)
>> diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-placeholder14.C b/gcc/testsuite/g++.dg/cpp2a/concepts-placeholder14.C
>> new file mode 100644
>> index 000000000..fcdbd7608
>> --- /dev/null
>> +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-placeholder14.C
>> @@ -0,0 +1,19 @@
>> +// PR c++/114915
>> +// { dg-do compile { target c++20 } }
>> +
>> +template<typename T>
>> +concept C = __is_same(T, int);
>> +
>> +template<typename T>
>> +void f() {
>> +}
>> +
>> +template<>
>> +void f<int>() {
>> +  C auto x = 1;
>> +}
>> +
>> +int main() {
>> +  f<int>();
>> +  return 0;
>> +}
>> diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-placeholder15.C b/gcc/testsuite/g++.dg/cpp2a/concepts-placeholder15.C
>> new file mode 100644
>> index 000000000..b507e4165
>> --- /dev/null
>> +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-placeholder15.C
>> @@ -0,0 +1,26 @@
>> +// PR c++/114915
>> +// { dg-do compile { target c++20 } }
>> +
>> +template<typename T, typename U>
>> +concept C = __is_same(T, U);
>> +
>> +template<typename T>
>> +int x = 0;
>> +
>> +template<>
>> +C<double> auto x<double> = 1.0;
>> +
>> +template <typename T>
>> +struct S {};
>> +
>> +template<typename T>
>> +int y = 0;
>> +
>> +template<typename T>
>> +C<char> auto y<S<T>> = 'c';
>> +
>> +int main() {
>> +  if (y<S<int>> != 'c')
>> +      return 1;
>> +  return 0;
>> +}
>> diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-placeholder16.C b/gcc/testsuite/g++.dg/cpp2a/concepts-placeholder16.C
>> new file mode 100644
>> index 000000000..f808ef1b6
>> --- /dev/null
>> +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-placeholder16.C
>> @@ -0,0 +1,33 @@
>> +// PR c++/114915
>> +// { dg-do compile { target c++20 } }
>> +
>> +template<typename T, typename U>
>> +concept C = __is_same(T, U);
>> +
>> +template<typename T>
>> +struct A
>> +{
>> +    template<typename U>
>> +    void f() {
>> +    }
>> +};
>> +
>> +template<>
>> +template<>
>> +void A<int>::f<int>() {
>> +  C<int> auto x = 1;
>> +}
>> +
>> +template<>
>> +template<typename U>
>> +void A<bool>::f() {
>> +  C<int> auto x = 1;
>> +}
>> +
>> +int main() {
>> +  A<bool> a;
>> +  a.f<char>();
>> +  A<int> b;
>> +  b.f<int>();
>> +  return 0;
>> +}
>> diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-placeholder17.C b/gcc/testsuite/g++.dg/cpp2a/concepts-placeholder17.C
>> new file mode 100644
>> index 000000000..a8bd48e39
>> --- /dev/null
>> +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-placeholder17.C
>> @@ -0,0 +1,20 @@
>> +// PR c++/115030
>> +// { dg-do compile { target c++20 } }
>> +
>> +template<typename T, typename U>
>> +concept C = __is_same(T, U);
>> +
>> +template <typename T>
>> +struct s {
>> +};
>> +
>> +template <typename T>
>> +char v = 'a';
>> +
>> +template<typename T>
>> +C<T> auto v<s<T>> = 'c';
>> +
>> +int main() {
>> +  v<s<char>> = 'b';
>> +  return 0;
>> +}
>> -- 
>> 2.46.0
>>
>>
>
diff mbox series

Patch

diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
index ebf4255e5..a1c3962c4 100644
--- a/gcc/cp/constraint.cc
+++ b/gcc/cp/constraint.cc
@@ -1332,7 +1332,7 @@  remove_constraints (tree t)
    for declaration matching.  */
 
 tree
-maybe_substitute_reqs_for (tree reqs, const_tree decl)
+maybe_substitute_reqs_for (tree reqs, tree decl)
 {
   if (reqs == NULL_TREE)
     return NULL_TREE;
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 9a8c86591..2d6733f57 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -7215,7 +7215,7 @@  extern tree maybe_set_retval_sentinel		(void);
 extern tree template_parms_to_args		(tree);
 extern tree template_parms_level_to_args	(tree);
 extern tree generic_targs_for			(tree);
-extern tree outer_template_args			(const_tree);
+extern tree outer_template_args			(tree, bool = true);
 
 /* in expr.cc */
 extern tree cplus_expand_constant		(tree);
@@ -8560,7 +8560,7 @@  extern void remove_constraints                  (tree);
 extern tree current_template_constraints	(void);
 extern tree associate_classtype_constraints     (tree);
 extern tree build_constraints                   (tree, tree);
-extern tree maybe_substitute_reqs_for		(tree, const_tree);
+extern tree maybe_substitute_reqs_for		(tree, tree);
 extern tree get_trailing_function_requirements	(tree);
 extern tree get_shorthand_constraints           (tree);
 
diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
index a139b293e..3a9344a32 100644
--- a/gcc/cp/decl.cc
+++ b/gcc/cp/decl.cc
@@ -8560,7 +8560,7 @@  cp_finish_decl (tree decl, tree init, bool init_const_expr_p,
 	/* The outer template arguments might be needed for satisfaction.
 	   (For function scope variables, do_auto_deduction will obtain the
 	   outer template arguments from current_function_decl.)  */
-	outer_targs = DECL_TI_ARGS (decl);
+        outer_targs = outer_template_args (decl, false);
       type = TREE_TYPE (decl) = do_auto_deduction (type, d_init, auto_node,
 						   tf_warning_or_error, adc,
 						   outer_targs, flags);
diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
index 32640f8e9..b446884c8 100644
--- a/gcc/cp/pt.cc
+++ b/gcc/cp/pt.cc
@@ -4998,23 +4998,70 @@  generic_targs_for (tree tmpl)
 /* Return the template arguments corresponding to the template parameters of
    DECL's enclosing scope.  When DECL is a member of a partial specialization,
    this returns the arguments for the partial specialization as opposed to those
-   for the primary template, which is the main difference between this function
-   and simply using e.g. the TYPE_TI_ARGS of DECL's DECL_CONTEXT.  */
-
+   for the primary template, and for a full specialization, it returns null.
+   STRIP_CURRENT specifies whether it should include currently declared 
+   templates or not.  */
 tree
-outer_template_args (const_tree decl)
+outer_template_args (tree decl, bool strip_current /* = true */) 
 {
   if (TREE_CODE (decl) == TEMPLATE_DECL)
     decl = DECL_TEMPLATE_RESULT (decl);
   tree ti = get_template_info (decl);
+  tree args = NULL_TREE;
+
   if (!ti)
-    return NULL_TREE;
-  tree args = TI_ARGS (ti);
-  if (!PRIMARY_TEMPLATE_P (TI_TEMPLATE (ti)))
-    return args;
-  if (TMPL_ARGS_DEPTH (args) == 1)
-    return NULL_TREE;
-  return strip_innermost_template_args (args, 1);
+    {
+      if (DECL_FUNCTION_SCOPE_P (decl))
+	args = outer_template_args (DECL_CONTEXT (decl), false);
+    }
+  else
+    {
+      if (PRIMARY_TEMPLATE_P (TI_TEMPLATE (ti)) && TI_PARTIAL_INFO (ti))
+	ti = TI_PARTIAL_INFO (ti);
+
+      /* For an explicitly specialized declaration. */
+      if (DECL_LANG_SPECIFIC (decl) && DECL_TEMPLATE_SPECIALIZATION (decl))
+	args = NULL_TREE;
+      else
+	{
+	  args = TI_ARGS (ti);
+
+	  int actual_depth = TMPL_ARGS_DEPTH (args);
+	  /* Finding explicitly specialized scopes */
+	  for (tree tmpl = TI_TEMPLATE(ti);
+	       DECL_LANG_SPECIFIC (tmpl) && DECL_TEMPLATE_INFO (tmpl);
+	       tmpl = DECL_TI_TEMPLATE (tmpl))
+	    {
+	      if (DECL_TEMPLATE_SPECIALIZATION (tmpl))
+		{
+		  actual_depth = TMPL_PARMS_DEPTH (DECL_TEMPLATE_PARMS (tmpl));
+		  break;
+		}
+	      if (TREE_CODE (DECL_TI_TEMPLATE (tmpl)) != TEMPLATE_DECL)
+		break;
+	    }
+
+	  /* If any of outer scopes are explicitly specialized. */
+	  if (TMPL_ARGS_DEPTH(args) > actual_depth)
+	    args = get_innermost_template_args (args, actual_depth);
+
+	  if (strip_current && PRIMARY_TEMPLATE_P (TI_TEMPLATE (ti)))
+	    {
+	      if (TMPL_ARGS_DEPTH (args) == 1)
+		return NULL_TREE;
+	      return strip_innermost_template_args (args, 1);
+	    }
+	}
+    }
+
+  /* Special treatment of lambda functions. */
+  if (LAMBDA_FUNCTION_P (decl) && !strip_current)
+    {
+	tree regen_targs = lambda_regenerating_args (decl);
+	args =  add_to_template_args (regen_targs, args);
+    }
+
+  return args;
 }
 
 /* Update the declared TYPE by doing any lookups which were thought to be
@@ -31233,20 +31280,7 @@  do_auto_deduction (tree type, tree init, tree auto_node,
 	  || context == adc_variable_type
 	  || context == adc_decomp_type)
 	if (tree fn = current_function_decl)
-	  if (DECL_TEMPLATE_INFO (fn) || LAMBDA_FUNCTION_P (fn))
-	    {
-	      outer_targs = DECL_TEMPLATE_INFO (fn)
-		? DECL_TI_ARGS (fn) : NULL_TREE;
-	      if (LAMBDA_FUNCTION_P (fn))
-		{
-		  /* As in satisfy_declaration_constraints.  */
-		  tree regen_args = lambda_regenerating_args (fn);
-		  if (outer_targs)
-		    outer_targs = add_to_template_args (regen_args, outer_targs);
-		  else
-		    outer_targs = regen_args;
-		}
-	    }
+	  outer_targs = outer_template_args(fn, false);
 
       tree full_targs = outer_targs;
       if (context == adc_unify && tmpl)
diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-placeholder14.C b/gcc/testsuite/g++.dg/cpp2a/concepts-placeholder14.C
new file mode 100644
index 000000000..fcdbd7608
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/concepts-placeholder14.C
@@ -0,0 +1,19 @@ 
+// PR c++/114915
+// { dg-do compile { target c++20 } }
+
+template<typename T>
+concept C = __is_same(T, int);
+
+template<typename T>
+void f() {
+}
+
+template<>
+void f<int>() {
+  C auto x = 1;
+}
+
+int main() {
+  f<int>();
+  return 0;
+}
diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-placeholder15.C b/gcc/testsuite/g++.dg/cpp2a/concepts-placeholder15.C
new file mode 100644
index 000000000..b507e4165
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/concepts-placeholder15.C
@@ -0,0 +1,26 @@ 
+// PR c++/114915
+// { dg-do compile { target c++20 } }
+
+template<typename T, typename U>
+concept C = __is_same(T, U);
+
+template<typename T>
+int x = 0;
+
+template<>
+C<double> auto x<double> = 1.0;
+
+template <typename T>
+struct S {};
+
+template<typename T>
+int y = 0;
+
+template<typename T>
+C<char> auto y<S<T>> = 'c';
+
+int main() {
+  if (y<S<int>> != 'c')
+      return 1;
+  return 0;
+}
diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-placeholder16.C b/gcc/testsuite/g++.dg/cpp2a/concepts-placeholder16.C
new file mode 100644
index 000000000..f808ef1b6
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/concepts-placeholder16.C
@@ -0,0 +1,33 @@ 
+// PR c++/114915
+// { dg-do compile { target c++20 } }
+
+template<typename T, typename U>
+concept C = __is_same(T, U);
+
+template<typename T>
+struct A
+{ 
+    template<typename U>
+    void f() {
+    }
+};
+ 
+template<>
+template<>
+void A<int>::f<int>() {
+  C<int> auto x = 1;
+}
+
+template<>
+template<typename U>
+void A<bool>::f() {
+  C<int> auto x = 1;
+}
+
+int main() {
+  A<bool> a;
+  a.f<char>();
+  A<int> b;
+  b.f<int>();
+  return 0;
+}
diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-placeholder17.C b/gcc/testsuite/g++.dg/cpp2a/concepts-placeholder17.C
new file mode 100644
index 000000000..a8bd48e39
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/concepts-placeholder17.C
@@ -0,0 +1,20 @@ 
+// PR c++/115030
+// { dg-do compile { target c++20 } }
+
+template<typename T, typename U>
+concept C = __is_same(T, U);
+
+template <typename T>
+struct s {
+};
+
+template <typename T>
+char v = 'a';
+
+template<typename T>
+C<T> auto v<s<T>> = 'c';
+
+int main() {
+  v<s<char>> = 'b';
+  return 0;
+}