[PATCH] c++: adc_unify deduction with constrained auto [PR99365]
Jason Merrill
jason@redhat.com
Fri Mar 5 21:14:58 GMT 2021
On 3/5/21 3:42 PM, Patrick Palka wrote:
> On Fri, 5 Mar 2021, Jason Merrill wrote:
>
>> On 3/4/21 9:55 PM, Patrick Palka wrote:
>>> On Thu, 4 Mar 2021, Patrick Palka wrote:
>>>
>>>> On Thu, 4 Mar 2021, Patrick Palka wrote:
>>>>
>>>>> On Thu, 4 Mar 2021, Jason Merrill wrote:
>>>>>
>>>>>> On 3/4/21 11:32 AM, Patrick Palka wrote:
>>>>>>> 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.
>>>>>>
>>>>>> Sure, it makes sense for adc_unify to behave differently, since in
>>>>>> deduction
>>>>>> context constraints are checked after all template args have been
>>>>>> deduced.
>>>>>
>>>>> I see.
>>>>>
>>>>>>
>>>>>>>> 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.)
>>>>>>
>>>>>> How did this work before?
>>>>>
>>>>> Before, it would work, but only if the constraint didn't also depend on
>>>>> any outer template arguments. do_auto_deduction would just "surgically"
>>>>> replace the auto in the concept-id with the type we deduced and leave
>>>>> the other template arguments of the concept-id alone. So if the
>>>>> constraint was non-dependent, satisfaction would work regardless of the
>>>>> template nesting level.
>>>>>
>>>>> Now that we try to do perform satisfaction properly, we're sensitive to
>>>>> the template nesting level even if the constraint is otherwise
>>>>> non-dependent, because the template nesting level determines the level
>>>>> of the auto that appears inside the constraint. So we rely on
>>>>> outer_targs to contain all levels of outer template arguments, because
>>>>> we tack on another level to the end of outer_targs which needs to
>>>>> map to the level of the auto for satisfaction.
>>>>>
>>>>> (As a hacky workaround, when outer_targs is incomplete, can probably
>>>>> just augment it with empty levels until it's
>>>>> TEMPLATE_TYPE_LEVEL(auto_node)-1
>>>>> levels deep, which would fix the nested template case as long as the
>>>>> constraint was depended only on the innermost level of template
>>>>> arguments.)
>>>>>
>>>>>>
>>>>>>>> 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. */
>>>>>>
>>>>>> Can't we check whether the constraint is dependent, and check
>>>>>> satisfaction if
>>>>>> it isn't? That might be necessary to make later expressions
>>>>>> non-dependent
>>>>>> that are supposed to be.
>>>>>
>>>>> We'd have to check if outer_targs (and the deduced type) is dependent
>>>>> too. But it seems tricky because outer_targs, during adc_unify
>>>>> deduction, is usually at least partially empty which is enough to make
>>>>> any_dependent_template_arguments_p return true.
>>>>
>>>> Ah sorry, I just realized you probably meant we should check whether the
>>>> constraint is dependent during adc_variable_type and adc_return_type
>>>> deduction. I think that might be straightforward; I'll try it. I'll
>>>> also experiment with the hacky workaround mentioned earlier.
>>>
>>> Here's a patch that incorporates these two ideas:
>>>
>>> -- >8 --
>>>
>>> Subject: [PATCH] c++: adc_unify deduction with constrained auto [PR99365]
>>>
>>> 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 the 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 the 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 when the caller has already done so, so this patch adds
>>> a TEMPLATE_TYPE_LEVEL check to do_auto_deduction to that effect.
>>>
>>> Overall, these changes fix partial specialization 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
>>> properly is perhaps too risky at this stage, so this patch adds a hack
>>> to do_auto_deduction to compensate for callers that don't supply all
>>> outer template arguments. The goal of this hack is to ensure
>>> placeholder type constraint checking continues to work whenever it
>>> worked before r11-7454, namely whenever the constraint is non-dependent.
>>>
>>> Finally, this patch allows do_auto_deduction to resolve a constrained
>>> placeholder type ahead of time (at template parse time), as long as the
>>> constraint is non-dependent.
>>>
>>> Bootstrapped and regtested on x86_64-pc-linux-gnu, does this look OK for
>>> trunk? Also tested on range-v3 and cmcstl2.
>>>
>>> gcc/cp/ChangeLog:
>>>
>>> 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. Otherwise don't
>>> punt unless any of the explicit arguments in the constraint are
>>> dependent. Add some clarifying sanity checks. Add a hack to
>>> add missing outermost template levels to outer_args before
>>> checking satisfaction. Don't substitute outer_targs into type
>>> if it's already been done.
>>>
>>> gcc/testsuite/ChangeLog:
>>>
>>> PR c++/99365
>>> * g++.dg/cpp2a/concepts-partial-spec9.C: New test.
>>> * g++.dg/cpp2a/concepts-placeholder4: New test.
>>> ---
>>> gcc/cp/pt.c | 44 ++++++++++++++++---
>>> .../g++.dg/cpp2a/concepts-partial-spec9.C | 23 ++++++++++
>>> .../g++.dg/cpp2a/concepts-placeholder4.C | 24 ++++++++++
>>> 3 files changed, 85 insertions(+), 6 deletions(-)
>>> create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-partial-spec9.C
>>> create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-placeholder4.C
>>>
>>> diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
>>> index 83589101c0d..31386a74a59 100644
>>> --- a/gcc/cp/pt.c
>>> +++ b/gcc/cp/pt.c
>>> @@ -23681,7 +23681,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;
>>> }
>>> @@ -29611,13 +29612,24 @@ 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. */;
>>
>> Let's say "constraints will be checked after deduction".
>
> Done.
>
>>
>>> + else if (tree constr = NON_ERROR (PLACEHOLDER_TYPE_CONSTRAINTS
>>> (auto_node)))
>>> {
>>> if (processing_template_decl)
>>> - /* In general we can't check satisfaction until we know all
>>> - template arguments. */
>>> + {
>>> + gcc_checking_assert (context == adc_variable_type
>>> + || context == adc_return_type);
>>> + gcc_checking_assert (!type_dependent_expression_p (init));
>>
>> This assert seems redundant with the condition at the top of the function,
>> though I suppose it's harmless to have it here as well.
>
> Yeah, I figured the assert might be beneficial to the reader, as a
> reminder that we're dealing with a non-dependent initializer, since the
> condition is a bit far away at this point.
>
>>
>>> + /* Check if any of the explicit arguments in the constraint
>>> + are dependent. If so, we need to wait until instantiation time
>>> + to resolve the constriant. */
>>> + tree cid = unpack_concept_check (constr);
>>> + tree cargs = TREE_OPERAND (cid, 1);
>>> + for (int i = 1; i < TREE_VEC_LENGTH (cargs); ++i)
>>> + if (dependent_template_arg_p (TREE_VEC_ELT (cargs, i)))
>>> return type;
>>
>> Let's factor this out; I imagine testing whether a concept-check is dependent
>> (apart from the placeholder type itself) may be more broadly useful.
>
> Done as well (into a predicate placeholder_type_constraint_dependent_p).
> How does the following look? (testing in progress)
OK, thanks.
> -- >8 --
>
> gcc/cp/ChangeLog:
>
> PR c++/99365
> * pt.c (unify) <case TEMPLATE_TYPE_PARM>: Pass targs as
> outer_targs to do_auto_deduction.
> (placeholder_type_constraint_dependent_p): Define.
> (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. Otherwise don't
> punt unless placeholder_type_constraint_dependent_p holds.
> Add some clarifying sanity checks. Add a hack to add missing
> outermost template levels to outer_args before checking
> satisfaction. Don't substitute outer_targs into type if it's
> already been done.
>
> gcc/testsuite/ChangeLog:
>
> PR c++/99365
> * g++.dg/cpp2a/concepts-partial-spec9.C: New test.
> * g++.dg/cpp2a/concepts-placeholder4: New test.
> ---
> gcc/cp/pt.c | 57 +++++++++++++++++--
> .../g++.dg/cpp2a/concepts-partial-spec9.C | 23 ++++++++
> .../g++.dg/cpp2a/concepts-placeholder4.C | 24 ++++++++
> 3 files changed, 98 insertions(+), 6 deletions(-)
> create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-partial-spec9.C
> create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-placeholder4.C
>
> diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
> index 8ca3dc8ec2b..b2ecd0b3213 100644
> --- a/gcc/cp/pt.c
> +++ b/gcc/cp/pt.c
> @@ -23683,7 +23683,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;
> }
> @@ -28205,6 +28206,23 @@ make_constrained_decltype_auto (tree con, tree args)
> return make_constrained_placeholder_type (type, con, args);
> }
>
> +/* Return true if the placeholder type constraint T has any dependent
> + (explicit) template arguments. */
> +
> +static bool
> +placeholder_type_constraint_dependent_p (tree t)
> +{
> + tree id = unpack_concept_check (t);
> + tree args = TREE_OPERAND (id, 1);
> + tree first = TREE_VEC_ELT (args, 0);
> + gcc_checking_assert (TREE_CODE (first) == WILDCARD_DECL
> + || is_auto (first));
> + for (int i = 1; i < TREE_VEC_LENGTH (args); ++i)
> + if (dependent_template_arg_p (TREE_VEC_ELT (args, i)))
> + return true;
> + return false;
> +}
> +
> /* Build and return a concept definition. Like other templates, the
> CONCEPT_DECL node is wrapped by a TEMPLATE_DECL. This returns the
> the TEMPLATE_DECL. */
> @@ -29613,13 +29631,20 @@ 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)
> + /* Constraints will be checked after deduction. */;
> + else if (tree constr = NON_ERROR (PLACEHOLDER_TYPE_CONSTRAINTS (auto_node)))
> {
> if (processing_template_decl)
> - /* In general we can't check satisfaction until we know all
> - template arguments. */
> + {
> + gcc_checking_assert (context == adc_variable_type
> + || context == adc_return_type);
> + gcc_checking_assert (!type_dependent_expression_p (init));
> + /* If the constraint is dependent, we need to wait until
> + instantiation time to resolve the placeholder. */
> + if (placeholder_type_constraint_dependent_p (constr))
> return type;
> + }
>
> if ((context == adc_return_type || context == adc_variable_type)
> && current_function_decl
> @@ -29627,6 +29652,23 @@ do_auto_deduction (tree type, tree init, tree auto_node,
> outer_targs = DECL_TI_ARGS (current_function_decl);
>
> tree full_targs = add_to_template_args (outer_targs, targs);
> +
> + /* 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
> + if the constraint depends on a template argument belonging to one of
> + 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)))
> + {
> + tree dummy_levels = make_tree_vec (missing_levels);
> + for (int i = 0; i < missing_levels; ++i)
> + TREE_VEC_ELT (dummy_levels, i) = make_tree_vec (0);
> + full_targs = add_to_template_args (dummy_levels, full_targs);
> + }
> +
> if (!constraints_satisfied_p (auto_node, full_targs))
> {
> if (complain & tf_warning_or_error)
> @@ -29658,7 +29700,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..3dae24d915a
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-partial-spec9.C
> @@ -0,0 +1,23 @@
> +// 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);
> +
> +template <class> struct O {
> + 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(O<void>::A<int, 0>::i == 1);
> +static_assert(O<void>::A<char, 0>::i == 0);
> +static_assert(O<void>::A<int, '0'>::i == 0);
> +static_assert(O<void>::A<char, '0'>::i == 1);
> diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-placeholder4.C b/gcc/testsuite/g++.dg/cpp2a/concepts-placeholder4.C
> new file mode 100644
> index 00000000000..082c5d36cbe
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-placeholder4.C
> @@ -0,0 +1,24 @@
> +// { dg-do compile { target c++20 } }
> +
> +template <class T, class U> concept same_as = __is_same(T, U);
> +
> +// non-dependent initializer, always-satisfied constraint resolved at parse time
> +template <class T> same_as<bool> auto x = true;
> +
> +template <auto V> same_as<int> auto y = V; // { dg-error "constraint" }
> +
> +template <class>
> +struct A {
> + template <auto V> static inline same_as<int> auto z = V; // { dg-error "constraint" }
> +};
> +
> +int main() {
> + x<int>; // { dg-bogus "" }
> + y<0>; // { dg-bogus "" }
> + y<'0'>; // { dg-message "required from here" }
> + A<void>::z<0>; // { dg-bogus "" }
> + A<void>::z<'0'>; // { dg-message "required from here" }
> +}
> +
> +// non-dependent initializer, never-satisfied constraint diagnosed at parse time
> +template <class T> same_as<int> auto z = true; // { dg-error "constraint" }
>
More information about the Gcc-patches
mailing list