public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
From: Patrick Palka <ppalka@redhat.com>
To: Seyed Sajad Kahani <sska1377@gmail.com>
Cc: gcc-patches@gcc.gnu.org, ppalka@redhat.com, jason@redhat.com
Subject: Re: [PATCH v2] Fix auto deduction for template specialization scopes [PR114915]
Date: Mon, 6 May 2024 14:46:01 -0400 (EDT)	[thread overview]
Message-ID: <12929011-5f01-3eee-d06f-83fee1f6b758@idea> (raw)
In-Reply-To: <20240504151503.661288-1-sska1377@gmail.com>

On Sat, 4 May 2024, Seyed Sajad Kahani wrote:

> The limitations of the initial patch (checking specializiation template usage), have been discussed.
> 
> > I realized that for the case where we have a member function template
> > of a class template, and a specialization of the enclosing class only
> > (like below),
> >
> > template <>
> > template <typename U>
> > void S<int>::f() {
> >   // some constrained auto
> > }
> >
> > When using S<int>::f<double>, DECL_TEMPLATE_INFO(fn) is non-zero, and
> > DECL_TEMPLATE_SPECIALIZATION(fn) is zero, while
> > DECL_TEMPLATE_SPECIALIZATION(DECL_TI_TEMPLATE(fn)) is non-zero.
> > So it means that the patch will extract DECL_TI_ARGS(fn) as
> > outer_targs, and it would be <int> <double> while the type of the
> > constrained auto will be as template <typename U> ... and will not be
> > dependent on the parameters of the enclosing class.
> > This means that again (outer_targs + targs) will have more depth than
> > auto_node levels.
> > This means that for the case where the function is not an explicit
> > specialization, but it is defined in an explicitly specialized scope,
> > the patch will not work.

Ah yes, good point!  This demonstrates that it doesn't suffice to
handle the TEMPLATE_TYPE_ORIG_LEVEL == 1 case contrary to what I
suggested earlier.

> 
> As described in more detail below, this patch attempts to resolve this issue by trimming full_targs.
> 
> > > Another more context-unaware approach to fix this might be to only
> > > use the innermost level from 'full_targs' for satisfaction if
> > > TEMPLATE_TYPE_ORIG_LEVEL is 1 (which means this constrained auto
> > > appeared in a context that doesn't have template parameters such as an
> > > explicit specialization or ordinary non-template function, and so
> > > only the level corresponding to the deduced type is needed for
> > > satisfaction.)
> > >
> > > Generalizing on that, another approach might be to handle missing_levels < 0
> > > by removing -missing_levels from full_targs via get_innermost_template_args.
> > > But AFAICT it should suffice to handle the TEMPLATE_TYPE_ORIG_LEVEL == 1
> > > case specifically.
> >
> > I was unable to understand why you think that it might not handle
> > TEMPLATE_TYPE_ORIG_LEVEL > 1 cases, so I tried to formulate my
> > reasoning as follows.

Yes, sorry about that misleading suggestion.

> >
> > Assuming contexts adc_variable_type, adc_return_type, adc_decomp_type:
> > For any case where missing_level < 0, it means that the type depends
> > on fewer levels than the template arguments used to materialize it.
> > This can only happen when the type is defined in an explicit
> > specialization scope. This explicit specialization might not occur in
> > its immediate scope.
> > Note that partial specialization (while changing the set of
> > parameters) cannot reduce the number of levels for the type.
> > Because of the fact that the enclosing scope of any explicit
> > specialization is explicitly specialized
> > (https://eel.is/c++draft/temp.expl.spec#16), the type will not depend
> > on template parameters outside of its innermost explicit specialized
> > scope.
> > Assuming that there are no real missing levels, by removing those
> > levels, missing_level should be = 0. As a result, by roughly doing
> >
> > if (missing_levels < 0) {
> >   tree trimmed_full_args = get_innermost_template_args(full_targs,
> > TEMPLATE_TYPE_ORIG_LEVEL(auto_node));
> >   full_targs = trimmed_full_args;
> > }
> > in pt.cc:31262, where we calculate and check missing_levels, we would
> > be able to fix the errors.
> > Note that, for the case where there are real missing levels, we are
> > putting irrelevant template arguments for the missing levels instead
> > of make_tree_vec(0). By this change:
> > - If the type is independent of those missing levels: it works fine either way.
> > - If the type is dependent on those missing levels: Instead of raising
> > an ICE, the compiler exhibits undefined behavior.

Makes sense.

> ---
>  gcc/cp/pt.cc                                  | 14 ++++++--
>  .../g++.dg/cpp2a/concepts-placeholder14.C     | 19 +++++++++++
>  .../g++.dg/cpp2a/concepts-placeholder15.C     | 15 +++++++++
>  .../g++.dg/cpp2a/concepts-placeholder16.C     | 33 +++++++++++++++++++
>  4 files changed, 78 insertions(+), 3 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
> 
> diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
> index 3b2106dd3..bdf03a1a7 100644
> --- a/gcc/cp/pt.cc
> +++ b/gcc/cp/pt.cc
> @@ -31044,7 +31044,8 @@ unparenthesized_id_or_class_member_access_p (tree init)
>     OUTER_TARGS is used during template argument deduction (context == adc_unify)
>     to properly substitute the result.  It's also used in the adc_unify and
>     adc_requirement contexts to communicate the necessary template arguments
> -   to satisfaction.  OUTER_TARGS is ignored in other contexts.
> +   to satisfaction.  OUTER_TARGS will be used for other contexts if it is a
> +   function scope deduction. Otherwise it is ignored.

The use of OUTER_TARGS for satisfaction is already generically covered
in the last paragraph of the function comment:

   For partial-concept-ids, extra args from OUTER_TARGS, TMPL and the current
   scope may be appended to the list of deduced template arguments prior to
   determining constraint satisfaction as appropriate.

So this change is probably unnecessary.

>  
>     Additionally for adc_unify contexts TMPL is the template for which TYPE
>     is a template parameter type.
> @@ -31260,8 +31261,15 @@ do_auto_deduction (tree type, tree init, tree auto_node,
>  	 these missing levels, but this hack otherwise allows us to handle a
>  	 large subset of possible constraints (including all non-dependent
>  	 constraints).  */
> -      if (int missing_levels = (TEMPLATE_TYPE_ORIG_LEVEL (auto_node)
> -				- TMPL_ARGS_DEPTH (full_targs)))
> +      int missing_levels = (TEMPLATE_TYPE_ORIG_LEVEL (auto_node)
> +				- TMPL_ARGS_DEPTH (full_targs));
> +      if (missing_levels < 0)
> +	{
> +	  tree trimmed_full_args = get_innermost_template_args(full_targs,
> +				TEMPLATE_TYPE_ORIG_LEVEL (auto_node));
> +	  full_targs = trimmed_full_args;
> +	}
> +      if (missing_levels > 0)

Looks correct to me!  (Though in order to be consistent the GNU coding
style while respecting the 80-character line limit, this should be
formatted as

      if (missing_levels < 0)
	{
	  tree trimmed_full_args = get_innermost_template_args
	    (full_targs, TEMPLATE_TYPE_ORIG_LEVEL (auto_node));
	  full_targs = trimmed_full_args;
	}

We could also/instead consider defining

  int want = TEMPLATE_TYPE_ORIG_LEVEL (auto_node);
  int have = TMPL_ARGS_DEPTH (full_targs);

and express the logic in terms of these instead of the possibly
negative 'missing_levels'.  The 'want < have' case could have a comment
mentioning that this occurs for a constrained auto within an explicit
specialization.)

Note that GCC doesn't currently support explicit specializations within
template scope, e.g.

    template<class T>
    struct A {
      template<class U> struct B { };
      template<> struct B<int> {
        static inline C auto x = 1;
      };
    };

which might otherwise complicate things here.  But since we don't yet
support it we don't have to worry about this case :)  I think stripping
the outermost extraneous levels as your patch does will currently always
do the right thing for us.  But in the above example which we don't
support, we probably would need to strip _innermost_ extraneous levels,
i.e. the <int>.

>  	{
>  	  tree dummy_levels = make_tree_vec (missing_levels);
>  	  for (int i = 0; i < missing_levels; ++i)
> 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..b4f73f407
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-placeholder15.C
> @@ -0,0 +1,15 @@
> +// 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;
> +
> +int main() {
> +  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;
> +}

Thanks for these extra testcases!

> -- 
> 2.45.0
> 
> 


  reply	other threads:[~2024-05-06 18:46 UTC|newest]

Thread overview: 16+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2024-05-01 22:52 [PATCH] Fix auto deduction for template specialization scopes [114915] Seyed Sajad Kahani
2024-05-03 16:24 ` Patrick Palka
2024-05-04 14:11   ` Seyed Sajad Kahani
2024-05-04 15:15     ` [PATCH v2] Fix auto deduction for template specialization scopes [PR114915] Seyed Sajad Kahani
2024-05-06 18:46       ` Patrick Palka [this message]
2024-05-10 12:43         ` Seyed Sajad Kahani
2024-05-10 12:47         ` [PATCH v3] " Seyed Sajad Kahani
2024-05-15 15:07           ` [PATCH v3] c++: " Patrick Palka
2024-05-15 15:09             ` Patrick Palka
2024-05-15 17:27               ` [PATCH v4] c++: fix constained auto deduction in templ spec " Seyed Sajad Kahani
2024-05-22 20:30                 ` Jason Merrill
2024-06-14 14:15                   ` sska1377
2024-06-14 14:18                   ` [PATCH v5] " Seyed Sajad Kahani
2024-05-22 15:41 ` [PATCH] Fix auto deduction for template specialization scopes [114915] Jason Merrill
2024-05-22 16:48   ` Patrick Palka
2024-05-22 17:32     ` Jason Merrill

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=12929011-5f01-3eee-d06f-83fee1f6b758@idea \
    --to=ppalka@redhat.com \
    --cc=gcc-patches@gcc.gnu.org \
    --cc=jason@redhat.com \
    --cc=sska1377@gmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).