Message ID | 20240614141809.946977-1-sska1377@gmail.com |
---|---|
State | New |
Headers | show |
Series | [v5] c++: fix constained auto deduction in templ spec scopes [PR114915] | expand |
On Fri, 14 Jun 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). > > This patch resolves PR114915 by replacing the logic that fills in the > missing levels in do_auto_deduction in cp/pt.cc. > The new approach now trims targs if the depth of targs is deeper than desired > (this will only happen in specific contexts), and still fills targs with empty > layers if it has fewer depths than expected. LGTM > > PR c++/114915 > > gcc/cp/ChangeLog: > > * pt.cc (do_auto_deduction): Handle excess outer template > arguments during constrained auto satisfaction. > > 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. > --- > gcc/cp/pt.cc | 20 ++++++++--- > .../g++.dg/cpp2a/concepts-placeholder14.C | 19 +++++++++++ > .../g++.dg/cpp2a/concepts-placeholder15.C | 26 +++++++++++++++ > .../g++.dg/cpp2a/concepts-placeholder16.C | 33 +++++++++++++++++++ > 4 files changed, 94 insertions(+), 4 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 32640f8e9..2206d9ffe 100644 > --- a/gcc/cp/pt.cc > +++ b/gcc/cp/pt.cc > @@ -31253,6 +31253,19 @@ do_auto_deduction (tree type, tree init, tree auto_node, > full_targs = add_outermost_template_args (tmpl, full_targs); > full_targs = add_to_template_args (full_targs, targs); > > + int want = TEMPLATE_TYPE_ORIG_LEVEL (auto_node); > + int have = TMPL_ARGS_DEPTH (full_targs); > + > + if (want < have) > + { > + /* If a constrained auto is declared in an explicit specialization. */ > + gcc_assert (context == adc_variable_type || context == adc_return_type > + || context == adc_decomp_type); > + tree trimmed_full_args > + = get_innermost_template_args (full_targs, want); > + full_targs = trimmed_full_args; > + } > + > /* HACK: Compensate for callers not always communicating all levels of > outer template arguments by filling in the outermost missing levels > with dummy levels before checking satisfaction. We'll still crash > @@ -31260,11 +31273,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))) > + if (want > have) > { > - tree dummy_levels = make_tree_vec (missing_levels); > - for (int i = 0; i < missing_levels; ++i) > + tree dummy_levels = make_tree_vec (want - have); > + for (int i = 0; i < want - have; ++i) > TREE_VEC_ELT (dummy_levels, i) = make_tree_vec (0); > full_targs = add_to_template_args (dummy_levels, full_targs); > } > 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; > +} > -- > 2.45.2 > >
diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc index 32640f8e9..2206d9ffe 100644 --- a/gcc/cp/pt.cc +++ b/gcc/cp/pt.cc @@ -31253,6 +31253,19 @@ do_auto_deduction (tree type, tree init, tree auto_node, full_targs = add_outermost_template_args (tmpl, full_targs); full_targs = add_to_template_args (full_targs, targs); + int want = TEMPLATE_TYPE_ORIG_LEVEL (auto_node); + int have = TMPL_ARGS_DEPTH (full_targs); + + if (want < have) + { + /* If a constrained auto is declared in an explicit specialization. */ + gcc_assert (context == adc_variable_type || context == adc_return_type + || context == adc_decomp_type); + tree trimmed_full_args + = get_innermost_template_args (full_targs, want); + full_targs = trimmed_full_args; + } + /* HACK: Compensate for callers not always communicating all levels of outer template arguments by filling in the outermost missing levels with dummy levels before checking satisfaction. We'll still crash @@ -31260,11 +31273,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))) + if (want > have) { - tree dummy_levels = make_tree_vec (missing_levels); - for (int i = 0; i < missing_levels; ++i) + tree dummy_levels = make_tree_vec (want - have); + for (int i = 0; i < want - have; ++i) TREE_VEC_ELT (dummy_levels, i) = make_tree_vec (0); full_targs = add_to_template_args (dummy_levels, full_targs); } 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; +}