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