public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
From: Jason Merrill <jason@redhat.com>
To: Marek Polacek <polacek@redhat.com>
Cc: GCC Patches <gcc-patches@gcc.gnu.org>
Subject: Re: [PATCH v3] c++: Implement C++23 P2266R1, Simpler implicit move [PR101165]
Date: Tue, 27 Sep 2022 17:44:12 -0400	[thread overview]
Message-ID: <39fb63d9-8128-512b-523d-f575317cf7c2@redhat.com> (raw)
In-Reply-To: <YzNcYqVuH+FsC8Wh@redhat.com>

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<B> (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>(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<float> g1()
>   {
>      float f[] = {1.1f, 2.3f};
> -   return f;
> +   return f; // { dg-error "cannot bind non-const" "" { target c++23 } }
>   }
>   
>   const A<float> &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<float> &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<typename T, typename U>
> +struct same_type { static const bool value = false; };
> +
> +template<typename T>
> +struct same_type<T, T> { static const bool value = true; };
> +
> +auto f1(int x) -> decltype(x) { return (x); }
> +static_assert(same_type<decltype(f1), int (int)>::value);
> +auto f2(int x) -> decltype((x)) { return (x); }   // { dg-error "cannot bind" }
> +static_assert(same_type<decltype(f2), int& (int)>::value);
> +auto f3(int x) -> decltype(auto) { return (x); }  // { dg-warning "reference to local variable" }
> +static_assert(same_type<decltype(f3), int&& (int)>::value);
> +auto g1(int x) -> decltype(x) { return x; }
> +static_assert(same_type<decltype(g1), int (int)>::value);
> +auto g2(int x) -> decltype((x)) { return x; }	  // { dg-error "cannot bind" }
> +static_assert(same_type<decltype(g2), int& (int)>::value);
> +auto g3(int x) -> decltype(auto) { return x; }
> +static_assert(same_type<decltype(g3), int (int)>::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<decltype(f4), X(X)>::value);
> +
> +auto&
> +f5 (X x)
> +{
> +  return x; // { dg-error "cannot bind non-const lvalue reference" }
> +}
> +static_assert(same_type<decltype(f5), X&(X)>::value);
> +
> +auto&&
> +f6 (X x)
> +{
> +  return x; // { dg-warning "reference to local variable" }
> +}
> +static_assert(same_type<decltype(f6), X&&(X)>::value);
> +
> +auto
> +f7 (X x)
> +{
> +  return (x);
> +}
> +static_assert(same_type<decltype(f7), X(X)>::value);
> +
> +auto&
> +f8 (X x)
> +{
> +  return (x); // { dg-error "cannot bind non-const lvalue reference" }
> +}
> +static_assert(same_type<decltype(f8), X&(X)>::value);
> +
> +auto&&
> +f9 (X x)
> +{
> +  return (x); // { dg-warning "reference to local variable" }
> +}
> +static_assert(same_type<decltype(f9), X&&(X)>::value);
> +
> +decltype(auto)
> +f10 (X x)
> +{
> +  return x;
> +}
> +static_assert(same_type<decltype(f10), X(X)>::value);
> +
> +decltype(auto)
> +f11 (X x)
> +{
> +  return (x); // { dg-warning "reference to local variable" }
> +}
> +static_assert(same_type<decltype(f11), X&&(X)>::value);
> +
> +decltype(auto)
> +f12 (X& x)
> +{
> +  return x;
> +}
> +static_assert(same_type<decltype(f12), X&(X&)>::value);
> +
> +decltype(auto)
> +f13 (X& x)
> +{
> +  return (x);
> +}
> +static_assert(same_type<decltype(f13), X&(X&)>::value);
> +
> +decltype(auto)
> +f14 (X&& x)
> +{
> +  return x;
> +}
> +static_assert(same_type<decltype(f14), X&&(X&&)>::value);
> +
> +decltype(auto)
> +f15 (X&& x)
> +{
> +  return (x);
> +}
> +static_assert(same_type<decltype(f15), X&&(X&&)>::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<typename T, typename U>
> +struct same_type { static const bool value = false; };
> +
> +template<typename T>
> +struct same_type<T, T> { static const bool value = true; };
> +
> +struct Widget {
> +  int x;
> +};
> +
> +Widget wg;
> +
> +decltype(auto) fn0(Widget&& x) {
> +    return (::wg);
> +}
> +static_assert(same_type<decltype(fn0), Widget& (Widget&&)>::value);
> +
> +decltype(auto) fn1(Widget&& x) {
> +    return ::wg;
> +}
> +static_assert(same_type<decltype(fn1), Widget (Widget&&)>::value);
> +
> +decltype(auto) fn2() {
> +    Widget w;
> +    return w;
> +}
> +static_assert(same_type<decltype(fn2), Widget ()>::value);
> +
> +decltype(auto) fn3() {
> +    Widget w;
> +    return (w); // { dg-warning "reference to local variable" }
> +}
> +static_assert(same_type<decltype(fn3), Widget&& ()>::value);
> +
> +decltype(auto) fn4() {
> +    Widget w;
> +    return w.x;
> +}
> +static_assert(same_type<decltype(fn4), int ()>::value);
> +
> +decltype(auto) fn5() {
> +    Widget w;
> +    return (w.x); // { dg-warning "reference to local variable" }
> +}
> +static_assert(same_type<decltype(fn5), int& ()>::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<typename _Tp>
> +    struct remove_reference
> +    { typedef _Tp   type; };
> +
> +  template<typename _Tp>
> +    struct remove_reference<_Tp&>
> +    { typedef _Tp   type; };
> +
> +  template<typename _Tp>
> +    struct remove_reference<_Tp&&>
> +    { typedef _Tp   type; };
> +
> +  template<typename _Tp>
> +    constexpr typename std::remove_reference<_Tp>::type&&
> +    move(_Tp&& __t) noexcept
> +    { return static_cast<typename std::remove_reference<_Tp>::type&&>(__t); }
> +}
> +
> +template<typename T, typename U>
> +struct same_type { static const bool value = false; };
> +
> +template<typename T>
> +struct same_type<T, T> { 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<class T>
> +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<decltype(eleven), Widget&& (Widget&&)>::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 <functional>
> +
> +struct Widget {
> +    Widget();
> +    Widget(Widget&&);
> +};
> +
> +std::reference_wrapper<Widget> 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<class T>
> +T& temporary1(T&& x) { return x; } // { dg-error "cannot bind non-const lvalue reference" }
> +
> +// Fixed by:
> +template<class T>
> +T& temporary2(T&& x) { return static_cast<T&>(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<typename T, typename U>
> +struct same_type { static const bool value = false; };
> +
> +template<typename T>
> +struct same_type<T, T> { static const bool value = true; };
> +
> +// In C++23, returns int&&; previously returned int&.
> +decltype(auto) f(int&& x) { return (x); }
> +static_assert(same_type<decltype(f), int&& (int&&)>::value);
> +
> +// This used to work in C++20.
> +int& g(int&& x) { return x; } // { dg-error "cannot bind non-const lvalue reference" }
> +
> +template<typename T>
> +decltype(auto) h(T&& x) { return (x); }
> +static_assert(same_type<decltype(h(42)), int&&>::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<typename T> T&&
> +f2 (T&& x)
> +{
> +  return x;
> +}
> +template X& f2<X&>(X&);
> +template X&& f2<X>(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<typename T> 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


  reply	other threads:[~2022-09-27 21:44 UTC|newest]

Thread overview: 10+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-09-03 16:42 [PATCH] " Marek Polacek
2022-09-07  2:38 ` Jason Merrill
2022-09-08 22:54   ` Marek Polacek
2022-09-12 20:27     ` Jason Merrill
2022-09-20 18:21       ` Marek Polacek
2022-09-20 18:19   ` [PATCH v2] " Marek Polacek
2022-09-26 17:29     ` Jason Merrill
2022-09-27 20:26       ` [PATCH v3] " Marek Polacek
2022-09-27 21:44         ` Jason Merrill [this message]
2022-09-27 23:39           ` Marek Polacek

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=39fb63d9-8128-512b-523d-f575317cf7c2@redhat.com \
    --to=jason@redhat.com \
    --cc=gcc-patches@gcc.gnu.org \
    --cc=polacek@redhat.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).