From patchwork Fri Jul 26 16:45:53 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jakub Jelinek X-Patchwork-Id: 1965362 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=Fjqfxznr; dkim-atps=neutral Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=gcc.gnu.org (client-ip=2620:52:3:1:0:246e:9693:128c; helo=server2.sourceware.org; envelope-from=gcc-patches-bounces~incoming=patchwork.ozlabs.org@gcc.gnu.org; receiver=patchwork.ozlabs.org) Received: from server2.sourceware.org (server2.sourceware.org [IPv6:2620:52:3:1:0:246e:9693:128c]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (secp384r1) server-digest SHA384) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4WVtv42K3yz1ybY for ; Sat, 27 Jul 2024 02:46:40 +1000 (AEST) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id F1B9F385840A for ; Fri, 26 Jul 2024 16:46:37 +0000 (GMT) X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by sourceware.org (Postfix) with ESMTP id 1A63A3858D26 for ; Fri, 26 Jul 2024 16:46:02 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 1A63A3858D26 Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=redhat.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=redhat.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 1A63A3858D26 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=170.10.129.124 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1722012368; cv=none; b=AGRnQKnDmcwNYyTEyWE6i3xb2uQRnxMhht58lQ6g+QCX7IRujsoQbJHMH4KyVTpxmFp7C0CJ99JFbLdlNKecYXAcCPBd0i+G/c3MudfMqeTE2V63N1WqqIq9k5OE6Rucg008glrVPwQStvtr2YLE1qMYUeGZq2X2jeko+XitzFo= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1722012368; c=relaxed/simple; bh=Z9JeeSTWdZtK3zi1qydlosLfaGLwEY9bi6RcZwF7byc=; h=DKIM-Signature:Date:From:To:Subject:Message-ID:MIME-Version; b=kCKUu2dKnRn9jXr9NIIRNyGBklPoh6I6cQAW++SUTDEhI9pCS4wIl25qElHL4QeWsxOJ/tD+TTz/+jnbLOu1dM3AKGQ4vTOaZUcXOukDMVmd0+6AIUaEShRFjzWrXNXH6Koc1trjEQkuYoUkkb5XldRIgTjAe0modsjmoomH/D8= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1722012361; h=from:from:reply-to:reply-to:subject:subject:date:date: message-id:message-id:to:to:cc:cc:mime-version:mime-version: content-type:content-type; bh=lJu3Ak0eatPQX955bHsnRA6DwvhjMbjraHqxcslMDEo=; b=Fjqfxznrhq6cvOXW/so7eq6n/rKpmzpVjscVfYA+tXDY7kurOAEwKYB3i0T1IdCJsn5JtB /Zv1Xm/ZESPlosahfG+6/Ws4vgAx4L1clAkamvlhJAJbI561jiXNVo++1RqTkwjh2EYwQd l4M4YbjFiVllIdPj3gjWcry5ULBne+Q= Received: from mx-prod-mc-02.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-85-SjNMqXYqOp6lc6bXs1ayXw-1; Fri, 26 Jul 2024 12:45:59 -0400 X-MC-Unique: SjNMqXYqOp6lc6bXs1ayXw-1 Received: from mx-prod-int-02.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-02.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.15]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-02.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 272C31955D44 for ; Fri, 26 Jul 2024 16:45:58 +0000 (UTC) Received: from tucnak.zalov.cz (unknown [10.45.224.25]) by mx-prod-int-02.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id BC3831955D42; Fri, 26 Jul 2024 16:45:56 +0000 (UTC) Received: from tucnak.zalov.cz (localhost [127.0.0.1]) by tucnak.zalov.cz (8.17.1/8.17.1) with ESMTPS id 46QGjr752170769 (version=TLSv1.3 cipher=TLS_AES_256_GCM_SHA384 bits=256 verify=NOT); Fri, 26 Jul 2024 18:45:54 +0200 Received: (from jakub@localhost) by tucnak.zalov.cz (8.17.1/8.17.1/Submit) id 46QGjrQA2170768; Fri, 26 Jul 2024 18:45:53 +0200 Date: Fri, 26 Jul 2024 18:45:53 +0200 From: Jakub Jelinek To: Jason Merrill , Patrick Palka Cc: gcc-patches@gcc.gnu.org Subject: [RFH PATCH] c++: Implement C++26 P2963R3 - Ordering of constraints involving fold expressions [PR115746] Message-ID: MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.0 on 10.30.177.15 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Disposition: inline X-Spam-Status: No, score=-1.1 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H3, RCVD_IN_MSPIKE_WL, SPF_HELO_NONE, SPF_NONE, TXREP, URIBL_DBL_SPAM, URIBL_SBL_A, URI_HEX autolearn=no autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: gcc-patches@gcc.gnu.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: Jakub Jelinek Errors-To: gcc-patches-bounces~incoming=patchwork.ozlabs.org@gcc.gnu.org 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 ()); in cpp26/fold-constr5.C is accepted by clang in C++26 mode and rejected by GCC. I believe GCC is right, C && ... && C where U is an empty pack should be normalized to (C /\ ...) /\ C so I think (C /\ ...) is satisfied but C should not as int::type is invalid, so the overall result should be not satisfied. Another difference is static_assert (U::bar ()); in cpp26/fold-constr6.C, which is rejected by clang in C++26 mode and accepted by GCC. This is for template requires ((C1 && ...) && ... && C1) static constexpr bool bar () { return false; } template requires ((C2 && ...) && ... && C2) static constexpr bool bar () { return true; } where both T and V are packs, I believe the first should be normalized to (C1 /\ ...) && (C1 /\ ...) and the second to (C2 /\ ...) && (C2 /\ ...) where C2 subsumes C1. clang++ accepts when it is written in the other order, so when normalized to (C2 /\ ...) && (C2 /\ ...) and in that case GCC and clang agree that the latter subsumes the former. C2 /\ ... subsumes C1 /\ ... and C2 /\ ... subsumes C1 /\ ... and C2 /\ ... does not subsume C1 /\ ... and C2 /\ ... does not subsume C1 /\ ... 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 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 requires ((((sizeof (T) + ...) < 8 * sizeof (int)) && C2) && ...) constexpr bool foo (T...) { return true; }; here actually the ((sizeof (T) + ...) < 8 * sizeof (int)) atomic constraint only needs the whole T pack and C2 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? 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 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. Jakub --- 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 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)); + } + 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 &folds () + { + return m_fold; + } /* Returns an iterator to the first clause in the formula. */ @@ -204,6 +219,7 @@ struct clause std::list m_terms; /* The list of terms. */ hash_set m_set; /* The set of atomic constraints. */ + auto_vec 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 &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 inline constexpr bool foo_v = false; @@ -7,7 +7,7 @@ template concept foo = (bool)(foo_v | foo_v); template -requires (foo && ...) // { dg-message "19:with Ts = .int, char... evaluated to .false." } +requires (foo && ...) // { dg-message "19:with Ts = .int, char... evaluated to .false." "" { target c++23_down } } void bar() { } @@ -16,7 +16,7 @@ template struct S { }; template -requires (foo> && ...) // { dg-message "22:with Is = .2, 3, 4... evaluated to .false." } +requires (foo> && ...) // { 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 requires requires { typename list; } // && (Sequence && ...) -struct zip {}; // { dg-error "does not specialize" } +struct zip {}; // { 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 -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(); --- 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 concept isint = __is_same (U, int); + +template requires (isint && ...) +constexpr int foo (V...) { return 1; }; + +template requires (... || isint) +constexpr int bar (U...) { return 1; }; + +template requires (isint && ... && isint) +constexpr int baz (T, S...) { return 1; } + +template requires (isint || ... || isint) +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 concept C1 = true; +template concept C2 = C1 && true; +template concept C3 = C1 && __is_same (T, int); + +template requires (C1) +constexpr bool foo (T) { return false; }; +template requires (C2 && ...) +constexpr bool foo (T...) { return true; }; + +static_assert (!foo (0)); +static_assert (!foo (1)); + +template requires (C1 && ...) +constexpr bool bar (T...) { return false; }; +template requires (C2 && ...) +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 requires (C1 && ...) +constexpr bool baz (T...) { return false; }; +template requires (... && (C1 && 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 requires (C1 || ... || true) +constexpr bool qux (T...) { return false; }; +template requires (C2 && ... && 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 +constexpr bool corge (C1 auto...) { return false; } +template +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 struct A; +struct Thingy { + static constexpr int compare (const Thingy &) { return 1; } +}; +template +void f (A *, A *) +requires (T::compare (U{}) && ...); // { dg-error "has type 'int', not 'bool'" "" { target c++26 } } +void +g (A *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 concept C1 = true; +template concept C2 = C1 && true; + +template requires (C1 && ...) +constexpr bool foo (T...) { return false; }; +template requires (C2 || ...) +constexpr bool foo (T...) { return true; }; + +static_assert (foo (0)); // { dg-error "call of overloaded 'foo\\\(int\\\)' is ambiguous" } + +template requires (C1 || ...) +constexpr bool bar (T...) { return false; }; +template requires (C2 && ...) +constexpr bool bar (T...) { return true; }; + +static_assert (bar (0)); // { dg-error "call of overloaded 'bar\\\(int\\\)' is ambiguous" } + +template requires (C1 || ...) +constexpr bool baz (T...) { return false; }; +template requires (C2 || ...) +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 +struct U { + template requires (... && C1) + static constexpr bool foo () { return false; } + template requires (... && C2) + static constexpr bool foo () { return true; } + template requires (... && C1) + static constexpr bool bar () { return false; } + template requires (... && C2) + static constexpr bool bar () { return true; } + template requires (... && C1) + static constexpr bool baz () { return false; } + template requires (... && C2) + static constexpr bool baz () { return true; } +}; + +static_assert (U::foo ()); // { dg-error "call of overloaded 'foo\\\(\\\)' is ambiguous" "" { target c++23_down } } +static_assert (U::bar ()); // { dg-error "call of overloaded 'bar\\\(\\\)' is ambiguous" "" { target c++23_down } } +static_assert (U::baz ()); // { 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 concept C = sizeof (T) < sizeof (int) * 64; + +template requires (C && ...) // { 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 ()); +static_assert (foo ()); +static_assert (foo ()); // { dg-error "no matching function for call" } +static_assert (foo ()); // { dg-error "no matching function for call" } +static_assert (foo ()); // { 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 requires (C && ... && C) // { dg-error "is not a class, struct, or union type" "" { target c++23_down } } +constexpr bool bar () { return true; }; + +static_assert (bar ()); +static_assert (bar ()); +static_assert (bar ()); // { dg-error "no matching function for call" } +static_assert (bar ()); // { dg-error "no matching function for call" } +static_assert (bar ()); // { 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 requires (C && ... && C) // { dg-error "is not a class, struct, or union type" "" { target c++23_down } } +constexpr bool baz () { return true; }; + +static_assert (baz ()); +static_assert (baz ()); +static_assert (baz ()); // { dg-error "no matching function for call" } +static_assert (baz ()); // { dg-error "no matching function for call" } +static_assert (baz ()); // { 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 requires (C || ...) // { 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 ()); +static_assert (qux ()); +static_assert (qux ()); // { dg-error "no matching function for call" } +static_assert (qux ()); // { dg-error "no matching function for call" "" { target c++23_down } } +static_assert (qux ()); // { 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 requires (C || ... || C) // { dg-error "is not a class, struct, or union type" "" { target c++23_down } } +constexpr bool corge () { return true; }; + +static_assert (corge ()); +static_assert (corge ()); +static_assert (corge ()); // { dg-error "no matching function for call" } +static_assert (corge ()); // { dg-error "no matching function for call" "" { target c++23_down } } +static_assert (corge ()); // { dg-error "no matching function for call" "" { target c++23_down } } + +template requires (C || ... || C) // { dg-error "is not a class, struct, or union type" "" { target c++23_down } } +constexpr bool garply () { return true; }; + +static_assert (garply ()); +static_assert (garply ()); +static_assert (garply ()); // { dg-error "no matching function for call" } +static_assert (garply ()); // { dg-error "no matching function for call" "" { target c++23_down } } +static_assert (garply ()); // { 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 concept C1 = true; +template concept C2 = C1 && true; + +template +struct U { + template requires ((C1 && ...) && ... && C1) + static constexpr bool foo () { return false; } + template requires ((C2 && ...) && ... && C2) + static constexpr bool foo () { return true; } + template requires ((C1 && ...) && ... && C1) + static constexpr bool bar () { return false; } + template requires ((C2 && ...) && ... && C2) + static constexpr bool bar () { return true; } +}; + +static_assert (U::foo ()); // { dg-error "call of overloaded 'foo\\\(\\\)' is ambiguous" "" { target c++23_down } } +static_assert (U::bar ()); // { 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 concept C1 = true; +template concept C2 = C1 && true; + +template requires ((((sizeof (T) + ...) < 8 * sizeof (int)) && C2) && ...) +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 concept C = __is_same (T, int); + +template +struct A {}; + +template requires ((C && C) && ...) // { dg-error "mismatched argument pack lengths while expanding '\\\(C \\\&\\\& C\\\)'" "" { target c++23_down } } +constexpr bool foo (A, A) { 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 {}, A {})); +static_assert (foo (A {}, A {})); // { dg-error "no matching function for call to" } + +template requires (C || ...) // { 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 concept C = __is_same (T, int); + +template requires ((C && ...) && ... && C) +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 concept C = true; + +template requires (C && ...) // { 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 ()); +static_assert (foo ()); +static_assert (foo ()); // { dg-error "no matching function for call" "" { target c++23_down } } +static_assert (foo ()); // { dg-error "no matching function for call" "" { target c++23_down } } +static_assert (foo ()); // { dg-error "no matching function for call" "" { target c++23_down } } + +template requires (C && ... && C) // { dg-error "is not a class, struct, or union type" "" { target c++23_down } } +constexpr bool bar () { return true; }; + +static_assert (bar ()); +static_assert (bar ()); +static_assert (bar ()); // { dg-error "no matching function for call" "" { target c++23_down } } +static_assert (bar ()); // { dg-error "no matching function for call" "" { target c++23_down } } +static_assert (bar ()); // { dg-error "no matching function for call" "" { target c++23_down } } + +template requires (C && ... && C) // { dg-error "is not a class, struct, or union type" "" { target c++23_down } } +constexpr bool baz () { return true; }; + +static_assert (baz ()); +static_assert (baz ()); +static_assert (baz ()); // { dg-error "no matching function for call" "" { target c++23_down } } +static_assert (baz ()); // { dg-error "no matching function for call" "" { target c++23_down } } +static_assert (baz ()); // { dg-error "no matching function for call" "" { target c++23_down } } + +template requires (C || ...) // { 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 ()); +static_assert (qux ()); +static_assert (qux ()); // { dg-error "no matching function for call" "" { target c++23_down } } +static_assert (qux ()); // { dg-error "no matching function for call" "" { target c++23_down } } +static_assert (qux ()); // { dg-error "no matching function for call" "" { target c++23_down } } + +template requires (C || ... || C) // { dg-error "is not a class, struct, or union type" "" { target c++23_down } } +constexpr bool corge () { return true; }; + +static_assert (corge ()); +static_assert (corge ()); +static_assert (corge ()); // { dg-error "no matching function for call" "" { target c++23_down } } +static_assert (corge ()); // { dg-error "no matching function for call" "" { target c++23_down } } +static_assert (corge ()); // { dg-error "no matching function for call" "" { target c++23_down } } + +template requires (C || ... || C) // { dg-error "is not a class, struct, or union type" "" { target c++23_down } } +constexpr bool garply () { return true; }; + +static_assert (garply ()); +static_assert (garply ()); +static_assert (garply ()); // { dg-error "no matching function for call" "" { target c++23_down } } +static_assert (garply ()); // { dg-error "no matching function for call" "" { target c++23_down } } +static_assert (garply ()); // { dg-error "no matching function for call" "" { target c++23_down } }