On Tue, Jul 30, 2024 at 07:51:34PM -0400, Jason Merrill wrote:
> Yeah.
> 
> In the paper a fold expanded constraint doesn't have a parameter mapping,
> only atomic constraints do.  Within the normal form of (__is_same (T, int)
> && ...) we have a single atomic constraint with parameter mapping T -> T,
> which only comes into play when we're checking satisfaction for each
> element.

> > > So, shall we file some https://github.com/cplusplus/CWG/ issue about this?
> > > Whether the packs [temp.constr.fold] talks about are the normalized ones
> > > only (in that case what happens if there are no packs), or all packs
> > > mentioned (in that case, whether there shouldn't be also template 
> > > parameter
> > > mappings on the fold expanded constraints like there are on the atomic
> > > constraints (for the unexpanded packs only)?
> 
> I think there should be parameter mappings for all parameter packs named in
> the fold-expression.  And I suppose for the other template parameters as
> well.

> > > Anyway, I'm afraid on the implementation side, ARGUMENT_PACK_SELECT
> > > didn't help almost at all.  The problem e.g. on fold-constr7.C testcase
> > > is that the ARGUMENT_PACK_SELECT is optimized away before it could be 
> > > used.
> > > tsubst_parameter_mapping (where I could remove the
> > >        if (cxx_dialect >= cxx26 && ARGUMENT_PACK_P (arg))
> > > hack without any behavior change) just tsubsts it into int type.
> > > With the hack removed, it will go through
> > >        if (ARGUMENT_PACK_P (arg))
> > >          new_arg = tsubst_argument_pack (arg, args, complain, in_decl);
> > > but that still sets new_arg to int INTEGER_TYPE; while if a pack is used
> > > in some nested pack expansion as well as outside of it, we'd need to 
> > > arrange
> > > to reconstruct ARGUMENT_PACK_SELECT in what tsubst_parameter_mapping
> > > arranges.
> > 
> > Ah right, because of the double substitution -- first satisfy_atom
> > substitutes into the parameter mapping, and then it substitutes this
> > substituted parameter mapping into the atomic constraint expression.
> > So after the first substitution the APS might already have gotten
> > "resolved", I think..
> > 
> > IIUC the normal form of the constraint in fold-constr7.C will have
> > the identity parameter mapping Ts -> {Ts...}.  And you'll be passing
> > Ts=APS<{int,int,...}, 0> etc to the recursive satisfy_constraint_r call
> > in satisfy_fold.
> > 
> > Does it work if you wrap the ARGUMENT_PACK_SELECT in a single-element
> > TYPE/NONTYPE_ARGUMENT_PACK?
> 
> I think trying to play games with APS in the normalized form is a mistake;
> I'd think we should only use it it when substituting elements of the
> argument pack into the atomic constraint's parameter mapping.

Thanks to you both.

I'm still lost what to do exactly.
The building of parameter mappings for ATOMIC_CONSTR is done by
build_parameter_mapping but that already during the discover part
(find_template_parameters) uses ctx_parms, so I assume I can't just build
the parameter mappings for FOLD_CONSTR based on the already found unexpanded
parameter packs and just map_arguments the copy of the list or something
like that because that doesn't depend on ctx_parms.
Would satisfy_fold then need to tsubst_parameter_mappings somehow as well?

If satisfy_atom shouldn't use ARGUMENT_PACK_SELECT yet and it should be
only created during tsubst_parameter_mappings, I guess it needs some other
way how to tell tsubst_parameter_mappings of the ATOMIC_CONSTR which of the
parameter packs should have ARGUMENT_PACK_SELECT.  On the side argument
(like the packs TREE_LIST I had in the first version of the patch which
didn't really work), something else?  In any case, it needs to be something
that e.g. the satisfaction cache hash and equal can take into account.
And still am not sure how that can work.  Consider
template <class U> concept C = requires { U (1); };
template <class ...T> struct A { A (int) {} };
template <class ...V> requires ((C <A <V>> && C <V> && (sizeof (V) + ...) < 80) 
&& ...)
constexpr bool foo () { return true; }
static_assert (foo <int, long, A <float>> ());
For the C <A <V>> case it needs to apply the ARGUMENT_PACK_SELECT right away
and just subst it into int (or later long and later A <float>), because
one needs to substitute A<int> into the ATOM_CONSTR expression, in the C <V>
case either that or could just add ARGUMENT_PACK_SELECT afterwards, and
for the last case it needs to be ARGUMENT_PACK_SELECT so that the pack
expansion expands it.

Also, if FOLD_CONSTR has the mappings, wonder if derive_fold_proof doesn't
need to change the way it checks whether the unexpanded packs are equal
(worried we could e.g. try to compare for subsumption FOLD_CONSTRs from
different templates, one from some concept check and another from a
different one or whatever, and not sure if template_args_equal will return
false if they are packs from different templates with the same index/level.

I've updated most of the testcases in the patch so that they actually use
the template argument (but left one with those).

In any case, while I've learned a lot of new stuff from C++ FE parts I
didn't even know they exist, I doubt I'd be able to finish this, do you
think you or Patrick could eventually take it over from where it is right
now (any time in the future, not necessarily now)?  I can attach it to the
PR as well.  Sorry.

2024-07-31  Jakub Jelinek  <ja...@redhat.com>

        PR c++/115746
gcc/cp/
        * cp-tree.def: Implement C++26 P2963R3 - Ordering of constraints
        involving fold expressions.
        (FOLD_CONSTR): New tree code.
        * cp-tree.h (CONSTR_P): Handle FOLD_CONSTR.
        (CONSTR_CHECK): Include FOLD_CONSTR.
        (FOLD_CONSTR_EXPR): Define.
        (FOLD_CONSTR_PACKS): Define.
        (FOLD_CONSTR_DISJ_P): Define.
        (ARGUMENT_PACK_SELECT_IMMUTABLE_P): Define.
        * cp-objcp-common.cc (cp_common_init_ts): Handle FOLD_CONSTR.
        * constraint.cc (normalize_fold_expr): New function.
        (normalize_expression): Use it for {U,BI}NARY_{LEFT,RIGHT}_FOLD_EXPR.
        (fold_constraints_identical_p): New function.
        (constraints_equivalent_p): Use it for FOLD_CONSTR.
        (add_constraint): Handle FOLD_CONSTR.
        (tsubst_parameter_mapping): Undo template_parm_to_arg returning
        parameter packs if inside fold expanded constraint.  Formatting fix
        in wrapper.
        (satisfy_fold): New function.
        (satisfy_constraint_r): Handle FOLD_CONSTR.
        * error.cc (dump_expr): Likewise.
        * logic.cc (struct clause): Add m_fold member.
        (clause::clause): Handle FOLD_CONSTR, for copy ctor copy over m_fold.
        (clause::replace, clause::insert): Handle FOLD_CONSTR.
        (clause::folds): New method.
        (atomic_p): Also return true for FOLD_CONSTR.
        (decompose_atom): Adjust function comment.
        (derive_fold_proof): New function.
        (derive_proof): Handle FOLD_CONSTR.  Change default: case to
        case ATOMIC_CONSTR, for default: add gcc_unreachable ().  Formatting
        fixes.
        (subsumes_constraints_nonnull): Use auto_cond_timevar instead of
        auto_timevar.
        * cxx-pretty-print.cc (cxx_pretty_printer::expression): Handle
        FOLD_CONSTR.
        (pp_cxx_fold_expanded_constraint): New function.
        (pp_cxx_constraint): Handle FOLD_CONSTR.
        * pt.cc (iterative_hash_template_arg, template_args_equal): Handle
        ARGUMENT_PACK_SELECT with ARGUMENT_PACK_SELECT_IMMUTABLE_P flag.
gcc/testsuite/
        * g++.dg/concepts/diagnostic3.C: Guard diagnostics on c++23_down.
        * g++.dg/concepts/variadic2.C: Likewise.
        * g++.dg/concepts/variadic4.C: Likewise.
        * g++.dg/cpp2a/concepts-requires33.C: Expect another error for c++26.
        * g++.dg/cpp26/fold-constr1.C: New test.
        * g++.dg/cpp26/fold-constr2.C: New test.
        * g++.dg/cpp26/fold-constr3.C: New test.
        * g++.dg/cpp26/fold-constr4.C: New test.
        * g++.dg/cpp26/fold-constr5.C: New test.
        * g++.dg/cpp26/fold-constr6.C: New test.
        * g++.dg/cpp26/fold-constr7.C: New test.
        * g++.dg/cpp26/fold-constr8.C: New test.
        * g++.dg/cpp26/fold-constr9.C: New test.
        * g++.dg/cpp26/fold-constr10.C: New test.

--- gcc/cp/cp-tree.def.jj       2024-07-29 09:15:52.634931633 +0200
+++ gcc/cp/cp-tree.def  2024-07-29 13:24:52.833379711 +0200
@@ -538,6 +538,12 @@ DEFTREECODE (ATOMIC_CONSTR, "atomic_cons
 DEFTREECODE (CONJ_CONSTR, "conj_constr", tcc_expression, 2)
 DEFTREECODE (DISJ_CONSTR, "disj_constr", tcc_expression, 2)
 
+/* Fold expanded constraint.
+   CONSTR_INFO provides source info to support diagnostics.
+   FOLD_CONSTR_EXPR is the constraint embedded in it,
+   FOLD_CONSTR_PACKS are the packs expanded by it.  */
+DEFTREECODE (FOLD_CONSTR, "fold_constr", tcc_expression, 2)
+
 /* The co_await expression is used to support coroutines.
 
   Op 0 is the cast expresssion (potentially modified by the
--- gcc/cp/cp-tree.h.jj 2024-07-29 09:15:52.688930926 +0200
+++ gcc/cp/cp-tree.h    2024-07-29 13:24:52.832379725 +0200
@@ -1679,11 +1679,12 @@ check_constraint_info (tree t)
 #define CONSTR_P(NODE)                  \
   (TREE_CODE (NODE) == ATOMIC_CONSTR    \
    || TREE_CODE (NODE) == CONJ_CONSTR   \
-   || TREE_CODE (NODE) == DISJ_CONSTR)
+   || TREE_CODE (NODE) == DISJ_CONSTR  \
+   || TREE_CODE (NODE) == FOLD_CONSTR)
 
 /* Valid for any normalized constraint.  */
 #define CONSTR_CHECK(NODE) \
-  TREE_CHECK3 (NODE, ATOMIC_CONSTR, CONJ_CONSTR, DISJ_CONSTR)
+  TREE_CHECK4 (NODE, ATOMIC_CONSTR, CONJ_CONSTR, DISJ_CONSTR, FOLD_CONSTR)
 
 /* The CONSTR_INFO stores normalization data for a constraint. It refers to
    the original expression and the expression or declaration
@@ -1724,6 +1725,18 @@ check_constraint_info (tree t)
 #define ATOMIC_CONSTR_EXPR(NODE) \
   CONSTR_EXPR (ATOMIC_CONSTR_CHECK (NODE))
 
+/* The constraint embedded in FOLD_CONSTR.  */
+#define FOLD_CONSTR_EXPR(NODE) \
+  TREE_OPERAND (FOLD_CONSTR_CHECK (NODE), 0)
+
+/* List of packs expanded by it.  */
+#define FOLD_CONSTR_PACKS(NODE) \
+  TREE_OPERAND (FOLD_CONSTR_CHECK (NODE), 1)
+
+/* True if FOLD_CONSTR has fold-operator ||, false for &&.  */
+#define FOLD_CONSTR_DISJ_P(NODE) \
+  TREE_STATIC (FOLD_CONSTR_CHECK (NODE))
+
 /* Whether a PARM_DECL represents a local parameter in a
    requires-expression.  */
 #define CONSTRAINT_VAR_P(NODE) \
@@ -4087,6 +4100,11 @@ struct GTY(()) lang_decl {
 #define ARGUMENT_PACK_SELECT_INDEX(NODE)                               \
   (((struct tree_argument_pack_select *)ARGUMENT_PACK_SELECT_CHECK 
(NODE))->index)
 
+/* True in ARGUMENT_PACK_SELECT which is immutable, won't be modified
+   once it is created and so can be safely hashed.  */
+#define ARGUMENT_PACK_SELECT_IMMUTABLE_P(NODE) \
+  TREE_STATIC (ARGUMENT_PACK_SELECT_CHECK (NODE))
+
 #define FOLD_EXPR_CHECK(NODE)                                          \
   TREE_CHECK4 (NODE, UNARY_LEFT_FOLD_EXPR, UNARY_RIGHT_FOLD_EXPR,      \
               BINARY_LEFT_FOLD_EXPR, BINARY_RIGHT_FOLD_EXPR)
--- gcc/cp/cp-objcp-common.cc.jj        2024-07-29 09:15:52.578932366 +0200
+++ gcc/cp/cp-objcp-common.cc   2024-07-29 13:24:52.832379725 +0200
@@ -705,6 +705,7 @@ cp_common_init_ts (void)
   MARK_TS_EXP (CONJ_CONSTR);
   MARK_TS_EXP (DISJ_CONSTR);
   MARK_TS_EXP (ATOMIC_CONSTR);
+  MARK_TS_EXP (FOLD_CONSTR);
   MARK_TS_EXP (NESTED_REQ);
   MARK_TS_EXP (REQUIRES_EXPR);
   MARK_TS_EXP (SIMPLE_REQ);
--- gcc/cp/constraint.cc.jj     2024-07-29 09:15:52.501933374 +0200
+++ gcc/cp/constraint.cc        2024-07-31 09:17:28.707743674 +0200
@@ -852,6 +852,39 @@ normalize_atom (tree t, tree args, norm_
   return atom;
 }
 
+/* Normalize {UNARY,BINARY}_{LEFT,RIGHT}_FOLD_EXPR.  */
+
+static tree
+normalize_fold_expr (tree t, tree args, norm_info info)
+{
+  if (cxx_dialect < cxx26
+      || (FOLD_EXPR_OP (t) != TRUTH_ANDIF_EXPR
+         && FOLD_EXPR_OP (t) != TRUTH_ORIF_EXPR)
+      || FOLD_EXPR_MODIFY_P (t))
+    return normalize_atom (t, args, info);
+
+  tree norm
+    = normalize_expression (PACK_EXPANSION_PATTERN (FOLD_EXPR_PACK (t)),
+                           args, info);
+  tree ci = (info.generate_diagnostics
+            ? build_tree_list (t, info.context) : NULL_TREE);
+  tree params = PACK_EXPANSION_PARAMETER_PACKS (FOLD_EXPR_PACK (t));
+  tree ret = build2 (FOLD_CONSTR, ci, norm, params);
+  if (FOLD_EXPR_OP (t) == TRUTH_ORIF_EXPR)
+    FOLD_CONSTR_DISJ_P (ret) = 1;
+  if (TREE_CODE (t) == BINARY_LEFT_FOLD_EXPR
+      || TREE_CODE (t) == BINARY_RIGHT_FOLD_EXPR)
+    {
+      tree init = normalize_expression (FOLD_EXPR_INIT (t), args, info);
+      tree_code code
+       = FOLD_EXPR_OP (t) == TRUTH_ANDIF_EXPR ? CONJ_CONSTR : DISJ_CONSTR;
+      if (TREE_CODE (t) == BINARY_LEFT_FOLD_EXPR)
+       std::swap (ret, init);
+      return build2 (code, ci, ret, init);
+    }
+  return ret;
+}
+
 /* Returns the normal form of an expression.  */
 
 static tree
@@ -869,6 +902,11 @@ normalize_expression (tree t, tree args,
       return normalize_logical_operation (t, args, CONJ_CONSTR, info);
     case TRUTH_ORIF_EXPR:
       return normalize_logical_operation (t, args, DISJ_CONSTR, info);
+    case UNARY_LEFT_FOLD_EXPR:
+    case UNARY_RIGHT_FOLD_EXPR:
+    case BINARY_LEFT_FOLD_EXPR:
+    case BINARY_RIGHT_FOLD_EXPR:
+      return normalize_fold_expr (t, args, info);
     default:
       return normalize_atom (t, args, info);
     }
@@ -1055,6 +1093,28 @@ atomic_constraints_identical_p (tree t1,
   return true;
 }
 
+/* Compare two fold expanded constraints T1 and T2.  */
+
+static bool
+fold_constraints_identical_p (tree t1, tree t2)
+{
+  gcc_assert (TREE_CODE (t1) == FOLD_CONSTR);
+  gcc_assert (TREE_CODE (t2) == FOLD_CONSTR);
+
+  if (FOLD_CONSTR_DISJ_P (t1) != FOLD_CONSTR_DISJ_P (t2))
+    return false;
+
+  tree p1 = FOLD_CONSTR_PACKS (t1);
+  tree p2 = FOLD_CONSTR_PACKS (t2);
+  for (; p1 && p2; p1 = TREE_CHAIN (p1), p2 = TREE_CHAIN (p2))
+    if (!template_args_equal (TREE_VALUE (p1), TREE_VALUE (p2)))
+      return false;
+  if (p1 || p2)
+    return false;
+  return constraints_equivalent_p (FOLD_CONSTR_EXPR (t1),
+                                  FOLD_CONSTR_EXPR (t2));
+}
+
 /* True if T1 and T2 are equivalent, meaning they have the same syntactic
    structure and all corresponding constraints are identical.  */
 
@@ -1082,6 +1142,10 @@ constraints_equivalent_p (tree t1, tree
       if (!atomic_constraints_identical_p (t1, t2))
        return false;
       break;
+    case FOLD_CONSTR:
+      if (!fold_constraints_identical_p (t1, t2))
+       return false;
+      break;
     default:
       gcc_unreachable ();
     }
@@ -1126,6 +1190,10 @@ add_constraint (tree t, hash& h)
     case ATOMIC_CONSTR:
       h.merge_hash (hash_atomic_constraint (t));
       break;
+    case FOLD_CONSTR:
+      h.add_int (FOLD_CONSTR_DISJ_P (t));
+      add_constraint (FOLD_CONSTR_EXPR (t), h);
+      break;
     default:
       gcc_unreachable ();
     }
@@ -2163,6 +2231,29 @@ tsubst_parameter_mapping (tree map, tree
       tree parm = TREE_VALUE (p);
       tree arg = TREE_PURPOSE (p);
       tree new_arg;
+      if (cxx_dialect >= cxx26 && ARGUMENT_PACK_P (arg))
+       {
+         /* template_parm_to_arg for packs wraps the template parm
+            with {,NON}TYPE_ARGUMENT_PACK with pack expansion.
+            For packs expanded by fold expanded constraint undo this
+            here.  */
+         tree v = ARGUMENT_PACK_ARGS (arg);
+         if (TREE_VEC_LENGTH (v) == 1
+             && PACK_EXPANSION_P (TREE_VEC_ELT (v, 0)))
+           {
+             tree t = PACK_EXPANSION_PATTERN (TREE_VEC_ELT (v, 0));
+             tree e = STRIP_REFERENCE_REF (t);
+             if (TEMPLATE_PARM_P (e))
+               {
+                 int level;
+                 int index;
+                 template_parm_level_and_index (e, &level, &index);
+                 tree a = TMPL_ARG (args, level, index);
+                 if (TREE_CODE (a) == ARGUMENT_PACK_SELECT)
+                   arg = t;
+               }
+           }
+       }
       if (ARGUMENT_PACK_P (arg))
        new_arg = tsubst_argument_pack (arg, args, complain, in_decl);
       else
@@ -2187,7 +2278,8 @@ tsubst_parameter_mapping (tree map, tree
 }
 
 tree
-tsubst_parameter_mapping (tree map, tree args, tsubst_flags_t complain, tree 
in_decl)
+tsubst_parameter_mapping (tree map, tree args, tsubst_flags_t complain,
+                         tree in_decl)
 {
   return tsubst_parameter_mapping (map, args, subst_info (complain, in_decl));
 }
@@ -2831,6 +2923,108 @@ satisfy_atom (tree t, tree args, sat_inf
   return cache.save (inst_cache.save (result));
 }
 
+/* Compute the satisfaction of a fold expanded constraint.  */
+
+static tree
+satisfy_fold (tree t, tree args, sat_info info)
+{
+  tree orig_args = args;
+  int len = -1;
+  auto_vec <int, 8> indices;
+  for (tree p = FOLD_CONSTR_PACKS (t); p; p = TREE_CHAIN (p))
+    {
+      int level, index;
+      template_parm_level_and_index (TREE_VALUE (p), &level, &index);
+      tree a = TMPL_ARG (orig_args, level, index);
+      if (TREE_CODE (a) == ARGUMENT_PACK_SELECT)
+       a = ARGUMENT_PACK_SELECT_FROM_PACK (a);
+      int this_len = TREE_VEC_LENGTH (ARGUMENT_PACK_ARGS (a));
+      if (len == -1)
+       len = this_len;
+      else if (this_len != len)
+       {
+         if (info.diagnose_unsatisfaction_p ())
+           {
+             diagnosing_failed_constraint failure (t, args, info.noisy ());
+             tree first = TREE_VALUE (FOLD_CONSTR_PACKS (t));
+             cp_expr fold_expr = CONSTR_EXPR (t);
+             inform (fold_expr.get_location (),
+                     "fold expanded constraint not satisfied because "
+                     "of pack length mismatch");
+             if (TREE_CODE (first) == TYPE_PACK_EXPANSION)
+               inform (fold_expr.get_location (),
+                       "%qT has length %d", first, len);
+             else
+               inform (fold_expr.get_location (),
+                       "%qE has length %d", first, len);
+             if (TREE_CODE (TREE_VALUE (p)) == TYPE_PACK_EXPANSION)
+               inform (fold_expr.get_location (),
+                       "%qT has length %d", TREE_VALUE (p), this_len);
+             else
+               inform (fold_expr.get_location (),
+                       "%qE has length %d", TREE_VALUE (p), this_len);
+           }
+         return error_mark_node;
+       }
+      if (len != 0)
+       {
+         indices.safe_push (level);
+         indices.safe_push (index);
+       }
+    }
+  gcc_checking_assert (len != -1);
+  if (len == 0)
+    {
+      /* For N = 0 fold expanded constraint with && fold operator is
+        satisfied.  */
+      if (!FOLD_CONSTR_DISJ_P (t))
+       return boolean_true_node;
+      if (info.diagnose_unsatisfaction_p ())
+       {
+         diagnosing_failed_constraint failure (t, args, info.noisy ());
+         cp_expr fold_expr = CONSTR_EXPR (t);
+         inform (fold_expr.get_location (),
+                 "fold expanded constraint not satisfied because "
+                 "of empty pack with %<||%> fold operator");
+       }
+      return boolean_false_node;
+    }
+  for (int i = 0; i < len; ++i)
+    {
+      unsigned j;
+      int level, index;
+      args = orig_args;
+      FOR_EACH_VEC_ELT (indices, j, level)
+       {
+         ++j;
+         indices.iterate (j, &index);
+         if (orig_args == args)
+           args = copy_node (orig_args);
+         if (TMPL_ARGS_HAVE_MULTIPLE_LEVELS (orig_args))
+           {
+             tree v = TMPL_ARGS_LEVEL (orig_args, level);
+             if (TMPL_ARGS_LEVEL (args, level) == v)
+               SET_TMPL_ARGS_LEVEL (args, level, copy_node (v));
+           }
+         tree a = TMPL_ARG (orig_args, level, index);
+         if (TREE_CODE (a) == ARGUMENT_PACK_SELECT)
+           a = ARGUMENT_PACK_SELECT_FROM_PACK (a);
+         tree aps = make_node (ARGUMENT_PACK_SELECT);
+         ARGUMENT_PACK_SELECT_FROM_PACK (aps) = a;
+         ARGUMENT_PACK_SELECT_INDEX (aps) = i;
+         ARGUMENT_PACK_SELECT_IMMUTABLE_P (aps) = 1;
+         TMPL_ARG (args, level, index) = aps;
+       }
+      tree result = satisfy_constraint_r (FOLD_CONSTR_EXPR (t), args, info);
+      if (result == error_mark_node)
+       return result;
+      if (result == (FOLD_CONSTR_DISJ_P (t)
+                    ? boolean_true_node : boolean_false_node))
+       return result;
+    }
+  return FOLD_CONSTR_DISJ_P (t) ? boolean_false_node : boolean_true_node;
+}
+
 /* Determine if the normalized constraint T is satisfied.
    Returns boolean_true_node if the expression/constraint is
    satisfied, boolean_false_node if not, and error_mark_node
@@ -2856,6 +3050,8 @@ satisfy_constraint_r (tree t, tree args,
       return satisfy_disjunction (t, args, info);
     case ATOMIC_CONSTR:
       return satisfy_atom (t, args, info);
+    case FOLD_CONSTR:
+      return satisfy_fold (t, args, info);
     default:
       gcc_unreachable ();
     }
--- gcc/cp/error.cc.jj  2024-07-29 09:15:52.772929827 +0200
+++ gcc/cp/error.cc     2024-07-29 13:24:52.833379711 +0200
@@ -3097,6 +3097,7 @@ dump_expr (cxx_pretty_printer *pp, tree
     case ATOMIC_CONSTR:
     case CONJ_CONSTR:
     case DISJ_CONSTR:
+    case FOLD_CONSTR:
       {
         pp_cxx_constraint (cxx_pp, t);
         break;
--- gcc/cp/logic.cc.jj  2024-07-29 09:15:52.802929435 +0200
+++ gcc/cp/logic.cc     2024-07-29 13:24:52.834379698 +0200
@@ -65,6 +65,8 @@ struct clause
     m_terms.push_back (t);
     if (TREE_CODE (t) == ATOMIC_CONSTR)
       m_set.add (t);
+    else if (TREE_CODE (t) == FOLD_CONSTR)
+      m_fold.safe_push (t);
 
     m_current = m_terms.begin ();
   }
@@ -74,8 +76,11 @@ struct clause
      copied list of terms.  */
 
   clause (clause const& c)
-    : m_terms (c.m_terms), m_set (c.m_set), m_current (m_terms.begin ())
+    : m_terms (c.m_terms), m_set (c.m_set), m_fold (c.m_fold.length ()),
+      m_current (m_terms.begin ())
   {
+    for (auto v : &c.m_fold)
+      m_fold.quick_push (v);
     std::advance (m_current, std::distance (c.begin (), c.current ()));
   }
 
@@ -109,6 +114,8 @@ struct clause
        if (m_set.add (t))
          return std::make_pair (m_terms.erase (iter), true);
       }
+    else if (TREE_CODE (t) == FOLD_CONSTR)
+      m_fold.safe_push (t);
     *iter = t;
     return std::make_pair (iter, false);
   }
@@ -126,6 +133,8 @@ struct clause
        if (m_set.add (t))
          return std::make_pair (iter, false);
       }
+    else if (TREE_CODE (t) == FOLD_CONSTR)
+      m_fold.safe_push (t);
     return std::make_pair (m_terms.insert (iter, t), true);
   }
 
@@ -166,6 +175,12 @@ struct clause
     return m_set.contains (t);
   }
 
+  /* Returns vector of FOLD_CONSTR terms.  */
+
+  auto_vec<tree> &folds ()
+  {
+    return m_fold;
+  }
 
   /* Returns an iterator to the first clause in the formula.  */
 
@@ -204,6 +219,7 @@ struct clause
 
   std::list<tree> m_terms; /* The list of terms.  */
   hash_set<tree, false, atom_hasher> m_set; /* The set of atomic constraints.  
*/
+  auto_vec<tree> m_fold; /* The vector of fold expanded constraints.  */
   iterator m_current; /* The current term.  */
 };
 
@@ -340,7 +356,7 @@ conjunction_p (tree t)
 static inline bool
 atomic_p (tree t)
 {
-  return TREE_CODE (t) == ATOMIC_CONSTR;
+  return TREE_CODE (t) == ATOMIC_CONSTR || TREE_CODE (t) == FOLD_CONSTR;
 }
 
 /* Recursively count the number of clauses produced when converting T
@@ -626,7 +642,7 @@ decompose_disjunction (formula& f, claus
     branch_clause (f, c, t);
 }
 
-/* An atomic constraint is already decomposed.  */
+/* An atomic or fold expanded constraint is already decomposed.  */
 inline void
 decompose_atom (clause& c)
 {
@@ -691,13 +707,48 @@ derive_atomic_proof (clause& c, tree t)
   return c.contains (t);
 }
 
+/* Derive a proof of the fold expanded constraint T in clause C.  */
+
+static bool
+derive_fold_proof (clause& c, tree t, rules r)
+{
+  auto_vec<tree> &folds = c.folds ();
+  for (auto v : &folds)
+    /* [temp.constr.order]/1 - a fold expanded constraint A subsumes
+       another fold expanded constraint B if they are compatible for
+       subsumption, have the same fold-operator, and the constraint
+       of A subsumes that of B.  */
+    if (FOLD_CONSTR_DISJ_P (v) == FOLD_CONSTR_DISJ_P (t))
+      {
+       bool compat = false;
+       for (tree p1 = FOLD_CONSTR_PACKS (t); p1 && !compat;
+            p1 = TREE_CHAIN (p1))
+         for (tree p2 = FOLD_CONSTR_PACKS (v); p2;
+              p2 = TREE_CHAIN (p2))
+           /* [temp.constr.fold]/5 - Two fold expanded constraints are
+              compatible for subsumption if their respective constraints
+              both contain an equivalent unexpanded pack.  */
+           if (template_args_equal (TREE_VALUE (p1), TREE_VALUE (p2)))
+             {
+               compat = true;
+               break;
+             }
+       if (compat
+           && (r == left
+               ? subsumes (FOLD_CONSTR_EXPR (v), FOLD_CONSTR_EXPR (t))
+               : subsumes (FOLD_CONSTR_EXPR (t), FOLD_CONSTR_EXPR (v))))
+         return true;
+      }
+  return false;
+}
+
 /* Derive a proof of T from the terms in C.  */
 
 static bool
 derive_proof (clause& c, tree t, rules r)
 {
   switch (TREE_CODE (t))
-  {
+    {
     case CONJ_CONSTR:
       if (r == left)
         return derive_proof_for_both_operands (c, t, r);
@@ -708,9 +759,13 @@ derive_proof (clause& c, tree t, rules r
         return derive_proof_for_either_operand (c, t, r);
       else
        return derive_proof_for_both_operands (c, t, r);
-    default:
+    case ATOMIC_CONSTR:
       return derive_atomic_proof (c, t);
-  }
+    case FOLD_CONSTR:
+      return derive_fold_proof (c, t, r);
+    default:
+      gcc_unreachable ();
+    }
 }
 
 /* Key/value pair for caching subsumption results.  This associates a pair of
@@ -787,7 +842,7 @@ save_subsumption (tree t1, tree t2, bool
 static bool
 subsumes_constraints_nonnull (tree lhs, tree rhs)
 {
-  auto_timevar time (TV_CONSTRAINT_SUB);
+  auto_cond_timevar time (TV_CONSTRAINT_SUB);
 
   if (bool *b = lookup_subsumption (lhs, rhs))
     return *b;
--- gcc/cp/cxx-pretty-print.cc.jj       2024-07-29 09:15:52.721930495 +0200
+++ gcc/cp/cxx-pretty-print.cc  2024-07-29 13:24:52.834379698 +0200
@@ -1259,6 +1259,7 @@ cxx_pretty_printer::expression (tree t)
     case ATOMIC_CONSTR:
     case CONJ_CONSTR:
     case DISJ_CONSTR:
+    case FOLD_CONSTR:
       pp_cxx_constraint (this, t);
       break;
 
@@ -2882,6 +2883,19 @@ pp_cxx_disjunction (cxx_pretty_printer *
 }
 
 void
+pp_cxx_fold_expanded_constraint (cxx_pretty_printer *pp, tree t)
+{
+  pp_left_paren (pp);
+  pp_cxx_constraint (pp, FOLD_CONSTR_EXPR (t));
+  pp_space (pp);
+  if (FOLD_CONSTR_DISJ_P (t))
+    pp_string (pp, "\\/");
+  else
+    pp_string (pp, "/\\");
+  pp_string (pp, " ...)");
+}
+
+void
 pp_cxx_constraint (cxx_pretty_printer *pp, tree t)
 {
   if (t == error_mark_node)
@@ -2901,6 +2915,10 @@ pp_cxx_constraint (cxx_pretty_printer *p
       pp_cxx_disjunction (pp, t);
       break;
 
+    case FOLD_CONSTR:
+      pp_cxx_fold_expanded_constraint (pp, t);
+      break;
+
     case EXPR_PACK_EXPANSION:
       pp->expression (TREE_OPERAND (t, 0));
       break;
--- gcc/cp/pt.cc.jj     2024-07-29 09:15:52.818929226 +0200
+++ gcc/cp/pt.cc        2024-07-29 13:24:52.831379738 +0200
@@ -1763,8 +1763,17 @@ iterative_hash_template_arg (tree arg, h
       /* Getting here with an ARGUMENT_PACK_SELECT means we're probably
         preserving it in a hash table, which is bad because it will change
         meaning when gen_elem_of_pack_expansion_instantiation changes the
-        ARGUMENT_PACK_SELECT_INDEX.  */
-      gcc_unreachable ();
+        ARGUMENT_PACK_SELECT_INDEX.  The exception is immutable
+        ARGUMENT_PACK_SELECTs created by constraint.cc (satisfy_fold).  */
+      gcc_assert (ARGUMENT_PACK_SELECT_IMMUTABLE_P (arg));
+      val = iterative_hash_template_arg (ARGUMENT_PACK_SELECT_FROM_PACK (arg),
+                                        val);
+      if (sizeof (ARGUMENT_PACK_SELECT_INDEX (arg)) <= sizeof (hashval_t))
+       return iterative_hash_hashval_t (ARGUMENT_PACK_SELECT_INDEX (arg),
+                                        val);
+      else
+       return iterative_hash_host_wide_int (ARGUMENT_PACK_SELECT_INDEX (arg),
+                                            val);
 
     case ERROR_MARK:
       return val;
@@ -9464,7 +9473,14 @@ template_args_equal (tree ot, tree nt)
   else if (ARGUMENT_PACK_P (ot) || ARGUMENT_PACK_P (nt))
     return cp_tree_equal (ot, nt);
   else if (TREE_CODE (ot) == ARGUMENT_PACK_SELECT)
-    gcc_unreachable ();
+    {
+      gcc_assert (ARGUMENT_PACK_SELECT_IMMUTABLE_P (ot)
+                 && ARGUMENT_PACK_SELECT_IMMUTABLE_P (nt));
+      return (ARGUMENT_PACK_SELECT_INDEX (ot)
+             == ARGUMENT_PACK_SELECT_INDEX (nt)
+             && template_args_equal (ARGUMENT_PACK_SELECT_FROM_PACK (ot),
+                                     ARGUMENT_PACK_SELECT_FROM_PACK (nt)));
+    }
   else if (TYPE_P (nt) || TYPE_P (ot))
     {
       if (!(TYPE_P (nt) && TYPE_P (ot)))
--- gcc/testsuite/g++.dg/concepts/diagnostic3.C.jj      2024-07-29 
09:15:52.989926988 +0200
+++ gcc/testsuite/g++.dg/concepts/diagnostic3.C 2024-07-29 13:24:52.872379199 
+0200
@@ -1,4 +1,4 @@
-// { dg-do compile { target c++2a } }
+// { dg-do compile { target c++20 } }
 
 template<typename T>
   inline constexpr bool foo_v = false;
@@ -7,7 +7,7 @@ template<typename T>
   concept foo = (bool)(foo_v<T> | foo_v<T&>);
 
 template<typename... Ts>
-requires (foo<Ts> && ...) // { dg-message "19:with Ts = .int, char... 
evaluated to .false." }
+requires (foo<Ts> && ...) // { dg-message "19:with Ts = .int, char... 
evaluated to .false." "" { target c++23_down } }
 void
 bar()
 { }
@@ -16,7 +16,7 @@ template<int>
 struct S { };
 
 template<int... Is>
-requires (foo<S<Is>> && ...) // { dg-message "22:with Is = .2, 3, 4... 
evaluated to .false." }
+requires (foo<S<Is>> && ...) // { dg-message "22:with Is = .2, 3, 4... 
evaluated to .false." "" { target c++23_down } }
 void
 baz()
 { }
--- gcc/testsuite/g++.dg/concepts/variadic2.C.jj        2024-07-29 
09:15:53.021926569 +0200
+++ gcc/testsuite/g++.dg/concepts/variadic2.C   2024-07-29 13:24:52.861379343 
+0200
@@ -13,6 +13,7 @@ constexpr int f(Ts...) { return 1; }
 
 int main()
 {
-  static_assert(f(42) == 1); // { dg-error "ambiguous" }
-  // The associated constraints of the two functions are incomparable.
+  static_assert(f(42) == 1); // { dg-error "ambiguous" "" { target c++23_down 
} }
+  // The associated constraints of the two functions are incomparable before
+  // C++26.
 }
--- gcc/testsuite/g++.dg/concepts/variadic4.C.jj        2024-07-29 
09:15:53.056926111 +0200
+++ gcc/testsuite/g++.dg/concepts/variadic4.C   2024-07-29 13:24:52.850379488 
+0200
@@ -12,9 +12,9 @@ struct zip;
 
 template<Sequence... Seqs>
     requires requires { typename list<Seqs...>; } // && (Sequence<Seqs> && ...)
-struct zip<Seqs...> {}; // { dg-error "does not specialize" }
+struct zip<Seqs...> {}; // { dg-error "does not specialize" "" { target 
c++23_down } }
 // The constraints of the specialization and the sequence are not
-// comparable; the specializations are unordered.
+// comparable before C++26; the specializations are unordered.
 
 int main()
 {
--- gcc/testsuite/g++.dg/cpp2a/concepts-requires33.C.jj 2024-07-29 
09:15:53.091925654 +0200
+++ gcc/testsuite/g++.dg/cpp2a/concepts-requires33.C    2024-07-29 
13:24:52.872379199 +0200
@@ -2,7 +2,7 @@
 // { dg-do compile { target c++20 } }
 
 template<class... T>
-void f() requires (requires (T x) { true; } && ...);
+void f() requires (requires (T x) { true; } && ...); // { dg-error "invalid 
parameter type 'void'" "" { target c++26 } }
 
 int main() {
   f<int>();
--- gcc/testsuite/g++.dg/cpp26/fold-constr1.C.jj        2024-07-29 
13:24:52.835379685 +0200
+++ gcc/testsuite/g++.dg/cpp26/fold-constr1.C   2024-07-29 13:24:52.835379685 
+0200
@@ -0,0 +1,37 @@
+// P2963R3 - Ordering of constraints involving fold expressions
+// { dg-do compile { target c++20 } }
+
+template <class U> concept isint = __is_same (U, int);
+
+template <class... V> requires (isint<V> && ...)
+constexpr int foo (V...) { return 1; };
+
+template <class... U> requires (... || isint<U>)
+constexpr int bar (U...) { return 1; };
+
+template <class T, class... S> requires (isint<S> && ... && isint<T>)
+constexpr int baz (T, S...) { return 1; }
+
+template <class T, class... R> requires (isint<T> || ... || isint<R>)
+constexpr int qux (T, R...) { return 1; }
+
+int v1 = foo ();
+int v2 = bar ();                       // { dg-error "no matching function for 
call to" }
+int v3 = foo (1, 2);
+int v4 = bar (1, 2);
+int v5 = foo (1L, 2);                  // { dg-error "no matching function for 
call to" }
+int v6 = foo (1, 2L);                  // { dg-error "no matching function for 
call to" }
+int v7 = bar (1L, 2);
+int v8 = bar (2L, 3.0, 4, 5.0);
+int v9 = bar (2LL, 3.0f, 5.0, 6ULL, 2U);// { dg-error "no matching function 
for call to" }
+int v10 = baz ();                      // { dg-error "no matching function for 
call to" }
+int v11 = baz (1);
+int v12 = baz (1L);                    // { dg-error "no matching function for 
call to" }
+int v13 = baz (1, 2, 3, 4, 5);
+int v14 = baz (1, 2, 3L, 4, 5);                // { dg-error "no matching 
function for call to" }
+int v15 = qux ();                      // { dg-error "no matching function for 
call to" }
+int v16 = qux (1);
+int v17 = qux (1L);                    // { dg-error "no matching function for 
call to" }
+int v18 = qux (1, 2.0, 3LL);
+int v19 = qux (1L, 2.0f, 3, 4ULL);
+int v20 = qux (0.0f, 1L, 2.0, 3L);     // { dg-error "no matching function for 
call to" }
--- gcc/testsuite/g++.dg/cpp26/fold-constr2.C.jj        2024-07-29 
13:24:52.835379685 +0200
+++ gcc/testsuite/g++.dg/cpp26/fold-constr2.C   2024-07-31 09:11:03.129942996 
+0200
@@ -0,0 +1,56 @@
+// P2963R3 - Ordering of constraints involving fold expressions
+// { dg-do compile { target c++20 } }
+
+template <class T> concept C1 = requires { T(1); };
+template <class T> concept C2 = C1<T> && true;
+template <class T> concept C3 = C1<T> && __is_same (T, int);
+
+template <class T> requires (C1<T>)
+constexpr bool foo (T) { return false; };
+template <class... T> requires (C2<T> && ...)
+constexpr bool foo (T...) { return true; };
+
+static_assert (!foo (0));
+static_assert (!foo (1));
+
+template <class... T> requires (C1<T> && ...)
+constexpr bool bar (T...) { return false; };
+template <class... T> requires (C2<T> && ...)
+constexpr bool bar (T...) { return true; };
+
+static_assert (bar (0));               // { dg-error "call of overloaded 
'bar\\\(int\\\)' is ambiguous" "" { target c++23_down } }
+static_assert (bar ());                        // { dg-error "call of 
overloaded 'bar\\\(\\\)' is ambiguous" "" { target c++23_down } }
+static_assert (bar (1, 2));            // { dg-error "call of overloaded 
'bar\\\(int, int\\\)' is ambiguous" "" { target c++23_down } }
+
+template <class... T> requires (C1<T> && ...)
+constexpr bool baz (T...) { return false; };
+template <class... T> requires (... && (C1<T> && true))
+constexpr bool baz (T...) { return true; };
+
+static_assert (baz (0));               // { dg-error "call of overloaded 
'baz\\\(int\\\)' is ambiguous" "" { target c++23_down } }
+static_assert (baz ());                        // { dg-error "call of 
overloaded 'baz\\\(\\\)' is ambiguous" "" { target c++23_down } }
+static_assert (baz (1, 2));            // { dg-error "call of overloaded 
'baz\\\(int, int\\\)' is ambiguous" "" { target c++23_down } }
+
+template <typename... T> requires (C1<T> || ... || true)
+constexpr bool qux (T...) { return false; };
+template <typename... T> requires (C2<T> && ... && true)
+constexpr bool qux (T...) { return true; };
+
+static_assert (qux (0));               // { dg-error "call of overloaded 
'qux\\\(int\\\)' is ambiguous" "" { target c++23_down } }
+static_assert (qux ());                        // { dg-error "call of 
overloaded 'qux\\\(\\\)' is ambiguous" "" { target c++23_down } }
+
+constexpr bool quux (C1 auto...) { return false; }
+constexpr bool quux (C3 auto...) { return true; }
+
+static_assert (quux ());               // { dg-error "call of overloaded 
'quux\\\(\\\)' is ambiguous" "" { target c++23_down } }
+static_assert (quux (0, 0));           // { dg-error "call of overloaded 
'quux\\\(int, int\\\)' is ambiguous" "" { target c++23_down } }
+static_assert (!quux (0L, 0));
+
+template <C1... T>
+constexpr bool corge (C1 auto...) { return false; }
+template <C3... T>
+constexpr bool corge (C3 auto...) { return true; }
+
+static_assert (corge ());              // { dg-error "call of overloaded 
'corge\\\(\\\)' is ambiguous" "" { target c++23_down } }
+static_assert (corge (0, 0));          // { dg-error "call of overloaded 
'corge\\\(int, int\\\)' is ambiguous" "" { target c++23_down } }
+static_assert (!corge (0L, 0));
--- gcc/testsuite/g++.dg/cpp26/fold-constr3.C.jj        2024-07-29 
13:24:52.835379685 +0200
+++ gcc/testsuite/g++.dg/cpp26/fold-constr3.C   2024-07-29 13:24:52.835379685 
+0200
@@ -0,0 +1,15 @@
+// P2963R3 - Ordering of constraints involving fold expressions
+// { dg-do compile { target c++20 } }
+
+template <typename ...V> struct A;
+struct Thingy {
+  static constexpr int compare (const Thingy &) { return 1; }
+};
+template <typename ...T, typename ...U>
+void f (A<T ...> *, A<U ...> *)
+requires (T::compare (U{}) && ...);    // { dg-error "has type 'int', not 
'bool'" "" { target c++26 } }
+void
+g (A<Thingy, Thingy> *ap)
+{
+  f (ap, ap);                          // { dg-error "no matching function for 
call to" "" { target c++26 } }
+}
--- gcc/testsuite/g++.dg/cpp26/fold-constr4.C.jj        2024-07-29 
13:24:52.835379685 +0200
+++ gcc/testsuite/g++.dg/cpp26/fold-constr4.C   2024-07-31 09:11:58.544195765 
+0200
@@ -0,0 +1,48 @@
+// P2963R3 - Ordering of constraints involving fold expressions
+// { dg-do compile { target c++20 } }
+
+template <class T> concept C1 = requires { T(1); };
+template <class T> concept C2 = C1<T> && true;
+
+template <class... T> requires (C1<T> && ...)
+constexpr bool foo (T...) { return false; };
+template <class... T> requires (C2<T> || ...)
+constexpr bool foo (T...) { return true; };
+
+static_assert (foo (0));       // { dg-error "call of overloaded 
'foo\\\(int\\\)' is ambiguous" }
+
+template <class... T> requires (C1<T> || ...)
+constexpr bool bar (T...) { return false; };
+template <class... T> requires (C2<T> && ...)
+constexpr bool bar (T...) { return true; };
+
+static_assert (bar (0));       // { dg-error "call of overloaded 
'bar\\\(int\\\)' is ambiguous" }
+
+template <class... T> requires (C1<T> || ...)
+constexpr bool baz (T...) { return false; };
+template <class... T> requires (C2<T> || ...)
+constexpr bool baz (T...) { return true; };
+
+static_assert (baz (0));       // { dg-error "call of overloaded 
'baz\\\(int\\\)' is ambiguous" "" { target c++23_down } }
+static_assert (baz ());                // { dg-error "no matching function for 
call to 'baz\\\(\\\)'" }
+static_assert (baz (1, 2));    // { dg-error "call of overloaded 'baz\\\(int, 
int\\\)' is ambiguous" "" { target c++23_down } }
+
+template <class... T>
+struct U {
+  template <class... V> requires (... && C1<V>)
+  static constexpr bool foo () { return false; }
+  template <class... V> requires (... && C2<V>)
+  static constexpr bool foo () { return true; }
+  template <class... V> requires (... && C1<T>)
+  static constexpr bool bar () { return false; }
+  template <class... V> requires (... && C2<T>)
+  static constexpr bool bar () { return true; }
+  template <class... V> requires (... && C1<V>)
+  static constexpr bool baz () { return false; }
+  template <class... V> requires (... && C2<T>)
+  static constexpr bool baz () { return true; }
+};
+
+static_assert (U<int>::foo<int> ());   // { dg-error "call of overloaded 
'foo\\\(\\\)' is ambiguous" "" { target c++23_down } }
+static_assert (U<int>::bar<int> ());   // { dg-error "call of overloaded 
'bar\\\(\\\)' is ambiguous" "" { target c++23_down } }
+static_assert (U<int>::baz<int> ());   // { dg-error "call of overloaded 
'baz\\\(\\\)' is ambiguous" }
--- gcc/testsuite/g++.dg/cpp26/fold-constr5.C.jj        2024-07-29 
13:24:52.834379698 +0200
+++ gcc/testsuite/g++.dg/cpp26/fold-constr5.C   2024-07-29 13:24:52.834379698 
+0200
@@ -0,0 +1,77 @@
+// P2963R3 - Ordering of constraints involving fold expressions
+// { dg-do compile { target c++20 } }
+
+struct A {
+  using type = int;
+};
+struct B {
+  using type = long;
+};
+
+template <class T> concept C = sizeof (T) < sizeof (int) * 64;
+
+template <class... T> requires (C<typename T::type> && ...)    // { dg-error 
"is not a class, struct, or union type" "" { target c++23_down } }
+constexpr bool foo () { return true; };
+
+static_assert (foo <> ());
+static_assert (foo <A> ());
+static_assert (foo <B, A, A, B> ());
+static_assert (foo <int> ());                                  // { dg-error 
"no matching function for call" }
+static_assert (foo <B, long> ());                              // { dg-error 
"no matching function for call" }
+static_assert (foo <unsigned, A, A> ());                       // { dg-error 
"no matching function for call" }
+// { dg-error "is not a class, struct, or union type" "" { target c++26 } .-3 }
+// { dg-error "is not a class, struct, or union type" "" { target c++26 } .-3 }
+// { dg-error "is not a class, struct, or union type" "" { target c++26 } .-3 }
+
+template <class T, class... U> requires (C<typename U::type> && ... && 
C<typename T::type>)    // { dg-error "is not a class, struct, or union type" 
"" { target c++23_down } }
+constexpr bool bar () { return true; };
+
+static_assert (bar <A> ());
+static_assert (bar <B, A, A, B> ());
+static_assert (bar <int> ());                                  // { dg-error 
"no matching function for call" }
+static_assert (bar <B, long> ());                              // { dg-error 
"no matching function for call" }
+static_assert (bar <unsigned, A, A> ());                       // { dg-error 
"no matching function for call" }
+// { dg-error "is not a class, struct, or union type" "" { target c++26 } .-3 }
+// { dg-error "is not a class, struct, or union type" "" { target c++26 } .-3 }
+// { dg-error "is not a class, struct, or union type" "" { target c++26 } .-3 }
+
+template <class T, class... U> requires (C<typename T::type> && ... && 
C<typename U::type>)    // { dg-error "is not a class, struct, or union type" 
"" { target c++23_down } }
+constexpr bool baz () { return true; };
+
+static_assert (baz <A> ());
+static_assert (baz <B, A, A, B> ());
+static_assert (baz <int> ());                                  // { dg-error 
"no matching function for call" }
+static_assert (baz <B, long> ());                              // { dg-error 
"no matching function for call" }
+static_assert (baz <unsigned, A, A> ());                       // { dg-error 
"no matching function for call" }
+// { dg-error "is not a class, struct, or union type" "" { target c++26 } .-3 }
+// { dg-error "is not a class, struct, or union type" "" { target c++26 } .-3 }
+// { dg-error "is not a class, struct, or union type" "" { target c++26 } .-3 }
+
+template <class... T> requires (C<typename T::type> || ...)    // { dg-error 
"is not a class, struct, or union type" "" { target c++23_down } }
+constexpr bool qux () { return true; };
+
+static_assert (qux <> ());                                     // { dg-error 
"no matching function for call" }
+static_assert (qux <A> ());
+static_assert (qux <B, A, A, B> ());
+static_assert (qux <int> ());                                  // { dg-error 
"no matching function for call" }
+static_assert (qux <B, long> ());                              // { dg-error 
"no matching function for call" "" { target c++23_down } }
+static_assert (qux <unsigned, A, A> ());                       // { dg-error 
"no matching function for call" "" { target c++23_down } }
+// { dg-error "is not a class, struct, or union type" "" { target c++26 } .-3 }
+
+template <class T, class... U> requires (C<typename U::type> || ... || 
C<typename T::type>)    // { dg-error "is not a class, struct, or union type" 
"" { target c++23_down } }
+constexpr bool corge () { return true; };
+
+static_assert (corge <A> ());
+static_assert (corge <B, A, A, B> ());
+static_assert (corge <int> ());                                        // { 
dg-error "no matching function for call" }
+static_assert (corge <B, long> ());                            // { dg-error 
"no matching function for call" "" { target c++23_down } }
+static_assert (corge <unsigned, A, A> ());                     // { dg-error 
"no matching function for call" "" { target c++23_down } }
+
+template <class T, class... U> requires (C<typename T::type> || ... || 
C<typename U::type>)    // { dg-error "is not a class, struct, or union type" 
"" { target c++23_down } }
+constexpr bool garply () { return true; };
+
+static_assert (garply <A> ());
+static_assert (garply <B, A, A, B> ());
+static_assert (garply <int> ());                               // { dg-error 
"no matching function for call" }
+static_assert (garply <B, long> ());                           // { dg-error 
"no matching function for call" "" { target c++23_down } }
+static_assert (garply <unsigned, A, A> ());                    // { dg-error 
"no matching function for call" "" { target c++23_down } }
--- gcc/testsuite/g++.dg/cpp26/fold-constr6.C.jj        2024-07-29 
13:24:52.834379698 +0200
+++ gcc/testsuite/g++.dg/cpp26/fold-constr6.C   2024-07-31 09:12:42.960596833 
+0200
@@ -0,0 +1,20 @@
+// P2963R3 - Ordering of constraints involving fold expressions
+// { dg-do compile { target c++20 } }
+
+template <class T> concept C1 = requires { T(1); };
+template <class T> concept C2 = C1<T> && true;
+
+template <class... T>
+struct U {
+  template <class... V> requires ((C1<T> && ...) && ... && C1<V>)
+  static constexpr bool foo () { return false; }
+  template <class... V> requires ((C2<T> && ...) && ... && C2<V>)
+  static constexpr bool foo () { return true; }
+  template <class... V> requires ((C1<T> && ...) && ... && C1<V>)
+  static constexpr bool bar () { return false; }
+  template <class... V> requires ((C2<V> && ...) && ... && C2<T>)
+  static constexpr bool bar () { return true; }
+};
+
+static_assert (U<int>::foo<int> ());   // { dg-error "call of overloaded 
'foo\\\(\\\)' is ambiguous" "" { target c++23_down } }
+static_assert (U<int>::bar<int> ());   // { dg-error "call of overloaded 
'bar\\\(\\\)' is ambiguous" "" { target c++23_down } }
--- gcc/testsuite/g++.dg/cpp26/fold-constr7.C.jj        2024-07-29 
13:24:52.834379698 +0200
+++ gcc/testsuite/g++.dg/cpp26/fold-constr7.C   2024-07-31 09:12:52.364470028 
+0200
@@ -0,0 +1,11 @@
+// P2963R3 - Ordering of constraints involving fold expressions
+// { dg-do compile { target c++20 } }
+
+template <class T> concept C1 = requires { T(1); };
+template <class T> concept C2 = C1<T> && true;
+
+template <class... T> requires ((((sizeof (T) + ...) < 8 * sizeof (int)) && 
C2<T>) && ...)
+constexpr bool foo (T...) { return true; };
+
+static_assert (foo (0));
+static_assert (foo (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16)); 
// { dg-error "no matching function for call" }
--- gcc/testsuite/g++.dg/cpp26/fold-constr8.C.jj        2024-07-29 
13:24:52.834379698 +0200
+++ gcc/testsuite/g++.dg/cpp26/fold-constr8.C   2024-07-29 13:24:52.834379698 
+0200
@@ -0,0 +1,22 @@
+// P2963R3 - Ordering of constraints involving fold expressions
+// { dg-do compile { target c++20 } }
+
+template <class T> concept C = __is_same (T, int);
+
+template <class ...T>
+struct A {};
+
+template <class ...T, class ...U> requires ((C<T> && C<U>) && ...)     // { 
dg-error "mismatched argument pack lengths while expanding '\\\(C<T> \\\&\\\& 
C<U>\\\)'" "" { target c++23_down } }
+constexpr bool foo (A<T...>, A<U...>) { return true; };
+// { dg-message "fold expanded constraint not satisfied because of pack length 
mismatch" "" { target c++26 } .-2 }
+// { dg-message "'U' has length 3" "" { target c++26 } .-3 }
+// { dg-message "'T' has length 2" "" { target c++26 } .-4 }
+
+static_assert (foo (A<int, int, int> {}, A<int, int, int> {}));
+static_assert (foo (A<int, int> {}, A<int, int, int> {}));     // { dg-error 
"no matching function for call to" }
+
+template <class ...T> requires (C<T> || ...)                   // { dg-message 
"fold expanded constraint not satisfied because of empty pack with '||' fold 
operator" "" { target c++26 } }
+constexpr bool bar (T...) { return true; };
+
+static_assert (bar (0));
+static_assert (bar ());                                                // { 
dg-error "no matching function for call to" }
--- gcc/testsuite/g++.dg/cpp26/fold-constr9.C.jj        2024-07-29 
13:24:52.835379685 +0200
+++ gcc/testsuite/g++.dg/cpp26/fold-constr9.C   2024-07-29 13:24:52.835379685 
+0200
@@ -0,0 +1,11 @@
+// P2963R3 - Ordering of constraints involving fold expressions
+// { dg-do compile { target c++20 } }
+
+template <class T> concept C = __is_same (T, int);
+
+template <class... T> requires ((C<T> && ...) && ... && C<T>)
+constexpr bool foo (T...) { return true; }
+
+static_assert (foo ());
+static_assert (foo (1, 2, 3));
+static_assert (foo (1L, 2L)); // { dg-error "no matching function for call" }
--- gcc/testsuite/g++.dg/cpp26/fold-constr10.C.jj       2024-07-29 
13:24:52.835379685 +0200
+++ gcc/testsuite/g++.dg/cpp26/fold-constr10.C  2024-07-29 13:24:52.835379685 
+0200
@@ -0,0 +1,67 @@
+// P2963R3 - Ordering of constraints involving fold expressions
+// { dg-do compile { target c++20 } }
+
+struct A {
+  using type = int;
+};
+struct B {
+  using type = long;
+};
+
+template <class T> concept C = true;
+
+template <class... T> requires (C<typename T::type> && ...)    // { dg-error 
"is not a class, struct, or union type" "" { target c++23_down } }
+constexpr bool foo () { return true; };
+
+static_assert (foo <> ());
+static_assert (foo <A> ());
+static_assert (foo <B, A, A, B> ());
+static_assert (foo <int> ());                                  // { dg-error 
"no matching function for call" "" { target c++23_down } }
+static_assert (foo <B, long> ());                              // { dg-error 
"no matching function for call" "" { target c++23_down } }
+static_assert (foo <unsigned, A, A> ());                       // { dg-error 
"no matching function for call" "" { target c++23_down } }
+
+template <class T, class... U> requires (C<typename U::type> && ... && 
C<typename T::type>)    // { dg-error "is not a class, struct, or union type" 
"" { target c++23_down } }
+constexpr bool bar () { return true; };
+
+static_assert (bar <A> ());
+static_assert (bar <B, A, A, B> ());
+static_assert (bar <int> ());                                  // { dg-error 
"no matching function for call" "" { target c++23_down } }
+static_assert (bar <B, long> ());                              // { dg-error 
"no matching function for call" "" { target c++23_down } }
+static_assert (bar <unsigned, A, A> ());                       // { dg-error 
"no matching function for call" "" { target c++23_down } }
+
+template <class T, class... U> requires (C<typename T::type> && ... && 
C<typename U::type>)    // { dg-error "is not a class, struct, or union type" 
"" { target c++23_down } }
+constexpr bool baz () { return true; };
+
+static_assert (baz <A> ());
+static_assert (baz <B, A, A, B> ());
+static_assert (baz <int> ());                                  // { dg-error 
"no matching function for call" "" { target c++23_down } }
+static_assert (baz <B, long> ());                              // { dg-error 
"no matching function for call" "" { target c++23_down } }
+static_assert (baz <unsigned, A, A> ());                       // { dg-error 
"no matching function for call" "" { target c++23_down } }
+
+template <class... T> requires (C<typename T::type> || ...)    // { dg-error 
"is not a class, struct, or union type" "" { target c++23_down } }
+constexpr bool qux () { return true; };
+
+static_assert (qux <> ());                                     // { dg-error 
"no matching function for call" }
+static_assert (qux <A> ());
+static_assert (qux <B, A, A, B> ());
+static_assert (qux <int> ());                                  // { dg-error 
"no matching function for call" "" { target c++23_down } }
+static_assert (qux <B, long> ());                              // { dg-error 
"no matching function for call" "" { target c++23_down } }
+static_assert (qux <unsigned, A, A> ());                       // { dg-error 
"no matching function for call" "" { target c++23_down } }
+
+template <class T, class... U> requires (C<typename U::type> || ... || 
C<typename T::type>)    // { dg-error "is not a class, struct, or union type" 
"" { target c++23_down } }
+constexpr bool corge () { return true; };
+
+static_assert (corge <A> ());
+static_assert (corge <B, A, A, B> ());
+static_assert (corge <int> ());                                        // { 
dg-error "no matching function for call" "" { target c++23_down } }
+static_assert (corge <B, long> ());                            // { dg-error 
"no matching function for call" "" { target c++23_down } }
+static_assert (corge <unsigned, A, A> ());                     // { dg-error 
"no matching function for call" "" { target c++23_down } }
+
+template <class T, class... U> requires (C<typename T::type> || ... || 
C<typename U::type>)    // { dg-error "is not a class, struct, or union type" 
"" { target c++23_down } }
+constexpr bool garply () { return true; };
+
+static_assert (garply <A> ());
+static_assert (garply <B, A, A, B> ());
+static_assert (garply <int> ());                               // { dg-error 
"no matching function for call" "" { target c++23_down } }
+static_assert (garply <B, long> ());                           // { dg-error 
"no matching function for call" "" { target c++23_down } }
+static_assert (garply <unsigned, A, A> ());                    // { dg-error 
"no matching function for call" "" { target c++23_down } }


        Jakub

Reply via email to