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