Re: [PATCH v2] Fix auto deduction for template specialization scopes [PR114915]
Thanks for your suggestions. I will apply them in the upcoming patch (v3). On Mon, May 6, 2024 at 7:46 PM Patrick Palka wrote: > 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.) I converted missing_levels into want and have variables. Additionally, I added an assertion to explicitly state that our trimming logic assumes the context to be one of adc_variable_type, adc_return_type, or adc_decomp_type. > > > 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. For the record, I have looked into the history of this hack to find out in which scenarios we may have to deal with missing levels. https://gcc.gnu.org/git/?p=gcc.git;a=commit;h=b49d23f3e238c08bdbc5b892b2ed0a57b5f5caf9 As far as I can understand, it was limited to the adc_unify context at that time, but it might have been changed since then. Since I was not sure about it, I have not added an assertion for this case. Many thanks!
Re: [PATCH v2] Fix auto deduction for template specialization scopes [PR114915]
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 > > void S::f() { > > // some constrained auto > > } > > > > When using S::f, 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 while the type of the > > constrained auto will be as template ... 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.
[PATCH v2] Fix auto deduction for template specialization scopes [PR114915]
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 > void S::f() { > // some constrained auto > } > > When using S::f, 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 while the type of the > constrained auto will be as template ... 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. 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. > > 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. --- 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. 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