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