On Thu, 17 Oct 2024, Patrick Palka wrote: > 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!
Ping. > > -- >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" } > > -- > 2.47.0.86.g15030f9556 > >