Thanks for your helpful feedback. It has totally shaped my understanding.

While I was trying to develop other tests, as you suggested:

> It would be good to also test an explicit variable tmpl spec and
> an explicit spec of a member template of a class template.

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.

I have thought of two ideas to fully solve the problem:

1. Trimming the full_targs by - missing_level, as you have mentioned.
2. Traversing the TI_TEMPLATE associated with different levels of
outer_targs, finding the first one that is
DECL_TEMPLATE_SPECIALIZATION, then trimming outer_targs by that point.

For the first idea,

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

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.

Now, for the second idea, we need to do something like

int meaningful_levels = 0;
for (tree sc = fn;
     DECL_TEMPLATE_INFO(sc) && !DECL_TEMPLATE_SPECIALIZATION(sc);
     sc = DECL_TI_TEMPLATE(sc))
  meaningful_levels ++;
outer_targs = get_innermost_template_args(DECL_TI_ARGS(fn), meaningful_levels);

in pt.cc:31238, instead of assigning DECL_TI_ARGS(fn) to outer_targs directly.
Note that we need to do the same thing in decl:8528 and maybe other places.

I would really appreciate your comments on these two ideas.
I will send another patch, applying the first idea in reply to this thread.

Reply via email to