diff --git a/gcc/c-family/c-cppbuiltin.cc b/gcc/c-family/c-cppbuiltin.cc
index a1557eb23d5..dd8b20d0c34 100644
--- a/gcc/c-family/c-cppbuiltin.cc
+++ b/gcc/c-family/c-cppbuiltin.cc
@@ -1081,6 +1081,7 @@ c_cpp_builtins (cpp_reader *pfile)
 	  cpp_define (pfile, "__cpp_constexpr=202110L");
 	  cpp_define (pfile, "__cpp_multidimensional_subscript=202110L");
 	  cpp_define (pfile, "__cpp_named_character_escapes=202207L");
+	  cpp_define (pfile, "__cpp_implicit_move=202207L");
 	}
       if (flag_concepts)
         {
diff --git a/gcc/cp/call.cc b/gcc/cp/call.cc
index d107a2814dc..cb4ae9ecf9d 100644
--- a/gcc/cp/call.cc
+++ b/gcc/cp/call.cc
@@ -1865,7 +1865,7 @@ 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
+	  && !(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 7b28405c3ac..4cdbbc34cb3 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -7278,6 +7278,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 cd0d8920ed0..b347a9a78fc 100644
--- a/gcc/cp/pt.cc
+++ b/gcc/cp/pt.cc
@@ -30405,6 +30405,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
@@ -30501,18 +30521,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 c678e3b9c4c..226ffd48519 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 b99947c10fd..fe88c1a7b84 100644
--- a/gcc/cp/typeck.cc
+++ b/gcc/cp/typeck.cc
@@ -10679,10 +10679,27 @@ check_return_expr (tree retval, bool *no_warning)
 	}
       else
 	{
-	  if (!retval)
-	    retval = void_node;
 	  auto_node = type_uses_auto (pattern);
-	  type = do_auto_deduction (pattern, retval, auto_node,
+	  tree r;
+	  if (!retval)
+	    r = void_node;
+	  /* 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 earlier.  But don't do it for
+	       decltype(auto) f(int x) { return x; }
+	     where we should deduce 'int' rather than 'int&&'; transmogrifying
+	     RETVAL to an rvalue would break that.  */
+	  else if (cxx_dialect >= cxx23
+		   && (!AUTO_IS_DECLTYPE (auto_node)
+		       || !unparenthesized_id_or_class_member_access_p (retval))
+		   && (r = treat_lvalue_as_rvalue_p
+		       (maybe_undo_parenthesized_ref (retval), /*return*/true)))
+	    /* Use it.  */;
+	  else
+	    r = retval;
+	  type = do_auto_deduction (pattern, r, auto_node,
 				    tf_warning_or_error, adc_return_type);
 	}
 
@@ -10876,8 +10893,9 @@ 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)
+	   || (cxx_dialect >= cxx23 && TYPE_REF_P (functype)))
 	  && (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.
+// { 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..8115739b7f9
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/elision1.C
@@ -0,0 +1,109 @@
+// 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&() &&;
+};
+
+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
+}
+
+// FIXME This is supposed to work but we reject it with
+// error: passing 'Mutt' as 'this' argument discards qualifiers
+#if 0
+int* five(Mutt x) {
+    return x;  // OK since C++20 because P1155
+}
+#endif
+
+int& six(Jeff x) {
+    return x;
+}
+
+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 d3e40724085..20453bb7b14 100644
--- a/gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C
+++ b/gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C
@@ -563,3 +563,9 @@
 #elif __cpp_named_character_escapes != 202207
 #  error "__cpp_named_character_escapes != 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.
+
 extern "C" int printf(const char *, ...);
 extern "C" void exit(int);
 
