Thanks for the patch! On Wed, 1 May 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). > > Then, we do not retrieve the deeper layers of the template arguments; > instead, we fill the missing levels with dummy levels (pt.cc:31260). > > The problem (that is shown in PR114915) is that we do not consider the case > where the deduction happens in a template specialization scope. In this case, > the type is not dependent on the outermost template arguments (which are the > specialization arguments). Yet, we still resolve the outermost template > arguments, and then the number of layers in the template arguments exceeds > the number of levels in the type. This causes the missing levels to be > negative. This leads to the rejection of valid code and ICEs (like segfault) > in the release mode. In the debug mode, it is possible to show as an > assertion failure (when creating a tree_vec with a negative size). > The code that generates the issue is added to the test suite as > `g++.dg/cpp2a/concepts-placeholder14.C`. > > This patch fixes the issue by checking that the template usage, whose > arguments are going to be used for satisfaction, is not a partial or explicit > specialization (and therefore it is an implicit or explicit instantiation). > This check is done in the two only places that affect the `outer_targs` for > the mentioned contexts. > > One might ask why this is not implemented as a simple `missing_level > 0` > check. The reason is that the recovery from the negative `missing_levels` > will not be easy, and it is not clear how to recover from it. Therefore, it > is better to prevent it from happening. > --- > gcc/cp/decl.cc | 1 + > gcc/cp/pt.cc | 16 ++++++++++----- > .../g++.dg/cpp2a/concepts-placeholder14.C | 20 +++++++++++++++++++ > 3 files changed, 32 insertions(+), 5 deletions(-) > create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-placeholder14.C > > diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc > index 65ab64885..7e51f926e 100644 > --- a/gcc/cp/decl.cc > +++ b/gcc/cp/decl.cc > @@ -8527,6 +8527,7 @@ cp_finish_decl (tree decl, tree init, bool > init_const_expr_p, > if (PLACEHOLDER_TYPE_CONSTRAINTS_INFO (auto_node) > && DECL_LANG_SPECIFIC (decl) > && DECL_TEMPLATE_INFO (decl) > + && DECL_USE_TEMPLATE (decl) != 2 We should check !DECL_TEMPLATE_SPECIALIZATION instead of checking DECL_USE_TEMPLATE directly. And since DECL_TEMPLATE_SPECIALIZATION is true for both partial and explicit specializations, I think we should check !in_template_context as well in order to single out explicit specializations? > && !DECL_FUNCTION_SCOPE_P (decl)) > /* The outer template arguments might be needed for satisfaction. > (For function scope variables, do_auto_deduction will obtain the > diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc > index 3b2106dd3..fd646d873 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. > @@ -31235,8 +31236,11 @@ do_auto_deduction (tree type, tree init, tree > auto_node, > 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; > + outer_targs = NULL_TREE; > + if (DECL_TEMPLATE_INFO (fn) && DECL_USE_TEMPLATE (fn) != 2) > + { > + outer_targs = DECL_TI_ARGS (fn); > + } Same here. > if (LAMBDA_FUNCTION_P (fn)) > { > /* As in satisfy_declaration_constraints. */ > @@ -31260,8 +31264,10 @@ 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) IIUC this change is not necessary with the above changes, right? Maybe we could assert than missing_levels is nonnegative. 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 wonder what Jason thinks? > { > 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..4a98ec7b3 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-placeholder14.C > @@ -0,0 +1,20 @@ > +// PR c++/114915 > +// { dg-do compile { target c++20 } } > + > +template<typename T> > +concept TheConcept = __is_same(T, int); > + > +template<typename T> > +void f() { > + TheConcept auto x = 1; > +} > + > +template<> > +void f<int>() { > + TheConcept auto x = 1; > +} > + > +int main() { > + f<int>(); > + return 0; > +} It would be good to also test an explicit variable tmpl spec and an explicit spec of a member template of a class template. > -- > 2.44.0 > >