On Thu, 4 Mar 2021, Patrick Palka wrote: > My recent r11-7454 changed the way do_auto_deduction handles constrained > placeholders during template argument deduction (context == adc_unify) > when processing_template_decl != 0. > > Before the patch, when processing_template_decl != 0 we would just > ignore the constraints on the placeholder in this situation, and proceed > with deduction: > > /* Check any placeholder constraints against the deduced type. */ > if (flag_concepts && !processing_template_decl) > if (tree check = NON_ERROR (PLACEHOLDER_TYPE_CONSTRAINTS (auto_node))) > { > ... > > After the patch, we now punt and return the original placeholder type: > > /* Check any placeholder constraints against the deduced type. */ > if (flag_concepts) > if (NON_ERROR (PLACEHOLDER_TYPE_CONSTRAINTS (auto_node))) > { > if (processing_template_decl) > /* In general we can't check satisfaction until we know all > template arguments. */ > return type; > ... > > While this change fixed instances where we'd prematurely resolve a > constrained placeholder return or variable type with non-dependent > initializer at template parse time (such as PR96444), it broke the > adc_unify callers that rely on this previous behavior. > > So this patch restores the previous behavior during adc_unify deduction > while retaining the new behavior only during adc_variable_type or > adc_return_type deduction. > > We additionally now need to pass outer template arguments to > do_auto_deduction during unify, for sake of constraint checking. > But we don't want do_auto_deduction to substitute these outer arguments > into type if it's already been done, hence the added TEMPLATE_TYPE_LEVEL > check. > > This fixes partial specializations of non-nested templates with > constrained 'auto' template parameters, but nested templates are still > broken, ultimately because most_specialized_partial_spec passes only the > innermost template arguments to get_partial_spec_bindings, and so > outer_targs during do_auto_deduction (called from unify) contains only > the innermost template arguments which makes satisfaction unhappy. > Fixing this might be too invasive at this stage, perhaps.. (Seems we > need to make most_specialized_partial_spec pass all template arguments > to get_partial_spec_bindings.) > > Bootstrapped and regtested on x86_64-pc-linux-gnu, does this look OK for > trunk? Also tested on range-v3 and cmcstl2.
Here's the same patch generated with -w which hides the noisy indentation changes: -- >8 -- PR c++/99365 * pt.c (do_auto_deduction): When processing_template_decl != 0 and context is adc_unify and we have constraints, pretend the constraints are satisfied instead of punting. Add some clarifying sanity checks. Don't substitute outer_targs into type if not needed. gcc/testsuite/ChangeLog: PR c++/99365 * g++.dg/cpp2a/concepts-partial-spec9.C: New test. --- gcc/cp/pt.c | 24 ++++++++++++++----- .../g++.dg/cpp2a/concepts-partial-spec9.C | 24 +++++++++++++++++++ 2 files changed, 42 insertions(+), 6 deletions(-) create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-partial-spec9.C diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c index a4686e0affb..ce537e4529a 100644 --- a/gcc/cp/pt.c +++ b/gcc/cp/pt.c @@ -23693,7 +23693,8 @@ unify (tree tparms, tree targs, tree parm, tree arg, int strict, if (tree a = type_uses_auto (tparm)) { - tparm = do_auto_deduction (tparm, arg, a, complain, adc_unify); + tparm = do_auto_deduction (tparm, arg, a, + complain, adc_unify, targs); if (tparm == error_mark_node) return 1; } @@ -29619,13 +29620,21 @@ do_auto_deduction (tree type, tree init, tree auto_node, } /* Check any placeholder constraints against the deduced type. */ - if (flag_concepts) - if (NON_ERROR (PLACEHOLDER_TYPE_CONSTRAINTS (auto_node))) + if (processing_template_decl && context == adc_unify) + /* Pretend constraints are satisfied. */; + else if (flag_concepts + && NON_ERROR (PLACEHOLDER_TYPE_CONSTRAINTS (auto_node))) { if (processing_template_decl) - /* In general we can't check satisfaction until we know all - template arguments. */ + { + /* Even though the initializer is non-dependent, we need to wait until + instantiation time to resolve this constrained placeholder variable + or return type, since the constraint itself may be dependent. */ + gcc_checking_assert (context == adc_variable_type + || context == adc_return_type); + gcc_checking_assert (!type_dependent_expression_p (init)); return type; + } if ((context == adc_return_type || context == adc_variable_type) && current_function_decl @@ -29664,7 +29673,10 @@ do_auto_deduction (tree type, tree init, tree auto_node, } } - if (context == adc_unify) + if (TEMPLATE_TYPE_LEVEL (auto_node) == 1) + /* The outer template arguments are already substituted into type + (but we still may have used them for constraint checking above). */; + else if (context == adc_unify) targs = add_to_template_args (outer_targs, targs); else if (processing_template_decl) targs = add_to_template_args (current_template_args (), targs); diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-partial-spec9.C b/gcc/testsuite/g++.dg/cpp2a/concepts-partial-spec9.C new file mode 100644 index 00000000000..b79d12b6f17 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-partial-spec9.C @@ -0,0 +1,24 @@ +// PR c++/99365 +// { dg-do compile { target c++20 } } + +template <class> concept C = true; +template <class T, class U> concept D = C<T> && __is_same(T, U); + +template <class, C auto> struct A { static const int i = 0; }; +template <class T, D<T> auto V> struct A<T, V> { static const int i = 1; }; + +static_assert(A<int, 0>::i == 1); +static_assert(A<char, 0>::i == 0); +static_assert(A<int, '0'>::i == 0); +static_assert(A<char, '0'>::i == 1); + +/* Partial specialization of nested class template with constrained 'auto' + template parameter still broken: + +template <class> struct O { + template <C auto> struct B { static const int i = 0; }; + template <D auto V> struct B<V> { static const int i = 1; }; +}; + +static_assert(O<void>::B<int, 0>::i == 0); +static_assert(O<void>::B<int, '0'>::i == 1); */ -- 2.31.0.rc0.75.gec125d1bc1