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

Reply via email to