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.

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