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" } -- 2.47.0.86.g15030f9556