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.129.124]) by sourceware.org (Postfix) with ESMTPS id 0C2E63858C2F for ; Tue, 27 Sep 2022 21:44:17 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org 0C2E63858C2F 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=1664315056; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=E4IbS8Te1NfcqD5rv0L8Wz+JFChkZAnzPiuIyzbz6Tc=; b=Vr7+7uTrauaGG3+07gWW3MzgHRg3ZGn37l3XgmajtZWx75DqskJNGW49pBoD2i5ea957gk HOAbXJj2FbU1f56vzr5A0/kkpwS6S7cCU9QiAR77g4n+BdJ4nUJkNMzyzQHDT8uoJuhUVt a4buyfBhgZmsNcFr6HxWUWPZ3ZjAcUE= Received: from mail-qt1-f197.google.com (mail-qt1-f197.google.com [209.85.160.197]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_128_GCM_SHA256) id us-mta-487-EJa6sPu-M0W8YJzflviRzQ-1; Tue, 27 Sep 2022 17:44:15 -0400 X-MC-Unique: EJa6sPu-M0W8YJzflviRzQ-1 Received: by mail-qt1-f197.google.com with SMTP id u9-20020a05622a14c900b0035cc7e8cbaeso7719474qtx.19 for ; Tue, 27 Sep 2022 14:44:15 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:in-reply-to:from:references:cc:to :content-language:subject:user-agent:mime-version:date:message-id :x-gm-message-state:from:to:cc:subject:date; bh=E4IbS8Te1NfcqD5rv0L8Wz+JFChkZAnzPiuIyzbz6Tc=; b=0fAbubJHfH5bgJMBwDHthwVNmlc58WTYuEj1rfQ4AI86EfGPopW0uJLC6CaTDzCLeE Si7wq0PpDnZaS8+b/IOf7vb8Mt5jLVvJq/BJk95GYg7ks5cm02kWSkFD0IYEiM0vh/H+ lYaC2NqLdki/LbtTtQUESy/1VvKLQ/foXoosfPpLOuzNM9UpY3oC97s6oh1u2CogAPQU gCAdfHr8UsWq8zBbKnbA+KoujATW8Z0he/KMb/nd/MqHke3wXsbYhbe7k87jw92NcLgi YDeexYwt7BBp5Kp2AzgGU3sM/OvcOsHauNBhGgu9IjExiuMtxBsI5KOYIXF0N7CeZnCW 55xg== X-Gm-Message-State: ACrzQf2JBb7jQzJivUpm//HC0/7r6IKbW0CIZ18/GhakySHU2wAhI7EA fgOPbd9LgmIHx/i4qoNpjTI74linqS4OHt7scCbSE0sTd5AQoejaTUTuJqsmtDL9/N+WwA+20HX mw8ti0GSW0jGv5hgnYg== X-Received: by 2002:a05:622a:148e:b0:35c:db7a:168d with SMTP id t14-20020a05622a148e00b0035cdb7a168dmr24924905qtx.171.1664315054298; Tue, 27 Sep 2022 14:44:14 -0700 (PDT) X-Google-Smtp-Source: AMsMyM6jJJY9T8W5rXwnouL9P8wZnR2LEUkygMZwSQGwRAtb+MPyrq0jl6QNnuxwjmjm4tJQXoqKdQ== X-Received: by 2002:a05:622a:148e:b0:35c:db7a:168d with SMTP id t14-20020a05622a148e00b0035cdb7a168dmr24924872qtx.171.1664315053587; Tue, 27 Sep 2022 14:44:13 -0700 (PDT) Received: from [192.168.1.101] (130-44-159-43.s15913.c3-0.arl-cbr1.sbo-arl.ma.cable.rcncustomer.com. [130.44.159.43]) by smtp.gmail.com with ESMTPSA id cb20-20020a05622a1f9400b0035a7070e909sm1600831qtb.38.2022.09.27.14.44.12 (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Tue, 27 Sep 2022 14:44:12 -0700 (PDT) Message-ID: <39fb63d9-8128-512b-523d-f575317cf7c2@redhat.com> Date: Tue, 27 Sep 2022 17:44:12 -0400 MIME-Version: 1.0 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:91.0) Gecko/20100101 Thunderbird/91.13.1 Subject: Re: [PATCH v3] c++: Implement C++23 P2266R1, Simpler implicit move [PR101165] To: Marek Polacek Cc: GCC Patches References: <20220903164200.17908-1-polacek@redhat.com> <9f9f6d62-c3c8-083e-d30d-808076c01eca@redhat.com> <548cdcb9-abed-ef66-0dfd-5264cfa4ff3c@redhat.com> From: Jason Merrill In-Reply-To: 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=-11.7 required=5.0 tests=BAYES_00,DKIMWL_WL_HIGH,DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,GIT_PATCH_0,KAM_SHORT,KAM_SOMETLD_ARE_BAD_TLD,NICE_REPLY_A,RCVD_IN_DNSWL_LOW,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 9/27/22 16:26, Marek Polacek wrote: > On Mon, Sep 26, 2022 at 01:29:35PM -0400, Jason Merrill wrote: >> On 9/20/22 14:19, Marek Polacek wrote: >>>>> There's one FIXME in elision1.C:five, which we should compile but reject >>>>> with "passing 'Mutt' as 'this' argument discards qualifiers". That >>>>> looks bogus to me, I think I'll open a PR for it. >>>> >>>> Let's fix that now, I think. >>> >>> OK, copypasting this bit from the other email so that we can have one >>> thread: >>> >>>> Can of worms. The test is >>>> >>>> struct Mutt { >>>> operator int*() &&; >>>> }; >>>> >>>> int* five(Mutt x) { >>>> return x; // OK since C++20 because P1155 >>>> } >>>> >>>> 'x' should be treated as an rvalue, therefore the operator fn taking >>>> an rvalue ref to Mutt should be used to convert 'x' to int*. We fail >>>> because we don't treat 'x' as an rvalue because the function doesn't >>>> return a class. So the patch should be just >>>> >>>> --- a/gcc/cp/typeck.cc >>>> +++ b/gcc/cp/typeck.cc >>>> @@ -10875,10 +10875,7 @@ check_return_expr (tree retval, bool *no_warning) >>>> Note that these conditions are similar to, but not as strict as, >>>> the conditions for the named return value optimization. */ >>>> bool converted = false; >>>> - tree moved; >>>> - /* This is only interesting for class type. */ >>>> - if (CLASS_TYPE_P (functype) >>>> - && (moved = treat_lvalue_as_rvalue_p (retval, /*return*/true))) >>>> + if (tree moved = treat_lvalue_as_rvalue_p (retval, /*return*/true)) >>>> { >>>> if (cxx_dialect < cxx20) >>>> { >>>> >>>> which fixes the test, but breaks a lot of middle-end warnings. For instance >>>> g++.dg/warn/nonnull3.C, where the patch above changes .gimple: >>>> >>>> bool A::foo (struct A * const this, <<< Unknown tree: offset_type >>> p) >>>> { >>>> - bool D.2146; >>>> + bool D.2150; >>>> - D.2146 = p != -1; >>>> - return D.2146; >>>> + p.0_1 = p; >>>> + D.2150 = p.0_1 != -1; >>>> + return D.2150; >>>> } >>>> >>>> and we no longer get the warning. I thought maybe I could undo the implicit >>>> rvalue conversion in cp_fold, when it sees implicit_rvalue_p, but that didn't >>>> work. So currently I'm stuck. Should we try to figure this out or push aside? >>> >>>> Can you undo the implicit rvalue conversion within check_return_expr, >>>> where we can still refer back to the original expression? >>> >>> Unfortunately no, one problem is that treat_lvalue_as_rvalue_p modifies >>> the underlying decl by setting TREE_ADDRESSABLE, which then presumably >>> breaks warnings. That is, treat_ can get 'VCE(x)' and produce >>> '*NLE<(X&) &x>' where 'x' flags have been modified, since we're taking >>> x's address. >>> >>>> Or avoid the rvalue conversion if the return type is scalar? >>> >>> I wish :(. In the 'five' example above, the return type is a pointer, >>> a scalar, but we have to convert to rvalue. >> >> OK, then when both the return type and the type of the return value are >> scalar? > > Good news! After more poking it seems we only need to do the rvalue > conversion when either to/from types is a class/reference! That is, > if either is a non-scalar type. And that doesn't upset the middle-end > diagnostics! I'm still limiting the broader conversion to C++23, but > the whole condition could be: > > if ((!SCALAR_TYPE_P (functype) || !SCALAR_TYPE_P (rettype)) > && treat_lvalue_as_rvalue_p ()) > ... > > Therefore I think we don't need ... > >>> It's sort of sad that this corner case causes so much trouble: I think >>> we have to do the conversion only because of ref-qualifiers, so that >>> the correct operator function is chosen. >>> >>> A way out may be setting a flag on the V_C_E that indicates it is an >>> rvalue, rather than performing the conversion above. This was your >>> idea so I don't want to take credit for it. Should I go ahead and >>> try it? >> >> Sure, probably in build_static_cast_1. > > ... this, after all, which is just fantastic. Besides the check_return_expr > hunk and removing a FIXME there are no other changes. Yay! > Next step: remove the double overload. > > Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk? > > -- >8 -- > This patch implements https://wg21.link/p2266, which, once again, > changes the implicit move rules. Here's a brief summary of various > changes in this area: > > r125211: Introduced moving from certain lvalues when returning them > r171071: CWG 1148, enable move from value parameter on return > r212099: CWG 1579, it's OK to call a converting ctor taking an rvalue > r251035: CWG 1579, do maybe-rvalue overload resolution twice > r11-2411: Avoid calling const copy ctor on implicit move > r11-2412: C++20 implicit move changes, remove the fallback overload > resolution, allow move on throw of parameters and implicit > move of rvalue references > > P2266 enables the implicit move even for functions that return references. > That is, we will now perform a move in > > X&& foo (X&& x) { > return x; > } > > P2266 also removes the fallback overload resolution, but this was > resolved by r11-2412: we only do convert_for_initialization with > LOOKUP_PREFER_RVALUE in C++17 and older. > P2266 also says that a returned move-eligible id-expression is always an > xvalue. This required some further short, but nontrivial changes, > especially when it comes to deduction, because we have to pay attention > to whether we have auto, auto&& (which is like T&&), or decltype(auto) > with (un)parenthesized argument. In C++23, > > decltype(auto) f(int&& x) { return (x); } > auto&& f(int x) { return x; } > > both should deduce to 'int&&' but > > decltype(auto) f(int x) { return x; } > > should deduce to 'int'. A cornucopia of tests attached. I've also > verified that we behave like clang++. > > xvalue_p seemed to be broken: since the introduction of clk_implicit_rval, > it cannot use '==' when checking for clk_rvalueref. > > Since this change breaks code, it's only enabled in C++23. In > particular, this code will not compile in C++23: > > int& g(int&& x) { return x; } > > because x is now treated as an rvalue, and you can't bind a non-const lvalue > reference to an rvalue. > > This patch also fixes PR106882 (the check_return_expr changes). > > PR c++/101165 > PR c++/106882 > > gcc/c-family/ChangeLog: > > * c-cppbuiltin.cc (c_cpp_builtins): Define __cpp_implicit_move. > > gcc/cp/ChangeLog: > > * call.cc (reference_binding): Check clk_implicit_rval in C++20 only. > * cp-tree.h (unparenthesized_id_or_class_member_access_p): Declare. > * pt.cc (unparenthesized_id_or_class_member_access_p): New function, > broken out of... > (do_auto_deduction): ...here. Use it. In C++23, maybe call > treat_lvalue_as_rvalue_p. > * tree.cc (xvalue_p): Check & clk_rvalueref, not == clk_rvalueref. > * typeck.cc (check_return_expr): Allow implicit move for functions > returning a reference as well, or when the return value type is not > a scalar type. > > gcc/testsuite/ChangeLog: > > * g++.dg/conversion/pr41426.C: Add dg-error for C++23. > * g++.dg/cpp0x/elision_weak.C: Likewise. > * g++.dg/cpp0x/move-return3.C: Only link in c++20_down. > * g++.dg/cpp1y/decltype-auto2.C: Add dg-error for C++23. > * g++.dg/cpp1y/lambda-generic-89419.C: Likewise. > * g++.dg/cpp23/feat-cxx2b.C: Test __cpp_implicit_move. > * g++.dg/gomp/pr56217.C: Only compile in c++20_down. > * g++.dg/warn/Wno-return-local-addr.C: Add dg-error for C++23. > * g++.dg/warn/Wreturn-local-addr.C: Adjust dg-error. > * g++.old-deja/g++.brendan/crash55.C: Add dg-error for C++23. > * g++.old-deja/g++.jason/temporary2.C: Likewise. > * g++.old-deja/g++.mike/p2846b.C: Only run in c++20_down. > * g++.dg/cpp1y/decltype-auto6.C: New test. > * g++.dg/cpp23/decltype1.C: New test. > * g++.dg/cpp23/decltype2.C: New test. > * g++.dg/cpp23/elision1.C: New test. > * g++.dg/cpp23/elision2.C: New test. > * g++.dg/cpp23/elision3.C: New test. > * g++.dg/cpp23/elision4.C: New test. > * g++.dg/cpp23/elision5.C: New test. > * g++.dg/cpp23/elision6.C: New test. > * g++.dg/cpp23/elision7.C: New test. > --- > gcc/c-family/c-cppbuiltin.cc | 1 + > gcc/cp/call.cc | 6 +- > gcc/cp/cp-tree.h | 1 + > gcc/cp/pt.cc | 50 ++++++-- > gcc/cp/tree.cc | 2 +- > gcc/cp/typeck.cc | 9 +- > gcc/testsuite/g++.dg/conversion/pr41426.C | 10 +- > gcc/testsuite/g++.dg/cpp0x/elision_weak.C | 4 +- > gcc/testsuite/g++.dg/cpp0x/move-return3.C | 3 +- > gcc/testsuite/g++.dg/cpp1y/decltype-auto2.C | 2 +- > gcc/testsuite/g++.dg/cpp1y/decltype-auto6.C | 19 +++ > .../g++.dg/cpp1y/lambda-generic-89419.C | 6 +- > gcc/testsuite/g++.dg/cpp23/decltype1.C | 113 +++++++++++++++++ > gcc/testsuite/g++.dg/cpp23/decltype2.C | 49 ++++++++ > gcc/testsuite/g++.dg/cpp23/elision1.C | 114 ++++++++++++++++++ > gcc/testsuite/g++.dg/cpp23/elision2.C | 46 +++++++ > gcc/testsuite/g++.dg/cpp23/elision3.C | 16 +++ > gcc/testsuite/g++.dg/cpp23/elision4.C | 38 ++++++ > gcc/testsuite/g++.dg/cpp23/elision5.C | 53 ++++++++ > gcc/testsuite/g++.dg/cpp23/elision6.C | 20 +++ > gcc/testsuite/g++.dg/cpp23/elision7.C | 72 +++++++++++ > gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C | 6 + > gcc/testsuite/g++.dg/gomp/pr56217.C | 4 +- > .../g++.dg/warn/Wno-return-local-addr.C | 2 +- > .../g++.dg/warn/Wreturn-local-addr.C | 2 +- > .../g++.old-deja/g++.brendan/crash55.C | 3 +- > .../g++.old-deja/g++.jason/temporary2.C | 2 +- > gcc/testsuite/g++.old-deja/g++.mike/p2846b.C | 4 +- > 28 files changed, 622 insertions(+), 35 deletions(-) > create mode 100644 gcc/testsuite/g++.dg/cpp1y/decltype-auto6.C > create mode 100644 gcc/testsuite/g++.dg/cpp23/decltype1.C > create mode 100644 gcc/testsuite/g++.dg/cpp23/decltype2.C > create mode 100644 gcc/testsuite/g++.dg/cpp23/elision1.C > create mode 100644 gcc/testsuite/g++.dg/cpp23/elision2.C > create mode 100644 gcc/testsuite/g++.dg/cpp23/elision3.C > create mode 100644 gcc/testsuite/g++.dg/cpp23/elision4.C > create mode 100644 gcc/testsuite/g++.dg/cpp23/elision5.C > create mode 100644 gcc/testsuite/g++.dg/cpp23/elision6.C > create mode 100644 gcc/testsuite/g++.dg/cpp23/elision7.C > > diff --git a/gcc/c-family/c-cppbuiltin.cc b/gcc/c-family/c-cppbuiltin.cc > index ca5f500e079..d4de5a0dc57 100644 > --- a/gcc/c-family/c-cppbuiltin.cc > +++ b/gcc/c-family/c-cppbuiltin.cc > @@ -1082,6 +1082,7 @@ c_cpp_builtins (cpp_reader *pfile) > cpp_define (pfile, "__cpp_multidimensional_subscript=202110L"); > cpp_define (pfile, "__cpp_named_character_escapes=202207L"); > cpp_define (pfile, "__cpp_static_call_operator=202207L"); > + cpp_define (pfile, "__cpp_implicit_move=202207L"); > } > if (flag_concepts) > { > diff --git a/gcc/cp/call.cc b/gcc/cp/call.cc > index fc86b74a5a4..3506b0fcfbb 100644 > --- a/gcc/cp/call.cc > +++ b/gcc/cp/call.cc > @@ -1880,8 +1880,10 @@ reference_binding (tree rto, tree rfrom, tree expr, bool c_cast_p, int flags, > > /* Nor the reverse. */ > if (!is_lvalue && !TYPE_REF_IS_RVALUE (rto) > - /* Unless it's really an lvalue. */ > - && !(cxx_dialect >= cxx20 > + /* Unless it's really a C++20 lvalue being treated as an xvalue. > + But in C++23, such an expression is just an xvalue, not a special > + lvalue, so the binding is once again ill-formed. */ > + && !(cxx_dialect == cxx20 > && (gl_kind & clk_implicit_rval)) > && (!CP_TYPE_CONST_NON_VOLATILE_P (to) > || (flags & LOOKUP_NO_RVAL_BIND)) > diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h > index 99b486b8002..19bbfbc557f 100644 > --- a/gcc/cp/cp-tree.h > +++ b/gcc/cp/cp-tree.h > @@ -7292,6 +7292,7 @@ extern tree make_constrained_decltype_auto (tree, tree); > extern tree make_template_placeholder (tree); > extern bool template_placeholder_p (tree); > extern bool ctad_template_p (tree); > +extern bool unparenthesized_id_or_class_member_access_p (tree); > extern tree do_auto_deduction (tree, tree, tree, > tsubst_flags_t > = tf_warning_or_error, > diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc > index 1c1e5735743..2d83dfd6954 100644 > --- a/gcc/cp/pt.cc > +++ b/gcc/cp/pt.cc > @@ -30408,6 +30408,26 @@ do_class_deduction (tree ptype, tree tmpl, tree init, > cp_type_quals (ptype)); > } > > +/* Return true if INIT is an unparenthesized id-expression or an > + unparenthesized class member access. Used for the argument of > + decltype(auto). */ > + > +bool > +unparenthesized_id_or_class_member_access_p (tree init) > +{ > + STRIP_ANY_LOCATION_WRAPPER (init); > + > + /* We need to be able to tell '(r)' and 'r' apart (when it's of > + reference type). Only the latter is an id-expression. */ > + if (REFERENCE_REF_P (init) > + && !REF_PARENTHESIZED_P (init)) > + init = TREE_OPERAND (init, 0); > + return (DECL_P (init) > + || ((TREE_CODE (init) == COMPONENT_REF > + || TREE_CODE (init) == SCOPE_REF) > + && !REF_PARENTHESIZED_P (init))); > +} > + > /* Replace occurrences of 'auto' in TYPE with the appropriate type deduced > from INIT. AUTO_NODE is the TEMPLATE_TYPE_PARM used for 'auto' in TYPE. > The CONTEXT determines the context in which auto deduction is performed > @@ -30443,6 +30463,23 @@ do_auto_deduction (tree type, tree init, tree auto_node, > auto_node. */ > complain &= ~tf_partial; > > + /* In C++23, we must deduce the type to int&& for code like > + decltype(auto) f(int&& x) { return (x); } > + or > + auto&& f(int x) { return x; } > + so we use treat_lvalue_as_rvalue_p. But don't do it for > + decltype(auto) f(int x) { return x; } > + where we should deduce 'int' rather than 'int&&'; transmogrifying > + INIT to an rvalue would break that. */ > + tree r; > + if (cxx_dialect >= cxx23 > + && context == adc_return_type > + && (!AUTO_IS_DECLTYPE (auto_node) > + || !unparenthesized_id_or_class_member_access_p (init)) > + && (r = treat_lvalue_as_rvalue_p (maybe_undo_parenthesized_ref (init), > + /*return*/true))) > + init = r; > + > if (tree tmpl = CLASS_PLACEHOLDER_TEMPLATE (auto_node)) > /* C++17 class template argument deduction. */ > return do_class_deduction (type, tmpl, init, flags, complain); > @@ -30504,18 +30541,7 @@ do_auto_deduction (tree type, tree init, tree auto_node, > } > else if (AUTO_IS_DECLTYPE (auto_node)) > { > - /* Figure out if INIT is an unparenthesized id-expression or an > - unparenthesized class member access. */ > - tree stripped_init = tree_strip_any_location_wrapper (init); > - /* We need to be able to tell '(r)' and 'r' apart (when it's of > - reference type). Only the latter is an id-expression. */ > - if (REFERENCE_REF_P (stripped_init) > - && !REF_PARENTHESIZED_P (stripped_init)) > - stripped_init = TREE_OPERAND (stripped_init, 0); > - const bool id = (DECL_P (stripped_init) > - || ((TREE_CODE (stripped_init) == COMPONENT_REF > - || TREE_CODE (stripped_init) == SCOPE_REF) > - && !REF_PARENTHESIZED_P (stripped_init))); > + const bool id = unparenthesized_id_or_class_member_access_p (init); > tree deduced = finish_decltype_type (init, id, complain); > deduced = canonicalize_type_argument (deduced, complain); > if (deduced == error_mark_node) > diff --git a/gcc/cp/tree.cc b/gcc/cp/tree.cc > index d0bd41ae5a0..ea4dfc651bb 100644 > --- a/gcc/cp/tree.cc > +++ b/gcc/cp/tree.cc > @@ -382,7 +382,7 @@ obvalue_p (const_tree ref) > bool > xvalue_p (const_tree ref) > { > - return (lvalue_kind (ref) == clk_rvalueref); > + return (lvalue_kind (ref) & clk_rvalueref); > } > > /* True if REF is a bit-field. */ > diff --git a/gcc/cp/typeck.cc b/gcc/cp/typeck.cc > index 4854b983765..771331b6df9 100644 > --- a/gcc/cp/typeck.cc > +++ b/gcc/cp/typeck.cc > @@ -11042,8 +11042,13 @@ check_return_expr (tree retval, bool *no_warning) > the conditions for the named return value optimization. */ > bool converted = false; > tree moved; > - /* This is only interesting for class type. */ > - if (CLASS_TYPE_P (functype) > + /* Until C++23, this was only interesting for class type... */ > + if ((CLASS_TYPE_P (functype) > + /* ...but in C++23, we should do the below when we're converting > + from/to a class/reference (a non-scalar type). */ > + || (cxx_dialect >= cxx23 > + && (!SCALAR_TYPE_P (functype) > + || !SCALAR_TYPE_P (TREE_TYPE (retval))))) You might reformat this as (cxx_dialect < cxx23 ? CLASS... : (!SCALAR... > && (moved = treat_lvalue_as_rvalue_p (retval, /*return*/true))) > { > if (cxx_dialect < cxx20) > diff --git a/gcc/testsuite/g++.dg/conversion/pr41426.C b/gcc/testsuite/g++.dg/conversion/pr41426.C > index 5493a91ecfa..b4ecbca5f3a 100644 > --- a/gcc/testsuite/g++.dg/conversion/pr41426.C > +++ b/gcc/testsuite/g++.dg/conversion/pr41426.C > @@ -11,19 +11,20 @@ struct A > A g1() > { > float f[] = {1.1f, 2.3f}; > - return f; > + return f; // { dg-error "cannot bind non-const" "" { target c++23 } } > } > > const A &g3() > { > float f[] = {1.1f, 2.3f}; > - return f; // { dg-warning "returning reference to temporary" } > + return f; // { dg-warning "returning reference to temporary" "" { target c++20_down } } > +// { dg-error "non-const lvalue|invalid user-defined conversion" "" { target c++23 } .-1 } > } > > A &g4() > { > float f[] = {1.1f, 2.3f}; > - return f; // { dg-error "cannot bind non-const lvalue ref" } > + return f; // { dg-error "cannot bind non-const lvalue ref|invalid user-defined conversion" } > } > > struct B > @@ -35,6 +36,5 @@ struct B > B g2() > { > int c[10]; > - return c; > + return c; // { dg-error "non-const lvalue" "" { target c++23 } } > } > - > diff --git a/gcc/testsuite/g++.dg/cpp0x/elision_weak.C b/gcc/testsuite/g++.dg/cpp0x/elision_weak.C > index e8ba7551d84..ddd12743130 100644 > --- a/gcc/testsuite/g++.dg/cpp0x/elision_weak.C > +++ b/gcc/testsuite/g++.dg/cpp0x/elision_weak.C > @@ -9,11 +9,11 @@ struct S > S f() > { > S s; > - return s; > + return s; // { dg-error "cannot bind non-const lvalue reference" "" { target c++23 } } > } > > void g() > { > S s; > - throw s; > + throw s; // { dg-error "cannot bind non-const lvalue reference" "" { target c++23 } } > } > diff --git a/gcc/testsuite/g++.dg/cpp0x/move-return3.C b/gcc/testsuite/g++.dg/cpp0x/move-return3.C > index c79f0591936..30a936fb35a 100644 > --- a/gcc/testsuite/g++.dg/cpp0x/move-return3.C > +++ b/gcc/testsuite/g++.dg/cpp0x/move-return3.C > @@ -1,6 +1,7 @@ > // PR c++/91212 > // Test that C++11 implicit move semantics don't call the const copy. > -// { dg-do link } > +// In C++23, we call #2. I guess that behavior is tested by elision2.C:twelve()? > +// { dg-do link { target c++20_down } } > > struct T { int i; }; > > diff --git a/gcc/testsuite/g++.dg/cpp1y/decltype-auto2.C b/gcc/testsuite/g++.dg/cpp1y/decltype-auto2.C > index 56e011e36f4..24b32edfacf 100644 > --- a/gcc/testsuite/g++.dg/cpp1y/decltype-auto2.C > +++ b/gcc/testsuite/g++.dg/cpp1y/decltype-auto2.C > @@ -8,5 +8,5 @@ auto constexpr RtoL1(T&& r) -> decltype(auto) { > int main() { > int t; > int x{3}; > - decltype (RtoL1(x+0)) y = t; > + decltype (RtoL1(x+0)) y = t; // { dg-error "cannot bind rvalue reference" "" { target c++23 } } > } > diff --git a/gcc/testsuite/g++.dg/cpp1y/decltype-auto6.C b/gcc/testsuite/g++.dg/cpp1y/decltype-auto6.C > new file mode 100644 > index 00000000000..da53278645c > --- /dev/null > +++ b/gcc/testsuite/g++.dg/cpp1y/decltype-auto6.C > @@ -0,0 +1,19 @@ > +// PR c++/101165 - P2266R1 - Simpler implicit move > +// { dg-do compile { target c++14 } } > +// A variant of cxx23/elision1.C:eight, just with (). > + > +struct Widget { > + Widget(Widget&&); > +}; > + > +Widget val(); > + > +decltype(auto) > +foo () > +{ > + decltype(auto) x = val(); // OK, x is Widget > + // We deduce the return type to int&&, therefore we're doing something > + // we ought not to be doing -- returning a reference to a local variable! > + // In C++20, we deduce to int&, but that has the same problem! > + return (x); // { dg-warning "reference to local variable" } > +} > diff --git a/gcc/testsuite/g++.dg/cpp1y/lambda-generic-89419.C b/gcc/testsuite/g++.dg/cpp1y/lambda-generic-89419.C > index 46ce909f3b8..8e64d4e64ab 100644 > --- a/gcc/testsuite/g++.dg/cpp1y/lambda-generic-89419.C > +++ b/gcc/testsuite/g++.dg/cpp1y/lambda-generic-89419.C > @@ -2,7 +2,7 @@ > // { dg-do compile { target c++14 } } > > struct A; > -struct B { > +struct B { // { dg-error "cannot bind" "" { target c++23 } } > struct C { C (); C (C &); } b; > }; > struct D { A operator* (); }; > @@ -13,12 +13,12 @@ struct E { > auto bar () { return e; } > D e; > }; > -struct F { B f; int g; }; > +struct F { B f; int g; }; // { dg-error "use of deleted function" "" { target c++23 } } > > int > main () > { > E e; > auto f = *e.bar (); > - auto i = [&] { F g; g.g = 1; auto h = [&](auto) { g.g = 0; }; f.foo (h); return g; }; > + auto i = [&] { F g; g.g = 1; auto h = [&](auto) { g.g = 0; }; f.foo (h); return g; }; // { dg-error "use of deleted function" "" { target c++23 } } > } > diff --git a/gcc/testsuite/g++.dg/cpp23/decltype1.C b/gcc/testsuite/g++.dg/cpp23/decltype1.C > new file mode 100644 > index 00000000000..6f3cd0d45d5 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/cpp23/decltype1.C > @@ -0,0 +1,113 @@ > +// PR c++/101165 - P2266R1 - Simpler implicit move > +// { dg-do compile { target c++23 } } > +// Tests from P2266R1, decltype-related changes in > +// $ 3.2.1. Interaction with decltype and decltype(auto) > + > +template > +struct same_type { static const bool value = false; }; > + > +template > +struct same_type { static const bool value = true; }; > + > +auto f1(int x) -> decltype(x) { return (x); } > +static_assert(same_type::value); > +auto f2(int x) -> decltype((x)) { return (x); } // { dg-error "cannot bind" } > +static_assert(same_type::value); > +auto f3(int x) -> decltype(auto) { return (x); } // { dg-warning "reference to local variable" } > +static_assert(same_type::value); > +auto g1(int x) -> decltype(x) { return x; } > +static_assert(same_type::value); > +auto g2(int x) -> decltype((x)) { return x; } // { dg-error "cannot bind" } > +static_assert(same_type::value); > +auto g3(int x) -> decltype(auto) { return x; } > +static_assert(same_type::value); > + > +// Note that f2 and g2 are well-formed in C++20, but we propose to make > +// f2 and g2 ill-formed, because they attempt to bind an lvalue reference > +// to a move-eligible xvalue expression. > + > +struct X { }; > + > +auto > +f4 (X x) > +{ > + return x; > +} > +static_assert(same_type::value); > + > +auto& > +f5 (X x) > +{ > + return x; // { dg-error "cannot bind non-const lvalue reference" } > +} > +static_assert(same_type::value); > + > +auto&& > +f6 (X x) > +{ > + return x; // { dg-warning "reference to local variable" } > +} > +static_assert(same_type::value); > + > +auto > +f7 (X x) > +{ > + return (x); > +} > +static_assert(same_type::value); > + > +auto& > +f8 (X x) > +{ > + return (x); // { dg-error "cannot bind non-const lvalue reference" } > +} > +static_assert(same_type::value); > + > +auto&& > +f9 (X x) > +{ > + return (x); // { dg-warning "reference to local variable" } > +} > +static_assert(same_type::value); > + > +decltype(auto) > +f10 (X x) > +{ > + return x; > +} > +static_assert(same_type::value); > + > +decltype(auto) > +f11 (X x) > +{ > + return (x); // { dg-warning "reference to local variable" } > +} > +static_assert(same_type::value); > + > +decltype(auto) > +f12 (X& x) > +{ > + return x; > +} > +static_assert(same_type::value); > + > +decltype(auto) > +f13 (X& x) > +{ > + return (x); > +} > +static_assert(same_type::value); > + > +decltype(auto) > +f14 (X&& x) > +{ > + return x; > +} > +static_assert(same_type::value); > + > +decltype(auto) > +f15 (X&& x) > +{ > + return (x); > +} > +static_assert(same_type::value); > diff --git a/gcc/testsuite/g++.dg/cpp23/decltype2.C b/gcc/testsuite/g++.dg/cpp23/decltype2.C > new file mode 100644 > index 00000000000..84679c48f82 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/cpp23/decltype2.C > @@ -0,0 +1,49 @@ > +// PR c++/101165 - P2266R1 - Simpler implicit move > +// { dg-do compile { target c++23 } } > +// Test decltype(auto) more. > + > +template > +struct same_type { static const bool value = false; }; > + > +template > +struct same_type { static const bool value = true; }; > + > +struct Widget { > + int x; > +}; > + > +Widget wg; > + > +decltype(auto) fn0(Widget&& x) { > + return (::wg); > +} > +static_assert(same_type::value); > + > +decltype(auto) fn1(Widget&& x) { > + return ::wg; > +} > +static_assert(same_type::value); > + > +decltype(auto) fn2() { > + Widget w; > + return w; > +} > +static_assert(same_type::value); > + > +decltype(auto) fn3() { > + Widget w; > + return (w); // { dg-warning "reference to local variable" } > +} > +static_assert(same_type::value); > + > +decltype(auto) fn4() { > + Widget w; > + return w.x; > +} > +static_assert(same_type::value); > + > +decltype(auto) fn5() { > + Widget w; > + return (w.x); // { dg-warning "reference to local variable" } > +} > +static_assert(same_type::value); > diff --git a/gcc/testsuite/g++.dg/cpp23/elision1.C b/gcc/testsuite/g++.dg/cpp23/elision1.C > new file mode 100644 > index 00000000000..f44fd2a061c > --- /dev/null > +++ b/gcc/testsuite/g++.dg/cpp23/elision1.C > @@ -0,0 +1,114 @@ > +// PR c++/101165 - P2266R1 - Simpler implicit move > +// { dg-do compile { target c++23 } } > +// Tests from P2266R1. > + > +namespace std { > + template > + struct remove_reference > + { typedef _Tp type; }; > + > + template > + struct remove_reference<_Tp&> > + { typedef _Tp type; }; > + > + template > + struct remove_reference<_Tp&&> > + { typedef _Tp type; }; > + > + template > + constexpr typename std::remove_reference<_Tp>::type&& > + move(_Tp&& __t) noexcept > + { return static_cast::type&&>(__t); } > +} > + > +template > +struct same_type { static const bool value = false; }; > + > +template > +struct same_type { static const bool value = true; }; > + > +struct Widget { > + Widget(Widget&&); > +}; > + > +struct RRefTaker { > + RRefTaker(Widget&&); > +}; > + > +struct Mutt { > + operator int*() &&; > +}; > + > +struct Jeff { > + operator int&() &&; > +}; > + > +struct Ella { > + operator int() &&; > +}; > + > +Widget one(Widget w) { > + return w; // OK since C++11 > +} > + > +RRefTaker two(Widget w) { > + return w; // OK since C++11 + CWG1579 > +} > + > +RRefTaker three(Widget&& w) { > + return w; // OK since C++20 because P0527 > +} > + > +// Tests that implicit move applies even to functions that return references. > +Widget&& four(Widget&& w) { > + return w; // OK since C++23 > +} > + > +// ... or pointers. > +int* five(Mutt x) { > + return x; // OK since C++20 because P1155 > +} > + > +int& six(Jeff x) { > + return x; > +} > + > +int test_ella(Ella e) { > + return e; > +} > + > +template > +T&& seven(T&& x) { return x; } > + > +void test_seven(Widget w) { > + Widget& r = seven(w); > + Widget&& rr = seven(std::move(w)); > +} > + > +Widget val(); > +Widget& lref(); > +Widget&& rref(); > + > +decltype(auto) eight() { > + decltype(auto) x = val(); // OK, x is Widget > + return x; // OK, return type is Widget, we get copy elision > +} > + > +decltype(auto) nine() { > + decltype(auto) x = lref(); // OK, x is Widget& > + return x; // OK, return type is Widget& > +} > + > +decltype(auto) ten() { > + decltype(auto) x = rref(); // OK, x is Widget&& > + // This was an error: return type is Widget&&, cannot bind to x. > + // But in C++23, x is treated as an rvalue. > + return x; > +} > + > +// Now returns Widget&&, not Widget&. > +// This is from $ 3.2.1. Interaction with decltype and decltype(auto). > +decltype(auto) eleven(Widget&& x) { > + return (x); > +} > +static_assert(same_type::value); > diff --git a/gcc/testsuite/g++.dg/cpp23/elision2.C b/gcc/testsuite/g++.dg/cpp23/elision2.C > new file mode 100644 > index 00000000000..ce2c7aeef66 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/cpp23/elision2.C > @@ -0,0 +1,46 @@ > +// PR c++/101165 - P2266R1 - Simpler implicit move > +// { dg-do compile { target c++20 } } > +// Test from P2266R1, $ 3.3. Two overload resolutions are overly confusing. > + > +struct Widget { > + Widget(); > + Widget(Widget&&); > +}; > + > +struct Frodo { > + Frodo(Widget&); > + Frodo(Widget&&) = delete; > +}; > + > +struct Sam { > + Sam(Widget&) = delete; // #1 > + Sam(const Widget&); // #2 > +}; > + > +Sam twelve() { > + Widget w; > + // This is supposed to calls #2 since C++20 because P1155. > + // But we actually choose #1 since r11-2411 (in C++20 only). > + return w; // { dg-error "deleted" "" { target c++20_only } } > +} > + > +Frodo thirteen() { > + Widget w; > + // This is a correct error in both C++20 and C++23. > + return w; // { dg-error "use of deleted function" } > +} > + > +struct Merry {}; > +struct Pippin {}; > +struct Together : Merry, Pippin {}; > +struct Quest { > + Quest(Merry&&); > + Quest(Pippin&&); > + Quest(Together&); > +}; > + > +Quest fourteen() { > + Together t; > + // C++20: calls Quest(Together&). Proposed: ill-formed. > + return t; // { dg-error "ambiguous" "" { target c++23 } } > +} > diff --git a/gcc/testsuite/g++.dg/cpp23/elision3.C b/gcc/testsuite/g++.dg/cpp23/elision3.C > new file mode 100644 > index 00000000000..246342e64d3 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/cpp23/elision3.C > @@ -0,0 +1,16 @@ > +// PR c++/101165 - P2266R1 - Simpler implicit move > +// { dg-do compile { target c++23 } } > +// Test from P2266R1, $ 3.4. A specific case involving reference_wrapper. > + > +#include > + > +struct Widget { > + Widget(); > + Widget(Widget&&); > +}; > + > +std::reference_wrapper fifteen() { > + Widget w; > + // OK until CWG1579; OK after LWG2993. Proposed: ill-formed > + return w; // { dg-error "could not convert" } > +} > diff --git a/gcc/testsuite/g++.dg/cpp23/elision4.C b/gcc/testsuite/g++.dg/cpp23/elision4.C > new file mode 100644 > index 00000000000..c19b86b8b5f > --- /dev/null > +++ b/gcc/testsuite/g++.dg/cpp23/elision4.C > @@ -0,0 +1,38 @@ > +// PR c++/101165 - P2266R1 - Simpler implicit move > +// { dg-do compile { target c++23 } } > +// Test from P2266R1, $ 5.2. LibreOffice OString constructor. > + > +struct X { > + X(auto&); > +}; > + > +// The following compiles in C++20 (deducing X(char (&)[10])) but not > +// after P2266 (because the returned expression now has type char (&&)[10], > +// which cannot bind to auto&). > +X f() { > + char a[10]; > + return a; // { dg-error "cannot bind non-const lvalue reference" } > +} > + > +// The solution was to change it by making the return convert explicitly > +// rather than implicitly: > +X fixed() { > + char a[10]; > + return X(a); > +} > + > +// $ 5.3. LibreOffice o3tl::temporary > + > +template > +T& temporary1(T&& x) { return x; } // { dg-error "cannot bind non-const lvalue reference" } > + > +// Fixed by: > +template > +T& temporary2(T&& x) { return static_cast(x); } > + > +void > +test () > +{ > + int& r1 = temporary1 (42); > + int& r2 = temporary2 (42); > +} > diff --git a/gcc/testsuite/g++.dg/cpp23/elision5.C b/gcc/testsuite/g++.dg/cpp23/elision5.C > new file mode 100644 > index 00000000000..a7d3e7c27c4 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/cpp23/elision5.C > @@ -0,0 +1,53 @@ > +// PR c++/101165 - P2266R1 - Simpler implicit move > +// { dg-do compile { target c++23 } } > +// Test from [class.copy.elision]/4. > + > +class Thing { > +public: > + Thing(); > + ~Thing(); > + Thing(Thing&&); > +private: > + Thing(const Thing&); > +}; > + > +Thing f(bool b) { > + Thing t; > + if (b) > + throw t; // OK, Thing(Thing&&) used (or elided) to throw t > + return t; // OK, Thing(Thing&&) used (or elided) to return t > +} > + > +Thing t2 = f(false); // OK, no extra copy/move performed, t2 constructed by call to f > + > +struct Weird { > + Weird(); > + Weird(Weird&); > +}; > + > +Weird g(bool b) { > + static Weird w1; > + Weird w2; > + if (b) { > + return w1; // OK: Weird(Weird&) > + } else { > + return w2; // { dg-error "cannot bind non-const lvalue reference" } > + } > +} > + > +int& h(bool b, int i) { > + static int s; > + if (b) > + return s; // OK > + else > + return i; // { dg-error "cannot bind non-const lvalue reference" } > +} > + > +decltype(auto) h2(Thing t) { > + return t; // OK, t is an xvalue and h2's return type is Thing > +} > + > +decltype(auto) h3(Thing t) { > + // OK, (t) is an xvalue and h3's return type is Thing&& > + return (t); // { dg-warning "reference to local variable" } > +} > diff --git a/gcc/testsuite/g++.dg/cpp23/elision6.C b/gcc/testsuite/g++.dg/cpp23/elision6.C > new file mode 100644 > index 00000000000..5d58da9e577 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/cpp23/elision6.C > @@ -0,0 +1,20 @@ > +// PR c++/101165 - P2266R1 - Simpler implicit move > +// { dg-do compile { target c++23 } } > +// From [diff.cpp20.expr]. > + > +template > +struct same_type { static const bool value = false; }; > + > +template > +struct same_type { static const bool value = true; }; > + > +// In C++23, returns int&&; previously returned int&. > +decltype(auto) f(int&& x) { return (x); } > +static_assert(same_type::value); > + > +// This used to work in C++20. > +int& g(int&& x) { return x; } // { dg-error "cannot bind non-const lvalue reference" } > + > +template > +decltype(auto) h(T&& x) { return (x); } > +static_assert(same_type::value); > diff --git a/gcc/testsuite/g++.dg/cpp23/elision7.C b/gcc/testsuite/g++.dg/cpp23/elision7.C > new file mode 100644 > index 00000000000..19fa89ae133 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/cpp23/elision7.C > @@ -0,0 +1,72 @@ > +// PR c++/101165 - P2266R1 - Simpler implicit move > +// { dg-do compile { target c++23 } } > + > +struct X { > + X (); > + X(X&&); > +}; > + > +X&& rref (); > + > +X&& > +f1 (X&& x) > +{ > + return x; > +} > + > +template T&& > +f2 (T&& x) > +{ > + return x; > +} > +template X& f2(X&); > +template X&& f2(X&&); > + > +X&& > +f3 () > +{ > + X&& x = rref (); > + return x; > +} > + > +void > +f4 () > +try { > + X x; > + throw x; > +} catch (...) { } > + > +void > +f5 () > +{ > + auto l1 = [](auto x) -> auto { return x; }; > + auto &&x1 = l1(X{}); > + auto l2 = [](auto x) -> auto& { return x; }; // { dg-error "cannot bind non-const lvalue reference" } > + auto &&x2 = l2(X{}); > + auto l3 = [](auto x) -> auto&& { return x; }; // { dg-warning "reference to local" } > + auto &&x3 = l3(X{}); > +} > + > +constexpr int & > +f6 (int &&n) > +{ > + return n; // { dg-error "cannot bind non-const lvalue reference" } > +} > + > +void > +do_f6 () > +{ > + auto x = f6 (42); > +} > + > +template auto & > +f7 (T &&t) > +{ > + return t; // { dg-error "cannot bind non-const lvalue reference" } > +} > + > +void > +do_f7 () > +{ > + const int &x = f7 (0); > +} > diff --git a/gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C b/gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C > index 2f6b21ead70..b52cf378b41 100644 > --- a/gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C > +++ b/gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C > @@ -569,3 +569,9 @@ > #elif __cpp_static_call_operator != 202207 > # error "__cpp_static_call_operator != 202207" > #endif > + > +#ifndef __cpp_implicit_move > +# error "__cpp_implicit_move" > +#elif __cpp_implicit_move != 202207 > +# error "__cpp_implicit_move != 202207" > +#endif > diff --git a/gcc/testsuite/g++.dg/gomp/pr56217.C b/gcc/testsuite/g++.dg/gomp/pr56217.C > index 03dfc5f180b..731c0c08811 100644 > --- a/gcc/testsuite/g++.dg/gomp/pr56217.C > +++ b/gcc/testsuite/g++.dg/gomp/pr56217.C > @@ -1,5 +1,5 @@ > // PR middle-end/56217 > -// { dg-do compile } > +// { dg-do compile { target c++20_down } } > // { dg-options "-fopenmp" } > > struct S { int *p; S (); S (S &); }; > @@ -10,5 +10,7 @@ foo () > S s; > #pragma omp task shared (s) > s.p = 0; > + // This fails in C++23, because "cannot bind non-const lvalue reference of > + // type 'S&' to an rvalue of type 'S'". > return s; > } > diff --git a/gcc/testsuite/g++.dg/warn/Wno-return-local-addr.C b/gcc/testsuite/g++.dg/warn/Wno-return-local-addr.C > index e15bfa24f54..cc9bb59770e 100644 > --- a/gcc/testsuite/g++.dg/warn/Wno-return-local-addr.C > +++ b/gcc/testsuite/g++.dg/warn/Wno-return-local-addr.C > @@ -4,7 +4,7 @@ > int& bad1() > { > int x = 0; > - return x; > + return x; // { dg-error "cannot bind non-const lvalue reference" "" { target c++23 } } > } > > int* bad2() > diff --git a/gcc/testsuite/g++.dg/warn/Wreturn-local-addr.C b/gcc/testsuite/g++.dg/warn/Wreturn-local-addr.C > index 642a5767e84..4c18c2f06a0 100644 > --- a/gcc/testsuite/g++.dg/warn/Wreturn-local-addr.C > +++ b/gcc/testsuite/g++.dg/warn/Wreturn-local-addr.C > @@ -5,7 +5,7 @@ > int& bad1() > { > int x = 0; > - return x; // { dg-error "reference to local variable" } > + return x; // { dg-error "reference to local variable|cannot bind non-const lvalue reference" } > } > > int* bad2() > diff --git a/gcc/testsuite/g++.old-deja/g++.brendan/crash55.C b/gcc/testsuite/g++.old-deja/g++.brendan/crash55.C > index fd4d4b65edb..b93e6e0c695 100644 > --- a/gcc/testsuite/g++.old-deja/g++.brendan/crash55.C > +++ b/gcc/testsuite/g++.old-deja/g++.brendan/crash55.C > @@ -8,5 +8,6 @@ > > local = x+2; > > - return local; // { dg-warning "reference to local" } > + return local; // { dg-warning "reference to local" "" { target c++20_down } } > +// { dg-error "non-const lvalue" "" { target c++23 } .-1 } > } > diff --git a/gcc/testsuite/g++.old-deja/g++.jason/temporary2.C b/gcc/testsuite/g++.old-deja/g++.jason/temporary2.C > index c855f8f4a07..2709b50e7f1 100644 > --- a/gcc/testsuite/g++.old-deja/g++.jason/temporary2.C > +++ b/gcc/testsuite/g++.old-deja/g++.jason/temporary2.C > @@ -8,7 +8,7 @@ public: > int i; > }; > > -X foo() { X x; return x; } > +X foo() { X x; return x; } // { dg-error "cannot bind non-const lvalue reference" "" { target c++23 } } > > int main() > { > diff --git a/gcc/testsuite/g++.old-deja/g++.mike/p2846b.C b/gcc/testsuite/g++.old-deja/g++.mike/p2846b.C > index 57422fe64df..68826649cfc 100644 > --- a/gcc/testsuite/g++.old-deja/g++.mike/p2846b.C > +++ b/gcc/testsuite/g++.old-deja/g++.mike/p2846b.C > @@ -1,4 +1,4 @@ > -// { dg-do run } > +// { dg-do run { target c++20_down } } > // Shows that problem of initializing one object's secondary base from > // another object via a user defined copy constructor for that base, > // the pointer for the secondary vtable is not set after implicit > @@ -11,6 +11,8 @@ > > // prms-id: 2846 > > +// This test fails in C++23 due to P2266. Instead of disabling this test for C++23, let's add a cast to B& in the return statement. OK with that change and optionally the ?: reformatting above. > extern "C" int printf(const char *, ...); > extern "C" void exit(int); > > > base-commit: e73d9fcafbd07bc3714fbaf8a82db71d50015c92