From patchwork Wed Aug 23 19:49:04 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Marek Polacek X-Patchwork-Id: 136718 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:a59:a7d1:0:b0:3f2:4152:657d with SMTP id p17csp686077vqm; Wed, 23 Aug 2023 12:50:29 -0700 (PDT) X-Google-Smtp-Source: AGHT+IF+S0svw83EXLMaYf0QXsTXvwYR3sJsTEZEloMRmQfeYLW8WYA2p9GRujI0Z65N3Non+Q90 X-Received: by 2002:a05:6512:3ca3:b0:500:89c2:1594 with SMTP id h35-20020a0565123ca300b0050089c21594mr6489677lfv.39.1692820229527; Wed, 23 Aug 2023 12:50:29 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1692820229; cv=none; d=google.com; s=arc-20160816; b=rTVOXqejyODJnzaOqu/4e2dcilhoagS1GZcvkkUb46pt3GbdLKc+3c0d0yc8K6f6oD /oWi/xuBaWI/cXHSctip5ONGhvKDN1hk7MJ5TtCOvz/Ah586pw0RM3PFqNMzPMyxjNxG GfTg+Zm2J/Npqrl4/uTNVPNp4Wt0OJ/DKUzR+nUQnoceq/su7QlIKN6NMIKQ3S1c6CTQ EGmmUSmtD6wNhgIoVjaA5pxqWuogbec+0zvpjtWWTEo4NWUzPfCSOex4QWcpWOSYelEq SmrKwZkXX/TQDxN17g0/3BQxuClcvpjVpfuP+BAjZfHq96ZoMjlqr+3qMQp29HFjQzdr e5tw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:reply-to:from:list-subscribe:list-help:list-post :list-archive:list-unsubscribe:list-id:precedence :content-transfer-encoding:mime-version:message-id:date:subject:to :dmarc-filter:delivered-to:dkim-signature:dkim-filter; bh=9bVOOZDNuQmqPSvkTfSU592l99TXDWI1xtEuV0OHYgE=; fh=1drzuSgAqPtVUclta6Ecfzgh/wYNtWpRJF7yhohVT0I=; b=WzgFEHnMEZofL4HlULrj/EWyB0/0yogJpZtve7EgEOvQw/VKT+YnnnS7ug5KVNekXP SCc6i8r09JqEqk+wt48wff8C5EcY8trLd/OOA4RjZuY9u3CatauaxEoJOvzSRSrC0Vbg rKdhNCzk9JGmGKquWmcDO6vFL6Sjx7FtqdX8kf3mena1aJPeMnO/+DStW9OsfbdhlPIf KhIUDFosTRfmnC+RBk5FAkypy/IBxXUgU9UaBKgi57hwyTVWE3ek1+QTLCTbl920zgAd e0GoGJRGMJKmrKMwJyfGnOCJ0Hhia3P3r6fL8Q2zvblS+qHhoaH//HhkSv2ph++grvSb x5Lg== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@gcc.gnu.org header.s=default header.b=Vo01QKEX; spf=pass (google.com: domain of gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org designates 8.43.85.97 as permitted sender) smtp.mailfrom="gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org"; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=gnu.org Received: from server2.sourceware.org (ip-8-43-85-97.sourceware.org. [8.43.85.97]) by mx.google.com with ESMTPS id c22-20020a056402121600b0052a08c25764si5368787edw.427.2023.08.23.12.50.29 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 23 Aug 2023 12:50:29 -0700 (PDT) Received-SPF: pass (google.com: domain of gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org designates 8.43.85.97 as permitted sender) client-ip=8.43.85.97; Authentication-Results: mx.google.com; dkim=pass header.i=@gcc.gnu.org header.s=default header.b=Vo01QKEX; spf=pass (google.com: domain of gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org designates 8.43.85.97 as permitted sender) smtp.mailfrom="gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org"; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=gnu.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 7427D3853D29 for ; Wed, 23 Aug 2023 19:50:27 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 7427D3853D29 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gcc.gnu.org; s=default; t=1692820227; bh=9bVOOZDNuQmqPSvkTfSU592l99TXDWI1xtEuV0OHYgE=; h=To:Subject:Date:List-Id:List-Unsubscribe:List-Archive:List-Post: List-Help:List-Subscribe:From:Reply-To:From; b=Vo01QKEX+mtK5X4c85U4GJCEI1hTKLhT68avkp+Xgi92aRWl/Llcr3jY8fRqtPE1r lQmyA4rL88ugu3cR+d0rIbyxet+C4sws7L7ZA8qZTQxMYuprXJhjbevJgHhYSDb5jS alG9sfl7iw3rXVmG0Uf9qWL8uUzeCBy+zO+v62Ow= 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.133.124]) by sourceware.org (Postfix) with ESMTPS id E2823385735A for ; Wed, 23 Aug 2023 19:49:27 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org E2823385735A Received: from mimecast-mx02.redhat.com (mimecast-mx02.redhat.com [66.187.233.88]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-652-2Oagmn-aOcmWh3b4PeW_Rg-1; Wed, 23 Aug 2023 15:49:16 -0400 X-MC-Unique: 2Oagmn-aOcmWh3b4PeW_Rg-1 Received: from smtp.corp.redhat.com (int-mx03.intmail.prod.int.rdu2.redhat.com [10.11.54.3]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id 9BA9C8533D8 for ; Wed, 23 Aug 2023 19:49:16 +0000 (UTC) Received: from pdp-11.lan (unknown [10.22.16.128]) by smtp.corp.redhat.com (Postfix) with ESMTP id 645D31121314; Wed, 23 Aug 2023 19:49:16 +0000 (UTC) To: GCC Patches , Jason Merrill Subject: [PATCH] c++: implement P2564, consteval needs to propagate up [PR107687] Date: Wed, 23 Aug 2023 15:49:04 -0400 Message-ID: <20230823194904.1925591-1-polacek@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.1 on 10.11.54.3 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com X-Spam-Status: No, score=-12.0 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, KAM_SHORT, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H4, RCVD_IN_MSPIKE_WL, SPF_HELO_NONE, SPF_NONE, TXREP autolearn=ham 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.29 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-Patchwork-Original-From: Marek Polacek via Gcc-patches From: Marek Polacek Reply-To: Marek Polacek Errors-To: gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org Sender: "Gcc-patches" X-getmail-retrieved-from-mailbox: INBOX X-GMAIL-THRID: 1775050664989312267 X-GMAIL-MSGID: 1775050664989312267 Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk? -- >8 -- This patch implements P2564, described at , whereby certain functions are promoted to consteval. For example: consteval int id(int i) { return i; } template constexpr int f(T t) { return t + id(t); // id causes f to be promoted to consteval } void g(int i) { f (3); } now compiles. Previously the code was ill-formed: we would complain that 't' in 'f' is not a constant expression. Since 'f' is now consteval, it means that the call to id(t) is in an immediate context, so doesn't have to produce a constant -- this is how we allow consteval functions composition. But making 'f' consteval also means that the call to 'f' in 'g' must yield a constant; failure to do so results in an error. I made the effort to have cc1plus explain to us what's going on. For example, calling f(i) produces this neat diagnostic: q.C: In function 'void g(int)': q.C:11:5: error: call to consteval function 'f(i)' is not a constant expression 11 | f (i); | ~~^~~ q.C:6:16: note: 'constexpr int f(T) [with T = int]' was promoted to an immediate function because its body contains an immediate-escalating expression 'id(t)' 6 | return t + id(t); | ~~^~~ which hopefully makes it clear what's going on. Implementing this proposal has been tricky. One problem was delayed instantiation: instantiating a function can set off a domino effect where one call promotes a function to consteval but that then means that another function should also be promoted, etc. I previously thought that I could get past that by implementing the propagation in cp_gimplify_expr at which point we have already instantiated everything via instantiate_pending_templates. But I realized that we don't gimplify e.g. static auto p = &id; and so we'd never detect taking the address of a consteval function. Therefore this patch instantiates immediate-escalating functions beforehand. And as usual, there were a lot of other things to handle. It's not just calls to consteval functions that we must detect, we also have to look for id-expressions that denote an immediate function. I discovered two crashes: , ICE-on-valid with NSDMI , missing ; causes an ICE which this patch doesn't address, but adds a dg-ice test for the former. I left one FIXME in the patch because I'm unclear on how to properly fix the modules problem. PR c++/107687 gcc/c-family/ChangeLog: * c-cppbuiltin.cc (c_cpp_builtins): Update __cpp_consteval. gcc/cp/ChangeLog: * call.cc (immediate_invocation_p): No longer static. (immediate_escalating_function_p): New. (maybe_promote_function_to_consteval): New. (build_over_call): Set ADDR_EXPR_DENOTES_CALL_P. Maybe promote current_function_decl to consteval. * constexpr.cc (instantiate_cx_fn_r): No longer static. * cp-gimplify.cc (struct find_escalating_expr_t): New. (find_escalating_expr_r): New. (maybe_explain_promoted_consteval): New. (maybe_escalate_decl_and_cfun): New. (cp_fold_r) : Handle promoting functions to consteval. : New case, handle promoting functions to consteval. : Handle promoting functions to consteval. * cp-tree.h (ADDR_EXPR_DENOTES_CALL_P): Define. (immediate_invocation_p): Declare. (immediate_escalating_function_p): Declare. (maybe_promote_function_to_consteval): Declare. (instantiate_constexpr_fns): Declare. * typeck.cc (cp_build_addr_expr_1): SET_EXPR_LOCATION on constexpr functions as well. libstdc++-v3/ChangeLog: * testsuite/20_util/integer_comparisons/greater_equal_neg.cc: Adjust expected diagnostic. * testsuite/20_util/integer_comparisons/greater_neg.cc: Likewise. * testsuite/20_util/integer_comparisons/less_equal_neg.cc: Likewise. * testsuite/20_util/optional/monadic/or_else_neg.cc: Likewise. * testsuite/23_containers/array/creation/3_neg.cc: Likewise. * testsuite/23_containers/span/first_neg.cc: Likewise. * testsuite/23_containers/span/last_neg.cc: Likewise. * testsuite/23_containers/span/subspan_neg.cc: Likewise. gcc/testsuite/ChangeLog: * g++.dg/cpp0x/constexpr-inst1.C: Add dg-error. * g++.dg/cpp23/consteval-if10.C: Remove dg-error. * g++.dg/cpp23/consteval-if2.C: Likewise. * g++.dg/cpp23/feat-cxx2b.C: Adjust expected value of __cpp_consteval. * g++.dg/cpp26/feat-cxx26.C: Likewise. * g++.dg/cpp2a/feat-cxx2a.C: Likewise. * g++.dg/cpp2a/consteval-prop1.C: New test. * g++.dg/cpp2a/consteval-prop10.C: New test. * g++.dg/cpp2a/consteval-prop11.C: New test. * g++.dg/cpp2a/consteval-prop12.C: New test. * g++.dg/cpp2a/consteval-prop13.C: New test. * g++.dg/cpp2a/consteval-prop14.C: New test. * g++.dg/cpp2a/consteval-prop15.C: New test. * g++.dg/cpp2a/consteval-prop16.C: New test. * g++.dg/cpp2a/consteval-prop17.C: New test. * g++.dg/cpp2a/consteval-prop2.C: New test. * g++.dg/cpp2a/consteval-prop3.C: New test. * g++.dg/cpp2a/consteval-prop4.C: New test. * g++.dg/cpp2a/consteval-prop5.C: New test. * g++.dg/cpp2a/consteval-prop6.C: New test. * g++.dg/cpp2a/consteval-prop7.C: New test. * g++.dg/cpp2a/consteval-prop8.C: New test. * g++.dg/cpp2a/consteval-prop9.C: New test. --- gcc/c-family/c-cppbuiltin.cc | 2 +- gcc/cp/call.cc | 74 +++++- gcc/cp/constexpr.cc | 2 +- gcc/cp/cp-gimplify.cc | 223 ++++++++++++++++-- gcc/cp/cp-tree.h | 9 + gcc/cp/typeck.cc | 4 +- gcc/testsuite/g++.dg/cpp0x/constexpr-inst1.C | 5 +- gcc/testsuite/g++.dg/cpp23/consteval-if10.C | 7 +- gcc/testsuite/g++.dg/cpp23/consteval-if2.C | 10 +- gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C | 4 +- gcc/testsuite/g++.dg/cpp26/feat-cxx26.C | 4 +- gcc/testsuite/g++.dg/cpp2a/consteval-prop1.C | 149 ++++++++++++ gcc/testsuite/g++.dg/cpp2a/consteval-prop10.C | 9 + gcc/testsuite/g++.dg/cpp2a/consteval-prop11.C | 48 ++++ gcc/testsuite/g++.dg/cpp2a/consteval-prop12.C | 30 +++ gcc/testsuite/g++.dg/cpp2a/consteval-prop13.C | 23 ++ gcc/testsuite/g++.dg/cpp2a/consteval-prop14.C | 72 ++++++ gcc/testsuite/g++.dg/cpp2a/consteval-prop15.C | 81 +++++++ gcc/testsuite/g++.dg/cpp2a/consteval-prop16.C | 73 ++++++ gcc/testsuite/g++.dg/cpp2a/consteval-prop17.C | 32 +++ gcc/testsuite/g++.dg/cpp2a/consteval-prop2.C | 54 +++++ gcc/testsuite/g++.dg/cpp2a/consteval-prop3.C | 31 +++ gcc/testsuite/g++.dg/cpp2a/consteval-prop4.C | 32 +++ gcc/testsuite/g++.dg/cpp2a/consteval-prop5.C | 27 +++ gcc/testsuite/g++.dg/cpp2a/consteval-prop6.C | 58 +++++ gcc/testsuite/g++.dg/cpp2a/consteval-prop7.C | 76 ++++++ gcc/testsuite/g++.dg/cpp2a/consteval-prop8.C | 71 ++++++ gcc/testsuite/g++.dg/cpp2a/consteval-prop9.C | 67 ++++++ gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C | 4 +- .../integer_comparisons/greater_equal_neg.cc | 24 +- .../integer_comparisons/greater_neg.cc | 24 +- .../integer_comparisons/less_equal_neg.cc | 24 +- .../20_util/optional/monadic/or_else_neg.cc | 10 +- .../23_containers/array/creation/3_neg.cc | 14 +- .../testsuite/23_containers/span/first_neg.cc | 4 +- .../testsuite/23_containers/span/last_neg.cc | 4 +- .../23_containers/span/subspan_neg.cc | 12 +- 37 files changed, 1306 insertions(+), 91 deletions(-) create mode 100644 gcc/testsuite/g++.dg/cpp2a/consteval-prop1.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/consteval-prop10.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/consteval-prop11.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/consteval-prop12.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/consteval-prop13.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/consteval-prop14.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/consteval-prop15.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/consteval-prop16.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/consteval-prop17.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/consteval-prop2.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/consteval-prop3.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/consteval-prop4.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/consteval-prop5.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/consteval-prop6.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/consteval-prop7.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/consteval-prop8.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/consteval-prop9.C base-commit: 0cfc9c953d0221ec3971a25e6509ebe1041f142e diff --git a/gcc/c-family/c-cppbuiltin.cc b/gcc/c-family/c-cppbuiltin.cc index f2b12fd63db..8f8ebc7e4bb 100644 --- a/gcc/c-family/c-cppbuiltin.cc +++ b/gcc/c-family/c-cppbuiltin.cc @@ -1058,7 +1058,7 @@ c_cpp_builtins (cpp_reader *pfile) cpp_define (pfile, "__cpp_constexpr=202002L"); cpp_define (pfile, "__cpp_constexpr_in_decltype=201711L"); cpp_define (pfile, "__cpp_conditional_explicit=201806L"); - cpp_define (pfile, "__cpp_consteval=201811L"); + cpp_define (pfile, "__cpp_consteval=202211L"); cpp_define (pfile, "__cpp_constinit=201907L"); cpp_define (pfile, "__cpp_deduction_guides=201907L"); cpp_define (pfile, "__cpp_nontype_template_args=201911L"); diff --git a/gcc/cp/call.cc b/gcc/cp/call.cc index 673ec91d60e..31f78b71fe1 100644 --- a/gcc/cp/call.cc +++ b/gcc/cp/call.cc @@ -9735,7 +9735,9 @@ build_trivial_dtor_call (tree instance, bool no_ptr_deref) /* Return true if in an immediate function context, or an unevaluated operand, or a default argument/member initializer, or a subexpression of an immediate - invocation. */ + invocation. + ??? P2564 says that "a subexpression of a manifestly constant-evaluated + expression or conversion" is also an immediate function context. */ bool in_immediate_context () @@ -9754,7 +9756,7 @@ in_immediate_context () /* Return true if a call to FN with number of arguments NARGS is an immediate invocation. */ -static bool +bool immediate_invocation_p (tree fn) { return (TREE_CODE (fn) == FUNCTION_DECL @@ -9762,6 +9764,54 @@ immediate_invocation_p (tree fn) && !in_immediate_context ()); } +/* Return true if FN is an immediate-escalating function. */ + +bool +immediate_escalating_function_p (tree fn) +{ + if (!fn) + return false; + + gcc_checking_assert (TREE_CODE (fn) == FUNCTION_DECL); + + /* An immediate-escalating function is + -- the call operator of a lambda that is not declared with the consteval + specifier */ + if (LAMBDA_FUNCTION_P (fn) && !DECL_IMMEDIATE_FUNCTION_P (fn)) + return true; + /* -- a defaulted special member function that is not declared with the + consteval specifier */ + special_function_kind sfk = special_memfn_p (fn); + if (sfk != sfk_none + && DECL_DEFAULTED_FN (fn) + && !DECL_IMMEDIATE_FUNCTION_P (fn)) + return true; + /* -- a function that results from the instantiation of a templated entity + defined with the constexpr specifier. */ + return is_instantiation_of_constexpr (fn); +} + +/* Promote FN to an immediate function, including its clones, if it is + an immediate-escalating function. Return true if we did promote; + false otherwise. */ + +bool +maybe_promote_function_to_consteval (tree fn) +{ + if (fn + && !DECL_IMMEDIATE_FUNCTION_P (fn) + && immediate_escalating_function_p (fn)) + { + SET_DECL_IMMEDIATE_FUNCTION_P (fn); + tree clone; + FOR_EACH_CLONE (clone, fn) + SET_DECL_IMMEDIATE_FUNCTION_P (clone); + return true; + } + + return false; +} + /* Subroutine of the various build_*_call functions. Overload resolution has chosen a winning candidate CAND; build up a CALL_EXPR accordingly. ARGS is a TREE_LIST of the unconverted arguments to the call. FLAGS is a @@ -10460,6 +10510,10 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain) UNAVAILABLE_DEPRECATED_SUPPRESS); fn = build_addr_func (fn, complain); + /* We're actually invoking the function. (Immediate functions get an + & when invoking it even though the user didn't use &.) */ + ADDR_EXPR_DENOTES_CALL_P (fn) = true; + if (fn == error_mark_node) return error_mark_node; } @@ -10484,6 +10538,7 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain) tree fndecl = STRIP_TEMPLATE (TREE_OPERAND (fn, 0)); if (immediate_invocation_p (fndecl)) { + tree orig_call = call; tree obj_arg = NULL_TREE; /* Undo convert_from_reference called by build_cxx_call. */ if (REFERENCE_REF_P (call)) @@ -10508,6 +10563,21 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain) obj_arg = TREE_OPERAND (addr, 0); } } + + /* [expr.const]p16 "An expression or conversion is + immediate-escalating if it is not initially in an immediate + function context and it is either + -- an immediate invocation that is not a constant expression and + is not a subexpression of an immediate invocation." + + If we are in an immediate-escalating function, the + immediate-escalating expression or conversion makes it an + immediate function. So CALL does not need to produce a constant + expression. ??? It's ugly to call cxx_constant_value twice in + some cases. */ + if (cxx_constant_value (call, obj_arg, tf_none) == error_mark_node + && maybe_promote_function_to_consteval (current_function_decl)) + return orig_call; call = cxx_constant_value (call, obj_arg, complain); if (obj_arg && !error_operand_p (call)) call = cp_build_init_expr (obj_arg, call); diff --git a/gcc/cp/constexpr.cc b/gcc/cp/constexpr.cc index da2c3116810..d85fb514015 100644 --- a/gcc/cp/constexpr.cc +++ b/gcc/cp/constexpr.cc @@ -8247,7 +8247,7 @@ instantiate_cx_fn_r (tree *tp, int *walk_subtrees, void */*data*/) return NULL_TREE; } -static void +void instantiate_constexpr_fns (tree t) { location_t loc = input_location; diff --git a/gcc/cp/cp-gimplify.cc b/gcc/cp/cp-gimplify.cc index 206e791fcfd..902c9d54741 100644 --- a/gcc/cp/cp-gimplify.cc +++ b/gcc/cp/cp-gimplify.cc @@ -426,6 +426,113 @@ lvalue_has_side_effects (tree e) return TREE_SIDE_EFFECTS (e); } +struct find_escalating_expr_t { + /* The function whose body we're traversing. Used to promote the current + function to consteval. */ + tree caller; + /* To avoid walking same trees multiple times. */ + hash_set *pset; +}; + +/* Find an immediate-escalating expression or conversion in *TP. + If DATA_ is non-null, this function will promote function to + consteval as it goes; otherwise, we're just looking for what + made a function consteval, for diagnostic purposes. This + function assumes that *TP was instantiated. */ + +static tree +find_escalating_expr_r (tree *tp, int *walk_subtrees, void *data_) +{ + auto data = static_cast(data_); + tree t = *tp; + const tree_code code = TREE_CODE (t); + + if (TYPE_P (t) || unevaluated_p (code)) + { +bail: + *walk_subtrees = 0; + return NULL_TREE; + } + + tree decl; + tree call; + + switch (code) + { + case CALL_EXPR: + decl = cp_get_callee_fndecl_nofold (t); + call = t; + break; + case ADDR_EXPR: + if (TREE_CODE (TREE_OPERAND (t, 0)) != FUNCTION_DECL) + goto bail; + decl = TREE_OPERAND (t, 0); + call = NULL_TREE; + break; + case PTRMEM_CST: + if (TREE_CODE (PTRMEM_CST_MEMBER (t)) != FUNCTION_DECL) + goto bail; + decl = PTRMEM_CST_MEMBER (t); + call = NULL_TREE; + break; + default: + return NULL_TREE; + } + + /* Now, voyager, sail thou forth, to seek and find. */ + if (!decl || (data->pset && data->pset->add (decl))) + goto bail; + + /* Not consteval yet, but could be. Have to look deeper. */ + if (!DECL_IMMEDIATE_FUNCTION_P (decl) + && immediate_escalating_function_p (decl) + /* If we're not escalating, just looking for the escalating + expression, we don't need to recurse. */ + && !data->pset) + { + find_escalating_expr_t d = { data->caller ? decl : NULL_TREE, + data->pset }; + cp_walk_tree (&DECL_SAVED_TREE (decl), find_escalating_expr_r, &d, + nullptr); + } + + /* If it turned out to be consteval, maybe promote the caller. */ + if (DECL_IMMEDIATE_FUNCTION_P (decl) + && (!call || cxx_constant_value (call, tf_none) == error_mark_node)) + { + /* We found the escalating expression. */ + maybe_promote_function_to_consteval (data->caller); + *walk_subtrees = 0; + return t; + } + + return NULL_TREE; +} + +/* Maybe say that FN (a function decl with DECL_IMMEDIATE_FUNCTION_P set) + was initially not an immediate function, but was promoted to one because + its body contained an immediate-escalating expression or conversion. */ + +static void +maybe_explain_promoted_consteval (location_t loc, tree fn) +{ + /* A function cannot be marked both constexpr and consteval + except when we've promoted the former to the latter. */ + if (is_instantiation_of_constexpr (fn)) + { + /* See if we can figure out what made the function consteval. */ + find_escalating_expr_t data = { NULL_TREE, nullptr }; + tree x = cp_walk_tree (&DECL_SAVED_TREE (fn), find_escalating_expr_r, + &data, nullptr); + if (x) + inform (cp_expr_loc_or_loc (x, loc), + "%qD was promoted to an immediate function because its " + "body contains an immediate-escalating expression %qE", fn, x); + else + inform (loc, "%qD was promoted to an immediate function", fn); + } +} + /* Gimplify *EXPR_P as rvalue into an expression that can't be modified by expressions with side-effects in other operands. */ @@ -1017,6 +1124,57 @@ maybe_replace_decl (tree *tp, tree decl, tree replacement) return true; } +/* Figure out if DECL should be promoted to consteval and if so, maybe also + promote the function we are in currently. CALL is the CALL_EXPR of DECL, + or NULL_TREE, if we're taking the address of a function. This function + will likely instantiate DECL. */ + +static void +maybe_escalate_decl_and_cfun (tree decl, tree call = NULL_TREE) +{ + if (cxx_dialect <= cxx17 || cp_unevaluated_operand) + return; + + /* Compiler-generated functions don't seem like good candidates for + promoting. */ + if (DECL_ARTIFICIAL (decl) || DECL_ABSTRACT_P (decl)) + return; + + /* What we're calling is not a consteval function but it may become + one. This requires recursing; DECL may be promoted to consteval + because it contains an escalating expression E, but E itself may + have to be promoted first, etc. */ + if (!DECL_IMMEDIATE_FUNCTION_P (decl) + && immediate_escalating_function_p (decl)) + { + /* PSET holds the set of functions we have already perused for + immediate-escalating expressions. */ + static hash_set pset; + find_escalating_expr_t data = { decl, &pset }; + /* We can't delay instantiating any further. We need to see the + whole tree to decide whether DECL is consteval. + ??? Consider adding a sentinel to instantiate_constexpr_fns so + that we don't escalate while we instantiate while we escalate... + which seems dodgy. + FIXME Instantiating a defaulted ctor breaks modules (ICE due to + cp_function_chain being null). */ + if (!(modules_p () && DECL_DEFAULTED_FN (decl))) + instantiate_constexpr_fns (decl); + cp_walk_tree (&DECL_SAVED_TREE (decl), find_escalating_expr_r, + &data, nullptr); + } + + /* In turn, maybe promote the function we find ourselves in... */ + if (DECL_IMMEDIATE_FUNCTION_P (decl) + /* ...but not if the call to DECL was constant; that is the + "an immediate invocation that is not a constant expression" + case. We do this here and not in find_escalating_expr_r, + because DECL could have already been consteval and we'd + never call f_e_e_r. */ + && (!call || cxx_constant_value (call, tf_none) == error_mark_node)) + maybe_promote_function_to_consteval (current_function_decl); +} + /* Genericization context. */ struct cp_genericize_data @@ -1046,27 +1204,64 @@ cp_fold_r (tree *stmt_p, int *walk_subtrees, void *data_) switch (code) { case PTRMEM_CST: - if (TREE_CODE (PTRMEM_CST_MEMBER (stmt)) == FUNCTION_DECL - && DECL_IMMEDIATE_FUNCTION_P (PTRMEM_CST_MEMBER (stmt))) + if (TREE_CODE (PTRMEM_CST_MEMBER (stmt)) == FUNCTION_DECL) { - if (!data->pset.add (stmt)) - error_at (PTRMEM_CST_LOCATION (stmt), - "taking address of an immediate function %qD", - PTRMEM_CST_MEMBER (stmt)); - stmt = *stmt_p = build_zero_cst (TREE_TYPE (stmt)); - break; + tree decl = PTRMEM_CST_MEMBER (stmt); + maybe_escalate_decl_and_cfun (decl); + /* Taking the address of a consteval function is not permitted. */ + if (immediate_invocation_p (decl)) + { + if (!data->pset.add (stmt)) + { + error_at (PTRMEM_CST_LOCATION (stmt), + "taking address of an immediate function %qD", + decl); + maybe_explain_promoted_consteval (PTRMEM_CST_LOCATION (stmt), + decl); + } + stmt = *stmt_p = build_zero_cst (TREE_TYPE (stmt)); + } } break; + case CALL_EXPR: + if (tree fn = CALL_EXPR_FN (stmt)) + if (TREE_CODE (fn) == ADDR_EXPR + && ADDR_EXPR_DENOTES_CALL_P (fn) + && TREE_CODE (TREE_OPERAND (fn, 0)) == FUNCTION_DECL) + { + tree decl = TREE_OPERAND (fn, 0); + maybe_escalate_decl_and_cfun (decl, stmt); + if (immediate_invocation_p (decl)) + { + tree e = cxx_constant_value (stmt, tf_none); + if (e == error_mark_node) + { + const location_t loc = cp_expr_loc_or_input_loc (stmt); + error_at (loc, "call to consteval function %qE is not a " + "constant expression", stmt); + maybe_explain_promoted_consteval (loc, decl); + } + *stmt_p = stmt = e; + } + } + break; + case ADDR_EXPR: if (TREE_CODE (TREE_OPERAND (stmt, 0)) == FUNCTION_DECL - && DECL_IMMEDIATE_FUNCTION_P (TREE_OPERAND (stmt, 0))) + && !ADDR_EXPR_DENOTES_CALL_P (stmt)) { - error_at (EXPR_LOCATION (stmt), - "taking address of an immediate function %qD", - TREE_OPERAND (stmt, 0)); - stmt = *stmt_p = build_zero_cst (TREE_TYPE (stmt)); - break; + tree decl = TREE_OPERAND (stmt, 0); + maybe_escalate_decl_and_cfun (decl); + /* Taking the address of a consteval function is not permitted. */ + if (immediate_invocation_p (decl)) + { + const location_t loc = cp_expr_loc_or_input_loc (stmt); + error_at (loc, "taking address of an immediate function %qD", + decl); + maybe_explain_promoted_consteval (loc, decl); + stmt = *stmt_p = build_zero_cst (TREE_TYPE (stmt)); + } } break; diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index eb901683b6d..36d76a98233 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -4784,6 +4784,11 @@ get_vec_init_expr (tree t) #define PTRMEM_OK_P(NODE) \ TREE_LANG_FLAG_0 (TREE_CHECK3 ((NODE), ADDR_EXPR, OFFSET_REF, SCOPE_REF)) +/* True if this ADDR_EXPR denotes a function call; that is, it's + fn() rather than &fn. */ +#define ADDR_EXPR_DENOTES_CALL_P(NODE) \ + (ADDR_EXPR_CHECK(NODE)->base.protected_flag) + /* Get the POINTER_TYPE to the METHOD_TYPE associated with this pointer to member function. TYPE_PTRMEMFUNC_P _must_ be true, before using this macro. */ @@ -6713,6 +6718,9 @@ extern tree perform_direct_initialization_if_possible (tree, tree, bool, extern vec *resolve_args (vec*, tsubst_flags_t); extern tree in_charge_arg_for_name (tree); extern bool in_immediate_context (); +extern bool immediate_invocation_p (tree); +extern bool immediate_escalating_function_p (tree); +extern bool maybe_promote_function_to_consteval (tree); extern tree build_cxx_call (tree, int, tree *, tsubst_flags_t, tree = NULL_TREE); @@ -8576,6 +8584,7 @@ extern bool reduced_constant_expression_p (tree); extern bool is_instantiation_of_constexpr (tree); extern bool var_in_constexpr_fn (tree); extern bool var_in_maybe_constexpr_fn (tree); +extern void instantiate_constexpr_fns (tree); extern bool maybe_constexpr_fn (tree); extern void explain_invalid_constexpr_fn (tree); extern vec cx_error_context (void); diff --git a/gcc/cp/typeck.cc b/gcc/cp/typeck.cc index d5c0c85ed51..03b642fefae 100644 --- a/gcc/cp/typeck.cc +++ b/gcc/cp/typeck.cc @@ -7248,7 +7248,9 @@ cp_build_addr_expr_1 (tree arg, bool strict_lvalue, tsubst_flags_t complain) set for possible later diagnostics. */ if (TREE_CODE (val) == ADDR_EXPR && TREE_CODE (TREE_OPERAND (val, 0)) == FUNCTION_DECL - && DECL_IMMEDIATE_FUNCTION_P (TREE_OPERAND (val, 0))) + && (DECL_IMMEDIATE_FUNCTION_P (TREE_OPERAND (val, 0)) + /* A constexpr function may be promoted to consteval. */ + || DECL_DECLARED_CONSTEXPR_P (TREE_OPERAND (val, 0)))) SET_EXPR_LOCATION (val, input_location); return val; diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-inst1.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-inst1.C index 3ce513d6e25..7b3e2db6c8a 100644 --- a/gcc/testsuite/g++.dg/cpp0x/constexpr-inst1.C +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-inst1.C @@ -1,6 +1,9 @@ +// This used to... // Test that we don't uselessly instantiate f immediately while parsing g. // Doing so is permitted by the standard, but has no benefit and breaks code // unnecessarily. +// ...but due to P2564 we actually do instantiate f, because we need to figure +// out if it should be promoted to consteval. :-( // { dg-do compile { target c++11 } } @@ -8,7 +11,7 @@ // { dg-additional-options -O } template -constexpr int f (const T* p) { return p->i; } +constexpr int f (const T* p) { return p->i; } // { dg-error "invalid use of incomplete type" "" { target c++20 } } constexpr int g(const struct A* p) { return f(p); } diff --git a/gcc/testsuite/g++.dg/cpp23/consteval-if10.C b/gcc/testsuite/g++.dg/cpp23/consteval-if10.C index 4c0523fe1d0..b8709beba85 100644 --- a/gcc/testsuite/g++.dg/cpp23/consteval-if10.C +++ b/gcc/testsuite/g++.dg/cpp23/consteval-if10.C @@ -2,6 +2,9 @@ // { dg-do compile { target c++20 } } // { dg-options "" } +// We used to give errors but the lambdas are now promoted to consteval +// and are in a immediate function context, so no errors. + consteval int foo (int x) { return x; } constexpr int @@ -10,7 +13,7 @@ bar (int x) int r = 0; if consteval // { dg-warning "'if consteval' only available with" "" { target c++20_only } } { - auto y = [=] { foo (x); }; // { dg-error "'x' is not a constant expression" } + auto y = [=] { foo (x); }; y (); } return r; @@ -23,7 +26,7 @@ baz (T x) T r = 0; if consteval // { dg-warning "'if consteval' only available with" "" { target c++20_only } } { - auto y = [=] { foo (x); }; // { dg-error "'x' is not a constant expression" } + auto y = [=] { foo (x); }; y (); } return r; diff --git a/gcc/testsuite/g++.dg/cpp23/consteval-if2.C b/gcc/testsuite/g++.dg/cpp23/consteval-if2.C index d1845da9e58..024ac2b2124 100644 --- a/gcc/testsuite/g++.dg/cpp23/consteval-if2.C +++ b/gcc/testsuite/g++.dg/cpp23/consteval-if2.C @@ -65,7 +65,7 @@ qux (int x) int r = 0; if not consteval // { dg-warning "'if consteval' only available with" "" { target c++20_only } } { - r += foo (x); // { dg-error "'x' is not a constant expression" } + r += foo (x); } else { @@ -97,7 +97,7 @@ corge (T x) T r = 0; if not consteval // { dg-warning "'if consteval' only available with" "" { target c++20_only } } { - r += foo (x); // { dg-error "'x' is not a constant expression" } + r += foo (x); } else { @@ -109,11 +109,11 @@ corge (T x) } else { - r += foo (8 * x); // { dg-error "is not a constant expression" } + r += foo (8 * x); } if ! consteval // { dg-warning "'if consteval' only available with" "" { target c++20_only } } { - r += foo (32 * x);// { dg-error "is not a constant expression" } + r += foo (32 * x); } if consteval // { dg-warning "'if consteval' only available with" "" { target c++20_only } } { @@ -125,5 +125,5 @@ corge (T x) int garply (int x) { - return corge (x); + return corge (x); // { dg-error "is not a constant expression" } } diff --git a/gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C b/gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C index 9e29b01adc1..2b21bd1bc0d 100644 --- a/gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C +++ b/gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C @@ -480,8 +480,8 @@ #ifndef __cpp_consteval # error "__cpp_consteval" -#elif __cpp_consteval != 201811 -# error "__cpp_consteval != 201811" +#elif __cpp_consteval != 202211L +# error "__cpp_consteval != 202211L" #endif #ifndef __cpp_concepts diff --git a/gcc/testsuite/g++.dg/cpp26/feat-cxx26.C b/gcc/testsuite/g++.dg/cpp26/feat-cxx26.C index 0977d964fe0..b1b9be2d24a 100644 --- a/gcc/testsuite/g++.dg/cpp26/feat-cxx26.C +++ b/gcc/testsuite/g++.dg/cpp26/feat-cxx26.C @@ -480,8 +480,8 @@ #ifndef __cpp_consteval # error "__cpp_consteval" -#elif __cpp_consteval != 201811 -# error "__cpp_consteval != 201811" +#elif __cpp_consteval != 202211L +# error "__cpp_consteval != 202211L" #endif #ifndef __cpp_concepts diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval-prop1.C b/gcc/testsuite/g++.dg/cpp2a/consteval-prop1.C new file mode 100644 index 00000000000..2ff16b88aa1 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/consteval-prop1.C @@ -0,0 +1,149 @@ +// P2564R3 +// { dg-do compile { target c++20 } } +// Some of these were cribbed from clang's cxx2b-consteval-propagate.cpp. + +consteval int id(int i) { return i; } + +template +constexpr int +f0 (T t) +{ + // OK, f0 promoted to consteval. + return id (t); +} + +constexpr auto a0 = f0 (3); + +// As a consequence of f0 being promoted to an immediate function, we +// can't take its address. +auto p0 = &f0; // { dg-error "taking address of an immediate function" } + +template +constexpr int +f1 (T t) +{ + // OK, f1 promoted to consteval. + return t + id (t); +} + +constexpr auto a1 = f1 (3); + +// As a consequence of f1 being promoted to an immediate function, we +// can't take its address. +auto p1 = &f1; // { dg-error "taking address of an immediate function" } + +template +constexpr int +f2 (T) +{ + // This produces a constant; f2 *not* promoted to consteval. + return id (42); +} + +// ... so we can take its address. +auto p2 = &f2; + +constexpr int +f3 (int i) +{ + // f3 isn't a function template and those don't get upgraded to consteval. + return id (i); // { dg-error "not a constant expression" } +} + +auto p3 = &f3; + +template +constexpr int +f4 (T t) +{ + auto p = id; + (void) p; + return t; +} + +// ??? We promote f4 to consteval but clang++ doesn't seem to. +auto p6 = &f4; // { dg-error "taking address of an immediate function" } + +static_assert (f4 (42) == 42); + +// Constructors. +consteval int zero (int) +{ + return 0; +} + +struct A { + // A::A(auto) promoted to consteval. + constexpr A(auto i) { zero (i); } +}; + +constexpr void +f5 (auto i) +{ + A a{i}; +} + +void +f6 () +{ + f5 (0); +} + +struct B { + constexpr B(int) { } +}; + +B b1(f0((f1(7)))); + +template +constexpr int cid(T t) { return t; } + +auto p4 = &cid; +auto p5 = &cid; + +int g = 7; + +B b2(f0(cid(g))); // { dg-error "not usable" } + +struct C { + // C::C(int) promoted to consteval. + consteval C (int) {}; +}; + +constexpr int +f7 (auto t) +{ + C c(t); + return 0; +} + +int i1 = f7 (g); // { dg-error "not a constant expression" } + +struct Y { + int y; + int x = id (y); + consteval Y (int i) : y (id (i)) {} +}; + +Y y1(1); +Y y2(g); // { dg-error "not usable" } + +auto l1 = [](int i) constexpr { + int t = id (i); + return id (0); +}; + +int (*p)(int) = l1; // { dg-error "returns address of immediate function" } + +// Not defined = won't produce a constant expression. +consteval int undef (); // { dg-warning "used but never defined" } + +struct S { + int a = [] { return undef (); }(); +}; + +struct S2 { // { dg-error "" } + int a = [] (int u = undef ()) { + return u; + }(); +} s2; diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval-prop10.C b/gcc/testsuite/g++.dg/cpp2a/consteval-prop10.C new file mode 100644 index 00000000000..00af77042cb --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/consteval-prop10.C @@ -0,0 +1,9 @@ +// P2564R3 +// { dg-do compile { target c++20 } } + +consteval int foo () { return 42; } +// Even though the standard doesn't define "invocation", this is one. +// But the user actually wrote '&' so presumably we should error. If +// we decide to accept this, move the ADDR_EXPR_DENOTES_CALL_P setting +// to build_cxx_call. +int bar () { return (*(&foo)) (); } // { dg-error "taking address" } diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval-prop11.C b/gcc/testsuite/g++.dg/cpp2a/consteval-prop11.C new file mode 100644 index 00000000000..b1b7a6acacb --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/consteval-prop11.C @@ -0,0 +1,48 @@ +// P2564R3 +// { dg-do compile { target c++20 } } +// { dg-options "-fdiagnostics-show-caret" } +// Test diagnostic. + +consteval int id (int i) { return i; } +constexpr int foo (int i ) { return i; } + +constexpr int +f (auto i) +{ + return i + id (i); + /* { dg-begin-multiline-output "" } + return i + id (i); + ~~~^~~ + { dg-end-multiline-output "" } */ +} + +void +g (int x) +{ + f (x); // { dg-error "5:call to consteval function .f\\(x\\). is not a constant expression" } + /* { dg-begin-multiline-output "" } + f (x); + ~~^~~ + { dg-end-multiline-output "" } */ +} + +constexpr int +f2 (auto i) +{ + auto p = &id; + /* { dg-begin-multiline-output "" } + auto p = &id; + ^~~ + { dg-end-multiline-output "" } */ + return p (i); +} + +void +g2 (int x) +{ + f2 (x); // { dg-error "6:call to consteval function .f2\\(x\\). is not a constant expression" } + /* { dg-begin-multiline-output "" } + f2 (x); + ~~~^~~ + { dg-end-multiline-output "" } */ +} diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval-prop12.C b/gcc/testsuite/g++.dg/cpp2a/consteval-prop12.C new file mode 100644 index 00000000000..2949ab83af8 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/consteval-prop12.C @@ -0,0 +1,30 @@ +// P2564R3 +// { dg-do compile { target c++20 } } + +consteval int +zero (int) +{ + return 0; +} + +constexpr int +f (auto i) +{ + return zero (i); +} + +constexpr int +g (auto) +{ + // This call is a constant expression, so don't promote g. + return f (42); +} + +void +do_test () +{ + g (2); +} + +// Must work. +auto q = &g; diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval-prop13.C b/gcc/testsuite/g++.dg/cpp2a/consteval-prop13.C new file mode 100644 index 00000000000..6c20b98a87c --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/consteval-prop13.C @@ -0,0 +1,23 @@ +// P2564R3 +// { dg-do compile { target c++20 } } +// Verify we don't recurse endlessly while determining whether a function +// should be propagated to consteval. + +consteval int id (int i) { return i; } + +constexpr int f2 (auto); + +constexpr int +f1 (auto i) +{ + return f2 (i); +} + +constexpr int +f2 (auto i) +{ + return f1 (i); +} + +auto p = &f1; +auto q = &f2; diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval-prop14.C b/gcc/testsuite/g++.dg/cpp2a/consteval-prop14.C new file mode 100644 index 00000000000..9755e226b17 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/consteval-prop14.C @@ -0,0 +1,72 @@ +// P2564R3 +// { dg-do compile { target c++20 } } +// Test more CALL_EXPRs in a function, some of which are escalating. + +consteval int id (int i) { return i; } +constexpr int neg (int i) { return -i; } +constexpr int foo (auto i) { return id (i); } + +constexpr int +f1 (auto i) +{ + auto x = id (i); // { dg-message "promoted to an immediate function because its body contains an immediate-escalating expression .id\\(i\\)." } + auto y = neg (i); + return x + y; +} + +constexpr int +f2 (auto i) +{ + return neg (id (i)); // { dg-message "promoted to an immediate function because its body contains an immediate-escalating expression .id\\(i\\)." } +} + +constexpr int +f3 (auto i) +{ + auto x = i + neg (neg (neg (id (neg (neg (i)))))); // { dg-message "promoted to an immediate function because its body contains an immediate-escalating expression .id\\(neg\\(neg\\(i\\)\\)\\)." } + return x; +} + +constexpr int +f4 (auto i) +{ + return i + neg ((id (2 * i) + neg (i)) / 2); // { dg-message "promoted to an immediate function because its body contains an immediate-escalating expression .id\\(\\(2 \\* i\\)\\)." } +} + +constexpr int +f5 (auto i) +{ + (void) neg (i); + (void) neg (i); + (void) neg (i); + (void) neg (i); + (void) neg (i); + (void) neg (i); + (void) +id (i); // { dg-message "promoted to an immediate function because its body contains an immediate-escalating expression .id\\(i\\)." } + (void) neg (i); + return i; +} + +constexpr int +f6 (auto i) +{ + auto x = neg (i + foo (i)); // { dg-message "promoted to an immediate function because its body contains an immediate-escalating expression .foo\\(i\\)." } + return x; +} + +void +g (int i) +{ + f1 (i); // { dg-error "call to consteval function .f1\\(i\\). is not a constant expression" } + f1 (42); + f2 (i); // { dg-error "call to consteval function .f2\\(i\\). is not a constant expression" } + f2 (42); + f3 (i); // { dg-error "call to consteval function .f3\\(i\\). is not a constant expression" } + f3 (42); + f4 (i); // { dg-error "call to consteval function .f4\\(i\\). is not a constant expression" } + f4 (42); + f5 (i); // { dg-error "call to consteval function .f5\\(i\\). is not a constant expression" } + f5 (42); + f6 (i); // { dg-error "call to consteval function .f6\\(i\\). is not a constant expression" } + f6 (42); +} diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval-prop15.C b/gcc/testsuite/g++.dg/cpp2a/consteval-prop15.C new file mode 100644 index 00000000000..f52ba07ec6c --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/consteval-prop15.C @@ -0,0 +1,81 @@ +// P2564R3 +// { dg-do compile { target c++20 } } +// { dg-options "-Wno-c++23-extensions" } + +consteval int id (int i) { return i; } + +constexpr int +f1 (auto i) +{ + auto p = &id; // { dg-message "promoted to an immediate function because its body contains an immediate-escalating expression .id." } + (void) p; + return i; +} + +constexpr int +f2 (auto i) +{ + return f1 (i); +} + +constexpr int +f3 (auto i) +{ + return f2 (i); +} + +constexpr int +f4 (auto i) +{ + return f3 (i); +} + +constexpr int +f5 (auto i) +{ + return f4 (i); // { dg-message "promoted to an immediate function because its body contains an immediate-escalating expression .f4\\(i\\)." } +} + +constexpr int +f6 (auto) +{ + // This call is a constant expression, so don't promote f6. + return f4 (42); +} + +constexpr int +f7 (auto i) +{ + if consteval { + auto p = &id; + (void) p; + } + return i; +} + +constexpr int +f8 (auto i) +{ + if not consteval { + (void) 0; + } else { + auto p = &id; + (void) p; + } + return i; +} + +void +g (int non_const) +{ + f1 (42); + f1 (non_const); // { dg-error "call to consteval function .f1\\(non_const\\). is not a constant expression" } + f5 (42); + f5 (non_const); // { dg-error "call to consteval function .f5\\(non_const\\). is not a constant expression" } + f6 (42); + f6 (non_const); + f7 (42); + f7 (non_const); + f8 (42); + f8 (non_const); +} diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval-prop16.C b/gcc/testsuite/g++.dg/cpp2a/consteval-prop16.C new file mode 100644 index 00000000000..7952d495d8b --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/consteval-prop16.C @@ -0,0 +1,73 @@ +// P2564R3 +// { dg-do compile { target c++20 } } +// Test unevaluated operands. + +consteval int id (int i) { return i; } + +constexpr int +f1 (auto i) +{ + // Unevaluated operand -> don't promote. + auto p = sizeof (&id); + (void) p; + return i; +} + +constexpr int +f2 (auto i) +{ + // Unevaluated operand -> don't promote. + auto p = noexcept (id); + (void) p; + return i; +} + +constexpr int +f3 (auto i) +{ + // Unevaluated operand -> don't promote. + auto p = noexcept (id (i)); + (void) p; + return i; +} + +constexpr int +f4 (auto i) +{ + // Unevaluated operand -> don't promote. + decltype(id) p; + (void) p; + return i; +} + +constexpr int +f5 (auto i) +{ + // Unevaluated operand -> don't promote. + __extension__ auto p = alignof (id (i)); + (void) p; + return i; +} + +constexpr int +f6 (auto i) requires requires { id (i); } +{ + return i; +} + +void +g (int non_const) +{ + f1 (42); + f1 (non_const); + f2 (42); + f2 (non_const); + f3 (42); + f3 (non_const); + f4 (42); + f4 (non_const); + f5 (42); + f5 (non_const); + f6 (42); + f6 (non_const); +} diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval-prop17.C b/gcc/testsuite/g++.dg/cpp2a/consteval-prop17.C new file mode 100644 index 00000000000..846e5603fee --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/consteval-prop17.C @@ -0,0 +1,32 @@ +// P2564R3 +// { dg-do compile { target c++20 } } +// Test default arguments. + +consteval int id (int i) { return i; } + +template +constexpr int +f1 (int i = id (42)) +{ + return i; +} + +int non_const; + +template +constexpr int +f2 (int i = id (non_const)) +{ + return i; +} + +void +g (int i) +{ + f1 (42); + f1 (i); + f1 (); + f2 (42); + f2 (i); + f2 (); // { dg-error "not usable" } +} diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval-prop2.C b/gcc/testsuite/g++.dg/cpp2a/consteval-prop2.C new file mode 100644 index 00000000000..a5803736d37 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/consteval-prop2.C @@ -0,0 +1,54 @@ +// P2564R3 +// { dg-do compile { target c++20 } } +// Testcase from P2564R3. + +consteval int id(int i) { return i; } +constexpr char id(char c) { return c; } + +template +constexpr int f(T t) { + return t + id(t); +} + +auto a = &f; // OK, f is not an immediate function +auto b = &f; // { dg-error "taking address of an immediate function" } + +static_assert(f(3) == 6); // OK + +template +constexpr int g(T t) { // g is not an immediate function + return t + id(42); // because id(42) is already a constant +} + +template +constexpr bool is_not(T t, F f) { + return not f(t); +} + +consteval bool is_even(int i) { return i % 2 == 0; } + +static_assert(is_not(5, is_even)); // OK + +int x = 0; + +template +constexpr T h(T t = id(x)) { // h is not an immediate function + return t; +} + +template +constexpr T hh() { // hh is an immediate function + return h(); // { dg-error "not usable in a constant expression" } +} + +int i = hh(); // error: hh() is an immediate-escalating expression + // outside of an immediate-escalating function +struct A { + int x; + int y = id(x); +}; + +template +constexpr int k(int) { // k is not an immediate function because A(42) is a + return A(42).y; // constant expression and thus not immediate-escalating +} diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval-prop3.C b/gcc/testsuite/g++.dg/cpp2a/consteval-prop3.C new file mode 100644 index 00000000000..35665304652 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/consteval-prop3.C @@ -0,0 +1,31 @@ +// P2564R3 +// { dg-do compile { target c++20 } } +// Cribbed from clang's cxx2b-consteval-propagate.cpp. + +consteval int id(int i) { return i; } + +template +constexpr int f(T t); + +auto a1 = &f; +auto b1 = &f; + +template +constexpr int f(T t) { + return id(0); +} + +template +constexpr int f2(T); + +// ??? clang++ emits +// error: immediate function 'f2' used before it is defined +// error: immediate function 'f2' used before it is defined +// but at this point we don't know that f2 will be updated to consteval? +auto a2 = &f2; +auto b2 = &f2; + +template +constexpr int f2(T t) { + return id(t); +} diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval-prop4.C b/gcc/testsuite/g++.dg/cpp2a/consteval-prop4.C new file mode 100644 index 00000000000..fb0129794b6 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/consteval-prop4.C @@ -0,0 +1,32 @@ +// P2564R3 +// { dg-do compile { target c++20 } } +// { dg-additional-options "-fchecking" } +// { dg-ice "PR110997" } +// From clang's cxx2b-consteval-propagate.cpp. This test ICEd when I worked on +// P2564. + +consteval int f (int); + +struct S { + int a = 0; + int b = f (a); +}; + +constexpr bool +g (auto i) +{ + S s{i}; + return s.b == 2 *i; +} + +consteval int +f (int i) +{ + return 2 * i; +} + +void +test () +{ + static_assert(g(42)); +} diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval-prop5.C b/gcc/testsuite/g++.dg/cpp2a/consteval-prop5.C new file mode 100644 index 00000000000..3bd1b9d1674 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/consteval-prop5.C @@ -0,0 +1,27 @@ +// P2564R3 +// { dg-do compile { target c++20 } } + +consteval int f (int i) { return i; } + +struct S { + int x = f(42); +}; + +constexpr S +immediate (auto) +{ + return S{}; +} + +void +g () +{ + immediate (0); +} + +consteval void +test () +{ + constexpr S s = immediate(0); + static_assert(s.x == 42); +} diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval-prop6.C b/gcc/testsuite/g++.dg/cpp2a/consteval-prop6.C new file mode 100644 index 00000000000..8cc08c7f6d8 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/consteval-prop6.C @@ -0,0 +1,58 @@ +// P2564R3 +// { dg-do compile { target c++20 } } +// From cxx2b-consteval-propagate.cpp. + +void side_effect(); + +consteval int +f (int x) +{ + if (!x) + side_effect(); // { dg-error "call to non-.constexpr. function" } + return x; +} + +struct SS { + int y = f(1); + int x = f(0); + SS(); +}; +SS::SS(){} + +consteval int +f2 (int x) +{ + if (!__builtin_is_constant_evaluated ()) + side_effect(); + return x; +} + +struct S2 { + int x = f2(0); + constexpr S2(); +}; + +constexpr S2::S2(){} +S2 s = {}; +constinit S2 s2 = {}; + +struct S3 { + int x = f2(0); + S3(); +}; +S3::S3(){} + +consteval int undef (int x); // { dg-warning "never defined" } + +struct X { + int a = sizeof(undef(0)); + int x = undef(0); + + X() = default; // { dg-error "used before its definition" } +}; + +void +test () +{ + [[maybe_unused]] X x; +} diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval-prop7.C b/gcc/testsuite/g++.dg/cpp2a/consteval-prop7.C new file mode 100644 index 00000000000..712b0baef1c --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/consteval-prop7.C @@ -0,0 +1,76 @@ +// P2564R3 +// { dg-do compile { target c++20 } } +// The problem here was that while parsing, we first process calling +// 'f' from 'g' but only when instantiating 'f' do we promote 'f' +// to consteval. When the var we're initializing is marked constexpr, +// store_init_value detects the problem that we're calling a consteval +// function with non-const argument. + +consteval int id(int i) { return i; } + +// Don't let the instantiations confuse us, e.g. instantiating a fn +// prior to entering 'g'. +template +constexpr int f1(T t) { return id (t); } + +template +constexpr int f2(T t) { return id (t); } + +template +constexpr int f3(T t) { return id (t); } + +template +constexpr int f4(T t) { return id (t); } + +template +constexpr int f5(T t) { return id (t); } + +template +constexpr int f6(T t) { return id (t); } + +template +constexpr int f7(T t) { return id (t); } + +template +constexpr int f8(T t) { return id (t); } + +template +constexpr int f9(T t) { return id (t); } + +template +constexpr int f10(T t) { return id (t); } + +template +constexpr int g1(T t) { auto p = id; return p (t); } + +int non_const; + +auto a1 = f1 (non_const); // { dg-error "not a constant" } +constexpr auto a2 = f2 (non_const); // { dg-error "not usable" } +auto a3 = f3 (42); +constexpr auto a4 = f4 (42); + +void +g () +{ + auto a5 = f5 (non_const); // { dg-error "not a constant" } + constexpr auto a6 = f6 (non_const); // { dg-error "not usable" } + auto a7 = f7 (42); + constexpr auto a8 = f8 (42); + (void) f9 (non_const); // { dg-error "not a constant" } + (void) f10 (42); + (void) g1 (non_const); // { dg-error "not a constant" } +} + +struct S { + int y; + int x = id (y); + // Promoted to consteval. + template + constexpr S(T t) : y (id (t)) {} +}; + +S s1(1); +S s2(non_const); // { dg-error "not usable" } +constexpr S s3(1); +constexpr S s4(non_const); // { dg-error "not usable" } diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval-prop8.C b/gcc/testsuite/g++.dg/cpp2a/consteval-prop8.C new file mode 100644 index 00000000000..41c47992ef7 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/consteval-prop8.C @@ -0,0 +1,71 @@ +// P2564R3 +// { dg-do compile { target c++20 } } +// { dg-options "-Wno-c++23-extensions" } + +consteval int zero (int) +{ + return 0; +} + +struct A { + // A::A(auto) promoted to consteval. + constexpr A(auto i) { zero (i); } +}; + +// 'f1' is an immediate function because its body contains a call to an +// immediate constructor 'A' and that call is not a constant expression +constexpr void +f1 (auto i) +{ + A a{i}; +} + +// 'f2' is an immediate function because its body contains a call to an +// immediate constructor 'A' and that call is not a constant expression +constexpr void +f2 (auto i) +{ + A a{i}; +} + +// ??? This doesn't give error when inline/constexpr and not called. +void +f3 (int i) +{ + A a{i}; // { dg-error "not a constant expression" } +} + +/* "An expression or conversion is immediate-escalating if it is not initially + in an immediate function context" but this one is, so we do *not* promote + f4 to consteval. */ +constexpr void +f4 (auto i) +{ + if consteval { + A a{i}; + } +} + +constexpr void +f5 (auto i) +{ + if not consteval { + (void) 0; + } else { + A a{i}; + } +} + +void +f6 (int x) +{ + f1 (0); + f1 (x); // { dg-error "not a constant expression" } + f2 (0); + f2 (x); // { dg-error "not a constant expression" } + f3 (0); + f4 (x); + f4 (0); + f5 (x); + f5 (0); +} diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval-prop9.C b/gcc/testsuite/g++.dg/cpp2a/consteval-prop9.C new file mode 100644 index 00000000000..9c4a23389ce --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/consteval-prop9.C @@ -0,0 +1,67 @@ +// P2564R3 +// { dg-do compile { target c++20 } } + +consteval int +zero (int) +{ + return 0; +} + +constexpr int +f1 (auto i) +{ + return zero (i); +} + +constexpr int +f2 (auto i) +{ + return f1 (i); +} + +constexpr int +f3 (auto i) +{ + return f2 (i); +} + +constexpr int +f4 (auto i) +{ + return f3 (i); +} + +constexpr int +f5 (auto i) +{ + return f4 (i); +} + +constexpr int +f6 (auto) +{ + // This call is a constant expression, so don't promote f6. + return f5 (42); +} + +constexpr int +f7 (auto) +{ + // This call is a constant expression, so don't promote f7. + return zero (42); +} + +auto p1 = &f5; // { dg-error "taking address" } +static auto p2 = &f4; // { dg-error "taking address" } +auto p3 = &f6; +static auto p4 = &f6; +auto p5 = &f7; +static auto p6 = &f7; + +void +g () +{ + static auto q1 = &f4; // { dg-error "taking address" } + static auto q2 = &f6; + static auto q3 = &f7; +} diff --git a/gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C b/gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C index 16bc0b85395..fc268d44e1a 100644 --- a/gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C +++ b/gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C @@ -480,8 +480,8 @@ #ifndef __cpp_consteval # error "__cpp_consteval" -#elif __cpp_consteval != 201811 -# error "__cpp_consteval != 201811" +#elif __cpp_consteval != 202211L +# error "__cpp_consteval != 202211L" #endif #ifndef __cpp_concepts diff --git a/libstdc++-v3/testsuite/20_util/integer_comparisons/greater_equal_neg.cc b/libstdc++-v3/testsuite/20_util/integer_comparisons/greater_equal_neg.cc index 1a6bc52e149..2f265a213b4 100644 --- a/libstdc++-v3/testsuite/20_util/integer_comparisons/greater_equal_neg.cc +++ b/libstdc++-v3/testsuite/20_util/integer_comparisons/greater_equal_neg.cc @@ -20,17 +20,17 @@ #include -bool a = std::cmp_greater_equal('1', 49); // { dg-error "constexpr" } -bool b = std::cmp_greater_equal(50, '2'); // { dg-error "constexpr" } -bool c = std::cmp_greater_equal(2, L'2'); // { dg-error "constexpr" } -bool d = std::cmp_greater_equal(L'2', 2); // { dg-error "constexpr" } -bool e = std::cmp_greater_equal(true, 1); // { dg-error "constexpr" } -bool f = std::cmp_greater_equal(0, false); // { dg-error "constexpr" } -bool g = std::cmp_greater_equal(97, u8'a'); // { dg-error "constexpr" } -bool h = std::cmp_greater_equal(u8'a', 97); // { dg-error "constexpr" } -bool i = std::cmp_greater_equal(97, u'a'); // { dg-error "constexpr" } -bool j = std::cmp_greater_equal(u'a', 97); // { dg-error "constexpr" } -bool k = std::cmp_greater_equal(97, U'a'); // { dg-error "constexpr" } -bool l = std::cmp_greater_equal(U'a', 97); // { dg-error "constexpr" } +bool a = std::cmp_greater_equal('1', 49); // { dg-error "required from" } +bool b = std::cmp_greater_equal(50, '2'); // { dg-error "required from" } +bool c = std::cmp_greater_equal(2, L'2'); // { dg-error "required from" } +bool d = std::cmp_greater_equal(L'2', 2); // { dg-error "required from" } +bool e = std::cmp_greater_equal(true, 1); // { dg-error "required from" } +bool f = std::cmp_greater_equal(0, false); // { dg-error "required from" } +bool g = std::cmp_greater_equal(97, u8'a'); // { dg-error "required from" } +bool h = std::cmp_greater_equal(u8'a', 97); // { dg-error "required from" } +bool i = std::cmp_greater_equal(97, u'a'); // { dg-error "required from" } +bool j = std::cmp_greater_equal(u'a', 97); // { dg-error "required from" } +bool k = std::cmp_greater_equal(97, U'a'); // { dg-error "required from" } +bool l = std::cmp_greater_equal(U'a', 97); // { dg-error "required from" } // { dg-error "static assertion failed" "" { target *-*-* } 0 } diff --git a/libstdc++-v3/testsuite/20_util/integer_comparisons/greater_neg.cc b/libstdc++-v3/testsuite/20_util/integer_comparisons/greater_neg.cc index d7097750307..e21c0ef0ef4 100644 --- a/libstdc++-v3/testsuite/20_util/integer_comparisons/greater_neg.cc +++ b/libstdc++-v3/testsuite/20_util/integer_comparisons/greater_neg.cc @@ -20,17 +20,17 @@ #include -bool a = std::cmp_greater('1', 49); // { dg-error "constexpr" } -bool b = std::cmp_greater(50, '2'); // { dg-error "constexpr" } -bool c = std::cmp_greater(2, L'2'); // { dg-error "constexpr" } -bool d = std::cmp_greater(L'2', 2); // { dg-error "constexpr" } -bool e = std::cmp_greater(true, 1); // { dg-error "constexpr" } -bool f = std::cmp_greater(0, false); // { dg-error "constexpr" } -bool g = std::cmp_greater(97, u8'a'); // { dg-error "constexpr" } -bool h = std::cmp_greater(u8'a', 97); // { dg-error "constexpr" } -bool i = std::cmp_greater(97, u'a'); // { dg-error "constexpr" } -bool j = std::cmp_greater(u'a', 97); // { dg-error "constexpr" } -bool k = std::cmp_greater(97, U'a'); // { dg-error "constexpr" } -bool l = std::cmp_greater(U'a', 97); // { dg-error "constexpr" } +bool a = std::cmp_greater('1', 49); // { dg-error "required from" } +bool b = std::cmp_greater(50, '2'); // { dg-error "required from" } +bool c = std::cmp_greater(2, L'2'); // { dg-error "required from" } +bool d = std::cmp_greater(L'2', 2); // { dg-error "required from" } +bool e = std::cmp_greater(true, 1); // { dg-error "required from" } +bool f = std::cmp_greater(0, false); // { dg-error "required from" } +bool g = std::cmp_greater(97, u8'a'); // { dg-error "required from" } +bool h = std::cmp_greater(u8'a', 97); // { dg-error "required from" } +bool i = std::cmp_greater(97, u'a'); // { dg-error "required from" } +bool j = std::cmp_greater(u'a', 97); // { dg-error "required from" } +bool k = std::cmp_greater(97, U'a'); // { dg-error "required from" } +bool l = std::cmp_greater(U'a', 97); // { dg-error "required from" } // { dg-error "static assertion failed" "" { target *-*-* } 0 } diff --git a/libstdc++-v3/testsuite/20_util/integer_comparisons/less_equal_neg.cc b/libstdc++-v3/testsuite/20_util/integer_comparisons/less_equal_neg.cc index 3d83f2ee607..881e8cf57f4 100644 --- a/libstdc++-v3/testsuite/20_util/integer_comparisons/less_equal_neg.cc +++ b/libstdc++-v3/testsuite/20_util/integer_comparisons/less_equal_neg.cc @@ -20,17 +20,17 @@ #include -bool a = std::cmp_less_equal('1', 49); // { dg-error "constexpr" } -bool b = std::cmp_less_equal(50, '2'); // { dg-error "constexpr" } -bool c = std::cmp_less_equal(2, L'2'); // { dg-error "constexpr" } -bool d = std::cmp_less_equal(L'2', 2); // { dg-error "constexpr" } -bool e = std::cmp_less_equal(true, 1); // { dg-error "constexpr" } -bool f = std::cmp_less_equal(0, false); // { dg-error "constexpr" } -bool g = std::cmp_less_equal(97, u8'a'); // { dg-error "constexpr" } -bool h = std::cmp_less_equal(u8'a', 97); // { dg-error "constexpr" } -bool i = std::cmp_less_equal(97, u'a'); // { dg-error "constexpr" } -bool j = std::cmp_less_equal(u'a', 97); // { dg-error "constexpr" } -bool k = std::cmp_less_equal(97, U'a'); // { dg-error "constexpr" } -bool l = std::cmp_less_equal(U'a', 97); // { dg-error "constexpr" } +bool a = std::cmp_less_equal('1', 49); // { dg-error "required from" } +bool b = std::cmp_less_equal(50, '2'); // { dg-error "required from" } +bool c = std::cmp_less_equal(2, L'2'); // { dg-error "required from" } +bool d = std::cmp_less_equal(L'2', 2); // { dg-error "required from" } +bool e = std::cmp_less_equal(true, 1); // { dg-error "required from" } +bool f = std::cmp_less_equal(0, false); // { dg-error "required from" } +bool g = std::cmp_less_equal(97, u8'a'); // { dg-error "required from" } +bool h = std::cmp_less_equal(u8'a', 97); // { dg-error "required from" } +bool i = std::cmp_less_equal(97, u'a'); // { dg-error "required from" } +bool j = std::cmp_less_equal(u'a', 97); // { dg-error "required from" } +bool k = std::cmp_less_equal(97, U'a'); // { dg-error "required from" } +bool l = std::cmp_less_equal(U'a', 97); // { dg-error "required from" } // { dg-error "static assertion failed" "" { target *-*-* } 0 } diff --git a/libstdc++-v3/testsuite/20_util/optional/monadic/or_else_neg.cc b/libstdc++-v3/testsuite/20_util/optional/monadic/or_else_neg.cc index 16e94864f3b..329f3a6cc33 100644 --- a/libstdc++-v3/testsuite/20_util/optional/monadic/or_else_neg.cc +++ b/libstdc++-v3/testsuite/20_util/optional/monadic/or_else_neg.cc @@ -8,11 +8,11 @@ test01() { std::optional o; o.or_else([&] { return o; }); // OK - o.or_else([] { return std::optional(); }); // { dg-error "here" } - o.or_else([] { return 1; }); // { dg-error "here" } - std::move(o).or_else([] { return std::optional(); }); // { dg-error "here" } - std::move(o).or_else([] { return 1; }); // { dg-error "here" } -} + o.or_else([] { return std::optional(); }); + o.or_else([] { return 1; }); + std::move(o).or_else([] { return std::optional(); }); + std::move(o).or_else([] { return 1; }); +} // { dg-error "here" } // { dg-prune-output "static assertion failed" } diff --git a/libstdc++-v3/testsuite/23_containers/array/creation/3_neg.cc b/libstdc++-v3/testsuite/23_containers/array/creation/3_neg.cc index eeabdcece14..a47499245dc 100644 --- a/libstdc++-v3/testsuite/23_containers/array/creation/3_neg.cc +++ b/libstdc++-v3/testsuite/23_containers/array/creation/3_neg.cc @@ -24,8 +24,8 @@ void test01() { int two_dee[3][4]; - std::to_array(two_dee); // { dg-error "here" } -} + std::to_array(two_dee); +} // { dg-error "here" } void test02() @@ -34,8 +34,8 @@ test02() { int two_dee[3][4]; }; - std::to_array(X{}.two_dee); // { dg-error "here" } -} + std::to_array(X{}.two_dee); +} // { dg-error "here" } void test03() @@ -47,10 +47,10 @@ test03() }; MoveOnly mo[2]; - std::to_array(mo); // { dg-error "here" } + std::to_array(mo); const MoveOnly cmo[3]; - std::to_array(std::move(cmo)); // { dg-error "here" } -} + std::to_array(std::move(cmo)); +} // { dg-error "here" } // { dg-prune-output "static assertion failed" } diff --git a/libstdc++-v3/testsuite/23_containers/span/first_neg.cc b/libstdc++-v3/testsuite/23_containers/span/first_neg.cc index 8ed68296263..d611638cd28 100644 --- a/libstdc++-v3/testsuite/23_containers/span/first_neg.cc +++ b/libstdc++-v3/testsuite/23_containers/span/first_neg.cc @@ -25,6 +25,6 @@ test01() { int a[4]; std::span s(a); - s.first<5>(); // { dg-error "here" } -} + s.first<5>(); +} // { dg-error "here" } // { dg-error "static assertion failed" "" { target *-*-* } 0 } diff --git a/libstdc++-v3/testsuite/23_containers/span/last_neg.cc b/libstdc++-v3/testsuite/23_containers/span/last_neg.cc index 9c360d2650e..d133013a9ef 100644 --- a/libstdc++-v3/testsuite/23_containers/span/last_neg.cc +++ b/libstdc++-v3/testsuite/23_containers/span/last_neg.cc @@ -25,6 +25,6 @@ test01() { int a[2]; std::span s(a); - s.last<3>(); // { dg-error "here" } -} + s.last<3>(); +} // { dg-error "here" } // { dg-error "static assertion failed" "" { target *-*-* } 0 } diff --git a/libstdc++-v3/testsuite/23_containers/span/subspan_neg.cc b/libstdc++-v3/testsuite/23_containers/span/subspan_neg.cc index 205bafd39dd..2740d198e0e 100644 --- a/libstdc++-v3/testsuite/23_containers/span/subspan_neg.cc +++ b/libstdc++-v3/testsuite/23_containers/span/subspan_neg.cc @@ -25,23 +25,23 @@ test01() { int a[4]; std::span s(a); - s.subspan<5, 0>(); // { dg-error "here" } -} + s.subspan<5, 0>(); +} // { dg-error "here" } void test02() { int a[4]; std::span s(a); - s.subspan<3, 5>(); // { dg-error "here" } -} + s.subspan<3, 5>(); +} // { dg-error "here" } void test03() { int a[4]; std::span s(a); - s.subspan<3, 2>(); // { dg-error "here" } -} + s.subspan<3, 2>(); +} // { dg-error "here" } // { dg-error "static assertion failed" "" { target *-*-* } 0 }