On Fri, 26 Jul 2024, Patrick Palka wrote:

> On Fri, 26 Jul 2024, Jakub Jelinek wrote:
> 
> > Hi!
> > 
> > I've tried to implement the C++26 fold expanded constraints paper but ran
> > into issues (see below).  Would appreciate some guidance/help, I'm afraid
> > I'm stuck.
> > 
> > The patch introduces a FOLD_CONSTR tree to represent fold expanded
> > constraints, normalizes for C++26 some {U,BI}NARY_{LEFT,RIGHT}_FOLD_EXPR
> > into those (+ CONJ_CONSTR or DISJ_CONSTR for the binary ones), attempts
> > to handle their satisfaction (that is the unresolved issue) and handle it
> > in the subsumption checking code.
> > 
> > Most of the newly added tests pass, compared to what clang++ trunk (which
> > claims to implement this paper) there are some differences:
> > static_assert (bar <int> ()); in cpp26/fold-constr5.C is accepted
> > by clang in C++26 mode and rejected by GCC.  I believe GCC is right,
> > C<typename U::type> && ... && C<typename T::type>
> > where U is an empty pack should be normalized to
> > (C<typename U::type> /\ ...) /\ C<typename T::type>
> > so I think (C<typename U::type> /\ ...) is satisfied but
> > C<typename T::type> should not as int::type is invalid, so the overall
> > result should be not satisfied.
> > Another difference is
> > static_assert (U<int>::bar<int> ()); in cpp26/fold-constr6.C, which is
> > rejected by clang in C++26 mode and accepted by GCC.
> > This is for
> >   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; }
> > where both T and V are packs, I believe the first should be normalized to
> > (C1<T> /\ ...) && (C1<V> /\ ...)
> > and the second to
> > (C2<V> /\ ...) && (C2<T> /\ ...)
> > where C2<int> subsumes C1<int>.  clang++ accepts when it is written in
> > the other order, so when normalized to
> > (C2<T> /\ ...) && (C2<V> /\ ...)
> > and in that case GCC and clang agree that the latter subsumes the former.
> > C2<V> /\ ... subsumes C1<V> /\ ...
> > and
> > C2<T> /\ ... subsumes C1<T> /\ ...
> > and
> > C2<T> /\ ... does not subsume C1<V> /\ ...
> > and
> > C2<V> /\ ... does not subsume C1<T> /\ ...
> > but overall I believe in either order the result is subsumes.
> > Another difference is in the cpp26/fold-constr10.C testcase, which is
> > essentially PR116106 and Patrick claims that unused template parameters in
> > the mapping aren't substituted into.
> > Also, the new code affects not just explicitly written fold expressions
> > in the constraints, but also implicitly created ones (say for
> > template <Concept... T> and similar), but here the GCC patch agrees with
> > clang trunk.
> > 
> > Anyway, the way I've attempted to implement satisfy_fold is through updating
> > the args vector or vectors (COW) to change parameter pack into parameter
> > pack's element which is currently checked for satisfaction.
> > That works most of the time, when all the occurrences of the template
> > parameter(s) recorded in PACK_EXPANSION_PARAMETER_PACKS (FOLD_EXPR_PACK (t))
> > of the original *_FOLD_EXPR are to be replaced by the pack element, which
> > should be fine for any such occurences unless they appear in another
> > expansion pack with the same template parameter.
> > The instantiation of those nested {TYPE,EXPR}_PACK_EXPANSION need to be able
> > to access the original whole pack rather than just one of its elements.
> > And some atomic constraints e.g. can refer to both; I've tried to
> > write this in cpp26/fold-constr7.C testcase (which is the only one
> > from newly added that FAILs with C++26):
> > template <class... T> requires ((((sizeof (T) + ...) < 8 * sizeof (int)) && 
> > C2<T>) && ...)
> > constexpr bool foo (T...) { return true; };
> > here actually the ((sizeof (T) + ...) < 8 * sizeof (int)) atomic constraint
> > only needs the whole T pack and C2<T> atomic constraint only needs the T's
> > pack element, but guess it can be also changed so that one atomic constraint
> > needs both whole pack and pack element.
> > Do you have some suggestions on how to handle this instead?
> 
> IIUC the way gen_elem_of_pack_expansion_instantiation handles this for
> ordinary pack expnasions is by replacing each ARGUMENT_PACK with an
> ARGUMENT_PACK_SELECT.  This ARGUMENT_PACK_SELECT contains the entire
> pack as well as the current index into the pack, and it essentially
> acts as an overloaded template argument that's treated either as a
> single pack element or as the entire pack itself as needed.

Ah but by using ARGUMENT_PACK_SELECT I bet you'll run into an
iterative_hash_template_arg ICE when the satisfaction cache tries to
hash it.

IMHO we should allow hashing/comparing ARGUMENT_PACK_SELECT, at least
"immutable" ones whose index isn't expected to change from under us,
accordin to some flag maybe.  (the ARGUMENT_PACK_SELECTs created from
gen_elem_of_pack_expansion_instantiation are reused/modified across
iterations so those wouldn't be safe to store in a hash table.)

> 
> On that note I wonder if there's any opportunity for code reuse from
> gen_elem_of_pack_expansion_instantiation / tsubst_pack_expansion?
> 
> > Besides
> > +FAIL: g++.dg/cpp26/fold-constr7.C  -std=c++26 (test for excess errors)
> > I spoke about there are some regressions on existing tests:
> > +FAIL: g++.dg/cpp2a/concepts-fn3.C  -std=c++26  (test for errors, line 36)
> > +FAIL: g++.dg/cpp2a/concepts-fn3.C  -std=c++26  (test for errors, line 38)
> > +FAIL: g++.dg/cpp2a/concepts-fn3.C  -std=c++26  (test for errors, line 43)
> > +FAIL: g++.dg/cpp2a/concepts-fn3.C  -std=c++26  (test for errors, line 45)
> > +FAIL: g++.dg/cpp2a/concepts-fn3.C  -std=c++26  (test for errors, line 47)
> > +FAIL: g++.dg/cpp2a/concepts-fn3.C  -std=c++26 (internal compiler error: 
> > tree check: expected type_argument_pack or nontype_argument_pack, have 
> > integer_type in satisfy_fold, at cp/constraint.cc:2940)
> > +FAIL: g++.dg/cpp2a/concepts-fn3.C  -std=c++26 (test for excess errors)
> > +FAIL: g++.dg/cpp2a/concepts-pr67860.C  -std=c++26 (internal compiler 
> > error: tree check: expected type_argument_pack or nontype_argument_pack, 
> > have integer_type in satisfy_fold, at cp/constraint.cc:2940)
> > +FAIL: g++.dg/cpp2a/concepts-pr67860.C  -std=c++26 (test for excess errors)
> > +FAIL: g++.dg/warn/Wdangling-reference17.C  -std=gnu++26 (internal compiler 
> > error: in keep_template_parm, at cp/pt.cc:10975)
> > +FAIL: g++.dg/warn/Wdangling-reference17.C  -std=gnu++26 (test for excess 
> > errors)
> > there concepts-fn3.C, concepts-pr67860.C are one ICE clearly caused by the
> > problem with the need to use whole pack inside of something that needs to
> > use just pack element inside of an fold expanded constraint,
> > Wdangling-reference17.C is an ICE that repeats then in
> > FAIL: 24_iterators/const_iterator/1.cc  -std=gnu++26 (test for excess 
> > errors)
> > UNRESOLVED: 24_iterators/const_iterator/1.cc  -std=gnu++26 compilation 
> > failed to produce executable
> > FAIL: 24_iterators/move_iterator/lwg3391.cc  -std=gnu++26 (test for excess 
> > errors)
> > FAIL: 25_algorithms/fold_left/1.cc  -std=gnu++26 (test for excess errors)
> > UNRESOLVED: 25_algorithms/fold_left/1.cc  -std=gnu++26 compilation failed 
> > to produce executable
> > FAIL: 25_algorithms/fold_right/1.cc  -std=gnu++26 (test for excess errors)
> > UNRESOLVED: 25_algorithms/fold_right/1.cc  -std=gnu++26 compilation failed 
> > to produce executable
> > FAIL: std/ranges/adaptors/100479.cc  -std=gnu++26 (test for excess errors)
> > UNRESOLVED: std/ranges/adaptors/100479.cc  -std=gnu++26 compilation failed 
> > to produce executable
> > FAIL: std/ranges/adaptors/100577.cc  -std=gnu++26  (test for errors, line 
> > 105)
> > FAIL: std/ranges/adaptors/100577.cc  -std=gnu++26  (test for errors, line 
> > 106)
> > FAIL: std/ranges/adaptors/100577.cc  -std=gnu++26  (test for errors, line 
> > 108)
> > FAIL: std/ranges/adaptors/100577.cc  -std=gnu++26  (test for errors, line 
> > 110)
> > FAIL: std/ranges/adaptors/100577.cc  -std=gnu++26  (test for errors, line 
> > 112)
> > FAIL: std/ranges/adaptors/100577.cc  -std=gnu++26  (test for errors, line 
> > 113)
> > FAIL: std/ranges/adaptors/100577.cc  -std=gnu++26  (test for errors, line 
> > 115)
> > FAIL: std/ranges/adaptors/100577.cc  -std=gnu++26  (test for errors, line 
> > 117)
> > FAIL: std/ranges/adaptors/100577.cc  -std=gnu++26  (test for errors, line 
> > 119)
> > FAIL: std/ranges/adaptors/100577.cc  -std=gnu++26  (test for errors, line 
> > 120)
> > FAIL: std/ranges/adaptors/100577.cc  -std=gnu++26  (test for errors, line 
> > 121)
> > FAIL: std/ranges/adaptors/100577.cc  -std=gnu++26  (test for errors, line 
> > 122)
> > FAIL: std/ranges/adaptors/100577.cc  -std=gnu++26  (test for errors, line 
> > 91)
> > FAIL: std/ranges/adaptors/100577.cc  -std=gnu++26  (test for errors, line 
> > 92)
> > FAIL: std/ranges/adaptors/100577.cc  -std=gnu++26  (test for errors, line 
> > 93)
> > FAIL: std/ranges/adaptors/100577.cc  -std=gnu++26  (test for errors, line 
> > 94)
> > FAIL: std/ranges/adaptors/100577.cc  -std=gnu++26  (test for errors, line 
> > 96)
> > FAIL: std/ranges/adaptors/100577.cc  -std=gnu++26  (test for errors, line 
> > 97)
> > FAIL: std/ranges/adaptors/100577.cc  -std=gnu++26  (test for errors, line 
> > 98)
> > FAIL: std/ranges/adaptors/100577.cc  -std=gnu++26  (test for errors, line 
> > 99)
> > FAIL: std/ranges/adaptors/100577.cc  -std=gnu++26 (test for excess errors)
> > FAIL: std/ranges/adaptors/116038.cc  -std=gnu++26 (test for excess errors)
> > FAIL: std/ranges/adaptors/95322.cc  -std=gnu++26 (test for excess errors)
> > UNRESOLVED: std/ranges/adaptors/95322.cc  -std=gnu++26 compilation failed 
> > to produce executable
> > FAIL: std/ranges/adaptors/99433.cc  -std=gnu++26 (test for excess errors)
> > FAIL: std/ranges/adaptors/adjacent/1.cc  -std=gnu++26 (test for excess 
> > errors)
> > UNRESOLVED: std/ranges/adaptors/adjacent/1.cc  -std=gnu++26 compilation 
> > failed to produce executable
> > FAIL: std/ranges/adaptors/adjacent_transform/1.cc  -std=gnu++26 (test for 
> > excess errors)
> > UNRESOLVED: std/ranges/adaptors/adjacent_transform/1.cc  -std=gnu++26 
> > compilation failed to produce executable
> > FAIL: std/ranges/adaptors/all.cc  -std=gnu++26 (test for excess errors)
> > UNRESOLVED: std/ranges/adaptors/all.cc  -std=gnu++26 compilation failed to 
> > produce executable
> > FAIL: std/ranges/adaptors/as_const/1.cc  -std=gnu++26 (test for excess 
> > errors)
> > UNRESOLVED: std/ranges/adaptors/as_const/1.cc  -std=gnu++26 compilation 
> > failed to produce executable
> > FAIL: std/ranges/adaptors/as_rvalue/1.cc  -std=gnu++26 (test for excess 
> > errors)
> > UNRESOLVED: std/ranges/adaptors/as_rvalue/1.cc  -std=gnu++26 compilation 
> > failed to produce executable
> > FAIL: std/ranges/adaptors/chunk/1.cc  -std=gnu++26 (test for excess errors)
> > UNRESOLVED: std/ranges/adaptors/chunk/1.cc  -std=gnu++26 compilation failed 
> > to produce executable
> > FAIL: std/ranges/adaptors/chunk_by/1.cc  -std=gnu++26 (test for excess 
> > errors)
> > UNRESOLVED: std/ranges/adaptors/chunk_by/1.cc  -std=gnu++26 compilation 
> > failed to produce executable
> > FAIL: std/ranges/adaptors/conditionally_borrowed.cc  -std=gnu++26 (test for 
> > excess errors)
> > UNRESOLVED: std/ranges/adaptors/conditionally_borrowed.cc  -std=gnu++26 
> > compilation failed to produce executable
> > FAIL: std/ranges/adaptors/drop.cc  -std=gnu++26 (test for excess errors)
> > UNRESOLVED: std/ranges/adaptors/drop.cc  -std=gnu++26 compilation failed to 
> > produce executable
> > FAIL: std/ranges/adaptors/drop_while.cc  -std=gnu++26 (test for excess 
> > errors)
> > UNRESOLVED: std/ranges/adaptors/drop_while.cc  -std=gnu++26 compilation 
> > failed to produce executable
> > FAIL: std/ranges/adaptors/elements.cc  -std=gnu++26 (test for excess errors)
> > UNRESOLVED: std/ranges/adaptors/elements.cc  -std=gnu++26 compilation 
> > failed to produce executable
> > FAIL: std/ranges/adaptors/filter.cc  -std=gnu++26 (test for excess errors)
> > UNRESOLVED: std/ranges/adaptors/filter.cc  -std=gnu++26 compilation failed 
> > to produce executable
> > FAIL: std/ranges/adaptors/join.cc  -std=gnu++26 (test for excess errors)
> > UNRESOLVED: std/ranges/adaptors/join.cc  -std=gnu++26 compilation failed to 
> > produce executable
> > FAIL: std/ranges/adaptors/join_with/1.cc  -std=gnu++26 (test for excess 
> > errors)
> > UNRESOLVED: std/ranges/adaptors/join_with/1.cc  -std=gnu++26 compilation 
> > failed to produce executable
> > FAIL: std/ranges/adaptors/lazy_split.cc  -std=gnu++26 (test for excess 
> > errors)
> > UNRESOLVED: std/ranges/adaptors/lazy_split.cc  -std=gnu++26 compilation 
> > failed to produce executable
> > FAIL: std/ranges/adaptors/lazy_split_neg.cc  -std=gnu++26  (test for 
> > errors, line 33)
> > FAIL: std/ranges/adaptors/lazy_split_neg.cc  -std=gnu++26  (test for 
> > errors, line 41)
> > FAIL: std/ranges/adaptors/lazy_split_neg.cc  -std=gnu++26  (test for 
> > errors, line 42)
> > FAIL: std/ranges/adaptors/lazy_split_neg.cc  -std=gnu++26 (test for excess 
> > errors)
> > FAIL: std/ranges/adaptors/lwg3286.cc  -std=gnu++26 (test for excess errors)
> > UNRESOLVED: std/ranges/adaptors/lwg3286.cc  -std=gnu++26 compilation failed 
> > to produce executable
> > FAIL: std/ranges/adaptors/lwg3715.cc  -std=gnu++26 (test for excess errors)
> > UNRESOLVED: std/ranges/adaptors/lwg3715.cc  -std=gnu++26 compilation failed 
> > to produce executable
> > FAIL: std/ranges/adaptors/p1739.cc  -std=gnu++26 (test for excess errors)
> > UNRESOLVED: std/ranges/adaptors/p1739.cc  -std=gnu++26 compilation failed 
> > to produce executable
> > FAIL: std/ranges/adaptors/p2281.cc  -std=gnu++26 (test for excess errors)
> > UNRESOLVED: std/ranges/adaptors/p2281.cc  -std=gnu++26 compilation failed 
> > to produce executable
> > FAIL: std/ranges/adaptors/p2770r0.cc  -std=gnu++26 (test for excess errors)
> > UNRESOLVED: std/ranges/adaptors/p2770r0.cc  -std=gnu++26 compilation failed 
> > to produce executable
> > FAIL: std/ranges/adaptors/reverse.cc  -std=gnu++26 (test for excess errors)
> > UNRESOLVED: std/ranges/adaptors/reverse.cc  -std=gnu++26 compilation failed 
> > to produce executable
> > FAIL: std/ranges/adaptors/slide/1.cc  -std=gnu++26 (test for excess errors)
> > UNRESOLVED: std/ranges/adaptors/slide/1.cc  -std=gnu++26 compilation failed 
> > to produce executable
> > FAIL: std/ranges/adaptors/split.cc  -std=gnu++26 (test for excess errors)
> > UNRESOLVED: std/ranges/adaptors/split.cc  -std=gnu++26 compilation failed 
> > to produce executable
> > FAIL: std/ranges/adaptors/stride/1.cc  -std=gnu++26 (test for excess errors)
> > UNRESOLVED: std/ranges/adaptors/stride/1.cc  -std=gnu++26 compilation 
> > failed to produce executable
> > FAIL: std/ranges/adaptors/take.cc  -std=gnu++26 (test for excess errors)
> > UNRESOLVED: std/ranges/adaptors/take.cc  -std=gnu++26 compilation failed to 
> > produce executable
> > FAIL: std/ranges/adaptors/take_while.cc  -std=gnu++26 (test for excess 
> > errors)
> > UNRESOLVED: std/ranges/adaptors/take_while.cc  -std=gnu++26 compilation 
> > failed to produce executable
> > FAIL: std/ranges/adaptors/transform.cc  -std=gnu++26 (test for excess 
> > errors)
> > UNRESOLVED: std/ranges/adaptors/transform.cc  -std=gnu++26 compilation 
> > failed to produce executable
> > FAIL: std/ranges/cartesian_product/1.cc  -std=gnu++26 (test for excess 
> > errors)
> > UNRESOLVED: std/ranges/cartesian_product/1.cc  -std=gnu++26 compilation 
> > failed to produce executable
> > FAIL: std/ranges/concat/1.cc  -std=gnu++26 (test for excess errors)
> > UNRESOLVED: std/ranges/concat/1.cc  -std=gnu++26 compilation failed to 
> > produce executable
> > FAIL: std/ranges/conv/1.cc  -std=gnu++26  (test for warnings, line 434)
> > FAIL: std/ranges/conv/1.cc  -std=gnu++26  (test for warnings, line 435)
> > FAIL: std/ranges/conv/1.cc  -std=gnu++26  (test for warnings, line 436)
> > FAIL: std/ranges/conv/1.cc  -std=gnu++26  (test for warnings, line 437)
> > FAIL: std/ranges/conv/1.cc  -std=gnu++26 (test for excess errors)
> > UNRESOLVED: std/ranges/conv/1.cc  -std=gnu++26 compilation failed to 
> > produce executable
> > FAIL: std/ranges/istream_view.cc  -std=gnu++26 (test for excess errors)
> > UNRESOLVED: std/ranges/istream_view.cc  -std=gnu++26 compilation failed to 
> > produce executable
> > FAIL: std/ranges/p2259.cc  -std=gnu++26 (test for excess errors)
> > FAIL: std/ranges/p2325.cc  -std=gnu++26 (test for excess errors)
> > FAIL: std/ranges/p2367.cc  -std=gnu++26 (test for excess errors)
> > FAIL: std/ranges/range_adaptor_closure.cc  -std=gnu++26 (test for excess 
> > errors)
> > UNRESOLVED: std/ranges/range_adaptor_closure.cc  -std=gnu++26 compilation 
> > failed to produce executable
> > FAIL: std/ranges/repeat/1.cc  -std=gnu++26 (test for excess errors)
> > UNRESOLVED: std/ranges/repeat/1.cc  -std=gnu++26 compilation failed to 
> > produce executable
> > FAIL: std/ranges/zip/1.cc  -std=gnu++26 (test for excess errors)
> > UNRESOLVED: std/ranges/zip/1.cc  -std=gnu++26 compilation failed to produce 
> > executable
> > FAIL: std/ranges/zip_transform/1.cc  -std=gnu++26 (test for excess errors)
> > UNRESOLVED: std/ranges/zip_transform/1.cc  -std=gnu++26 compilation failed 
> > to produce executable
> > inside of libstdc++ all at c++26 and I think is most likely the same
> > problem, just different exact ICE.
> > 
> > Bootstrapped/regtested on x86_64-linux and i686-linux with the above
> > mentioned regressions.
> > 
> > 2024-07-26  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.
> >     * 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.
> > 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-25 21:34:46.791268760 +0200
> > +++ gcc/cp/cp-tree.def      2024-07-26 09:20:05.256197019 +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-26 08:34:18.104160097 +0200
> > +++ gcc/cp/cp-tree.h        2024-07-26 09:19:10.626908903 +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) \
> > --- gcc/cp/cp-objcp-common.cc.jj    2024-07-25 21:34:46.791268760 +0200
> > +++ gcc/cp/cp-objcp-common.cc       2024-07-26 08:34:40.532867127 +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-25 21:34:46.791268760 +0200
> > +++ gcc/cp/constraint.cc    2024-07-26 15:03:04.438445216 +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 (!ARGUMENT_PACK_P (a))
> > +               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 +2924,101 @@ 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);
> > +      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));
> > +       }
> 
> IIUC you can use copy_template_args here.  And sadly I think you may
> need to make a copy at each iteration, not just once, because the
> satisfaction cache assumes args are immutable when adding a new
> entry in the cache :/
> 
> Not sure how big of a memory leak this would be in practice,
> but one way to mitigate this might be to ggc_free this copy
> if in the recursive call to satisfy_constraint_r there was
> no cache miss.
> 
> > +     tree a = TMPL_ARG (orig_args, level, index);
> > +     TMPL_ARG (args, level, index)
> > +       = TREE_VEC_ELT (ARGUMENT_PACK_ARGS (a), i);
> > +   }
> > +      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 +3044,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-25 21:34:46.793268734 +0200
> > +++ gcc/cp/error.cc 2024-07-26 08:41:23.555602719 +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-25 21:34:46.793268734 +0200
> > +++ gcc/cp/logic.cc 2024-07-26 09:22:29.047325077 +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-25 21:34:46.793268734 +0200
> > +++ gcc/cp/cxx-pretty-print.cc      2024-07-26 14:19:27.435984262 +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/testsuite/g++.dg/concepts/diagnostic3.C.jj  2023-10-16 
> > 17:25:32.456781462 +0200
> > +++ gcc/testsuite/g++.dg/concepts/diagnostic3.C     2024-07-26 
> > 09:32:49.042262192 +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-25 
> > 21:34:46.809268529 +0200
> > +++ gcc/testsuite/g++.dg/concepts/variadic2.C       2024-07-26 
> > 08:34:40.535867087 +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-25 
> > 21:34:46.809268529 +0200
> > +++ gcc/testsuite/g++.dg/concepts/variadic4.C       2024-07-26 
> > 08:34:40.535867087 +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        2022-12-05 
> > 11:10:37.712671571 +0100
> > +++ gcc/testsuite/g++.dg/cpp2a/concepts-requires33.C        2024-07-26 
> > 18:37:41.867864077 +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-26 
> > 08:34:40.535867087 +0200
> > +++ gcc/testsuite/g++.dg/cpp26/fold-constr1.C       2024-07-26 
> > 08:34:40.535867087 +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-26 
> > 08:34:40.535867087 +0200
> > +++ gcc/testsuite/g++.dg/cpp26/fold-constr2.C       2024-07-26 
> > 08:34:40.535867087 +0200
> > @@ -0,0 +1,56 @@
> > +// P2963R3 - Ordering of constraints involving fold expressions
> > +// { dg-do compile { target c++20 } }
> > +
> > +template <class T> concept C1 = true;
> > +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-26 
> > 08:34:40.535867087 +0200
> > +++ gcc/testsuite/g++.dg/cpp26/fold-constr3.C       2024-07-26 
> > 11:12:09.129830502 +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-26 
> > 08:34:40.535867087 +0200
> > +++ gcc/testsuite/g++.dg/cpp26/fold-constr4.C       2024-07-26 
> > 08:34:40.535867087 +0200
> > @@ -0,0 +1,48 @@
> > +// P2963R3 - Ordering of constraints involving fold expressions
> > +// { dg-do compile { target c++20 } }
> > +
> > +template <class T> concept C1 = true;
> > +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-26 
> > 08:34:40.535867087 +0200
> > +++ gcc/testsuite/g++.dg/cpp26/fold-constr5.C       2024-07-26 
> > 13:37:12.802463309 +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-26 
> > 08:34:40.535867087 +0200
> > +++ gcc/testsuite/g++.dg/cpp26/fold-constr6.C       2024-07-26 
> > 14:32:28.121132795 +0200
> > @@ -0,0 +1,20 @@
> > +// P2963R3 - Ordering of constraints involving fold expressions
> > +// { dg-do compile { target c++20 } }
> > +
> > +template <class T> concept C1 = true;
> > +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-26 
> > 11:35:01.499085336 +0200
> > +++ gcc/testsuite/g++.dg/cpp26/fold-constr7.C       2024-07-26 
> > 15:11:46.186674513 +0200
> > @@ -0,0 +1,11 @@
> > +// P2963R3 - Ordering of constraints involving fold expressions
> > +// { dg-do compile { target c++20 } }
> > +
> > +template <class T> concept C1 = true;
> > +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-26 
> > 15:05:59.636183243 +0200
> > +++ gcc/testsuite/g++.dg/cpp26/fold-constr8.C       2024-07-26 
> > 15:05:15.027759668 +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-26 
> > 15:12:12.570331261 +0200
> > +++ gcc/testsuite/g++.dg/cpp26/fold-constr9.C       2024-07-26 
> > 15:12:06.613408758 +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-26 
> > 16:02:26.099421284 +0200
> > +++ gcc/testsuite/g++.dg/cpp26/fold-constr10.C      2024-07-26 
> > 16:03:35.256534023 +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