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