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