mizvekov updated this revision to Diff 339349.
mizvekov added a comment.

.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D100000/new/

https://reviews.llvm.org/D100000

Files:
  clang/include/clang/AST/Expr.h
  clang/include/clang/Basic/DiagnosticSemaKinds.td
  clang/include/clang/Basic/Specifiers.h
  clang/include/clang/Sema/Sema.h
  clang/lib/AST/ASTContext.cpp
  clang/lib/AST/ExprClassification.cpp
  clang/lib/AST/JSONNodeDumper.cpp
  clang/lib/AST/TextNodeDumper.cpp
  clang/lib/CodeGen/CGDecl.cpp
  clang/lib/CodeGen/CGObjC.cpp
  clang/lib/Sema/SemaCodeComplete.cpp
  clang/lib/Sema/SemaCoroutine.cpp
  clang/lib/Sema/SemaExprCXX.cpp
  clang/lib/Sema/SemaInit.cpp
  clang/lib/Sema/SemaOverload.cpp
  clang/lib/Sema/SemaStmt.cpp
  clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
  clang/test/CXX/class/class.init/class.copy.elision/p3.cpp
  clang/test/SemaCXX/P1155.cpp
  clang/test/SemaCXX/conversion-function.cpp
  clang/test/SemaCXX/coroutine-rvo.cpp
  clang/test/SemaCXX/warn-return-std-move.cpp

Index: clang/test/SemaCXX/warn-return-std-move.cpp
===================================================================
--- clang/test/SemaCXX/warn-return-std-move.cpp
+++ clang/test/SemaCXX/warn-return-std-move.cpp
@@ -1,12 +1,12 @@
-// RUN: %clang_cc1 -std=c++2b -fsyntax-only -verify=cxx20_2b,cxx2b -fcxx-exceptions -Wreturn-std-move %s
-// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify=cxx20_2b       -fcxx-exceptions -Wreturn-std-move %s
-// RUN: %clang_cc1 -std=c++17 -fsyntax-only -verify=cxx11_17       -fcxx-exceptions -Wreturn-std-move %s
-// RUN: %clang_cc1 -std=c++14 -fsyntax-only -verify=cxx11_17       -fcxx-exceptions -Wreturn-std-move %s
-// RUN: %clang_cc1 -std=c++11 -fsyntax-only -verify=cxx11_17       -fcxx-exceptions -Wreturn-std-move %s
+// RUN: %clang_cc1 -std=c++2b -fsyntax-only -verify=expected,cxx20_2b,cxx2b    -fcxx-exceptions -Wreturn-std-move %s
+// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify=expected,cxx20_2b,cxx11_20 -fcxx-exceptions -Wreturn-std-move %s
+// RUN: %clang_cc1 -std=c++17 -fsyntax-only -verify=expected,cxx11_17,cxx11_20 -fcxx-exceptions -Wreturn-std-move %s
+// RUN: %clang_cc1 -std=c++14 -fsyntax-only -verify=expected,cxx11_17,cxx11_20 -fcxx-exceptions -Wreturn-std-move %s
+// RUN: %clang_cc1 -std=c++11 -fsyntax-only -verify=expected,cxx11_17,cxx11_20 -fcxx-exceptions -Wreturn-std-move %s
 
-// RUN: %clang_cc1 -std=c++17 -fsyntax-only -fcxx-exceptions -Wreturn-std-move -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s -check-prefix=CHECK
-// RUN: %clang_cc1 -std=c++14 -fsyntax-only -fcxx-exceptions -Wreturn-std-move -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s -check-prefix=CHECK
-// RUN: %clang_cc1 -std=c++11 -fsyntax-only -fcxx-exceptions -Wreturn-std-move -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s -check-prefix=CHECK
+// RUN: %clang_cc1 -std=c++17 -fsyntax-only -fcxx-exceptions -Wreturn-std-move -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s -check-prefixes=CHECK,CHECK_14_17
+// RUN: %clang_cc1 -std=c++14 -fsyntax-only -fcxx-exceptions -Wreturn-std-move -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s -check-prefixes=CHECK,CHECK_14_17
+// RUN: %clang_cc1 -std=c++11 -fsyntax-only -fcxx-exceptions -Wreturn-std-move -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s -check-prefixes=CHECK
 
 // definitions for std::move
 namespace std {
@@ -78,9 +78,6 @@
 Base test2() {
     Derived d2;
     return d2;  // e1
-    // cxx11_17-warning@-1{{will be copied despite being returned by name}}
-    // cxx11_17-note@-2{{to avoid copying}}
-    // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:14}:"std::move(d2)"
 }
 ConstructFromDerived test3() {
   Derived d3;
@@ -89,23 +86,14 @@
 ConstructFromBase test4() {
     Derived d4;
     return d4;  // e3
-    // cxx11_17-warning@-1{{will be copied despite being returned by name}}
-    // cxx11_17-note@-2{{to avoid copying}}
-    // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:14}:"std::move(d4)"
 }
 ConvertFromDerived test5() {
     Derived d5;
     return d5;  // e4
-    // cxx11_17-warning@-1{{will be copied despite being returned by name}}
-    // cxx11_17-note@-2{{to avoid copying}}
-    // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:14}:"std::move(d5)"
 }
 ConvertFromBase test6() {
     Derived d6;
     return d6;  // e5
-    // cxx11_17-warning@-1{{will be copied despite being returned by name}}
-    // cxx11_17-note@-2{{to avoid copying}}
-    // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:14}:"std::move(d6)"
 }
 
 // These test cases should not produce the warning.
@@ -152,30 +140,18 @@
 Derived testParam1(Derived d) { return d; }
 Base testParam2(Derived d) {
     return d;  // e6
-    // cxx11_17-warning@-1{{will be copied despite being returned by name}}
-    // cxx11_17-note@-2{{to avoid copying}}
-    // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:13}:"std::move(d)"
 }
 ConstructFromDerived testParam3(Derived d) {
   return d; // ok
 }
 ConstructFromBase testParam4(Derived d) {
     return d;  // e8
-    // cxx11_17-warning@-1{{will be copied despite being returned by name}}
-    // cxx11_17-note@-2{{to avoid copying}}
-    // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:13}:"std::move(d)"
 }
 ConvertFromDerived testParam5(Derived d) {
     return d;  // e9
-    // cxx11_17-warning@-1{{will be copied despite being returned by name}}
-    // cxx11_17-note@-2{{to avoid copying}}
-    // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:13}:"std::move(d)"
 }
 ConvertFromBase testParam6(Derived d) {
     return d;  // e10
-    // cxx11_17-warning@-1{{will be copied despite being returned by name}}
-    // cxx11_17-note@-2{{to avoid copying}}
-    // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:13}:"std::move(d)"
 }
 
 // If the target is an rvalue reference parameter, do apply the diagnostic.
@@ -216,21 +192,37 @@
     // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:13}:"std::move(d)"
 }
 
+// FIXME: For the next four tests, under cxx11_20, we give good advice, but in a bad way.
+
 // But if the return type is a reference type, then moving would be wrong.
-Derived &testRetRef1(Derived &&d) { return d; } // cxx2b-error {{non-const lvalue reference to type 'Derived' cannot bind to a temporary of type 'Derived'}}
-Base &testRetRef2(Derived &&d) { return d; }    // cxx2b-error {{non-const lvalue reference to type 'Base' cannot bind to a temporary of type 'Derived'}}
+Derived &testRetRef1(Derived &&d) { return d; }
+// cxx2b-error@-1 {{non-const lvalue reference to type 'Derived' cannot bind to a temporary of type 'Derived'}}
+// cxx11_20-warning@-2{{will be copied despite being returned by name}}
+// cxx11_20-note@-3{{to avoid copying}}
+// CHECK: fix-it:"{{.*}}":{[[@LINE-4]]:44-[[@LINE-4]]:45}:"std::move(d)"
+
+Base &testRetRef2(Derived &&d) { return d; }
+// cxx2b-error@-1 {{non-const lvalue reference to type 'Base' cannot bind to a temporary of type 'Derived'}}
+// cxx11_20-warning@-2{{will be copied despite being returned by name}}
+// cxx11_20-note@-3{{to avoid copying}}
+// CHECK: fix-it:"{{.*}}":{[[@LINE-4]]:41-[[@LINE-4]]:42}:"std::move(d)"
+
 #if __cplusplus >= 201402L
 auto&& testRetRef3(Derived&& d) { return d; }
+// cxx11_20-warning@-1{{will be copied despite being returned by name}}
+// cxx11_20-note@-2{{to avoid copying}}
+// CHECK_14_17: fix-it:"{{.*}}":{[[@LINE-3]]:42-[[@LINE-3]]:43}:"std::move(d)"
+
 decltype(auto) testRetRef4(Derived&& d) { return (d); }
+// cxx11_20-warning@-1{{will be copied despite being returned by name}}
+// cxx11_20-note@-2{{to avoid copying}}
+// CHECK_14_17: fix-it:"{{.*}}":{[[@LINE-3]]:50-[[@LINE-3]]:53}:"std::move(d)"
 #endif
 
 // As long as we're checking parentheses, make sure parentheses don't disable the warning.
 Base testParens1() {
     Derived d;
     return (d);  // e17
-    // cxx11_17-warning@-1{{will be copied despite being returned by name}}
-    // cxx11_17-note@-2{{to avoid copying}}
-    // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:15}:"std::move(d)"
 }
 ConstructFromDerived testParens2() {
   Derived d;
@@ -334,18 +326,3 @@
 void ok_throw6(Derived &d) { throw static_cast<Derived &&>(d); }
 void ok_throw7(TriviallyCopyable d) { throw d; }
 void ok_throw8(OnlyCopyable d) { throw d; }
-
-namespace test_delete {
-struct Base {
-  Base();
-  Base(Base &&) = delete; // cxx20_2b-note {{'Base' has been explicitly marked deleted here}}
-  Base(Base const &);
-};
-
-struct Derived : public Base {};
-
-Base test_ok() {
-  Derived d;
-  return d; // cxx20_2b-error {{call to deleted constructor of 'test_delete::Base'}}
-}
-} // namespace test_delete
Index: clang/test/SemaCXX/coroutine-rvo.cpp
===================================================================
--- clang/test/SemaCXX/coroutine-rvo.cpp
+++ clang/test/SemaCXX/coroutine-rvo.cpp
@@ -1,4 +1,6 @@
-// RUN: %clang_cc1 -verify -std=c++17 -fcoroutines-ts -fsyntax-only %s
+// RUN: %clang_cc1 -std=c++2b                 -fsyntax-only -verify=expected,cxx2b %s
+// RUN: %clang_cc1 -std=c++20                 -fsyntax-only -verify=expected       %s
+// RUN: %clang_cc1 -std=c++17 -fcoroutines-ts -fsyntax-only -verify=expected,cxx17 %s
 
 namespace std::experimental {
 template <class Promise = void> struct coroutine_handle {
@@ -56,7 +58,10 @@
     auto final_suspend() noexcept { return suspend_never{}; }
     auto get_return_object() { return task{}; }
     static void unhandled_exception() {}
-    void return_value(T &&value) {} // expected-note 4{{passing argument}}
+    void return_value(T &&value) {}
+    // cxx2b-note@-1    2{{passing argument}}
+    // cxx17-note@-2     {{passing argument}}
+    // expected-note@-3 2{{passing argument}}
   };
 };
 
@@ -67,7 +72,7 @@
 
 task<NoCopyNoMove &> local2ref() {
   NoCopyNoMove value;
-  co_return value; // expected-error {{non-const lvalue reference to type 'NoCopyNoMove' cannot bind to a temporary of type 'NoCopyNoMove'}}
+  co_return value; // cxx2b-error {{non-const lvalue reference to type 'NoCopyNoMove' cannot bind to a temporary of type 'NoCopyNoMove'}}
 }
 
 // We need the move constructor for construction of the coroutine.
@@ -80,7 +85,7 @@
 }
 
 task<NoCopyNoMove> rvalue2val(NoCopyNoMove &&value) {
-  co_return value;
+  co_return value; // cxx17-error {{rvalue reference to type 'NoCopyNoMove' cannot bind to lvalue of type 'NoCopyNoMove'}}
 }
 
 task<NoCopyNoMove &> lvalue2ref(NoCopyNoMove &value) {
@@ -88,7 +93,7 @@
 }
 
 task<NoCopyNoMove &> rvalue2ref(NoCopyNoMove &&value) {
-  co_return value; // expected-error {{non-const lvalue reference to type 'NoCopyNoMove' cannot bind to a temporary of type 'NoCopyNoMove'}}
+  co_return value; // cxx2b-error {{non-const lvalue reference to type 'NoCopyNoMove' cannot bind to a temporary of type 'NoCopyNoMove'}}
 }
 
 struct To {
Index: clang/test/SemaCXX/conversion-function.cpp
===================================================================
--- clang/test/SemaCXX/conversion-function.cpp
+++ clang/test/SemaCXX/conversion-function.cpp
@@ -1,9 +1,9 @@
-// RUN: %clang_cc1 -std=c++2b -fsyntax-only -verify=expected          -triple %itanium_abi_triple -Wbind-to-temporary-copy %s
-// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify=expected          -triple %itanium_abi_triple -Wbind-to-temporary-copy %s
-// RUN: %clang_cc1 -std=c++14 -fsyntax-only -verify=expected,cxx98_14 -triple %itanium_abi_triple -Wbind-to-temporary-copy %s
-// RUN: %clang_cc1 -std=c++11 -fsyntax-only -verify=expected,cxx98_14 -triple %itanium_abi_triple -Wbind-to-temporary-copy %s
-// RUN: %clang_cc1 -std=c++98 -fsyntax-only -verify=expected,cxx98_14 -triple %itanium_abi_triple -Wbind-to-temporary-copy %s
-// RUN: %clang_cc1            -fsyntax-only -verify=expected,cxx98_14 -triple %itanium_abi_triple -Wbind-to-temporary-copy %s
+// RUN: %clang_cc1 -std=c++2b -fsyntax-only -verify=expected                   -triple %itanium_abi_triple -Wbind-to-temporary-copy %s
+// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify=expected,cxx98_20          -triple %itanium_abi_triple -Wbind-to-temporary-copy %s
+// RUN: %clang_cc1 -std=c++14 -fsyntax-only -verify=expected,cxx98_14,cxx98_20 -triple %itanium_abi_triple -Wbind-to-temporary-copy %s
+// RUN: %clang_cc1 -std=c++11 -fsyntax-only -verify=expected,cxx98_14,cxx98_20 -triple %itanium_abi_triple -Wbind-to-temporary-copy %s
+// RUN: %clang_cc1 -std=c++98 -fsyntax-only -verify=expected,cxx98_14,cxx98_20 -triple %itanium_abi_triple -Wbind-to-temporary-copy %s
+// RUN: %clang_cc1            -fsyntax-only -verify=expected,cxx98_14,cxx98_20 -triple %itanium_abi_triple -Wbind-to-temporary-copy %s
 
 class X {
 public:
@@ -126,7 +126,7 @@
 class AutoPtrRef { };
 
 class AutoPtr {
-  AutoPtr(AutoPtr &); // cxx98_14-note{{declared private here}}
+  AutoPtr(AutoPtr &); // cxx98_20-note{{declared private here}}
 
 public:
   AutoPtr();
@@ -142,7 +142,7 @@
 
   AutoPtr p;
   if (Cond)
-    return p; // cxx98_14-error{{calling a private constructor}}
+    return p; // cxx98_20-error{{calling a private constructor}}
 
   return AutoPtr();
 }
Index: clang/test/SemaCXX/P1155.cpp
===================================================================
--- clang/test/SemaCXX/P1155.cpp
+++ clang/test/SemaCXX/P1155.cpp
@@ -1,9 +1,9 @@
-// RUN: %clang_cc1 -std=c++2b -fsyntax-only -fcxx-exceptions -verify=cxx20_2b %s
-// RUN: %clang_cc1 -std=c++20 -fsyntax-only -fcxx-exceptions -verify=cxx20_2b %s
-// RUN: %clang_cc1 -std=c++17 -fsyntax-only -fcxx-exceptions -verify=cxx11_17 %s
-// RUN: %clang_cc1 -std=c++14 -fsyntax-only -fcxx-exceptions -verify=cxx11_17 %s
-// RUN: %clang_cc1 -std=c++11 -fsyntax-only -fcxx-exceptions -verify=cxx11_17 %s
-// cxx20_2b-no-diagnostics
+// RUN: %clang_cc1 -std=c++2b -fsyntax-only -fcxx-exceptions -verify %s
+// RUN: %clang_cc1 -std=c++20 -fsyntax-only -fcxx-exceptions -verify %s
+// RUN: %clang_cc1 -std=c++17 -fsyntax-only -fcxx-exceptions -verify %s
+// RUN: %clang_cc1 -std=c++14 -fsyntax-only -fcxx-exceptions -verify %s
+// RUN: %clang_cc1 -std=c++11 -fsyntax-only -fcxx-exceptions -verify %s
+// expected-no-diagnostics
 
 // Throwing
 namespace test_throwing {
@@ -23,13 +23,13 @@
 class Widget {};
 
 struct To {
-  operator Widget() const & = delete; // cxx11_17-note {{'operator Widget' has been explicitly marked deleted here}}
+  operator Widget() const & = delete;
   operator Widget() &&;
 };
 
 Widget nine() {
   To t;
-  return t; // cxx11_17-error {{conversion function from 'test_non_constructor_conversion::To' to 'test_non_constructor_conversion::Widget' invokes a deleted function}}
+  return t;
 }
 } // namespace test_non_constructor_conversion
 
@@ -39,16 +39,16 @@
 public:
   Widget();
   Widget(Widget &&);
-  Widget(const Widget &) = delete; // cxx11_17-note {{'Widget' has been explicitly marked deleted here}}
+  Widget(const Widget &) = delete;
 };
 
 struct Fowl {
-  Fowl(Widget); // cxx11_17-note {{passing argument to parameter here}}
+  Fowl(Widget);
 };
 
 Fowl eleven() {
   Widget w;
-  return w; // cxx11_17-error {{call to deleted constructor of 'test_by_value_sinks::Widget'}}
+  return w;
 }
 } // namespace test_by_value_sinks
 
@@ -58,13 +58,13 @@
 public:
   Base();
   Base(Base &&);
-  Base(Base const &) = delete; // cxx11_17-note {{'Base' has been explicitly marked deleted here}}
+  Base(Base const &) = delete;
 };
 
 class Derived : public Base {};
 
 Base thirteen() {
   Derived result;
-  return result; // cxx11_17-error {{call to deleted constructor of 'test_slicing::Base'}}
+  return result;
 }
 } // namespace test_slicing
Index: clang/test/CXX/class/class.init/class.copy.elision/p3.cpp
===================================================================
--- clang/test/CXX/class/class.init/class.copy.elision/p3.cpp
+++ clang/test/CXX/class/class.init/class.copy.elision/p3.cpp
@@ -136,42 +136,42 @@
 
 struct A1 {
   operator C() &&;
-  operator C() const & = delete; // cxx11_17-note {{'operator C' has been explicitly marked deleted here}}
+  operator C() const & = delete;
 };
 C test1() {
   A1 a;
-  return a; // cxx11_17-error {{conversion function from 'test_non_ctor_conversion::A1' to 'test_non_ctor_conversion::C' invokes a deleted function}}
+  return a;
 }
 
 struct A2 {
   operator C() &&;
 
 private:
-  operator C() const &; // cxx11_17-note {{declared private here}}
+  operator C() const &;
 };
 C test2() {
   A2 a;
-  return a; // cxx11_17-error {{'operator C' is a private member of 'test_non_ctor_conversion::A2'}}
+  return a;
 }
 
 struct B1 {
   operator C() const &;
-  operator C() && = delete; // cxx20_2b-note {{'operator C' has been explicitly marked deleted here}}
+  operator C() && = delete; // expected-note {{'operator C' has been explicitly marked deleted here}}
 };
 C test3() {
   B1 b;
-  return b; // cxx20_2b-error {{conversion function from 'test_non_ctor_conversion::B1' to 'test_non_ctor_conversion::C' invokes a deleted function}}
+  return b; // expected-error {{conversion function from 'test_non_ctor_conversion::B1' to 'test_non_ctor_conversion::C' invokes a deleted function}}
 }
 
 struct B2 {
   operator C() const &;
 
 private:
-  operator C() &&; // cxx20_2b-note {{declared private here}}
+  operator C() &&; // expected-note {{declared private here}}
 };
 C test4() {
   B2 b;
-  return b; // cxx20_2b-error {{'operator C' is a private member of 'test_non_ctor_conversion::B2'}}
+  return b; // expected-error {{'operator C' is a private member of 'test_non_ctor_conversion::B2'}}
 }
 } // namespace test_non_ctor_conversion
 
@@ -190,35 +190,35 @@
   NeedRvalueRef(B2 &&);
 };
 struct NeedValue {
-  NeedValue(A1); // cxx11_17-note 2 {{passing argument to parameter here}}
+  NeedValue(A1);
   NeedValue(A2);
-  NeedValue(B1); // cxx20_2b-note 2 {{passing argument to parameter here}}
+  NeedValue(B1); // expected-note 2 {{passing argument to parameter here}}
   NeedValue(B2);
 };
 
 struct A1 {
   A1();
   A1(A1 &&);
-  A1(const A1 &) = delete; // cxx11_17-note 3 {{'A1' has been explicitly marked deleted here}}
+  A1(const A1 &) = delete;
 };
 NeedValue test_1_1() {
   // not rvalue reference
   // same type
   A1 a;
-  return a; // cxx11_17-error {{call to deleted constructor of 'test_ctor_param_rvalue_ref::A1'}}
+  return a;
 }
 class DerivedA1 : public A1 {};
 A1 test_1_2() {
   // rvalue reference
   // not same type
   DerivedA1 a;
-  return a; // cxx11_17-error {{call to deleted constructor of 'test_ctor_param_rvalue_ref::A1'}}
+  return a;
 }
 NeedValue test_1_3() {
   // not rvalue reference
   // not same type
   DerivedA1 a;
-  return a; // cxx11_17-error {{call to deleted constructor of 'test_ctor_param_rvalue_ref::A1'}}
+  return a;
 }
 
 struct A2 {
@@ -226,51 +226,51 @@
   A2(A2 &&);
 
 private:
-  A2(const A2 &); // cxx11_17-note 3 {{declared private here}}
+  A2(const A2 &);
 };
 NeedValue test_2_1() {
   // not rvalue reference
   // same type
   A2 a;
-  return a; // cxx11_17-error {{calling a private constructor of class 'test_ctor_param_rvalue_ref::A2'}}
+  return a;
 }
 class DerivedA2 : public A2 {};
 A2 test_2_2() {
   // rvalue reference
   // not same type
   DerivedA2 a;
-  return a; // cxx11_17-error {{calling a private constructor of class 'test_ctor_param_rvalue_ref::A2'}}
+  return a;
 }
 NeedValue test_2_3() {
   // not rvalue reference
   // not same type
   DerivedA2 a;
-  return a; // cxx11_17-error {{calling a private constructor of class 'test_ctor_param_rvalue_ref::A2'}}
+  return a;
 }
 
 struct B1 {
   B1();
   B1(const B1 &);
-  B1(B1 &&) = delete; // cxx20_2b-note 3 {{'B1' has been explicitly marked deleted here}}
+  B1(B1 &&) = delete; // expected-note 3 {{'B1' has been explicitly marked deleted here}}
 };
 NeedValue test_3_1() {
   // not rvalue reference
   // same type
   B1 b;
-  return b; // cxx20_2b-error {{call to deleted constructor of 'test_ctor_param_rvalue_ref::B1'}}
+  return b; // expected-error {{call to deleted constructor of 'test_ctor_param_rvalue_ref::B1'}}
 }
 class DerivedB1 : public B1 {};
 B1 test_3_2() {
   // rvalue reference
   // not same type
   DerivedB1 b;
-  return b; // cxx20_2b-error {{call to deleted constructor of 'test_ctor_param_rvalue_ref::B1'}}
+  return b; // expected-error {{call to deleted constructor of 'test_ctor_param_rvalue_ref::B1'}}
 }
 NeedValue test_3_3() {
   // not rvalue reference
   // not same type
   DerivedB1 b;
-  return b; // cxx20_2b-error {{call to deleted constructor of 'test_ctor_param_rvalue_ref::B1'}}
+  return b; // expected-error {{call to deleted constructor of 'test_ctor_param_rvalue_ref::B1'}}
 }
 
 struct B2 {
@@ -278,26 +278,26 @@
   B2(const B2 &);
 
 private:
-  B2(B2 &&); // cxx20_2b-note 3 {{declared private here}}
+  B2(B2 &&); // expected-note 3 {{declared private here}}
 };
 NeedValue test_4_1() {
   // not rvalue reference
   // same type
   B2 b;
-  return b; // cxx20_2b-error {{calling a private constructor of class 'test_ctor_param_rvalue_ref::B2'}}
+  return b; // expected-error {{calling a private constructor of class 'test_ctor_param_rvalue_ref::B2'}}
 }
 class DerivedB2 : public B2 {};
 B2 test_4_2() {
   // rvalue reference
   // not same type
   DerivedB2 b;
-  return b; // cxx20_2b-error {{calling a private constructor of class 'test_ctor_param_rvalue_ref::B2'}}
+  return b; // expected-error {{calling a private constructor of class 'test_ctor_param_rvalue_ref::B2'}}
 }
 NeedValue test_4_3() {
   // not rvalue reference
   // not same type
   DerivedB2 b;
-  return b; // cxx20_2b-error {{calling a private constructor of class 'test_ctor_param_rvalue_ref::B2'}}
+  return b; // expected-error {{calling a private constructor of class 'test_ctor_param_rvalue_ref::B2'}}
 }
 } // namespace test_ctor_param_rvalue_ref
 
Index: clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
===================================================================
--- clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -1071,10 +1071,9 @@
     // 'optimization'. Notably, functions with 'auto' return types won't have it
     // deduced by this point. Coupled with the limitation described
     // previously, this makes it very hard to support copy elision for these.
-    Sema::NamedReturnInfo Info = SemaRef.getNamedReturnInfo(Var);
-    bool NRVO = SemaRef.getCopyElisionCandidate(
-                    Info, cast<FunctionType>(FT)->getReturnType()) != nullptr;
-    Var->setNRVOVariable(NRVO);
+    Sema::NamedReturnInfo Info = SemaRef.getNamedReturnInfo(
+        Var, cast<FunctionType>(FT)->getReturnType(), /*Parenthesized=*/false);
+    Var->setNRVOVariable(Info.isCopyElidable());
   }
 
   Var->setImplicit(D->isImplicit());
Index: clang/lib/Sema/SemaStmt.cpp
===================================================================
--- clang/lib/Sema/SemaStmt.cpp
+++ clang/lib/Sema/SemaStmt.cpp
@@ -3050,37 +3050,49 @@
 /// \returns An aggregate which contains the Candidate and isMoveEligible
 /// and isCopyElidable methods. If Candidate is non-null, it means
 /// isMoveEligible() would be true under the most permissive language standard.
-Sema::NamedReturnInfo Sema::getNamedReturnInfo(Expr *&E, bool ForceCXX2b) {
+const VarDecl *Sema::getCopyElisionCandidate(Expr *&E, NRVOExprContext C,
+                                             QualType ReturnType) {
   if (!E)
-    return NamedReturnInfo();
+    return nullptr;
   // - in a return statement in a function [where] ...
   // ... the expression is the name of a non-volatile automatic object ...
   const auto *DR = dyn_cast<DeclRefExpr>(E->IgnoreParens());
   if (!DR || DR->refersToEnclosingVariableOrCapture())
-    return NamedReturnInfo();
+    return nullptr;
   const auto *VD = dyn_cast<VarDecl>(DR->getDecl());
   if (!VD)
-    return NamedReturnInfo();
-  NamedReturnInfo Res = getNamedReturnInfo(VD, /*ForceCXX20=*/ForceCXX2b);
-  if (Res.Candidate && !E->isXValue() &&
-      (ForceCXX2b || getLangOpts().CPlusPlus2b)) {
+    return nullptr;
+
+  NamedReturnInfo Info = getNamedReturnInfo(VD, ReturnType, isa<ParenExpr>(E));
+  if (Info.isMoveEligible() && (E->isLValue() || E->isRValue())) {
+    ExprValueKind CastToKind =
+        getLangOpts().CPlusPlus2b ? VK_XValue : VK_YValue;
     E = ImplicitCastExpr::Create(Context, VD->getType().getNonReferenceType(),
-                                 CK_NoOp, E, nullptr, VK_XValue,
+                                 CK_NoOp, E, nullptr, CastToKind,
                                  FPOptionsOverride());
+    return Info.isCopyElidable() ? Info.Candidate : nullptr;
   }
-  return Res;
-}
 
-/// Updates the status in the given NamedReturnInfo object to disallow
-/// copy elision, and optionally also implicit move.
-///
-/// \param Info The NamedReturnInfo object to update.
-///
-/// \param CanMove If true, disallow only copy elision.
-/// If false, also disallow implcit move.
-static void disallowNRVO(Sema::NamedReturnInfo &Info, bool CanMove) {
-  Info.S = std::min(Info.S, CanMove ? Sema::NamedReturnInfo::MoveEligible
-                                    : Sema::NamedReturnInfo::None);
+  if (Info.Candidate && !getDiagnostics().isIgnored(diag::warn_return_std_move,
+                                                    E->getExprLoc())) {
+    QualType QT = Info.Candidate->getType();
+    if (QT.getNonReferenceType().getUnqualifiedType().isTriviallyCopyableType(
+            Context)) {
+      // Adding 'std::move' around a trivially copyable variable is probably
+      // pointless. Don't suggest it.
+    } else {
+      Diag(E->getExprLoc(), diag::warn_return_std_move)
+          << E->getSourceRange() << Info.Candidate->getDeclName() << int(C);
+
+      SmallString<32> Str;
+      Str += "std::move(";
+      Str += Info.Candidate->getDeclName().getAsString();
+      Str += ")";
+      Diag(E->getExprLoc(), diag::note_add_std_move)
+          << FixItHint::CreateReplacement(E->getSourceRange(), Str);
+    }
+  }
+  return nullptr;
 }
 
 /// Determine whether the given NRVO candidate variable is move-eligible or
@@ -3095,22 +3107,28 @@
 /// and isCopyElidable methods. If Candidate is non-null, it means
 /// isMoveEligible() would be true under the most permissive language standard.
 Sema::NamedReturnInfo Sema::getNamedReturnInfo(const VarDecl *VD,
-                                               bool ForceCXX20) {
-  bool hasCXX11 = getLangOpts().CPlusPlus11 || ForceCXX20;
-  bool hasCXX20 = getLangOpts().CPlusPlus20 || ForceCXX20;
+                                               QualType ReturnType,
+                                               bool Parenthesized) {
   NamedReturnInfo Info{VD, NamedReturnInfo::MoveEligibleAndCopyElidable};
 
+  // Updates the status in Info to disallow copy elision.
+  // Optionally also disallows implicit move.
+  auto disallowNRVO = [&Info](bool CanMove) {
+    Info.S = std::min(Info.S, CanMove ? NamedReturnInfo::MoveEligible
+                                      : NamedReturnInfo::None);
+  };
+
   // C++20 [class.copy.elision]p3:
   // - in a return statement in a function with ...
   // (other than a function ... parameter)
   if (VD->getKind() == Decl::ParmVar)
-    disallowNRVO(Info, hasCXX11);
+    disallowNRVO(getLangOpts().CPlusPlus11);
   else if (VD->getKind() != Decl::Var)
     return NamedReturnInfo();
 
   // (other than ... a catch-clause parameter)
   if (VD->isExceptionVariable())
-    disallowNRVO(Info, hasCXX20);
+    disallowNRVO(getLangOpts().CPlusPlus20);
 
   // ...automatic...
   if (!VD->hasLocalStorage())
@@ -3135,7 +3153,7 @@
     if (VDReferencedType.isVolatileQualified() ||
         !VDReferencedType->isObjectType())
       return NamedReturnInfo();
-    disallowNRVO(Info, hasCXX20);
+    disallowNRVO(getLangOpts().CPlusPlus20);
   } else {
     return NamedReturnInfo();
   }
@@ -3144,204 +3162,48 @@
   // alignment cannot use NRVO.
   if (!VDType->isDependentType() && VD->hasAttr<AlignedAttr>() &&
       Context.getDeclAlign(VD) > Context.getTypeAlignInChars(VDType))
-    disallowNRVO(Info, hasCXX11);
-
-  return Info;
-}
-
-/// Updates given NamedReturnInfo's move-eligible and
-/// copy-elidable statuses, considering the function
-/// return type criteria as applicable to return statements.
-///
-/// \param Info The NamedReturnInfo object to update.
-///
-/// \param ReturnType This is the return type of the function.
-/// \returns The copy elision candidate, in case the initial return expression
-/// was copy elidable, or nullptr otherwise.
-const VarDecl *Sema::getCopyElisionCandidate(NamedReturnInfo &Info,
-                                             QualType ReturnType) {
-  if (!Info.Candidate)
-    return nullptr;
-
-  auto invalidNRVO = [&] {
-    Info = NamedReturnInfo();
-    return nullptr;
-  };
-
-  // If we got a non-deduced auto ReturnType, we are in a dependent context and
-  // there is no point in allowing copy elision since we won't have it deduced
-  // by the point the VardDecl is instantiated, which is the last chance we have
-  // of deciding if the candidate is really copy elidable.
-  if ((ReturnType->getTypeClass() == Type::TypeClass::Auto &&
-       ReturnType->isCanonicalUnqualified()) ||
-      ReturnType->isSpecificBuiltinType(BuiltinType::Dependent))
-    return invalidNRVO();
-
-  if (!ReturnType->isDependentType()) {
-    // - in a return statement in a function with ...
-    // ... a class return type ...
-    if (!ReturnType->isRecordType())
-      return invalidNRVO();
-
-    QualType VDType = Info.Candidate->getType();
-    // ... the same cv-unqualified type as the function return type ...
-    // When considering moving this expression out, allow dissimilar types.
-    if (!VDType->isDependentType() &&
-        !Context.hasSameUnqualifiedType(ReturnType, VDType))
-      disallowNRVO(Info, getLangOpts().CPlusPlus11);
-  }
-  return Info.isCopyElidable() ? Info.Candidate : nullptr;
-}
-
-/// Try to perform the initialization of a potentially-movable value,
-/// which is the operand to a return or throw statement.
-///
-/// This routine implements C++20 [class.copy.elision]p3, which attempts to
-/// treat returned lvalues as rvalues in certain cases (to prefer move
-/// construction), then falls back to treating them as lvalues if that failed.
-///
-/// \param ConvertingConstructorsOnly If true, follow [class.copy.elision]p3 and
-/// reject resolutions that find non-constructors, such as derived-to-base
-/// conversions or `operator T()&&` member functions. If false, do consider such
-/// conversion sequences.
-///
-/// \param Res We will fill this in if move-initialization was possible.
-/// If move-initialization is not possible, such that we must fall back to
-/// treating the operand as an lvalue, we will leave Res in its original
-/// invalid state.
-///
-/// \returns Whether we need to do the second overload resolution. If the first
-/// overload resolution fails, or if the first overload resolution succeeds but
-/// the selected constructor/operator doesn't match the additional criteria, we
-/// need to do the second overload resolution.
-static bool TryMoveInitialization(Sema &S, const InitializedEntity &Entity,
-                                  const VarDecl *NRVOCandidate, Expr *&Value,
-                                  bool ConvertingConstructorsOnly,
-                                  bool IsDiagnosticsCheck, ExprResult &Res) {
-  ImplicitCastExpr AsRvalue(ImplicitCastExpr::OnStack, Value->getType(),
-                            CK_NoOp, Value, VK_XValue, FPOptionsOverride());
-
-  Expr *InitExpr = &AsRvalue;
-
-  InitializationKind Kind = InitializationKind::CreateCopy(
-      Value->getBeginLoc(), Value->getBeginLoc());
-
-  InitializationSequence Seq(S, Entity, Kind, InitExpr);
-
-  bool NeedSecondOverloadResolution = true;
-  if (!Seq &&
-      (IsDiagnosticsCheck || Seq.getFailedOverloadResult() != OR_Deleted)) {
-    return NeedSecondOverloadResolution;
-  }
-
-  for (const InitializationSequence::Step &Step : Seq.steps()) {
-    if (Step.Kind != InitializationSequence::SK_ConstructorInitialization &&
-        Step.Kind != InitializationSequence::SK_UserConversion)
-      continue;
-
-    FunctionDecl *FD = Step.Function.Function;
-    if (ConvertingConstructorsOnly) {
-      if (isa<CXXConstructorDecl>(FD)) {
-        // C++11 [class.copy]p32:
-        // C++14 [class.copy]p32:
-        // C++17 [class.copy.elision]p3:
-        // [...] if the type of the first parameter of the selected constructor
-        // is not an rvalue reference to the object's type (possibly
-        // cv-qualified), overload resolution is performed again, considering
-        // the object as an lvalue.
-        const RValueReferenceType *RRefType =
-            FD->getParamDecl(0)->getType()->getAs<RValueReferenceType>();
-        if (!RRefType)
-          break;
-        if (!S.Context.hasSameUnqualifiedType(RRefType->getPointeeType(),
-                                              NRVOCandidate->getType()))
-          break;
-      } else {
-        continue;
+    disallowNRVO(getLangOpts().CPlusPlus11);
+
+  if (!ReturnType.isNull()) {
+    if (ReturnType->isDependentType()) {
+      if ((ReturnType->getTypeClass() == Type::TypeClass::Auto &&
+           ReturnType->isCanonicalUnqualified()) ||
+          ReturnType->isSpecificBuiltinType(BuiltinType::Dependent)) {
+        return NamedReturnInfo();
       }
     } else {
-      if (isa<CXXConstructorDecl>(FD)) {
-        // Check that overload resolution selected a constructor taking an
-        // rvalue reference. If it selected an lvalue reference, then we
-        // didn't need to cast this thing to an rvalue in the first place.
-        if (IsDiagnosticsCheck &&
-            !isa<RValueReferenceType>(FD->getParamDecl(0)->getType()))
-          break;
-      } else if (isa<CXXMethodDecl>(FD)) {
-        // Check that overload resolution selected a conversion operator
-        // taking an rvalue reference.
-        if (cast<CXXMethodDecl>(FD)->getRefQualifier() != RQ_RValue)
-          break;
-      } else {
-        continue;
+      if (ReturnType->isSpecificBuiltinType(BuiltinType::Dependent)) {
+        ReturnType = Context.getAutoDeductType();
       }
-    }
-
-    NeedSecondOverloadResolution = false;
-    // Promote "AsRvalue" to the heap, since we now need this
-    // expression node to persist.
-    Value =
-        ImplicitCastExpr::Create(S.Context, Value->getType(), CK_NoOp, Value,
-                                 nullptr, VK_XValue, FPOptionsOverride());
-
-    // Complete type-checking the initialization of the return type
-    // using the constructor we found.
-    Res = Seq.Perform(S, Entity, Kind, Value);
-  }
-
-  return NeedSecondOverloadResolution;
-}
-
-/// Perform the initialization of a potentially-movable value, which
-/// is the result of return value.
-///
-/// This routine implements C++20 [class.copy.elision]p3, which attempts to
-/// treat returned lvalues as rvalues in certain cases (to prefer move
-/// construction), then falls back to treating them as lvalues if that failed.
-ExprResult
-Sema::PerformMoveOrCopyInitialization(const InitializedEntity &Entity,
-                                      const NamedReturnInfo &NRInfo,
-                                      Expr *Value) {
-
-  if (NRInfo.Candidate && !getLangOpts().CPlusPlus2b) {
-    if (NRInfo.isMoveEligible()) {
-      ExprResult Res;
-      if (!TryMoveInitialization(*this, Entity, NRInfo.Candidate, Value,
-                                 !getLangOpts().CPlusPlus20, false, Res))
-        return Res;
-    }
-    if (!getDiagnostics().isIgnored(diag::warn_return_std_move,
-                                    Value->getExprLoc())) {
-      QualType QT = NRInfo.Candidate->getType();
-      if (QT.getNonReferenceType().getUnqualifiedType().isTriviallyCopyableType(
-              Context)) {
-        // Adding 'std::move' around a trivially copyable variable is probably
-        // pointless. Don't suggest it.
-      } else {
-        ExprResult FakeRes = ExprError();
-        Expr *FakeValue = Value;
-        TryMoveInitialization(*this, Entity, NRInfo.Candidate, FakeValue, false,
-                              true, FakeRes);
-        if (!FakeRes.isInvalid()) {
-          bool IsThrow = (Entity.getKind() == InitializedEntity::EK_Exception);
-          SmallString<32> Str;
-          Str += "std::move(";
-          Str += NRInfo.Candidate->getDeclName().getAsString();
-          Str += ")";
-          Diag(Value->getExprLoc(), diag::warn_return_std_move)
-              << Value->getSourceRange() << NRInfo.Candidate->getDeclName()
-              << IsThrow;
-          Diag(Value->getExprLoc(), diag::note_add_std_move)
-              << FixItHint::CreateReplacement(Value->getSourceRange(), Str);
+      if (ReturnType->isCanonicalUnqualified()) {
+        if (AutoType *AT = ReturnType->getContainedAutoType()) {
+          QualType T = VDType.getNonReferenceType().getUnqualifiedType();
+          if (AT->isDecltypeAuto())
+            ReturnType = Parenthesized ? Context.getLValueReferenceType(T) : T;
+          else if (ReturnType->isLValueReferenceType())
+            ReturnType = Context.getLValueReferenceType(T);
+          else if (ReturnType->isRValueReferenceType())
+            ReturnType = Context.getRValueReferenceType(T);
+          else
+            ReturnType = T;
         }
       }
+
+      // - in a return statement in a function with ...
+      // ... a class return type ...
+      if (!ReturnType->isRecordType())
+        disallowNRVO(getLangOpts().CPlusPlus2b);
+
+      // ... the same cv-unqualified type as the function return type ...
+      // When considering moving this expression out, allow dissimilar
+      // types.
+      if (!VDType->isDependentType() &&
+          !Context.hasSameUnqualifiedType(ReturnType, VDType))
+        disallowNRVO(getLangOpts().CPlusPlus11);
     }
   }
 
-  // Either we didn't meet the criteria for treating an lvalue as an rvalue,
-  // above, or overload resolution failed. Either way, we need to try
-  // (again) now with the return value expression as written.
-  return PerformCopyInitialization(Entity, SourceLocation(), Value);
+  return Info;
 }
 
 /// Determine whether the declared return type of the specified function
@@ -3356,8 +3218,7 @@
 /// for capturing scopes.
 ///
 StmtResult Sema::ActOnCapScopeReturnStmt(SourceLocation ReturnLoc,
-                                         Expr *RetValExp,
-                                         NamedReturnInfo &NRInfo) {
+                                         Expr *RetValExp) {
   // If this is the first return we've seen, infer the return type.
   // [expr.prim.lambda]p4 in C++11; block literals follow the same rules.
   CapturingScopeInfo *CurCap = cast<CapturingScopeInfo>(getCurFunction());
@@ -3380,6 +3241,7 @@
                               /* NRVOCandidate=*/nullptr);
   }
 
+  const VarDecl *NRVOCandidate = nullptr;
   if (HasDeducedReturnType) {
     FunctionDecl *FD = CurLambda->CallOperator;
     // If we've already decided this lambda is invalid, e.g. because
@@ -3392,6 +3254,9 @@
     if (CurCap->ReturnType.isNull())
       CurCap->ReturnType = FD->getReturnType();
 
+    NRVOCandidate = getCopyElisionCandidate(
+        RetValExp, NRVOExprContext::Returned, CurCap->ReturnType);
+
     AutoType *AT = CurCap->ReturnType->getContainedAutoType();
     assert(AT && "lost auto type from lambda return type");
     if (DeduceFunctionTypeFromReturnExpr(FD, ReturnLoc, RetValExp, AT)) {
@@ -3400,43 +3265,47 @@
       return StmtError();
     }
     CurCap->ReturnType = FnRetType = FD->getReturnType();
-  } else if (CurCap->HasImplicitReturnType) {
-    // For blocks/lambdas with implicit return types, we check each return
-    // statement individually, and deduce the common return type when the block
-    // or lambda is completed.
-    // FIXME: Fold this into the 'auto' codepath above.
-    if (RetValExp && !isa<InitListExpr>(RetValExp)) {
-      ExprResult Result = DefaultFunctionArrayLvalueConversion(RetValExp);
-      if (Result.isInvalid())
-        return StmtError();
-      RetValExp = Result.get();
-
-      // DR1048: even prior to C++14, we should use the 'auto' deduction rules
-      // when deducing a return type for a lambda-expression (or by extension
-      // for a block). These rules differ from the stated C++11 rules only in
-      // that they remove top-level cv-qualifiers.
-      if (!CurContext->isDependentContext())
-        FnRetType = RetValExp->getType().getUnqualifiedType();
-      else
-        FnRetType = CurCap->ReturnType = Context.DependentTy;
-    } else {
-      if (RetValExp) {
-        // C++11 [expr.lambda.prim]p4 bans inferring the result from an
-        // initializer list, because it is not an expression (even
-        // though we represent it as one). We still deduce 'void'.
-        Diag(ReturnLoc, diag::err_lambda_return_init_list)
-          << RetValExp->getSourceRange();
+  } else {
+    if (CurCap->HasImplicitReturnType) {
+      // For blocks/lambdas with implicit return types, we check each return
+      // statement individually, and deduce the common return type when the
+      // block or lambda is completed.
+      // FIXME: Fold this into the 'auto' codepath above.
+      if (RetValExp && !isa<InitListExpr>(RetValExp)) {
+        ExprResult Result = DefaultFunctionArrayLvalueConversion(RetValExp);
+        if (Result.isInvalid())
+          return StmtError();
+        RetValExp = Result.get();
+
+        // DR1048: even prior to C++14, we should use the 'auto' deduction rules
+        // when deducing a return type for a lambda-expression (or by extension
+        // for a block). These rules differ from the stated C++11 rules only in
+        // that they remove top-level cv-qualifiers.
+        if (!CurContext->isDependentContext())
+          FnRetType = RetValExp->getType().getUnqualifiedType();
+        else
+          FnRetType = CurCap->ReturnType = Context.DependentTy;
+      } else {
+        if (RetValExp) {
+          // C++11 [expr.lambda.prim]p4 bans inferring the result from an
+          // initializer list, because it is not an expression (even
+          // though we represent it as one). We still deduce 'void'.
+          Diag(ReturnLoc, diag::err_lambda_return_init_list)
+              << RetValExp->getSourceRange();
+        }
+
+        FnRetType = Context.VoidTy;
       }
 
-      FnRetType = Context.VoidTy;
+      // Although we'll properly infer the type of the block once it's
+      // completed, make sure we provide a return type now for better error
+      // recovery.
+      if (CurCap->ReturnType.isNull())
+        CurCap->ReturnType = FnRetType;
     }
-
-    // Although we'll properly infer the type of the block once it's completed,
-    // make sure we provide a return type now for better error recovery.
-    if (CurCap->ReturnType.isNull())
-      CurCap->ReturnType = FnRetType;
+    NRVOCandidate = getCopyElisionCandidate(
+        RetValExp, NRVOExprContext::Returned, CurCap->ReturnType);
   }
-  const VarDecl *NRVOCandidate = getCopyElisionCandidate(NRInfo, FnRetType);
 
   if (auto *CurBlock = dyn_cast<BlockScopeInfo>(CurCap)) {
     if (CurBlock->FunctionType->castAs<FunctionType>()->getNoReturnAttr()) {
@@ -3488,7 +3357,8 @@
     // the C version of which boils down to CheckSingleAssignmentConstraints.
     InitializedEntity Entity = InitializedEntity::InitializeResult(
         ReturnLoc, FnRetType, NRVOCandidate != nullptr);
-    ExprResult Res = PerformMoveOrCopyInitialization(Entity, NRInfo, RetValExp);
+    ExprResult Res =
+        PerformCopyInitialization(Entity, SourceLocation(), RetValExp);
     if (Res.isInvalid()) {
       // FIXME: Cleanup temporaries here, anyway?
       return StmtError();
@@ -3703,10 +3573,8 @@
   if (RetValExp && DiagnoseUnexpandedParameterPack(RetValExp))
     return StmtError();
 
-  NamedReturnInfo NRInfo = getNamedReturnInfo(RetValExp);
-
   if (isa<CapturingScopeInfo>(getCurFunction()))
-    return ActOnCapScopeReturnStmt(ReturnLoc, RetValExp, NRInfo);
+    return ActOnCapScopeReturnStmt(ReturnLoc, RetValExp);
 
   QualType FnRetType;
   QualType RelatedRetType;
@@ -3760,6 +3628,8 @@
                               /* NRVOCandidate=*/nullptr);
   }
 
+  const VarDecl *NRVOCandidate =
+      getCopyElisionCandidate(RetValExp, NRVOExprContext::Returned, FnRetType);
   // FIXME: Add a flag to the ScopeInfo to indicate whether we're performing
   // deduction.
   if (getLangOpts().CPlusPlus14) {
@@ -3778,7 +3648,6 @@
       }
     }
   }
-  const VarDecl *NRVOCandidate = getCopyElisionCandidate(NRInfo, FnRetType);
 
   bool HasDependentReturnType = FnRetType->isDependentType();
 
@@ -3898,7 +3767,7 @@
       InitializedEntity Entity = InitializedEntity::InitializeResult(
           ReturnLoc, RetType, NRVOCandidate != nullptr);
       ExprResult Res =
-          PerformMoveOrCopyInitialization(Entity, NRInfo, RetValExp);
+          PerformCopyInitialization(Entity, SourceLocation(), RetValExp);
       if (Res.isInvalid()) {
         // FIXME: Clean up temporaries here anyway?
         return StmtError();
Index: clang/lib/Sema/SemaOverload.cpp
===================================================================
--- clang/lib/Sema/SemaOverload.cpp
+++ clang/lib/Sema/SemaOverload.cpp
@@ -4768,10 +4768,11 @@
     // FIXME: As a speculative fix to a defect introduced by CWG2352, we rank
     // a reference binding that performs a non-top-level qualification
     // conversion as a qualification conversion, not as an identity conversion.
-    ICS.Standard.Third = (RefConv &
-                              Sema::ReferenceConversions::NestedQualification)
-                             ? ICK_Qualification
-                             : ICK_Identity;
+    ICS.Standard.Third =
+        (RefConv & Sema::ReferenceConversions::NestedQualification) ||
+                (Init->isYValue() && !isRValRef)
+            ? ICK_Qualification
+            : ICK_Identity;
     ICS.Standard.setFromType(T2);
     ICS.Standard.setToType(0, T2);
     ICS.Standard.setToType(1, T1);
@@ -4830,15 +4831,16 @@
                                    AllowExplicit))
         return ICS;
     }
-  }
 
-  //     -- Otherwise, the reference shall be an lvalue reference to a
-  //        non-volatile const type (i.e., cv1 shall be const), or the reference
-  //        shall be an rvalue reference.
-  if (!isRValRef && (!T1.isConstQualified() || T1.isVolatileQualified())) {
-    if (InitCategory.isRValue() && RefRelationship != Sema::Ref_Incompatible)
-      ICS.setBad(BadConversionSequence::lvalue_ref_to_rvalue, Init, DeclType);
-    return ICS;
+    //     -- Otherwise, the reference shall be an lvalue reference to a
+    //        non-volatile const type (i.e., cv1 shall be const), or the
+    //        reference shall be an rvalue reference.
+    if (!Init->isYValue() &&
+        (!T1.isConstQualified() || T1.isVolatileQualified())) {
+      if (InitCategory.isRValue() && RefRelationship != Sema::Ref_Incompatible)
+        ICS.setBad(BadConversionSequence::lvalue_ref_to_rvalue, Init, DeclType);
+      return ICS;
+    }
   }
 
   //       -- If the initializer expression
Index: clang/lib/Sema/SemaInit.cpp
===================================================================
--- clang/lib/Sema/SemaInit.cpp
+++ clang/lib/Sema/SemaInit.cpp
@@ -3585,6 +3585,7 @@
   Step S;
   switch (VK) {
   case VK_RValue: S.Kind = SK_CastDerivedToBaseRValue; break;
+  case VK_YValue:
   case VK_XValue: S.Kind = SK_CastDerivedToBaseXValue; break;
   case VK_LValue: S.Kind = SK_CastDerivedToBaseLValue; break;
   }
@@ -3636,6 +3637,7 @@
   case VK_RValue:
     S.Kind = SK_QualificationConversionRValue;
     break;
+  case VK_YValue:
   case VK_XValue:
     S.Kind = SK_QualificationConversionXValue;
     break;
@@ -4806,7 +4808,8 @@
   OverloadingResult ConvOvlResult = OR_Success;
   bool T1Function = T1->isFunctionType();
   if (isLValueRef || T1Function) {
-    if (InitCategory.isLValue() && !isNonReferenceableGLValue(Initializer) &&
+    if ((InitCategory.isLValue() || Initializer->isYValue()) &&
+        !isNonReferenceableGLValue(Initializer) &&
         (RefRelationship == Sema::Ref_Compatible ||
          (Kind.isCStyleOrFunctionalCast() &&
           RefRelationship == Sema::Ref_Related))) {
Index: clang/lib/Sema/SemaExprCXX.cpp
===================================================================
--- clang/lib/Sema/SemaExprCXX.cpp
+++ clang/lib/Sema/SemaExprCXX.cpp
@@ -866,17 +866,18 @@
     //       operation from the operand to the exception object (15.1) can be
     //       omitted by constructing the automatic object directly into the
     //       exception object
-    NamedReturnInfo NRInfo =
-        IsThrownVarInScope ? getNamedReturnInfo(Ex) : NamedReturnInfo();
+    bool CopyElidable =
+        IsThrownVarInScope &&
+        getCopyElisionCandidate(Ex, NRVOExprContext::Thrown) != nullptr;
 
     QualType ExceptionObjectTy = Context.getExceptionObjectType(Ex->getType());
     if (CheckCXXThrowOperand(OpLoc, ExceptionObjectTy, Ex))
       return ExprError();
 
-    InitializedEntity Entity = InitializedEntity::InitializeException(
-        OpLoc, ExceptionObjectTy,
-        /*NRVO=*/NRInfo.isCopyElidable());
-    ExprResult Res = PerformMoveOrCopyInitialization(Entity, NRInfo, Ex);
+    InitializedEntity Entity =
+        InitializedEntity::InitializeException(OpLoc, ExceptionObjectTy,
+                                               /*NRVO=*/CopyElidable);
+    ExprResult Res = PerformCopyInitialization(Entity, SourceLocation(), Ex);
     if (Res.isInvalid())
       return ExprError();
     Ex = Res.get();
Index: clang/lib/Sema/SemaCoroutine.cpp
===================================================================
--- clang/lib/Sema/SemaCoroutine.cpp
+++ clang/lib/Sema/SemaCoroutine.cpp
@@ -997,7 +997,7 @@
   VarDecl *Promise = FSI->CoroutinePromise;
   ExprResult PC;
   if (E && (isa<InitListExpr>(E) || !E->getType()->isVoidType())) {
-    getNamedReturnInfo(E, /*ForceCXX2b=*/true);
+    getCopyElisionCandidate(E, NRVOExprContext::Co_Returned);
     PC = buildPromiseCall(*this, Promise, Loc, "return_value", E);
   } else {
     E = MakeFullDiscardedValueExpr(E).get();
Index: clang/lib/Sema/SemaCodeComplete.cpp
===================================================================
--- clang/lib/Sema/SemaCodeComplete.cpp
+++ clang/lib/Sema/SemaCodeComplete.cpp
@@ -1241,7 +1241,7 @@
 
     // For xvalue objects, we prefer the rvalue overload even if we have to
     // add qualifiers (which is rare, because const&& is rare).
-    if (ObjectKind == clang::VK_XValue)
+    if (ObjectKind == clang::VK_XValue || ObjectKind == clang::VK_YValue)
       return CandidateRef == RQ_RValue ? OverloadCompare::Dominates
                                        : OverloadCompare::Dominated;
   }
Index: clang/lib/CodeGen/CGObjC.cpp
===================================================================
--- clang/lib/CodeGen/CGObjC.cpp
+++ clang/lib/CodeGen/CGObjC.cpp
@@ -2887,8 +2887,7 @@
   // If we're loading retained from a __strong xvalue, we can avoid
   // an extra retain/release pair by zeroing out the source of this
   // "move" operation.
-  if (e->isXValue() &&
-      !type.isConstQualified() &&
+  if ((e->isXValue() || e->isYValue()) && !type.isConstQualified() &&
       type.getObjCLifetime() == Qualifiers::OCL_Strong) {
     // Emit the lvalue.
     LValue lv = CGF.EmitLValue(e);
Index: clang/lib/CodeGen/CGDecl.cpp
===================================================================
--- clang/lib/CodeGen/CGDecl.cpp
+++ clang/lib/CodeGen/CGDecl.cpp
@@ -711,7 +711,7 @@
       if (srcExpr->isLValue()) {
         CGF.EmitARCCopyWeak(destLV.getAddress(CGF), srcAddr);
       } else {
-        assert(srcExpr->isXValue());
+        assert(srcExpr->isXValue() || srcExpr->isYValue());
         CGF.EmitARCMoveWeak(destLV.getAddress(CGF), srcAddr);
       }
       return true;
Index: clang/lib/AST/TextNodeDumper.cpp
===================================================================
--- clang/lib/AST/TextNodeDumper.cpp
+++ clang/lib/AST/TextNodeDumper.cpp
@@ -152,6 +152,9 @@
       case VK_XValue:
         OS << " xvalue";
         break;
+      case VK_YValue:
+        OS << " yvalue";
+        break;
       }
     }
 
Index: clang/lib/AST/JSONNodeDumper.cpp
===================================================================
--- clang/lib/AST/JSONNodeDumper.cpp
+++ clang/lib/AST/JSONNodeDumper.cpp
@@ -59,6 +59,9 @@
     switch (E->getValueKind()) {
     case VK_LValue: Category = "lvalue"; break;
     case VK_XValue: Category = "xvalue"; break;
+    case VK_YValue:
+      Category = "yvalue";
+      break;
     case VK_RValue: Category = "rvalue"; break;
     }
     JOS.attribute("valueCategory", Category);
Index: clang/lib/AST/ExprClassification.cpp
===================================================================
--- clang/lib/AST/ExprClassification.cpp
+++ clang/lib/AST/ExprClassification.cpp
@@ -57,7 +57,7 @@
     assert(isLValue());
     break;
   case Cl::CL_XValue:
-    assert(isXValue());
+    assert(isXValue() || isYValue());
     break;
   case Cl::CL_Function:
   case Cl::CL_Void:
@@ -99,6 +99,7 @@
     return Lang.CPlusPlus ? ClassifyTemporary(E->getType()) : Cl::CL_PRValue;
   case VK_LValue:
     return Cl::CL_LValue;
+  case VK_YValue:
   case VK_XValue:
     return Cl::CL_XValue;
   }
Index: clang/lib/AST/ASTContext.cpp
===================================================================
--- clang/lib/AST/ASTContext.cpp
+++ clang/lib/AST/ASTContext.cpp
@@ -5446,6 +5446,7 @@
     return getRValueReferenceType(T);
   //     - otherwise, if e is an lvalue, decltype(e) is T&, where T is the
   //       type of e;
+  case VK_YValue:
   case VK_LValue:
     return getLValueReferenceType(T);
   //  - otherwise, decltype(e) is the type of e.
Index: clang/include/clang/Sema/Sema.h
===================================================================
--- clang/include/clang/Sema/Sema.h
+++ clang/include/clang/Sema/Sema.h
@@ -4733,24 +4733,19 @@
     enum Status : uint8_t { None, MoveEligible, MoveEligibleAndCopyElidable };
     Status S;
 
-    bool isMoveEligible() const { return S != None; };
+    bool isMoveEligible() const { return S >= MoveEligible; };
     bool isCopyElidable() const { return S == MoveEligibleAndCopyElidable; }
   };
-  NamedReturnInfo getNamedReturnInfo(Expr *&E, bool ForceCXX2b = false);
-  NamedReturnInfo getNamedReturnInfo(const VarDecl *VD,
-                                     bool ForceCXX20 = false);
-  const VarDecl *getCopyElisionCandidate(NamedReturnInfo &Info,
-                                         QualType ReturnType);
-
-  ExprResult PerformMoveOrCopyInitialization(const InitializedEntity &Entity,
-                                             const NamedReturnInfo &NRInfo,
-                                             Expr *Value);
+  NamedReturnInfo getNamedReturnInfo(const VarDecl *VD, QualType ReturnType,
+                                     bool Parenthesized);
+  enum class NRVOExprContext { Returned, Co_Returned, Thrown };
+  const VarDecl *getCopyElisionCandidate(Expr *&E, NRVOExprContext C,
+                                         QualType ReturnType = QualType());
 
   StmtResult ActOnReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp,
                              Scope *CurScope);
   StmtResult BuildReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp);
-  StmtResult ActOnCapScopeReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp,
-                                     NamedReturnInfo &NRInfo);
+  StmtResult ActOnCapScopeReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp);
 
   StmtResult ActOnGCCAsmStmt(SourceLocation AsmLoc, bool IsSimple,
                              bool IsVolatile, unsigned NumOutputs,
Index: clang/include/clang/Basic/Specifiers.h
===================================================================
--- clang/include/clang/Basic/Specifiers.h
+++ clang/include/clang/Basic/Specifiers.h
@@ -116,7 +116,11 @@
     /// An x-value expression is a reference to an object with
     /// independent storage but which can be "moved", i.e.
     /// efficiently cannibalized for its resources.
-    VK_XValue
+    VK_XValue,
+
+    // Is just like an x-value, but will bind to a
+    // l-value reference, at a lower rank.
+    VK_YValue,
   };
 
   /// A further classification of the kind of object referenced by an
Index: clang/include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -6378,7 +6378,8 @@
 def note_remove_move : Note<"remove std::move call here">;
 
 def warn_return_std_move : Warning<
-  "local variable %0 will be copied despite being %select{returned|thrown}1 by name">,
+  "local variable %0 will be copied despite being "
+  "%select{returned|co_returned|thrown}1 by name">,
   InGroup<ReturnStdMove>, DefaultIgnore;
 def note_add_std_move : Note<
   "call 'std::move' explicitly to avoid copying">;
Index: clang/include/clang/AST/Expr.h
===================================================================
--- clang/include/clang/AST/Expr.h
+++ clang/include/clang/AST/Expr.h
@@ -272,6 +272,7 @@
   bool isLValue() const { return getValueKind() == VK_LValue; }
   bool isRValue() const { return getValueKind() == VK_RValue; }
   bool isXValue() const { return getValueKind() == VK_XValue; }
+  bool isYValue() const { return getValueKind() == VK_YValue; }
   bool isGLValue() const { return getValueKind() != VK_RValue; }
 
   enum LValueClassification {
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to