From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: 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 E471C3858D20 for ; Tue, 29 Aug 2023 19:26:52 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org E471C3858D20 Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=redhat.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=redhat.com DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1693337212; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=/R/3ONGqsBshwHZbCvNdK/M8AmC04RXuYSQWntBuV3A=; b=ednwerLHo4bLe85opUxgPO32p+tI654ZVffB8jpJBeR9J89ntIjsw9FKhRRNjc7y1+XaVF 7TG3st7eRwddG3hViEOgCaqGS0NWgRk3M0oAKnYWjgV1WFIlFrZjDVpMQtllZ+4FQowpfP 87JWbSitf7MsfTVal1t6vcOt/wW468Y= Received: from mail-qt1-f198.google.com (mail-qt1-f198.google.com [209.85.160.198]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-684-TVbWhZTzOvyH5L8I-Npc5g-1; Tue, 29 Aug 2023 15:26:50 -0400 X-MC-Unique: TVbWhZTzOvyH5L8I-Npc5g-1 Received: by mail-qt1-f198.google.com with SMTP id d75a77b69052e-412190efed6so43326521cf.2 for ; Tue, 29 Aug 2023 12:26:50 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1693337209; x=1693942009; h=content-transfer-encoding:in-reply-to:content-language:references :to:subject:from:user-agent:mime-version:date:message-id :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=/R/3ONGqsBshwHZbCvNdK/M8AmC04RXuYSQWntBuV3A=; b=aKN/iW2N5DByq0IXBF20hi1IkvfD42i1rLIRTpn681/xhucsv8kNzwYDrGdDGe6m4D 6cRsWm5OrYN4kcCQHj+t0jkgg4Xcz6Y9eUvcxpqlIExgMx7gKKZQVTW8vW4CU5RMSRJq lBUCM3u9H4xr1d/+cMGM2/sZe7mBzACNvjHUDTqJvIZkXOFvSRWwAud530PnM9Mf62jv EXyb1UTK3z3Yd/4EUzt40D6+nmm3AVrFxepFhoW4NVB7K8r6YAirgRoptvPGKv8FQ+Pd ZNpP4kjEpl4P2Y5zyTxDa35MafhQQ2c7zL+q5I/28PHn1ShMoEORXTNUYf/WowJz4YEl AfBA== X-Gm-Message-State: AOJu0YzMdAepxtHM+ZacfM0QEDrs8f5AaYOdiObuGYiBlHx675+2+Xyt GZVaAK9Ea6weNfxhjsfMYKLUisCIYbLXIzUGTYm1M5G1JocR/rM0YHgnUt9auukYi3OfGHbLjyf z6dGHUD0NtpZMRw5cug== X-Received: by 2002:a05:622a:14d0:b0:410:344c:9309 with SMTP id u16-20020a05622a14d000b00410344c9309mr35287810qtx.30.1693337209230; Tue, 29 Aug 2023 12:26:49 -0700 (PDT) X-Google-Smtp-Source: AGHT+IHWt12I7ktmz2SW6MFi1GYAw/8RNUXAVp9cCYQMi5L0C784FBKDQOmdzRr1WmL5ZQo9c4UN8g== X-Received: by 2002:a05:622a:14d0:b0:410:344c:9309 with SMTP id u16-20020a05622a14d000b00410344c9309mr35287776qtx.30.1693337208502; Tue, 29 Aug 2023 12:26:48 -0700 (PDT) Received: from [192.168.1.108] (130-44-146-16.s12558.c3-0.arl-cbr1.sbo-arl.ma.cable.rcncustomer.com. [130.44.146.16]) by smtp.gmail.com with ESMTPSA id u10-20020ac8750a000000b004053bcffe49sm3243682qtq.9.2023.08.29.12.26.47 (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Tue, 29 Aug 2023 12:26:47 -0700 (PDT) Message-ID: <974dbde5-e1d8-90dc-a023-df214883403c@redhat.com> Date: Tue, 29 Aug 2023 15:26:46 -0400 MIME-Version: 1.0 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101 Thunderbird/102.14.0 From: Jason Merrill Subject: Re: [PATCH] c++: implement P2564, consteval needs to propagate up [PR107687] To: Marek Polacek , GCC Patches References: <20230823194904.1925591-1-polacek@redhat.com> In-Reply-To: <20230823194904.1925591-1-polacek@redhat.com> X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Language: en-US Content-Type: text/plain; charset=UTF-8; format=flowed Content-Transfer-Encoding: 7bit X-Spam-Status: No, score=-13.1 required=5.0 tests=BAYES_00,DKIMWL_WL_HIGH,DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,GIT_PATCH_0,KAM_SHORT,NICE_REPLY_A,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 List-Id: On 8/23/23 15:49, Marek Polacek wrote: > 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: Great, thanks! > 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. What I expected was that we would instantiate when we see a call, i.e. immediate_invocation_p would instantiate its argument if it's an immediate-escalating function. But... > 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. ...this sounds like a clever way to avoid the problems we tend to see with eager instantiation (e.g. constexpr-inst1.C). It still seems promising to me. > But I realized that we don't gimplify e.g. > > static auto p = &id; Indeed, just cp_fully_fold_init them, at least until we build the global init function. > and so we'd never detect taking the address of a consteval function. > Therefore this patch instantiates immediate-escalating functions > beforehand. Relatedly, in one of the testcases you have > +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); This is a case where we can't immediately resolve the question anyway, we need to remember where we've seen bare references to immediate-escalating functions outside of other immediate-escalating (or already immediate) functions, and complain later if they became consteval when instantiated. I don't see why this is a significant obstacle to doing escalation late. > * cp-tree.h (ADDR_EXPR_DENOTES_CALL_P): Define. Is this flag just for rejecting consteval-prop10.C? I think we should not try to reject it. This came up previously in the context of my patch for CWG2631, at which point I argued to CWG that it should be allowed, but nobody responded. I'll ping that thread now, but in the mean time let's not go out of our way to reject this testcase that seems to me pretty clearly allowed by the current wording: that expression invokes an immediate function, so it's an immediate invocation. > 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. */ Yes, that was to allow the consteval-prop2.C static_assert(is_not(5, is_even)); Since the operand of static_assert is manifestly constant-evaluated, it's OK that we name consteval is_even there. We currently use this function to trigger evaluating immediate evaluations, and later (through immediate_invocation_p) to trigger errors, and other constant-evaluation happens between those two, so in most cases it's OK that this function does not identify that situation. But there's another example in the paper, that this patch doesn't handle: consteval int g(int p) { return p; } template constexpr auto f(T) { return g; } int r = f(1)(2); // proposed ok int s = f(1)(2) + r; // error Here f(1) looks like an immediate invocation but is not itself constant, and GCC with this patch tries to immediately evaluate f(1) and gives an error. But since it's in a manifestly constant-evaluated expression, it isn't actually an immediate invocation, and giving an error is wrong. Maybe we want to stop doing immediate evaluation in build_over_call (and bot_replace) at all, and instead build the call as normal and perhaps error later in cp_fold_r? If I just disable the immediate_invocation_p blocks in build_over_call the testcase above works as the comments indicate. Maybe removing the bot_replace code would also help with 110997? > +/* 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); I think we might want to have a flag for this, like I did with -fnew-ttp-matching. Say, -fimmediate-escalation? > + /* [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); As discussed above, I think promoting or complaining here is now wrong because we might be in a manifestly constant-evaluated expression. At this point I think there's probably no point even trying to do evaluation here, we can just wait until cp_fold_r (or the later escalation time?). > 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 > > +/* 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; Return early if cfun is not immediate-escalating? > + /* Compiler-generated functions don't seem like good candidates for > + promoting. */ > + if (DECL_ARTIFICIAL (decl) || DECL_ABSTRACT_P (decl)) Why DECL_ABSTRACT_P? I think that will be set on pre-cloning constructors. > + 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; It will also hold pointers to all the function-internal expressions that we ever walk through, which seems like a problem. IMO it would be better to have a persistent pset that just tracks the functions, in addition to the usual tree-walk pset that gets thrown away each time. That is, if we need to do this walk at all; see the next comment. > + 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. It doesn't seem that dodgy to me. But if we're doing escalation at finish_function time rather than trying to defer to EOF we shouldn't need to do this tree walk at all, only instantiation, and the instantiation will deal with its own escalation (or not). And if we do end up deferring to EOF, we shouldn't need to do any instantiation because as you mentioned in the intro we will have already done instantiate_pending_templates. > + FIXME Instantiating a defaulted ctor breaks modules (ICE due to > + cp_function_chain being null). */ Sounds like this needs more investigation. Incidentally, defaulted functions aren't instantiated, they're synthesized. Which instantiate_constexpr_fns deals with, though it seems a bit odd to do a "walk" over a single node, maybe we should factor that bit out? > + /* 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. */ It seems unfortunate to call cxx_constant_value here and then again immediately in cp_fold_r in the case that call is indeed an immediate invocation. > + && (!call || cxx_constant_value (call, tf_none) == error_mark_node)) > + maybe_promote_function_to_consteval (current_function_decl); > +} > + > @@ -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)); > + } Can we share most of this code between PTRMEM_CST and ADDR_EXPR? > + 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); I think here we also want the usual cxx_constant_value errors explaining why the call is not constant. > 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) As mentioned above, I don't think we need this. > 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)))) Hmm, why don't we just do this for all functions? > 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. :-( This is an example that would work better if we can wait until EOF to do escalation. > 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 > +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" } I imagine clang doesn't promote because it optimizes away the unused reference to id. That seems like a clang bug. > +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}; > +} Maybe also a non-template function that gets an error? > +struct C { > + // C::C(int) promoted to consteval. > + consteval C (int) {}; Looks explicitly declared consteval, not promoted. :) > +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" } How about a variant of this where Y(int) is promoted to consteval? > +auto l1 = [](int i) constexpr { > + int t = id (i); > + return id (0); > +}; > + > +int (*p)(int) = l1; // { dg-error "returns address of immediate function" } What if the lambda isn't explicitly constexpr? > 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" } As above, I think we can/should accept this, so I don't see the purpose of ADDR_EXPR_DENOTES_CALL_P. > 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 > +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; What if we return id(i) in the if consteval? > + } > + return i; > +} > + > +constexpr int > +f8 (auto i) > +{ > + if not consteval { > + (void) 0; > + } else { > + auto p = &id; > + (void) p; And here. > 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" } > +} Also test that a function f3(auto) that calls f2() gets promoted? > 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 > +} I don't see any uses of g to test the comment? > +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" } Why is there an error on this line? > +} > + > +int i = hh(); // error: hh() is an immediate-escalating expression > + // outside of an immediate-escalating function ...and not on this line? > +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 > +} Needs use(s) of k to test the comment. > 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? Right, as mentioned above it seems that we need to keep track of forward references to immediate-escalating functions in case they become consteval. If we're promoting early, only ones that aren't defined yet. > +auto a2 = &f2; > +auto b2 = &f2; > + > +template > +constexpr int f2(T t) { > + return id(t); > +} > 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(){} Maybe a dg-message here, assuming the context for the above error mentions this line? > +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); No diagnostic on this line? > + > + X() = default; // { dg-error "used before its definition" } > +}; > + > +void > +test () > +{ > + [[maybe_unused]] X x; > +} > 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. Is that still true with the current patch? If so, it should be fixed. In any case, it should be tested. > +void > +f3 (int i) > +{ > + A a{i}; // { dg-error "not a constant expression" } > +} > 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" } This seems like a significant regression in source location quality. Likewise for several other libstdc++ tests. Jason