[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