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
> 
> 

Reply via email to