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

Reply via email to