diff mbox series

[2/2] c++: constrained auto NTTP vs associated constraints

Message ID 20241015044637.1487927-1-ppalka@redhat.com
State New
Headers show
Series [1/2] c++: some further concepts cleanups | expand

Commit Message

Patrick Palka Oct. 15, 2024, 4:46 a.m. UTC
According to [temp.param]/11, the constraint on an auto NTTP is an
associated constraint and so should be checked as part of satisfaction
of the overall associated constraints rather than checked individually
during coerion/deduction.

In order to implement this we mainly need to make handling of
constrained auto NTTPs go through finish_constrained_parameter so that
TEMPLATE_PARMS_CONSTRAINTS gets set on them.

gcc/cp/ChangeLog:

	* constraint.cc (finish_shorthand_constraint): Add is_non_type
	parameter for handling constrained (auto) NTTPS.
	* cp-tree.h (do_auto_deduction): Adjust declaration.
	(copy_template_args): Declare.
	(finish_shorthand_constraint): Adjust declaration.
	* parser.cc (cp_parser_constrained_type_template_parm): Inline
	into its only caller and remove.
	(cp_parser_constrained_non_type_template_parm): Likewise.
	(finish_constrained_parameter): Simplify after the above.
	(cp_parser_template_parameter): Dispatch to
	finish_constrained_parameter for a constrained auto NTTP.
	* pt.cc (process_template_parm): Pass is_non_type to
	finish_shorthand_constraint.
	(convert_template_argument): Adjust call to do_auto_deduction.
	(copy_template_args): Remove static.
	(unify): Adjust call to do_auto_deduction.
	(make_constrained_placeholder_type): Return the type not the
	TYPE_NAME for consistency with make_auto etc.
	(do_auto_deduction): Remove now unused tmpl parameter.  Don't
	check constraints on an auto NTTP even in a non-template
	context.

gcc/testsuite/ChangeLog:

	* g++.dg/cpp2a/concepts-placeholder12.C: Adjust expected error
	upon constrained auto NTTP satisfaction failure.
	* g++.dg/cpp2a/concepts-pr97093.C: Likewise.
	* g++.dg/cpp2a/concepts-template-parm2.C: Likewise.
	* g++.dg/cpp2a/concepts-template-parm6.C: Likewise.
---
 gcc/cp/constraint.cc                          | 32 +++++++++--
 gcc/cp/cp-tree.h                              |  6 +--
 gcc/cp/parser.cc                              | 54 +++++++------------
 gcc/cp/pt.cc                                  | 35 +++++-------
 .../g++.dg/cpp2a/concepts-placeholder12.C     |  4 +-
 gcc/testsuite/g++.dg/cpp2a/concepts-pr97093.C |  2 +-
 .../g++.dg/cpp2a/concepts-template-parm2.C    |  2 +-
 .../g++.dg/cpp2a/concepts-template-parm6.C    |  2 +-
 8 files changed, 66 insertions(+), 71 deletions(-)

Comments

Patrick Palka Oct. 15, 2024, 2:14 p.m. UTC | #1
On Tue, 15 Oct 2024, Patrick Palka wrote:

> According to [temp.param]/11, the constraint on an auto NTTP is an
> associated constraint and so should be checked as part of satisfaction
> of the overall associated constraints rather than checked individually
> during coerion/deduction.

By the way, I wonder if such associated constraints should be relevant for
subsumption now?

    template<class T> concept C = true;

    template<class T> concept D = C<T> && true;

    template<C auto V> void f(); // #1
    template<D auto V> void f(); // #2

    int main() {
      f<0>(); // still ambiguous?
    }

With this patch the above call is still ambiguous despite #2 now being
more constrained than #1 because "more constrained" is only considered for
function templates with the same signatures as per
https://eel.is/c++draft/temp.func.order#6.2.2 and we deem their signatures
to be different due to the different type-constraint.

MSVC also rejects, but Clang accepts and selects #2.

> 
> In order to implement this we mainly need to make handling of
> constrained auto NTTPs go through finish_constrained_parameter so that
> TEMPLATE_PARMS_CONSTRAINTS gets set on them.
> 
> gcc/cp/ChangeLog:
> 
> 	* constraint.cc (finish_shorthand_constraint): Add is_non_type
> 	parameter for handling constrained (auto) NTTPS.
> 	* cp-tree.h (do_auto_deduction): Adjust declaration.
> 	(copy_template_args): Declare.
> 	(finish_shorthand_constraint): Adjust declaration.
> 	* parser.cc (cp_parser_constrained_type_template_parm): Inline
> 	into its only caller and remove.
> 	(cp_parser_constrained_non_type_template_parm): Likewise.
> 	(finish_constrained_parameter): Simplify after the above.
> 	(cp_parser_template_parameter): Dispatch to
> 	finish_constrained_parameter for a constrained auto NTTP.
> 	* pt.cc (process_template_parm): Pass is_non_type to
> 	finish_shorthand_constraint.
> 	(convert_template_argument): Adjust call to do_auto_deduction.
> 	(copy_template_args): Remove static.
> 	(unify): Adjust call to do_auto_deduction.
> 	(make_constrained_placeholder_type): Return the type not the
> 	TYPE_NAME for consistency with make_auto etc.
> 	(do_auto_deduction): Remove now unused tmpl parameter.  Don't
> 	check constraints on an auto NTTP even in a non-template
> 	context.
> 
> gcc/testsuite/ChangeLog:
> 
> 	* g++.dg/cpp2a/concepts-placeholder12.C: Adjust expected error
> 	upon constrained auto NTTP satisfaction failure.
> 	* g++.dg/cpp2a/concepts-pr97093.C: Likewise.
> 	* g++.dg/cpp2a/concepts-template-parm2.C: Likewise.
> 	* g++.dg/cpp2a/concepts-template-parm6.C: Likewise.
> ---
>  gcc/cp/constraint.cc                          | 32 +++++++++--
>  gcc/cp/cp-tree.h                              |  6 +--
>  gcc/cp/parser.cc                              | 54 +++++++------------
>  gcc/cp/pt.cc                                  | 35 +++++-------
>  .../g++.dg/cpp2a/concepts-placeholder12.C     |  4 +-
>  gcc/testsuite/g++.dg/cpp2a/concepts-pr97093.C |  2 +-
>  .../g++.dg/cpp2a/concepts-template-parm2.C    |  2 +-
>  .../g++.dg/cpp2a/concepts-template-parm6.C    |  2 +-
>  8 files changed, 66 insertions(+), 71 deletions(-)
> 
> diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
> index 35be9cc2b41..9394bea8835 100644
> --- a/gcc/cp/constraint.cc
> +++ b/gcc/cp/constraint.cc
> @@ -1189,7 +1189,7 @@ build_constrained_parameter (tree cnc, tree proto, tree args)
>     done only after the requires clause has been parsed (or not).  */
>  
>  tree
> -finish_shorthand_constraint (tree decl, tree constr)
> +finish_shorthand_constraint (tree decl, tree constr, bool is_non_type)
>  {
>    /* No requirements means no constraints.  */
>    if (!constr)
> @@ -1198,9 +1198,22 @@ finish_shorthand_constraint (tree decl, tree constr)
>    if (error_operand_p (constr))
>      return NULL_TREE;
>  
> -  tree proto = CONSTRAINED_PARM_PROTOTYPE (constr);
> -  tree con = CONSTRAINED_PARM_CONCEPT (constr);
> -  tree args = CONSTRAINED_PARM_EXTRA_ARGS (constr);
> +  tree proto, con, args;
> +  if (is_non_type)
> +    {
> +      tree id = PLACEHOLDER_TYPE_CONSTRAINTS (constr);
> +      tree tmpl = TREE_OPERAND (id, 0);
> +      tree parms = DECL_INNERMOST_TEMPLATE_PARMS (tmpl);
> +      proto = TREE_VALUE (TREE_VEC_ELT (parms, 0));
> +      con = DECL_TEMPLATE_RESULT (tmpl);
> +      args = TREE_OPERAND (id, 1);
> +    }
> +  else
> +    {
> +      proto = CONSTRAINED_PARM_PROTOTYPE (constr);
> +      con = CONSTRAINED_PARM_CONCEPT (constr);
> +      args = CONSTRAINED_PARM_EXTRA_ARGS (constr);
> +    }
>  
>    bool variadic_concept_p = template_parameter_pack_p (proto);
>    bool declared_pack_p = template_parameter_pack_p (decl);
> @@ -1214,7 +1227,16 @@ finish_shorthand_constraint (tree decl, tree constr)
>  
>    /* Build the concept constraint-expression.  */
>    tree tmpl = DECL_TI_TEMPLATE (con);
> -  tree check = build_concept_check (tmpl, arg, args, tf_warning_or_error);
> +  tree check;
> +  if (is_non_type)
> +    {
> +      arg = finish_decltype_type (arg, /*id_expr=*/true, tf_warning_or_error);
> +      args = copy_template_args (args);
> +      TREE_VEC_ELT (args, 0) = arg;
> +      check = build_concept_check (tmpl, args, tf_warning_or_error);
> +    }
> +  else
> +    check = build_concept_check (tmpl, arg, args, tf_warning_or_error);
>  
>    /* Make the check a fold-expression if needed.
>       Use UNKNOWN_LOCATION so write_template_args can tell the
> diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
> index 1864ab205ae..1e202e17658 100644
> --- a/gcc/cp/cp-tree.h
> +++ b/gcc/cp/cp-tree.h
> @@ -7528,8 +7528,7 @@ extern tree do_auto_deduction                   (tree, tree, tree,
>                                                   auto_deduction_context
>  						 = adc_unspecified,
>  						 tree = NULL_TREE,
> -						 int = LOOKUP_NORMAL,
> -						 tree = NULL_TREE);
> +						 int = LOOKUP_NORMAL);
>  extern tree type_uses_auto			(tree);
>  extern tree convert_generic_types_to_packs	(tree, int, int);
>  extern tree splice_late_return_type		(tree, tree);
> @@ -7587,6 +7586,7 @@ extern bool is_specialization_of_friend		(tree, tree);
>  extern bool comp_template_args			(tree, tree, tree * = NULL,
>  						 tree * = NULL);
>  extern int template_args_equal                  (tree, tree);
> +extern tree copy_template_args			(tree);
>  extern tree maybe_process_partial_specialization (tree);
>  extern tree most_specialized_instantiation	(tree);
>  extern tree most_specialized_partial_spec       (tree, tsubst_flags_t, bool = false);
> @@ -8598,7 +8598,7 @@ extern tree build_concept_check                 (tree, tree, tree, tsubst_flags_
>  extern tree build_constrained_parameter         (tree, tree, tree = NULL_TREE);
>  extern bool equivalent_placeholder_constraints  (tree, tree);
>  extern hashval_t iterative_hash_placeholder_constraint	(tree, hashval_t);
> -extern tree finish_shorthand_constraint         (tree, tree);
> +extern tree finish_shorthand_constraint         (tree, tree, bool);
>  extern tree finish_requires_expr                (location_t, tree, tree);
>  extern tree finish_simple_requirement           (location_t, tree);
>  extern tree finish_type_requirement             (location_t, tree);
> diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
> index 856508e3e4f..9a12e35a759 100644
> --- a/gcc/cp/parser.cc
> +++ b/gcc/cp/parser.cc
> @@ -18599,34 +18599,6 @@ cp_parser_check_constrained_type_parm (cp_parser *parser,
>    return true;
>  }
>  
> -/* Finish parsing/processing a template type parameter and checking
> -   various restrictions. */
> -
> -static inline tree
> -cp_parser_constrained_type_template_parm (cp_parser *parser,
> -                                          tree id,
> -                                          cp_parameter_declarator* parmdecl)
> -{
> -  if (cp_parser_check_constrained_type_parm (parser, parmdecl))
> -    return finish_template_type_parm (class_type_node, id);
> -  else
> -    return error_mark_node;
> -}
> -
> -/* Create a new non-type template parameter from the given PARM
> -   declarator.  */
> -
> -static tree
> -cp_parser_constrained_non_type_template_parm (bool *is_non_type,
> -					      cp_parameter_declarator *parm)
> -{
> -  *is_non_type = true;
> -  cp_declarator *decl = parm->declarator;
> -  cp_decl_specifier_seq *specs = &parm->decl_specifiers;
> -  specs->type = TREE_TYPE (DECL_INITIAL (specs->type));
> -  return grokdeclarator (decl, specs, TPARM, 0, NULL);
> -}
> -
>  /* Build a constrained template parameter based on the PARMDECL
>     declarator. The type of PARMDECL is the constrained type, which
>     refers to the prototype template parameter that ultimately
> @@ -18637,24 +18609,33 @@ finish_constrained_parameter (cp_parser *parser,
>                                cp_parameter_declarator *parmdecl,
>                                bool *is_non_type)
>  {
> -  tree decl = parmdecl->decl_specifiers.type;
> +  tree type = parmdecl->decl_specifiers.type;
>    tree id = get_unqualified_id (parmdecl->declarator);
>    tree def = parmdecl->default_argument;
> -  tree proto = DECL_INITIAL (decl);
>  
>    /* Build the parameter. Return an error if the declarator was invalid. */
>    tree parm;
> -  if (TREE_CODE (proto) == TYPE_DECL)
> -    parm = cp_parser_constrained_type_template_parm (parser, id, parmdecl);
> +  if (is_auto (type))
> +    {
> +      *is_non_type = true;
> +      parm = grokdeclarator (parmdecl->declarator,
> +			     &parmdecl->decl_specifiers,
> +			     TPARM, /*initialized=*/0, /*attrlist=*/NULL);
> +    }
>    else
> -    parm = cp_parser_constrained_non_type_template_parm (is_non_type, parmdecl);
> +    {
> +      if (cp_parser_check_constrained_type_parm (parser, parmdecl))
> +	parm = finish_template_type_parm (class_type_node, id);
> +      else
> +	parm = error_mark_node;
> +    }
>    if (parm == error_mark_node)
>      return error_mark_node;
>  
>    /* Finish the parameter decl and create a node attaching the
>       default argument and constraint.  */
>    parm = build_tree_list (def, parm);
> -  TEMPLATE_PARM_CONSTRAINTS (parm) = decl;
> +  TEMPLATE_PARM_CONSTRAINTS (parm) = type;
>  
>    return parm;
>  }
> @@ -18841,9 +18822,10 @@ cp_parser_template_parameter (cp_parser* parser, bool *is_non_type,
>  	cp_lexer_consume_token (parser->lexer);
>      }
>  
> -  /* The parameter may have been constrained type parameter.  */
> +  /* The parameter may be a constrained type or non-type parameter.  */
>    tree type = parameter_declarator->decl_specifiers.type;
> -  if (declares_constrained_type_template_parameter (type))
> +  if (declares_constrained_type_template_parameter (type)
> +      || (type && is_constrained_auto (type)))
>      return finish_constrained_parameter (parser,
>                                           parameter_declarator,
>                                           is_non_type);
> diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
> index 3d037bd6948..af784a41265 100644
> --- a/gcc/cp/pt.cc
> +++ b/gcc/cp/pt.cc
> @@ -183,7 +183,6 @@ static int template_decl_level (tree);
>  static int check_cv_quals_for_unify (int, tree, tree);
>  static int unify_pack_expansion (tree, tree, tree,
>  				 tree, unification_kind_t, bool, bool);
> -static tree copy_template_args (tree);
>  static tree tsubst_template_parms (tree, tree, tsubst_flags_t);
>  static void tsubst_each_template_parm_constraints (tree, tree, tsubst_flags_t);
>  static tree tsubst_arg_types (tree, tree, tree, tsubst_flags_t, tree);
> @@ -4736,7 +4735,7 @@ process_template_parm (tree list, location_t parm_loc, tree parm,
>    /* Build requirements for the type/template parameter.
>       This must be done after SET_DECL_TEMPLATE_PARM_P or
>       process_template_parm could fail. */
> -  tree reqs = finish_shorthand_constraint (parm, constr);
> +  tree reqs = finish_shorthand_constraint (parm, constr, is_non_type);
>  
>    decl = pushdecl (decl);
>    if (!is_non_type)
> @@ -8831,7 +8830,7 @@ convert_template_argument (tree parm,
>        else if (tree a = type_uses_auto (t))
>  	{
>  	  t = do_auto_deduction (t, arg, a, complain, adc_unify, args,
> -				 LOOKUP_IMPLICIT, /*tmpl=*/in_decl);
> +				 LOOKUP_IMPLICIT);
>  	  if (t == error_mark_node)
>  	    return error_mark_node;
>  	}
> @@ -13967,7 +13966,7 @@ make_argument_pack (tree vec)
>  /* Return an exact copy of template args T that can be modified
>     independently.  */
>  
> -static tree
> +tree
>  copy_template_args (tree t)
>  {
>    if (t == error_mark_node)
> @@ -25044,8 +25043,7 @@ unify (tree tparms, tree targs, tree parm, tree arg, int strict,
>  	    {
>  	      tparm = do_auto_deduction (tparm, arg, a,
>  					 complain, adc_unify, targs,
> -					 LOOKUP_NORMAL,
> -					 TPARMS_PRIMARY_TEMPLATE (tparms));
> +					 LOOKUP_NORMAL);
>  	      if (tparm == error_mark_node)
>  		return 1;
>  	    }
> @@ -29566,8 +29564,7 @@ make_constrained_placeholder_type (tree type, tree con, tree args)
>    /* Our canonical type depends on the constraint.  */
>    TYPE_CANONICAL (type) = canonical_type_parameter (type);
>  
> -  /* Attach the constraint to the type declaration. */
> -  return TYPE_NAME (type);
> +  return type;
>  }
>  
>  /* Make a "constrained auto" type-specifier.  */
> @@ -31187,10 +31184,7 @@ unparenthesized_id_or_class_member_access_p (tree init)
>     adc_requirement contexts to communicate the necessary template arguments
>     to satisfaction.  OUTER_TARGS is ignored in other contexts.
>  
> -   Additionally for adc_unify contexts TMPL is the template for which TYPE
> -   is a template parameter type.
> -
> -   For partial-concept-ids, extra args from OUTER_TARGS, TMPL and the current
> +   For partial-concept-ids, extra args from OUTER_TARGS and the current
>     scope may be appended to the list of deduced template arguments prior to
>     determining constraint satisfaction as appropriate.  */
>  
> @@ -31199,8 +31193,7 @@ do_auto_deduction (tree type, tree init, tree auto_node,
>  		   tsubst_flags_t complain /* = tf_warning_or_error */,
>  		   auto_deduction_context context /* = adc_unspecified */,
>  		   tree outer_targs /* = NULL_TREE */,
> -		   int flags /* = LOOKUP_NORMAL */,
> -		   tree tmpl /* = NULL_TREE */)
> +		   int flags /* = LOOKUP_NORMAL */)
>  {
>    if (type == error_mark_node || init == error_mark_node)
>      return error_mark_node;
> @@ -31348,8 +31341,10 @@ do_auto_deduction (tree type, tree init, tree auto_node,
>      }
>  
>    /* Check any placeholder constraints against the deduced type. */
> -  if (processing_template_decl && context == adc_unify)
> -    /* Constraints will be checked after deduction.  */;
> +  if (context == adc_unify)
> +    /* These constraints correspond to a constrained auto NTTP and are part
> +       of the template's associated constraints which will get checked as a
> +       whole after coercion/deduction.  */;
>    else if (tree constr = NON_ERROR (PLACEHOLDER_TYPE_CONSTRAINTS (auto_node)))
>      {
>        if (processing_template_decl)
> @@ -31383,10 +31378,7 @@ do_auto_deduction (tree type, tree init, tree auto_node,
>  		}
>  	    }
>  
> -      tree full_targs = outer_targs;
> -      if (context == adc_unify && tmpl)
> -	full_targs = add_outermost_template_args (tmpl, full_targs);
> -      full_targs = add_to_template_args (full_targs, targs);
> +      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
> @@ -31411,8 +31403,7 @@ do_auto_deduction (tree type, tree init, tree auto_node,
>  	      auto_diagnostic_group d;
>  	      switch (context)
>  		{
> -		case adc_unspecified:
> -		case adc_unify:
> +		default:
>  		  error_at (loc, "placeholder constraints not satisfied");
>  		  break;
>  		case adc_variable_type:
> diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-placeholder12.C b/gcc/testsuite/g++.dg/cpp2a/concepts-placeholder12.C
> index 22f0ac5e26a..edca8f7199b 100644
> --- a/gcc/testsuite/g++.dg/cpp2a/concepts-placeholder12.C
> +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-placeholder12.C
> @@ -22,8 +22,8 @@ int main() {
>    A<false>::g(X<0>{}); // { dg-error "no match|constraints" }
>  
>    bool v1 = A<true>::value<0>;
> -  bool v2 = A<false>::value<0>;  // { dg-error "constraints" }
> +  bool v2 = A<false>::value<0>;  // { dg-error "invalid variable template" }
>  
>    A<true>::D<0> d1;
> -  A<false>::D<0> d2; // { dg-error "constraints" }
> +  A<false>::D<0> d2; // { dg-error "constraint failure" }
>  }
> diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-pr97093.C b/gcc/testsuite/g++.dg/cpp2a/concepts-pr97093.C
> index d662552614e..355f195ac0a 100644
> --- a/gcc/testsuite/g++.dg/cpp2a/concepts-pr97093.C
> +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-pr97093.C
> @@ -29,4 +29,4 @@ struct pc
>  };
>  
>  constexpr auto cc = pc {};
> -constexpr auto mmcc = m <cc> {}; // { dg-error "not satisfied" }
> +constexpr auto mmcc = m <cc> {}; // { dg-error "constraint failure" }
> diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-template-parm2.C b/gcc/testsuite/g++.dg/cpp2a/concepts-template-parm2.C
> index 3bb2f576a87..a9b15dabc0c 100644
> --- a/gcc/testsuite/g++.dg/cpp2a/concepts-template-parm2.C
> +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-template-parm2.C
> @@ -12,4 +12,4 @@ template<Int T = char> struct S1 { };
>  template<Int auto X = false> struct S2 { };
>  
>  S1<> s1; // { dg-error "constraint failure" }
> -S2<> s2; // { dg-error "placeholder constraints not satisfied" }
> +S2<> s2; // { dg-error "constraint failure" }
> diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-template-parm6.C b/gcc/testsuite/g++.dg/cpp2a/concepts-template-parm6.C
> index c7d9964f738..04c2e1c70ba 100644
> --- a/gcc/testsuite/g++.dg/cpp2a/concepts-template-parm6.C
> +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-template-parm6.C
> @@ -40,5 +40,5 @@ template<Int... Ts> struct S3 { }; // requires (C<Ts> && ...)
>  S3<int, int, char> x0; // { dg-error "template constraint failure" }
>  
>  template<Int auto... Xs> struct S4 { }; // requires (C<X> && ...) with each X deduced
> -S4<0, 1, 2, 'a'> x1; // { dg-error "placeholder constraints not satisfied" }
> +S4<0, 1, 2, 'a'> x1; // { dg-error "template constraint failure" }
>  
> -- 
> 2.47.0.72.gef8ce8f3d4
> 
>
Patrick Palka Oct. 17, 2024, 12:53 p.m. UTC | #2
On Tue, 15 Oct 2024, Patrick Palka wrote:

> On Tue, 15 Oct 2024, Patrick Palka wrote:
> 
> > According to [temp.param]/11, the constraint on an auto NTTP is an
> > associated constraint and so should be checked as part of satisfaction
> > of the overall associated constraints rather than checked individually
> > during coerion/deduction.
> 
> By the way, I wonder if such associated constraints should be relevant for
> subsumption now?
> 
>     template<class T> concept C = true;
> 
>     template<class T> concept D = C<T> && true;
> 
>     template<C auto V> void f(); // #1
>     template<D auto V> void f(); // #2
> 
>     int main() {
>       f<0>(); // still ambiguous?
>     }
> 
> With this patch the above call is still ambiguous despite #2 now being
> more constrained than #1 because "more constrained" is only considered for
> function templates with the same signatures as per
> https://eel.is/c++draft/temp.func.order#6.2.2 and we deem their signatures
> to be different due to the different type-constraint.

I think I convinced myself that this example should be accepted, and the
way to go about that is to replace the constrained auto in the NTTP type
with an ordinary auto once we set its TEMPLATE_PARM_CONSTRAINTS.  That
way both templates have the same signature modulo associated constraints.

> 
> MSVC also rejects, but Clang accepts and selects #2.
> 
> > 
> > In order to implement this we mainly need to make handling of
> > constrained auto NTTPs go through finish_constrained_parameter so that
> > TEMPLATE_PARMS_CONSTRAINTS gets set on them.
> > 
> > gcc/cp/ChangeLog:
> > 
> > 	* constraint.cc (finish_shorthand_constraint): Add is_non_type
> > 	parameter for handling constrained (auto) NTTPS.
> > 	* cp-tree.h (do_auto_deduction): Adjust declaration.
> > 	(copy_template_args): Declare.
> > 	(finish_shorthand_constraint): Adjust declaration.
> > 	* parser.cc (cp_parser_constrained_type_template_parm): Inline
> > 	into its only caller and remove.
> > 	(cp_parser_constrained_non_type_template_parm): Likewise.
> > 	(finish_constrained_parameter): Simplify after the above.
> > 	(cp_parser_template_parameter): Dispatch to
> > 	finish_constrained_parameter for a constrained auto NTTP.
> > 	* pt.cc (process_template_parm): Pass is_non_type to
> > 	finish_shorthand_constraint.
> > 	(convert_template_argument): Adjust call to do_auto_deduction.
> > 	(copy_template_args): Remove static.
> > 	(unify): Adjust call to do_auto_deduction.
> > 	(make_constrained_placeholder_type): Return the type not the
> > 	TYPE_NAME for consistency with make_auto etc.
> > 	(do_auto_deduction): Remove now unused tmpl parameter.  Don't
> > 	check constraints on an auto NTTP even in a non-template
> > 	context.
> > 
> > gcc/testsuite/ChangeLog:
> > 
> > 	* g++.dg/cpp2a/concepts-placeholder12.C: Adjust expected error
> > 	upon constrained auto NTTP satisfaction failure.
> > 	* g++.dg/cpp2a/concepts-pr97093.C: Likewise.
> > 	* g++.dg/cpp2a/concepts-template-parm2.C: Likewise.
> > 	* g++.dg/cpp2a/concepts-template-parm6.C: Likewise.
> > ---
> >  gcc/cp/constraint.cc                          | 32 +++++++++--
> >  gcc/cp/cp-tree.h                              |  6 +--
> >  gcc/cp/parser.cc                              | 54 +++++++------------
> >  gcc/cp/pt.cc                                  | 35 +++++-------
> >  .../g++.dg/cpp2a/concepts-placeholder12.C     |  4 +-
> >  gcc/testsuite/g++.dg/cpp2a/concepts-pr97093.C |  2 +-
> >  .../g++.dg/cpp2a/concepts-template-parm2.C    |  2 +-
> >  .../g++.dg/cpp2a/concepts-template-parm6.C    |  2 +-
> >  8 files changed, 66 insertions(+), 71 deletions(-)
> > 
> > diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
> > index 35be9cc2b41..9394bea8835 100644
> > --- a/gcc/cp/constraint.cc
> > +++ b/gcc/cp/constraint.cc
> > @@ -1189,7 +1189,7 @@ build_constrained_parameter (tree cnc, tree proto, tree args)
> >     done only after the requires clause has been parsed (or not).  */
> >  
> >  tree
> > -finish_shorthand_constraint (tree decl, tree constr)
> > +finish_shorthand_constraint (tree decl, tree constr, bool is_non_type)
> >  {
> >    /* No requirements means no constraints.  */
> >    if (!constr)
> > @@ -1198,9 +1198,22 @@ finish_shorthand_constraint (tree decl, tree constr)
> >    if (error_operand_p (constr))
> >      return NULL_TREE;
> >  
> > -  tree proto = CONSTRAINED_PARM_PROTOTYPE (constr);
> > -  tree con = CONSTRAINED_PARM_CONCEPT (constr);
> > -  tree args = CONSTRAINED_PARM_EXTRA_ARGS (constr);
> > +  tree proto, con, args;
> > +  if (is_non_type)
> > +    {
> > +      tree id = PLACEHOLDER_TYPE_CONSTRAINTS (constr);
> > +      tree tmpl = TREE_OPERAND (id, 0);
> > +      tree parms = DECL_INNERMOST_TEMPLATE_PARMS (tmpl);
> > +      proto = TREE_VALUE (TREE_VEC_ELT (parms, 0));
> > +      con = DECL_TEMPLATE_RESULT (tmpl);
> > +      args = TREE_OPERAND (id, 1);
> > +    }
> > +  else
> > +    {
> > +      proto = CONSTRAINED_PARM_PROTOTYPE (constr);
> > +      con = CONSTRAINED_PARM_CONCEPT (constr);
> > +      args = CONSTRAINED_PARM_EXTRA_ARGS (constr);
> > +    }
> >  
> >    bool variadic_concept_p = template_parameter_pack_p (proto);
> >    bool declared_pack_p = template_parameter_pack_p (decl);
> > @@ -1214,7 +1227,16 @@ finish_shorthand_constraint (tree decl, tree constr)
> >  
> >    /* Build the concept constraint-expression.  */
> >    tree tmpl = DECL_TI_TEMPLATE (con);
> > -  tree check = build_concept_check (tmpl, arg, args, tf_warning_or_error);
> > +  tree check;
> > +  if (is_non_type)
> > +    {
> > +      arg = finish_decltype_type (arg, /*id_expr=*/true, tf_warning_or_error);
> > +      args = copy_template_args (args);
> > +      TREE_VEC_ELT (args, 0) = arg;
> > +      check = build_concept_check (tmpl, args, tf_warning_or_error);
> > +    }
> > +  else
> > +    check = build_concept_check (tmpl, arg, args, tf_warning_or_error);
> >  
> >    /* Make the check a fold-expression if needed.
> >       Use UNKNOWN_LOCATION so write_template_args can tell the
> > diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
> > index 1864ab205ae..1e202e17658 100644
> > --- a/gcc/cp/cp-tree.h
> > +++ b/gcc/cp/cp-tree.h
> > @@ -7528,8 +7528,7 @@ extern tree do_auto_deduction                   (tree, tree, tree,
> >                                                   auto_deduction_context
> >  						 = adc_unspecified,
> >  						 tree = NULL_TREE,
> > -						 int = LOOKUP_NORMAL,
> > -						 tree = NULL_TREE);
> > +						 int = LOOKUP_NORMAL);
> >  extern tree type_uses_auto			(tree);
> >  extern tree convert_generic_types_to_packs	(tree, int, int);
> >  extern tree splice_late_return_type		(tree, tree);
> > @@ -7587,6 +7586,7 @@ extern bool is_specialization_of_friend		(tree, tree);
> >  extern bool comp_template_args			(tree, tree, tree * = NULL,
> >  						 tree * = NULL);
> >  extern int template_args_equal                  (tree, tree);
> > +extern tree copy_template_args			(tree);
> >  extern tree maybe_process_partial_specialization (tree);
> >  extern tree most_specialized_instantiation	(tree);
> >  extern tree most_specialized_partial_spec       (tree, tsubst_flags_t, bool = false);
> > @@ -8598,7 +8598,7 @@ extern tree build_concept_check                 (tree, tree, tree, tsubst_flags_
> >  extern tree build_constrained_parameter         (tree, tree, tree = NULL_TREE);
> >  extern bool equivalent_placeholder_constraints  (tree, tree);
> >  extern hashval_t iterative_hash_placeholder_constraint	(tree, hashval_t);
> > -extern tree finish_shorthand_constraint         (tree, tree);
> > +extern tree finish_shorthand_constraint         (tree, tree, bool);
> >  extern tree finish_requires_expr                (location_t, tree, tree);
> >  extern tree finish_simple_requirement           (location_t, tree);
> >  extern tree finish_type_requirement             (location_t, tree);
> > diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
> > index 856508e3e4f..9a12e35a759 100644
> > --- a/gcc/cp/parser.cc
> > +++ b/gcc/cp/parser.cc
> > @@ -18599,34 +18599,6 @@ cp_parser_check_constrained_type_parm (cp_parser *parser,
> >    return true;
> >  }
> >  
> > -/* Finish parsing/processing a template type parameter and checking
> > -   various restrictions. */
> > -
> > -static inline tree
> > -cp_parser_constrained_type_template_parm (cp_parser *parser,
> > -                                          tree id,
> > -                                          cp_parameter_declarator* parmdecl)
> > -{
> > -  if (cp_parser_check_constrained_type_parm (parser, parmdecl))
> > -    return finish_template_type_parm (class_type_node, id);
> > -  else
> > -    return error_mark_node;
> > -}
> > -
> > -/* Create a new non-type template parameter from the given PARM
> > -   declarator.  */
> > -
> > -static tree
> > -cp_parser_constrained_non_type_template_parm (bool *is_non_type,
> > -					      cp_parameter_declarator *parm)
> > -{
> > -  *is_non_type = true;
> > -  cp_declarator *decl = parm->declarator;
> > -  cp_decl_specifier_seq *specs = &parm->decl_specifiers;
> > -  specs->type = TREE_TYPE (DECL_INITIAL (specs->type));
> > -  return grokdeclarator (decl, specs, TPARM, 0, NULL);
> > -}
> > -
> >  /* Build a constrained template parameter based on the PARMDECL
> >     declarator. The type of PARMDECL is the constrained type, which
> >     refers to the prototype template parameter that ultimately
> > @@ -18637,24 +18609,33 @@ finish_constrained_parameter (cp_parser *parser,
> >                                cp_parameter_declarator *parmdecl,
> >                                bool *is_non_type)
> >  {
> > -  tree decl = parmdecl->decl_specifiers.type;
> > +  tree type = parmdecl->decl_specifiers.type;
> >    tree id = get_unqualified_id (parmdecl->declarator);
> >    tree def = parmdecl->default_argument;
> > -  tree proto = DECL_INITIAL (decl);
> >  
> >    /* Build the parameter. Return an error if the declarator was invalid. */
> >    tree parm;
> > -  if (TREE_CODE (proto) == TYPE_DECL)
> > -    parm = cp_parser_constrained_type_template_parm (parser, id, parmdecl);
> > +  if (is_auto (type))
> > +    {
> > +      *is_non_type = true;
> > +      parm = grokdeclarator (parmdecl->declarator,
> > +			     &parmdecl->decl_specifiers,
> > +			     TPARM, /*initialized=*/0, /*attrlist=*/NULL);
> > +    }
> >    else
> > -    parm = cp_parser_constrained_non_type_template_parm (is_non_type, parmdecl);
> > +    {
> > +      if (cp_parser_check_constrained_type_parm (parser, parmdecl))
> > +	parm = finish_template_type_parm (class_type_node, id);
> > +      else
> > +	parm = error_mark_node;
> > +    }
> >    if (parm == error_mark_node)
> >      return error_mark_node;
> >  
> >    /* Finish the parameter decl and create a node attaching the
> >       default argument and constraint.  */
> >    parm = build_tree_list (def, parm);
> > -  TEMPLATE_PARM_CONSTRAINTS (parm) = decl;
> > +  TEMPLATE_PARM_CONSTRAINTS (parm) = type;
> >  
> >    return parm;
> >  }
> > @@ -18841,9 +18822,10 @@ cp_parser_template_parameter (cp_parser* parser, bool *is_non_type,
> >  	cp_lexer_consume_token (parser->lexer);
> >      }
> >  
> > -  /* The parameter may have been constrained type parameter.  */
> > +  /* The parameter may be a constrained type or non-type parameter.  */
> >    tree type = parameter_declarator->decl_specifiers.type;
> > -  if (declares_constrained_type_template_parameter (type))
> > +  if (declares_constrained_type_template_parameter (type)
> > +      || (type && is_constrained_auto (type)))
> >      return finish_constrained_parameter (parser,
> >                                           parameter_declarator,
> >                                           is_non_type);
> > diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
> > index 3d037bd6948..af784a41265 100644
> > --- a/gcc/cp/pt.cc
> > +++ b/gcc/cp/pt.cc
> > @@ -183,7 +183,6 @@ static int template_decl_level (tree);
> >  static int check_cv_quals_for_unify (int, tree, tree);
> >  static int unify_pack_expansion (tree, tree, tree,
> >  				 tree, unification_kind_t, bool, bool);
> > -static tree copy_template_args (tree);
> >  static tree tsubst_template_parms (tree, tree, tsubst_flags_t);
> >  static void tsubst_each_template_parm_constraints (tree, tree, tsubst_flags_t);
> >  static tree tsubst_arg_types (tree, tree, tree, tsubst_flags_t, tree);
> > @@ -4736,7 +4735,7 @@ process_template_parm (tree list, location_t parm_loc, tree parm,
> >    /* Build requirements for the type/template parameter.
> >       This must be done after SET_DECL_TEMPLATE_PARM_P or
> >       process_template_parm could fail. */
> > -  tree reqs = finish_shorthand_constraint (parm, constr);
> > +  tree reqs = finish_shorthand_constraint (parm, constr, is_non_type);
> >  
> >    decl = pushdecl (decl);
> >    if (!is_non_type)
> > @@ -8831,7 +8830,7 @@ convert_template_argument (tree parm,
> >        else if (tree a = type_uses_auto (t))
> >  	{
> >  	  t = do_auto_deduction (t, arg, a, complain, adc_unify, args,
> > -				 LOOKUP_IMPLICIT, /*tmpl=*/in_decl);
> > +				 LOOKUP_IMPLICIT);
> >  	  if (t == error_mark_node)
> >  	    return error_mark_node;
> >  	}
> > @@ -13967,7 +13966,7 @@ make_argument_pack (tree vec)
> >  /* Return an exact copy of template args T that can be modified
> >     independently.  */
> >  
> > -static tree
> > +tree
> >  copy_template_args (tree t)
> >  {
> >    if (t == error_mark_node)
> > @@ -25044,8 +25043,7 @@ unify (tree tparms, tree targs, tree parm, tree arg, int strict,
> >  	    {
> >  	      tparm = do_auto_deduction (tparm, arg, a,
> >  					 complain, adc_unify, targs,
> > -					 LOOKUP_NORMAL,
> > -					 TPARMS_PRIMARY_TEMPLATE (tparms));
> > +					 LOOKUP_NORMAL);
> >  	      if (tparm == error_mark_node)
> >  		return 1;
> >  	    }
> > @@ -29566,8 +29564,7 @@ make_constrained_placeholder_type (tree type, tree con, tree args)
> >    /* Our canonical type depends on the constraint.  */
> >    TYPE_CANONICAL (type) = canonical_type_parameter (type);
> >  
> > -  /* Attach the constraint to the type declaration. */
> > -  return TYPE_NAME (type);
> > +  return type;
> >  }
> >  
> >  /* Make a "constrained auto" type-specifier.  */
> > @@ -31187,10 +31184,7 @@ unparenthesized_id_or_class_member_access_p (tree init)
> >     adc_requirement contexts to communicate the necessary template arguments
> >     to satisfaction.  OUTER_TARGS is ignored in other contexts.
> >  
> > -   Additionally for adc_unify contexts TMPL is the template for which TYPE
> > -   is a template parameter type.
> > -
> > -   For partial-concept-ids, extra args from OUTER_TARGS, TMPL and the current
> > +   For partial-concept-ids, extra args from OUTER_TARGS and the current
> >     scope may be appended to the list of deduced template arguments prior to
> >     determining constraint satisfaction as appropriate.  */
> >  
> > @@ -31199,8 +31193,7 @@ do_auto_deduction (tree type, tree init, tree auto_node,
> >  		   tsubst_flags_t complain /* = tf_warning_or_error */,
> >  		   auto_deduction_context context /* = adc_unspecified */,
> >  		   tree outer_targs /* = NULL_TREE */,
> > -		   int flags /* = LOOKUP_NORMAL */,
> > -		   tree tmpl /* = NULL_TREE */)
> > +		   int flags /* = LOOKUP_NORMAL */)
> >  {
> >    if (type == error_mark_node || init == error_mark_node)
> >      return error_mark_node;
> > @@ -31348,8 +31341,10 @@ do_auto_deduction (tree type, tree init, tree auto_node,
> >      }
> >  
> >    /* Check any placeholder constraints against the deduced type. */
> > -  if (processing_template_decl && context == adc_unify)
> > -    /* Constraints will be checked after deduction.  */;
> > +  if (context == adc_unify)
> > +    /* These constraints correspond to a constrained auto NTTP and are part
> > +       of the template's associated constraints which will get checked as a
> > +       whole after coercion/deduction.  */;
> >    else if (tree constr = NON_ERROR (PLACEHOLDER_TYPE_CONSTRAINTS (auto_node)))
> >      {
> >        if (processing_template_decl)
> > @@ -31383,10 +31378,7 @@ do_auto_deduction (tree type, tree init, tree auto_node,
> >  		}
> >  	    }
> >  
> > -      tree full_targs = outer_targs;
> > -      if (context == adc_unify && tmpl)
> > -	full_targs = add_outermost_template_args (tmpl, full_targs);
> > -      full_targs = add_to_template_args (full_targs, targs);
> > +      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
> > @@ -31411,8 +31403,7 @@ do_auto_deduction (tree type, tree init, tree auto_node,
> >  	      auto_diagnostic_group d;
> >  	      switch (context)
> >  		{
> > -		case adc_unspecified:
> > -		case adc_unify:
> > +		default:
> >  		  error_at (loc, "placeholder constraints not satisfied");
> >  		  break;
> >  		case adc_variable_type:
> > diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-placeholder12.C b/gcc/testsuite/g++.dg/cpp2a/concepts-placeholder12.C
> > index 22f0ac5e26a..edca8f7199b 100644
> > --- a/gcc/testsuite/g++.dg/cpp2a/concepts-placeholder12.C
> > +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-placeholder12.C
> > @@ -22,8 +22,8 @@ int main() {
> >    A<false>::g(X<0>{}); // { dg-error "no match|constraints" }
> >  
> >    bool v1 = A<true>::value<0>;
> > -  bool v2 = A<false>::value<0>;  // { dg-error "constraints" }
> > +  bool v2 = A<false>::value<0>;  // { dg-error "invalid variable template" }
> >  
> >    A<true>::D<0> d1;
> > -  A<false>::D<0> d2; // { dg-error "constraints" }
> > +  A<false>::D<0> d2; // { dg-error "constraint failure" }
> >  }
> > diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-pr97093.C b/gcc/testsuite/g++.dg/cpp2a/concepts-pr97093.C
> > index d662552614e..355f195ac0a 100644
> > --- a/gcc/testsuite/g++.dg/cpp2a/concepts-pr97093.C
> > +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-pr97093.C
> > @@ -29,4 +29,4 @@ struct pc
> >  };
> >  
> >  constexpr auto cc = pc {};
> > -constexpr auto mmcc = m <cc> {}; // { dg-error "not satisfied" }
> > +constexpr auto mmcc = m <cc> {}; // { dg-error "constraint failure" }
> > diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-template-parm2.C b/gcc/testsuite/g++.dg/cpp2a/concepts-template-parm2.C
> > index 3bb2f576a87..a9b15dabc0c 100644
> > --- a/gcc/testsuite/g++.dg/cpp2a/concepts-template-parm2.C
> > +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-template-parm2.C
> > @@ -12,4 +12,4 @@ template<Int T = char> struct S1 { };
> >  template<Int auto X = false> struct S2 { };
> >  
> >  S1<> s1; // { dg-error "constraint failure" }
> > -S2<> s2; // { dg-error "placeholder constraints not satisfied" }
> > +S2<> s2; // { dg-error "constraint failure" }
> > diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-template-parm6.C b/gcc/testsuite/g++.dg/cpp2a/concepts-template-parm6.C
> > index c7d9964f738..04c2e1c70ba 100644
> > --- a/gcc/testsuite/g++.dg/cpp2a/concepts-template-parm6.C
> > +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-template-parm6.C
> > @@ -40,5 +40,5 @@ template<Int... Ts> struct S3 { }; // requires (C<Ts> && ...)
> >  S3<int, int, char> x0; // { dg-error "template constraint failure" }
> >  
> >  template<Int auto... Xs> struct S4 { }; // requires (C<X> && ...) with each X deduced
> > -S4<0, 1, 2, 'a'> x1; // { dg-error "placeholder constraints not satisfied" }
> > +S4<0, 1, 2, 'a'> x1; // { dg-error "template constraint failure" }
> >  
> > -- 
> > 2.47.0.72.gef8ce8f3d4
> > 
> > 
>
Patrick Palka Oct. 17, 2024, 5:10 p.m. UTC | #3
On Thu, 17 Oct 2024, Patrick Palka wrote:

> On Tue, 15 Oct 2024, Patrick Palka wrote:
> 
> > On Tue, 15 Oct 2024, Patrick Palka wrote:
> > 
> > > According to [temp.param]/11, the constraint on an auto NTTP is an
> > > associated constraint and so should be checked as part of satisfaction
> > > of the overall associated constraints rather than checked individually
> > > during coerion/deduction.
> > 
> > By the way, I wonder if such associated constraints should be relevant for
> > subsumption now?
> > 
> >     template<class T> concept C = true;
> > 
> >     template<class T> concept D = C<T> && true;
> > 
> >     template<C auto V> void f(); // #1
> >     template<D auto V> void f(); // #2
> > 
> >     int main() {
> >       f<0>(); // still ambiguous?
> >     }
> > 
> > With this patch the above call is still ambiguous despite #2 now being
> > more constrained than #1 because "more constrained" is only considered for
> > function templates with the same signatures as per
> > https://eel.is/c++draft/temp.func.order#6.2.2 and we deem their signatures
> > to be different due to the different type-constraint.
> 
> I think I convinced myself that this example should be accepted, and the
> way to go about that is to replace the constrained auto in the NTTP type
> with an ordinary auto once we set its TEMPLATE_PARM_CONSTRAINTS.  That
> way both templates have the same signature modulo associated constraints.

Here is v2 which implements this in finish_constrained_parameter.  Now
we can assert that do_auto_deduction doesn't see a constrained auto
during adc_unify deduction!

-- >8 --

Subject: [PATCH v2 2/2] c++: constrained auto NTTP vs associated constraints

According to [temp.param]/11, the constraint on an auto NTTP is an
associated constraint and so should be checked as part of satisfaction
of the overall associated constraints rather than checked individually
during coerion/deduction.

In order to implement this we mainly need to make handling of
constrained auto NTTPs go through finish_constrained_parameter so that
TEMPLATE_PARM_CONSTRAINTS gets set on them, and teach
finish_shorthand_constraint how to build an associated constraint
corresponding to the written type-constraint.

gcc/cp/ChangeLog:

	* constraint.cc (finish_shorthand_constraint): Add is_non_type
	parameter for handling constrained (auto) NTTPS.
	* cp-tree.h (do_auto_deduction): Adjust declaration.
	(copy_template_args): Declare.
	(finish_shorthand_constraint): Adjust declaration.
	* mangle.cc (write_template_param_decl): Obtain constraints of
	an (auto) NTTP through TEMPLATE_PARM_CONSTRAINTS instead of
	PLACEHOLDER_TYPE_CONSTRAINTS.
	* parser.cc (cp_parser_constrained_type_template_parm): Inline
	into its only caller and remove.
	(cp_parser_constrained_non_type_template_parm): Likewise.
	(finish_constrained_parameter): Simplify after the above.  Replace
	the type of a constrained (auto) NTTP with an ordinary auto.
	(cp_parser_template_parameter): Dispatch to
	finish_constrained_parameter for a constrained auto NTTP.
	* pt.cc (process_template_parm): Pass is_non_type to
	finish_shorthand_constraint.
	(convert_template_argument): Adjust call to do_auto_deduction.
	(copy_template_args): Remove static.
	(unify): Adjust call to do_auto_deduction.
	(make_constrained_placeholder_type): Return the type not the
	TYPE_NAME for consistency with make_auto etc.
	(do_auto_deduction): Remove now unused tmpl parameter.  Assert
	we no longer see constrained autos during coercion/deduction.

gcc/testsuite/ChangeLog:

	* g++.dg/cpp2a/concepts-placeholder12.C: Adjust expected error
	upon constrained auto NTTP satisfaction failure.
	* g++.dg/cpp2a/concepts-pr97093.C: Likewise.
	* g++.dg/cpp2a/concepts-template-parm2.C: Likewise.
	* g++.dg/cpp2a/concepts-template-parm6.C: Likewise.
	* g++.dg/cpp2a/concepts-template-parm12.C: New test.
---
 gcc/cp/constraint.cc                          | 32 ++++++++--
 gcc/cp/cp-tree.h                              |  6 +-
 gcc/cp/mangle.cc                              |  2 +-
 gcc/cp/parser.cc                              | 64 ++++++++-----------
 gcc/cp/pt.cc                                  | 38 +++++------
 .../g++.dg/cpp2a/concepts-placeholder12.C     |  4 +-
 gcc/testsuite/g++.dg/cpp2a/concepts-pr97093.C |  2 +-
 .../g++.dg/cpp2a/concepts-template-parm12.C   | 22 +++++++
 .../g++.dg/cpp2a/concepts-template-parm2.C    |  2 +-
 .../g++.dg/cpp2a/concepts-template-parm6.C    |  2 +-
 10 files changed, 101 insertions(+), 73 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-template-parm12.C

diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
index 35be9cc2b41..9394bea8835 100644
--- a/gcc/cp/constraint.cc
+++ b/gcc/cp/constraint.cc
@@ -1189,7 +1189,7 @@ build_constrained_parameter (tree cnc, tree proto, tree args)
    done only after the requires clause has been parsed (or not).  */
 
 tree
-finish_shorthand_constraint (tree decl, tree constr)
+finish_shorthand_constraint (tree decl, tree constr, bool is_non_type)
 {
   /* No requirements means no constraints.  */
   if (!constr)
@@ -1198,9 +1198,22 @@ finish_shorthand_constraint (tree decl, tree constr)
   if (error_operand_p (constr))
     return NULL_TREE;
 
-  tree proto = CONSTRAINED_PARM_PROTOTYPE (constr);
-  tree con = CONSTRAINED_PARM_CONCEPT (constr);
-  tree args = CONSTRAINED_PARM_EXTRA_ARGS (constr);
+  tree proto, con, args;
+  if (is_non_type)
+    {
+      tree id = PLACEHOLDER_TYPE_CONSTRAINTS (constr);
+      tree tmpl = TREE_OPERAND (id, 0);
+      tree parms = DECL_INNERMOST_TEMPLATE_PARMS (tmpl);
+      proto = TREE_VALUE (TREE_VEC_ELT (parms, 0));
+      con = DECL_TEMPLATE_RESULT (tmpl);
+      args = TREE_OPERAND (id, 1);
+    }
+  else
+    {
+      proto = CONSTRAINED_PARM_PROTOTYPE (constr);
+      con = CONSTRAINED_PARM_CONCEPT (constr);
+      args = CONSTRAINED_PARM_EXTRA_ARGS (constr);
+    }
 
   bool variadic_concept_p = template_parameter_pack_p (proto);
   bool declared_pack_p = template_parameter_pack_p (decl);
@@ -1214,7 +1227,16 @@ finish_shorthand_constraint (tree decl, tree constr)
 
   /* Build the concept constraint-expression.  */
   tree tmpl = DECL_TI_TEMPLATE (con);
-  tree check = build_concept_check (tmpl, arg, args, tf_warning_or_error);
+  tree check;
+  if (is_non_type)
+    {
+      arg = finish_decltype_type (arg, /*id_expr=*/true, tf_warning_or_error);
+      args = copy_template_args (args);
+      TREE_VEC_ELT (args, 0) = arg;
+      check = build_concept_check (tmpl, args, tf_warning_or_error);
+    }
+  else
+    check = build_concept_check (tmpl, arg, args, tf_warning_or_error);
 
   /* Make the check a fold-expression if needed.
      Use UNKNOWN_LOCATION so write_template_args can tell the
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 1864ab205ae..1e202e17658 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -7528,8 +7528,7 @@ extern tree do_auto_deduction                   (tree, tree, tree,
                                                  auto_deduction_context
 						 = adc_unspecified,
 						 tree = NULL_TREE,
-						 int = LOOKUP_NORMAL,
-						 tree = NULL_TREE);
+						 int = LOOKUP_NORMAL);
 extern tree type_uses_auto			(tree);
 extern tree convert_generic_types_to_packs	(tree, int, int);
 extern tree splice_late_return_type		(tree, tree);
@@ -7587,6 +7586,7 @@ extern bool is_specialization_of_friend		(tree, tree);
 extern bool comp_template_args			(tree, tree, tree * = NULL,
 						 tree * = NULL);
 extern int template_args_equal                  (tree, tree);
+extern tree copy_template_args			(tree);
 extern tree maybe_process_partial_specialization (tree);
 extern tree most_specialized_instantiation	(tree);
 extern tree most_specialized_partial_spec       (tree, tsubst_flags_t, bool = false);
@@ -8598,7 +8598,7 @@ extern tree build_concept_check                 (tree, tree, tree, tsubst_flags_
 extern tree build_constrained_parameter         (tree, tree, tree = NULL_TREE);
 extern bool equivalent_placeholder_constraints  (tree, tree);
 extern hashval_t iterative_hash_placeholder_constraint	(tree, hashval_t);
-extern tree finish_shorthand_constraint         (tree, tree);
+extern tree finish_shorthand_constraint         (tree, tree, bool);
 extern tree finish_requires_expr                (location_t, tree, tree);
 extern tree finish_simple_requirement           (location_t, tree);
 extern tree finish_type_requirement             (location_t, tree);
diff --git a/gcc/cp/mangle.cc b/gcc/cp/mangle.cc
index 17988d69e1e..d016622526f 100644
--- a/gcc/cp/mangle.cc
+++ b/gcc/cp/mangle.cc
@@ -1896,7 +1896,7 @@ write_template_param_decl (tree parm)
 
 	tree type = TREE_TYPE (decl);
 	if (tree c = (is_auto (type)
-		      ? PLACEHOLDER_TYPE_CONSTRAINTS (type)
+		      ? TEMPLATE_PARM_CONSTRAINTS (parm)
 		      : NULL_TREE))
 	  {
 	    if (AUTO_IS_DECLTYPE (type))
diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
index 856508e3e4f..0bad62978dc 100644
--- a/gcc/cp/parser.cc
+++ b/gcc/cp/parser.cc
@@ -18599,34 +18599,6 @@ cp_parser_check_constrained_type_parm (cp_parser *parser,
   return true;
 }
 
-/* Finish parsing/processing a template type parameter and checking
-   various restrictions. */
-
-static inline tree
-cp_parser_constrained_type_template_parm (cp_parser *parser,
-                                          tree id,
-                                          cp_parameter_declarator* parmdecl)
-{
-  if (cp_parser_check_constrained_type_parm (parser, parmdecl))
-    return finish_template_type_parm (class_type_node, id);
-  else
-    return error_mark_node;
-}
-
-/* Create a new non-type template parameter from the given PARM
-   declarator.  */
-
-static tree
-cp_parser_constrained_non_type_template_parm (bool *is_non_type,
-					      cp_parameter_declarator *parm)
-{
-  *is_non_type = true;
-  cp_declarator *decl = parm->declarator;
-  cp_decl_specifier_seq *specs = &parm->decl_specifiers;
-  specs->type = TREE_TYPE (DECL_INITIAL (specs->type));
-  return grokdeclarator (decl, specs, TPARM, 0, NULL);
-}
-
 /* Build a constrained template parameter based on the PARMDECL
    declarator. The type of PARMDECL is the constrained type, which
    refers to the prototype template parameter that ultimately
@@ -18637,24 +18609,43 @@ finish_constrained_parameter (cp_parser *parser,
                               cp_parameter_declarator *parmdecl,
                               bool *is_non_type)
 {
-  tree decl = parmdecl->decl_specifiers.type;
+  tree constr = parmdecl->decl_specifiers.type;
   tree id = get_unqualified_id (parmdecl->declarator);
   tree def = parmdecl->default_argument;
-  tree proto = DECL_INITIAL (decl);
 
   /* Build the parameter. Return an error if the declarator was invalid. */
   tree parm;
-  if (TREE_CODE (proto) == TYPE_DECL)
-    parm = cp_parser_constrained_type_template_parm (parser, id, parmdecl);
+  if (is_constrained_auto (constr))
+    {
+      /* Constrained non-type parameter.  */
+      *is_non_type = true;
+      parm = grokdeclarator (parmdecl->declarator,
+			     &parmdecl->decl_specifiers,
+			     TPARM, /*initialized=*/0, /*attrlist=*/NULL);
+      /* Replace the type of this constrained (auto) NTTP with an ordinary
+	 auto; its constraint gets saved in TEMPLATE_PARM_CONSTRAINTS to be
+	 associated with the template.  */
+      if (parm != error_mark_node)
+	TREE_TYPE (parm) = (AUTO_IS_DECLTYPE (constr)
+			    ? make_decltype_auto ()
+			    : make_auto ());
+    }
   else
-    parm = cp_parser_constrained_non_type_template_parm (is_non_type, parmdecl);
+    {
+      /* Constrained type parameter.  */
+      gcc_checking_assert (CONSTRAINED_PARM_CONCEPT (constr));
+      if (cp_parser_check_constrained_type_parm (parser, parmdecl))
+	parm = finish_template_type_parm (class_type_node, id);
+      else
+	parm = error_mark_node;
+    }
   if (parm == error_mark_node)
     return error_mark_node;
 
   /* Finish the parameter decl and create a node attaching the
      default argument and constraint.  */
   parm = build_tree_list (def, parm);
-  TEMPLATE_PARM_CONSTRAINTS (parm) = decl;
+  TEMPLATE_PARM_CONSTRAINTS (parm) = constr;
 
   return parm;
 }
@@ -18841,9 +18832,10 @@ cp_parser_template_parameter (cp_parser* parser, bool *is_non_type,
 	cp_lexer_consume_token (parser->lexer);
     }
 
-  /* The parameter may have been constrained type parameter.  */
+  /* The parameter may be constrained (type or non-type).  */
   tree type = parameter_declarator->decl_specifiers.type;
-  if (declares_constrained_type_template_parameter (type))
+  if (declares_constrained_type_template_parameter (type)
+      || (type && is_constrained_auto (type)))
     return finish_constrained_parameter (parser,
                                          parameter_declarator,
                                          is_non_type);
diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
index f716a98f840..8b183a139d7 100644
--- a/gcc/cp/pt.cc
+++ b/gcc/cp/pt.cc
@@ -183,7 +183,6 @@ static int template_decl_level (tree);
 static int check_cv_quals_for_unify (int, tree, tree);
 static int unify_pack_expansion (tree, tree, tree,
 				 tree, unification_kind_t, bool, bool);
-static tree copy_template_args (tree);
 static tree tsubst_template_parms (tree, tree, tsubst_flags_t);
 static void tsubst_each_template_parm_constraints (tree, tree, tsubst_flags_t);
 static tree tsubst_arg_types (tree, tree, tree, tsubst_flags_t, tree);
@@ -4736,7 +4735,7 @@ process_template_parm (tree list, location_t parm_loc, tree parm,
   /* Build requirements for the type/template parameter.
      This must be done after SET_DECL_TEMPLATE_PARM_P or
      process_template_parm could fail. */
-  tree reqs = finish_shorthand_constraint (parm, constr);
+  tree reqs = finish_shorthand_constraint (parm, constr, is_non_type);
 
   decl = pushdecl (decl);
   if (!is_non_type)
@@ -8831,7 +8830,7 @@ convert_template_argument (tree parm,
       else if (tree a = type_uses_auto (t))
 	{
 	  t = do_auto_deduction (t, arg, a, complain, adc_unify, args,
-				 LOOKUP_IMPLICIT, /*tmpl=*/in_decl);
+				 LOOKUP_IMPLICIT);
 	  if (t == error_mark_node)
 	    return error_mark_node;
 	}
@@ -13972,7 +13971,7 @@ make_argument_pack (tree vec)
 /* Return an exact copy of template args T that can be modified
    independently.  */
 
-static tree
+tree
 copy_template_args (tree t)
 {
   if (t == error_mark_node)
@@ -25049,8 +25048,7 @@ unify (tree tparms, tree targs, tree parm, tree arg, int strict,
 	    {
 	      tparm = do_auto_deduction (tparm, arg, a,
 					 complain, adc_unify, targs,
-					 LOOKUP_NORMAL,
-					 TPARMS_PRIMARY_TEMPLATE (tparms));
+					 LOOKUP_NORMAL);
 	      if (tparm == error_mark_node)
 		return 1;
 	    }
@@ -29572,8 +29570,7 @@ make_constrained_placeholder_type (tree type, tree con, tree args)
   /* Our canonical type depends on the constraint.  */
   TYPE_CANONICAL (type) = canonical_type_parameter (type);
 
-  /* Attach the constraint to the type declaration. */
-  return TYPE_NAME (type);
+  return type;
 }
 
 /* Make a "constrained auto" type-specifier.  */
@@ -31193,10 +31190,7 @@ unparenthesized_id_or_class_member_access_p (tree init)
    adc_requirement contexts to communicate the necessary template arguments
    to satisfaction.  OUTER_TARGS is ignored in other contexts.
 
-   Additionally for adc_unify contexts TMPL is the template for which TYPE
-   is a template parameter type.
-
-   For partial-concept-ids, extra args from OUTER_TARGS, TMPL and the current
+   For partial-concept-ids, extra args from OUTER_TARGS and the current
    scope may be appended to the list of deduced template arguments prior to
    determining constraint satisfaction as appropriate.  */
 
@@ -31205,8 +31199,7 @@ do_auto_deduction (tree type, tree init, tree auto_node,
 		   tsubst_flags_t complain /* = tf_warning_or_error */,
 		   auto_deduction_context context /* = adc_unspecified */,
 		   tree outer_targs /* = NULL_TREE */,
-		   int flags /* = LOOKUP_NORMAL */,
-		   tree tmpl /* = NULL_TREE */)
+		   int flags /* = LOOKUP_NORMAL */)
 {
   if (type == error_mark_node || init == error_mark_node)
     return error_mark_node;
@@ -31354,10 +31347,13 @@ do_auto_deduction (tree type, tree init, tree auto_node,
     }
 
   /* Check any placeholder constraints against the deduced type. */
-  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 (tree constr = NON_ERROR (PLACEHOLDER_TYPE_CONSTRAINTS (auto_node)))
     {
+      /* Constrained auto NTTPs get replaced by an ordinary auto once processed
+	 and their constraints get associated with the corresponding template,
+	 so we shouldn't see any during coercion/deduction.  */
+      gcc_checking_assert (context != adc_unify);
+
       if (processing_template_decl)
 	{
 	  gcc_checking_assert (context == adc_variable_type
@@ -31389,10 +31385,7 @@ do_auto_deduction (tree type, tree init, tree auto_node,
 		}
 	    }
 
-      tree full_targs = outer_targs;
-      if (context == adc_unify && tmpl)
-	full_targs = add_outermost_template_args (tmpl, full_targs);
-      full_targs = add_to_template_args (full_targs, targs);
+      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
@@ -31417,8 +31410,7 @@ do_auto_deduction (tree type, tree init, tree auto_node,
 	      auto_diagnostic_group d;
 	      switch (context)
 		{
-		case adc_unspecified:
-		case adc_unify:
+		default:
 		  error_at (loc, "placeholder constraints not satisfied");
 		  break;
 		case adc_variable_type:
diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-placeholder12.C b/gcc/testsuite/g++.dg/cpp2a/concepts-placeholder12.C
index 22f0ac5e26a..edca8f7199b 100644
--- a/gcc/testsuite/g++.dg/cpp2a/concepts-placeholder12.C
+++ b/gcc/testsuite/g++.dg/cpp2a/concepts-placeholder12.C
@@ -22,8 +22,8 @@ int main() {
   A<false>::g(X<0>{}); // { dg-error "no match|constraints" }
 
   bool v1 = A<true>::value<0>;
-  bool v2 = A<false>::value<0>;  // { dg-error "constraints" }
+  bool v2 = A<false>::value<0>;  // { dg-error "invalid variable template" }
 
   A<true>::D<0> d1;
-  A<false>::D<0> d2; // { dg-error "constraints" }
+  A<false>::D<0> d2; // { dg-error "constraint failure" }
 }
diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-pr97093.C b/gcc/testsuite/g++.dg/cpp2a/concepts-pr97093.C
index d662552614e..355f195ac0a 100644
--- a/gcc/testsuite/g++.dg/cpp2a/concepts-pr97093.C
+++ b/gcc/testsuite/g++.dg/cpp2a/concepts-pr97093.C
@@ -29,4 +29,4 @@ struct pc
 };
 
 constexpr auto cc = pc {};
-constexpr auto mmcc = m <cc> {}; // { dg-error "not satisfied" }
+constexpr auto mmcc = m <cc> {}; // { dg-error "constraint failure" }
diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-template-parm12.C b/gcc/testsuite/g++.dg/cpp2a/concepts-template-parm12.C
new file mode 100644
index 00000000000..f2c260adbdd
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/concepts-template-parm12.C
@@ -0,0 +1,22 @@
+// { dg-do compile { target c++20 } }
+// Verify partial ordering with respect to associated constraints
+// works in the presence of constrained NTTPs.
+
+template<class T> concept C = true;
+
+template<class T> concept D = C<T> && true;
+
+template<C auto V> void f() = delete;
+template<D auto V> void f(); // more constrained
+
+template<C auto V> void g();
+template<C auto V> void g(); // redeclaration
+
+template<C auto V> struct A;
+template<D auto V> struct A<V> { };
+
+int main() {
+  f<0>();
+  g<0>();
+  A<0> a;
+}
diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-template-parm2.C b/gcc/testsuite/g++.dg/cpp2a/concepts-template-parm2.C
index 3bb2f576a87..a9b15dabc0c 100644
--- a/gcc/testsuite/g++.dg/cpp2a/concepts-template-parm2.C
+++ b/gcc/testsuite/g++.dg/cpp2a/concepts-template-parm2.C
@@ -12,4 +12,4 @@ template<Int T = char> struct S1 { };
 template<Int auto X = false> struct S2 { };
 
 S1<> s1; // { dg-error "constraint failure" }
-S2<> s2; // { dg-error "placeholder constraints not satisfied" }
+S2<> s2; // { dg-error "constraint failure" }
diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-template-parm6.C b/gcc/testsuite/g++.dg/cpp2a/concepts-template-parm6.C
index c7d9964f738..04c2e1c70ba 100644
--- a/gcc/testsuite/g++.dg/cpp2a/concepts-template-parm6.C
+++ b/gcc/testsuite/g++.dg/cpp2a/concepts-template-parm6.C
@@ -40,5 +40,5 @@ template<Int... Ts> struct S3 { }; // requires (C<Ts> && ...)
 S3<int, int, char> x0; // { dg-error "template constraint failure" }
 
 template<Int auto... Xs> struct S4 { }; // requires (C<X> && ...) with each X deduced
-S4<0, 1, 2, 'a'> x1; // { dg-error "placeholder constraints not satisfied" }
+S4<0, 1, 2, 'a'> x1; // { dg-error "template constraint failure" }
diff mbox series

Patch

diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
index 35be9cc2b41..9394bea8835 100644
--- a/gcc/cp/constraint.cc
+++ b/gcc/cp/constraint.cc
@@ -1189,7 +1189,7 @@  build_constrained_parameter (tree cnc, tree proto, tree args)
    done only after the requires clause has been parsed (or not).  */
 
 tree
-finish_shorthand_constraint (tree decl, tree constr)
+finish_shorthand_constraint (tree decl, tree constr, bool is_non_type)
 {
   /* No requirements means no constraints.  */
   if (!constr)
@@ -1198,9 +1198,22 @@  finish_shorthand_constraint (tree decl, tree constr)
   if (error_operand_p (constr))
     return NULL_TREE;
 
-  tree proto = CONSTRAINED_PARM_PROTOTYPE (constr);
-  tree con = CONSTRAINED_PARM_CONCEPT (constr);
-  tree args = CONSTRAINED_PARM_EXTRA_ARGS (constr);
+  tree proto, con, args;
+  if (is_non_type)
+    {
+      tree id = PLACEHOLDER_TYPE_CONSTRAINTS (constr);
+      tree tmpl = TREE_OPERAND (id, 0);
+      tree parms = DECL_INNERMOST_TEMPLATE_PARMS (tmpl);
+      proto = TREE_VALUE (TREE_VEC_ELT (parms, 0));
+      con = DECL_TEMPLATE_RESULT (tmpl);
+      args = TREE_OPERAND (id, 1);
+    }
+  else
+    {
+      proto = CONSTRAINED_PARM_PROTOTYPE (constr);
+      con = CONSTRAINED_PARM_CONCEPT (constr);
+      args = CONSTRAINED_PARM_EXTRA_ARGS (constr);
+    }
 
   bool variadic_concept_p = template_parameter_pack_p (proto);
   bool declared_pack_p = template_parameter_pack_p (decl);
@@ -1214,7 +1227,16 @@  finish_shorthand_constraint (tree decl, tree constr)
 
   /* Build the concept constraint-expression.  */
   tree tmpl = DECL_TI_TEMPLATE (con);
-  tree check = build_concept_check (tmpl, arg, args, tf_warning_or_error);
+  tree check;
+  if (is_non_type)
+    {
+      arg = finish_decltype_type (arg, /*id_expr=*/true, tf_warning_or_error);
+      args = copy_template_args (args);
+      TREE_VEC_ELT (args, 0) = arg;
+      check = build_concept_check (tmpl, args, tf_warning_or_error);
+    }
+  else
+    check = build_concept_check (tmpl, arg, args, tf_warning_or_error);
 
   /* Make the check a fold-expression if needed.
      Use UNKNOWN_LOCATION so write_template_args can tell the
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 1864ab205ae..1e202e17658 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -7528,8 +7528,7 @@  extern tree do_auto_deduction                   (tree, tree, tree,
                                                  auto_deduction_context
 						 = adc_unspecified,
 						 tree = NULL_TREE,
-						 int = LOOKUP_NORMAL,
-						 tree = NULL_TREE);
+						 int = LOOKUP_NORMAL);
 extern tree type_uses_auto			(tree);
 extern tree convert_generic_types_to_packs	(tree, int, int);
 extern tree splice_late_return_type		(tree, tree);
@@ -7587,6 +7586,7 @@  extern bool is_specialization_of_friend		(tree, tree);
 extern bool comp_template_args			(tree, tree, tree * = NULL,
 						 tree * = NULL);
 extern int template_args_equal                  (tree, tree);
+extern tree copy_template_args			(tree);
 extern tree maybe_process_partial_specialization (tree);
 extern tree most_specialized_instantiation	(tree);
 extern tree most_specialized_partial_spec       (tree, tsubst_flags_t, bool = false);
@@ -8598,7 +8598,7 @@  extern tree build_concept_check                 (tree, tree, tree, tsubst_flags_
 extern tree build_constrained_parameter         (tree, tree, tree = NULL_TREE);
 extern bool equivalent_placeholder_constraints  (tree, tree);
 extern hashval_t iterative_hash_placeholder_constraint	(tree, hashval_t);
-extern tree finish_shorthand_constraint         (tree, tree);
+extern tree finish_shorthand_constraint         (tree, tree, bool);
 extern tree finish_requires_expr                (location_t, tree, tree);
 extern tree finish_simple_requirement           (location_t, tree);
 extern tree finish_type_requirement             (location_t, tree);
diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
index 856508e3e4f..9a12e35a759 100644
--- a/gcc/cp/parser.cc
+++ b/gcc/cp/parser.cc
@@ -18599,34 +18599,6 @@  cp_parser_check_constrained_type_parm (cp_parser *parser,
   return true;
 }
 
-/* Finish parsing/processing a template type parameter and checking
-   various restrictions. */
-
-static inline tree
-cp_parser_constrained_type_template_parm (cp_parser *parser,
-                                          tree id,
-                                          cp_parameter_declarator* parmdecl)
-{
-  if (cp_parser_check_constrained_type_parm (parser, parmdecl))
-    return finish_template_type_parm (class_type_node, id);
-  else
-    return error_mark_node;
-}
-
-/* Create a new non-type template parameter from the given PARM
-   declarator.  */
-
-static tree
-cp_parser_constrained_non_type_template_parm (bool *is_non_type,
-					      cp_parameter_declarator *parm)
-{
-  *is_non_type = true;
-  cp_declarator *decl = parm->declarator;
-  cp_decl_specifier_seq *specs = &parm->decl_specifiers;
-  specs->type = TREE_TYPE (DECL_INITIAL (specs->type));
-  return grokdeclarator (decl, specs, TPARM, 0, NULL);
-}
-
 /* Build a constrained template parameter based on the PARMDECL
    declarator. The type of PARMDECL is the constrained type, which
    refers to the prototype template parameter that ultimately
@@ -18637,24 +18609,33 @@  finish_constrained_parameter (cp_parser *parser,
                               cp_parameter_declarator *parmdecl,
                               bool *is_non_type)
 {
-  tree decl = parmdecl->decl_specifiers.type;
+  tree type = parmdecl->decl_specifiers.type;
   tree id = get_unqualified_id (parmdecl->declarator);
   tree def = parmdecl->default_argument;
-  tree proto = DECL_INITIAL (decl);
 
   /* Build the parameter. Return an error if the declarator was invalid. */
   tree parm;
-  if (TREE_CODE (proto) == TYPE_DECL)
-    parm = cp_parser_constrained_type_template_parm (parser, id, parmdecl);
+  if (is_auto (type))
+    {
+      *is_non_type = true;
+      parm = grokdeclarator (parmdecl->declarator,
+			     &parmdecl->decl_specifiers,
+			     TPARM, /*initialized=*/0, /*attrlist=*/NULL);
+    }
   else
-    parm = cp_parser_constrained_non_type_template_parm (is_non_type, parmdecl);
+    {
+      if (cp_parser_check_constrained_type_parm (parser, parmdecl))
+	parm = finish_template_type_parm (class_type_node, id);
+      else
+	parm = error_mark_node;
+    }
   if (parm == error_mark_node)
     return error_mark_node;
 
   /* Finish the parameter decl and create a node attaching the
      default argument and constraint.  */
   parm = build_tree_list (def, parm);
-  TEMPLATE_PARM_CONSTRAINTS (parm) = decl;
+  TEMPLATE_PARM_CONSTRAINTS (parm) = type;
 
   return parm;
 }
@@ -18841,9 +18822,10 @@  cp_parser_template_parameter (cp_parser* parser, bool *is_non_type,
 	cp_lexer_consume_token (parser->lexer);
     }
 
-  /* The parameter may have been constrained type parameter.  */
+  /* The parameter may be a constrained type or non-type parameter.  */
   tree type = parameter_declarator->decl_specifiers.type;
-  if (declares_constrained_type_template_parameter (type))
+  if (declares_constrained_type_template_parameter (type)
+      || (type && is_constrained_auto (type)))
     return finish_constrained_parameter (parser,
                                          parameter_declarator,
                                          is_non_type);
diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
index 3d037bd6948..af784a41265 100644
--- a/gcc/cp/pt.cc
+++ b/gcc/cp/pt.cc
@@ -183,7 +183,6 @@  static int template_decl_level (tree);
 static int check_cv_quals_for_unify (int, tree, tree);
 static int unify_pack_expansion (tree, tree, tree,
 				 tree, unification_kind_t, bool, bool);
-static tree copy_template_args (tree);
 static tree tsubst_template_parms (tree, tree, tsubst_flags_t);
 static void tsubst_each_template_parm_constraints (tree, tree, tsubst_flags_t);
 static tree tsubst_arg_types (tree, tree, tree, tsubst_flags_t, tree);
@@ -4736,7 +4735,7 @@  process_template_parm (tree list, location_t parm_loc, tree parm,
   /* Build requirements for the type/template parameter.
      This must be done after SET_DECL_TEMPLATE_PARM_P or
      process_template_parm could fail. */
-  tree reqs = finish_shorthand_constraint (parm, constr);
+  tree reqs = finish_shorthand_constraint (parm, constr, is_non_type);
 
   decl = pushdecl (decl);
   if (!is_non_type)
@@ -8831,7 +8830,7 @@  convert_template_argument (tree parm,
       else if (tree a = type_uses_auto (t))
 	{
 	  t = do_auto_deduction (t, arg, a, complain, adc_unify, args,
-				 LOOKUP_IMPLICIT, /*tmpl=*/in_decl);
+				 LOOKUP_IMPLICIT);
 	  if (t == error_mark_node)
 	    return error_mark_node;
 	}
@@ -13967,7 +13966,7 @@  make_argument_pack (tree vec)
 /* Return an exact copy of template args T that can be modified
    independently.  */
 
-static tree
+tree
 copy_template_args (tree t)
 {
   if (t == error_mark_node)
@@ -25044,8 +25043,7 @@  unify (tree tparms, tree targs, tree parm, tree arg, int strict,
 	    {
 	      tparm = do_auto_deduction (tparm, arg, a,
 					 complain, adc_unify, targs,
-					 LOOKUP_NORMAL,
-					 TPARMS_PRIMARY_TEMPLATE (tparms));
+					 LOOKUP_NORMAL);
 	      if (tparm == error_mark_node)
 		return 1;
 	    }
@@ -29566,8 +29564,7 @@  make_constrained_placeholder_type (tree type, tree con, tree args)
   /* Our canonical type depends on the constraint.  */
   TYPE_CANONICAL (type) = canonical_type_parameter (type);
 
-  /* Attach the constraint to the type declaration. */
-  return TYPE_NAME (type);
+  return type;
 }
 
 /* Make a "constrained auto" type-specifier.  */
@@ -31187,10 +31184,7 @@  unparenthesized_id_or_class_member_access_p (tree init)
    adc_requirement contexts to communicate the necessary template arguments
    to satisfaction.  OUTER_TARGS is ignored in other contexts.
 
-   Additionally for adc_unify contexts TMPL is the template for which TYPE
-   is a template parameter type.
-
-   For partial-concept-ids, extra args from OUTER_TARGS, TMPL and the current
+   For partial-concept-ids, extra args from OUTER_TARGS and the current
    scope may be appended to the list of deduced template arguments prior to
    determining constraint satisfaction as appropriate.  */
 
@@ -31199,8 +31193,7 @@  do_auto_deduction (tree type, tree init, tree auto_node,
 		   tsubst_flags_t complain /* = tf_warning_or_error */,
 		   auto_deduction_context context /* = adc_unspecified */,
 		   tree outer_targs /* = NULL_TREE */,
-		   int flags /* = LOOKUP_NORMAL */,
-		   tree tmpl /* = NULL_TREE */)
+		   int flags /* = LOOKUP_NORMAL */)
 {
   if (type == error_mark_node || init == error_mark_node)
     return error_mark_node;
@@ -31348,8 +31341,10 @@  do_auto_deduction (tree type, tree init, tree auto_node,
     }
 
   /* Check any placeholder constraints against the deduced type. */
-  if (processing_template_decl && context == adc_unify)
-    /* Constraints will be checked after deduction.  */;
+  if (context == adc_unify)
+    /* These constraints correspond to a constrained auto NTTP and are part
+       of the template's associated constraints which will get checked as a
+       whole after coercion/deduction.  */;
   else if (tree constr = NON_ERROR (PLACEHOLDER_TYPE_CONSTRAINTS (auto_node)))
     {
       if (processing_template_decl)
@@ -31383,10 +31378,7 @@  do_auto_deduction (tree type, tree init, tree auto_node,
 		}
 	    }
 
-      tree full_targs = outer_targs;
-      if (context == adc_unify && tmpl)
-	full_targs = add_outermost_template_args (tmpl, full_targs);
-      full_targs = add_to_template_args (full_targs, targs);
+      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
@@ -31411,8 +31403,7 @@  do_auto_deduction (tree type, tree init, tree auto_node,
 	      auto_diagnostic_group d;
 	      switch (context)
 		{
-		case adc_unspecified:
-		case adc_unify:
+		default:
 		  error_at (loc, "placeholder constraints not satisfied");
 		  break;
 		case adc_variable_type:
diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-placeholder12.C b/gcc/testsuite/g++.dg/cpp2a/concepts-placeholder12.C
index 22f0ac5e26a..edca8f7199b 100644
--- a/gcc/testsuite/g++.dg/cpp2a/concepts-placeholder12.C
+++ b/gcc/testsuite/g++.dg/cpp2a/concepts-placeholder12.C
@@ -22,8 +22,8 @@  int main() {
   A<false>::g(X<0>{}); // { dg-error "no match|constraints" }
 
   bool v1 = A<true>::value<0>;
-  bool v2 = A<false>::value<0>;  // { dg-error "constraints" }
+  bool v2 = A<false>::value<0>;  // { dg-error "invalid variable template" }
 
   A<true>::D<0> d1;
-  A<false>::D<0> d2; // { dg-error "constraints" }
+  A<false>::D<0> d2; // { dg-error "constraint failure" }
 }
diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-pr97093.C b/gcc/testsuite/g++.dg/cpp2a/concepts-pr97093.C
index d662552614e..355f195ac0a 100644
--- a/gcc/testsuite/g++.dg/cpp2a/concepts-pr97093.C
+++ b/gcc/testsuite/g++.dg/cpp2a/concepts-pr97093.C
@@ -29,4 +29,4 @@  struct pc
 };
 
 constexpr auto cc = pc {};
-constexpr auto mmcc = m <cc> {}; // { dg-error "not satisfied" }
+constexpr auto mmcc = m <cc> {}; // { dg-error "constraint failure" }
diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-template-parm2.C b/gcc/testsuite/g++.dg/cpp2a/concepts-template-parm2.C
index 3bb2f576a87..a9b15dabc0c 100644
--- a/gcc/testsuite/g++.dg/cpp2a/concepts-template-parm2.C
+++ b/gcc/testsuite/g++.dg/cpp2a/concepts-template-parm2.C
@@ -12,4 +12,4 @@  template<Int T = char> struct S1 { };
 template<Int auto X = false> struct S2 { };
 
 S1<> s1; // { dg-error "constraint failure" }
-S2<> s2; // { dg-error "placeholder constraints not satisfied" }
+S2<> s2; // { dg-error "constraint failure" }
diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-template-parm6.C b/gcc/testsuite/g++.dg/cpp2a/concepts-template-parm6.C
index c7d9964f738..04c2e1c70ba 100644
--- a/gcc/testsuite/g++.dg/cpp2a/concepts-template-parm6.C
+++ b/gcc/testsuite/g++.dg/cpp2a/concepts-template-parm6.C
@@ -40,5 +40,5 @@  template<Int... Ts> struct S3 { }; // requires (C<Ts> && ...)
 S3<int, int, char> x0; // { dg-error "template constraint failure" }
 
 template<Int auto... Xs> struct S4 { }; // requires (C<X> && ...) with each X deduced
-S4<0, 1, 2, 'a'> x1; // { dg-error "placeholder constraints not satisfied" }
+S4<0, 1, 2, 'a'> x1; // { dg-error "template constraint failure" }