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

Small update making some invariants more clear, with some simplifications.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D99005

Files:
  clang/include/clang/Sema/Sema.h
  clang/lib/Sema/Sema.cpp
  clang/lib/Sema/SemaCoroutine.cpp
  clang/lib/Sema/SemaExprCXX.cpp
  clang/lib/Sema/SemaStmt.cpp
  clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
  clang/lib/Sema/SemaType.cpp
  clang/test/CXX/class/class.init/class.copy.elision/p3.cpp
  clang/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p7-cxx14.cpp
  clang/test/CXX/drs/dr3xx.cpp
  clang/test/CXX/expr/expr.prim/expr.prim.lambda/p4-cxx14.cpp
  clang/test/CXX/temp/temp.decls/temp.mem/p5.cpp
  clang/test/SemaCXX/constant-expression-cxx11.cpp
  clang/test/SemaCXX/constant-expression-cxx14.cpp
  clang/test/SemaCXX/coroutine-rvo.cpp
  clang/test/SemaCXX/coroutines.cpp
  clang/test/SemaCXX/deduced-return-type-cxx14.cpp
  clang/test/SemaCXX/return-stack-addr.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,8 +1,8 @@
-// RUN: %clang_cc1 -std=c++2b -fsyntax-only -verify=cxx20_2b -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=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++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
@@ -217,8 +217,8 @@
 }
 
 // But if the return type is a reference type, then moving would be wrong.
-Derived& testRetRef1(Derived&& d) { return d; }
-Base& testRetRef2(Derived&& d) { return d; }
+Derived &testRetRef1(Derived &&d) { return d; } // cxx2b-error {{on-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'}}
 #if __cplusplus >= 201402L
 auto&& testRetRef3(Derived&& d) { return d; }
 decltype(auto) testRetRef4(Derived&& d) { return (d); }
Index: clang/test/SemaCXX/return-stack-addr.cpp
===================================================================
--- clang/test/SemaCXX/return-stack-addr.cpp
+++ clang/test/SemaCXX/return-stack-addr.cpp
@@ -1,6 +1,6 @@
-// RUN: %clang_cc1 -std=c++2b -fsyntax-only -verify=expected       %s
-// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify=expected       %s
-// RUN: %clang_cc1 -std=c++11 -fsyntax-only -verify=expected,cxx11 %s
+// RUN: %clang_cc1 -std=c++2b -fsyntax-only -verify=expected,cxx2b          %s
+// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify=expected,cxx11_20       %s
+// RUN: %clang_cc1 -std=c++11 -fsyntax-only -verify=expected,cxx11_20,cxx11 %s
 
 int* ret_local() {
   int x = 1;
@@ -29,7 +29,8 @@
 
 int& ret_local_ref() {
   int x = 1;
-  return x;  // expected-warning {{reference to stack memory}}
+  return x; // cxx11_20-warning {{reference to stack memory}}
+  // cxx2b-error@-1 {{non-const lvalue reference to type 'int' cannot bind to a temporary of type 'int'}}
 }
 
 int* ret_local_addrOf() {
@@ -154,8 +155,10 @@
   (void) [&]() -> int& { return b; };
   (void) [=]() mutable -> int& { return a; };
   (void) [=]() mutable -> int& { return b; };
-  (void) [&]() -> int& { int a; return a; }; // expected-warning {{reference to stack}}
-  (void) [=]() -> int& { int a; return a; }; // expected-warning {{reference to stack}}
+  (void) [&]() -> int& { int a; return a; }; // cxx11_20-warning {{reference to stack}}
+  // cxx2b-error@-1 {{non-const lvalue reference to type 'int' cannot bind to a temporary of type 'int'}}
+  (void) [=]() -> int& { int a; return a; }; // cxx11_20-warning {{reference to stack}}
+  // cxx2b-error@-1 {{non-const lvalue reference to type 'int' cannot bind to a temporary of type 'int'}}
   (void) [&]() -> int& { int &a = b; return a; };
   (void) [=]() mutable -> int& { int &a = b; return a; };
 
Index: clang/test/SemaCXX/deduced-return-type-cxx14.cpp
===================================================================
--- clang/test/SemaCXX/deduced-return-type-cxx14.cpp
+++ clang/test/SemaCXX/deduced-return-type-cxx14.cpp
@@ -1,11 +1,11 @@
-// RUN: %clang_cc1 -std=c++2b -fsyntax-only -verify=expected,cxx20_2b %s
-// RUN: %clang_cc1 -std=c++2b -fsyntax-only -verify=expected,cxx20_2b %s -fdelayed-template-parsing -DDELAYED_TEMPLATE_PARSING
+// RUN: %clang_cc1 -std=c++2b -fsyntax-only -verify=expected,cxx20_2b,cxx2b    %s
+// RUN: %clang_cc1 -std=c++2b -fsyntax-only -verify=expected,cxx20_2b,cxx2b    %s -fdelayed-template-parsing -DDELAYED_TEMPLATE_PARSING
 
-// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify=expected,cxx20_2b %s
-// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify=expected,cxx20_2b %s -fdelayed-template-parsing -DDELAYED_TEMPLATE_PARSING
+// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify=expected,cxx14_20,cxx20_2b %s
+// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify=expected,cxx14_20,cxx20_2b %s -fdelayed-template-parsing -DDELAYED_TEMPLATE_PARSING
 
-// RUN: %clang_cc1 -std=c++14 -fsyntax-only -verify=expected,cxx14    %s
-// RUN: %clang_cc1 -std=c++14 -fsyntax-only -verify=expected,cxx14    %s -fdelayed-template-parsing -DDELAYED_TEMPLATE_PARSING
+// RUN: %clang_cc1 -std=c++14 -fsyntax-only -verify=expected,cxx14_20,cxx14    %s
+// RUN: %clang_cc1 -std=c++14 -fsyntax-only -verify=expected,cxx14_20,cxx14    %s -fdelayed-template-parsing -DDELAYED_TEMPLATE_PARSING
 
 auto f(); // expected-note {{previous}}
 int f(); // expected-error {{differ only in their return type}}
@@ -129,10 +129,14 @@
     return T() + 1;
   }
   template<typename T> auto &f2(T &&v) { return v; }
+  // cxx2b-error@-1 {{non-const lvalue reference to type 'int' cannot bind to a temporary of type 'int'}}
+  // cxx2b-error@-2 {{non-const lvalue reference to type 'double' cannot bind to a temporary of type 'double'}}
+  // cxx2b-note@-3  {{candidate template ignored: substitution failure [with T = double]}}
   int a = f1<int>();
-  const int &b = f2(0);
+  const int &b = f2(0); // cxx2b-note {{in instantiation of function template specialization 'Templates::f2<int>' requested here}}
   double d;
   float &c = f2(0.0); // expected-error {{non-const lvalue reference to type 'float' cannot bind to a value of unrelated type 'double'}}
+  // cxx2b-note@-1 {{in instantiation of function template specialization 'Templates::f2<double>' requested here}}
 
   template<typename T> auto fwd_decl(); // expected-note {{declared here}}
   int e = fwd_decl<int>(); // expected-error {{cannot be used before it is defined}}
@@ -145,8 +149,9 @@
   auto (*p)() = f1; // expected-error {{incompatible initializer}}
   auto (*q)() = f1<int>; // ok
 
-  typedef decltype(f2(1.2)) dbl; // expected-note {{previous}}
-  typedef float dbl; // expected-error {{typedef redefinition with different types ('float' vs 'decltype(f2(1.2))' (aka 'double &'))}}
+  typedef decltype(f2(1.2)) dbl; // cxx14_20-note {{previous}}
+  // cxx2b-error@-1 {{no matching function for call to 'f2'}}
+  typedef float dbl; // cxx14_20-error {{typedef redefinition with different types ('float' vs 'decltype(f2(1.2))' (aka 'double &'))}}
 
   extern template auto fwd_decl<double>();
   int k1 = fwd_decl<double>();
Index: clang/test/SemaCXX/coroutines.cpp
===================================================================
--- clang/test/SemaCXX/coroutines.cpp
+++ clang/test/SemaCXX/coroutines.cpp
@@ -1,9 +1,9 @@
 // This file contains references to sections of the Coroutines TS, which can be
 // found at http://wg21.link/coroutines.
 
-// RUN: %clang_cc1 -std=c++2b                 -fsyntax-only -verify=expected,cxx20_2b %s -fcxx-exceptions -fexceptions -Wunused-result
-// RUN: %clang_cc1 -std=c++20                 -fsyntax-only -verify=expected,cxx20_2b %s -fcxx-exceptions -fexceptions -Wunused-result
-// RUN: %clang_cc1 -std=c++14 -fcoroutines-ts -fsyntax-only -verify=expected          %s -fcxx-exceptions -fexceptions -Wunused-result
+// RUN: %clang_cc1 -std=c++2b                 -fsyntax-only -verify=expected,cxx20_2b,cxx2b    %s -fcxx-exceptions -fexceptions -Wunused-result
+// RUN: %clang_cc1 -std=c++20                 -fsyntax-only -verify=expected,cxx14_20,cxx20_2b %s -fcxx-exceptions -fexceptions -Wunused-result
+// RUN: %clang_cc1 -std=c++14 -fcoroutines-ts -fsyntax-only -verify=expected,cxx14_20          %s -fcxx-exceptions -fexceptions -Wunused-result
 
 void no_coroutine_traits_bad_arg_await() {
   co_await a; // expected-error {{include <experimental/coroutine>}}
@@ -934,7 +934,8 @@
 };
 
 extern "C" int f(mismatch_gro_type_tag2) {
-  // expected-error@-1 {{cannot initialize return object of type 'int' with an lvalue of type 'void *'}}
+  // cxx2b-error@-1 {{cannot initialize return object of type 'int' with an rvalue of type 'void *'}}
+  // cxx14_20-error@-2 {{cannot initialize return object of type 'int' with an lvalue of type 'void *'}}
   co_return; //expected-note {{function is a coroutine due to use of 'co_return' here}}
 }
 
Index: clang/test/SemaCXX/coroutine-rvo.cpp
===================================================================
--- clang/test/SemaCXX/coroutine-rvo.cpp
+++ clang/test/SemaCXX/coroutine-rvo.cpp
@@ -46,7 +46,7 @@
 
 struct NoCopyNoMove {
   NoCopyNoMove() = default;
-  NoCopyNoMove(const NoCopyNoMove &) = delete; // expected-note 4{{'NoCopyNoMove' has been explicitly marked deleted here}}
+  NoCopyNoMove(const NoCopyNoMove &) = delete;
 };
 
 template <typename T>
@@ -62,13 +62,12 @@
 
 task<NoCopyNoMove> local2val() {
   NoCopyNoMove value;
-  co_return value; // expected-error {{call to deleted constructor of 'NoCopyNoMove'}}
-  // expected-error@-1 {{value reference to type 'NoCopyNoMove' cannot bind to lvalue of type 'NoCopyNoMove'}}
+  co_return value;
 }
 
 task<NoCopyNoMove &> local2ref() {
   NoCopyNoMove value;
-  co_return value; // expected-error {{call to deleted constructor of 'NoCopyNoMove'}}
+  co_return value; // expected-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.
@@ -81,8 +80,7 @@
 }
 
 task<NoCopyNoMove> rvalue2val(NoCopyNoMove &&value) {
-  co_return value; // expected-error {{rvalue reference to type 'NoCopyNoMove' cannot bind to lvalue of type 'NoCopyNoMove'}}
-  // expected-error@-1 {{call to deleted constructor of 'NoCopyNoMove'}}
+  co_return value;
 }
 
 task<NoCopyNoMove &> lvalue2ref(NoCopyNoMove &value) {
@@ -90,7 +88,7 @@
 }
 
 task<NoCopyNoMove &> rvalue2ref(NoCopyNoMove &&value) {
-  co_return value; // expected-error {{call to deleted constructor of 'NoCopyNoMove'}}
+  co_return value; // expected-error {{non-const lvalue reference to type 'NoCopyNoMove' cannot bind to a temporary of type 'NoCopyNoMove'}}
 }
 
 struct To {
Index: clang/test/SemaCXX/constant-expression-cxx14.cpp
===================================================================
--- clang/test/SemaCXX/constant-expression-cxx14.cpp
+++ clang/test/SemaCXX/constant-expression-cxx14.cpp
@@ -1,6 +1,6 @@
-// RUN: %clang_cc1 -std=c++2b -fsyntax-only -verify=expected,cxx20_2b %s -fcxx-exceptions -triple=x86_64-linux-gnu
-// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify=expected,cxx20_2b %s -fcxx-exceptions -triple=x86_64-linux-gnu
-// RUN: %clang_cc1 -std=c++14 -fsyntax-only -verify=expected,cxx14    %s -fcxx-exceptions -triple=x86_64-linux-gnu
+// RUN: %clang_cc1 -std=c++2b -fsyntax-only -verify=expected,cxx20_2b,cxx2b    %s -fcxx-exceptions -triple=x86_64-linux-gnu
+// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify=expected,cxx14_20,cxx20_2b %s -fcxx-exceptions -triple=x86_64-linux-gnu
+// RUN: %clang_cc1 -std=c++14 -fsyntax-only -verify=expected,cxx14_20,cxx14    %s -fcxx-exceptions -triple=x86_64-linux-gnu
 
 struct S {
   // dummy ctor to make this a literal type
@@ -269,16 +269,23 @@
 
 namespace incdec {
   template<typename T> constexpr T &ref(T &&r) { return r; }
+  // cxx2b-error@-1 {{non-const lvalue reference to type 'int' cannot bind to a temporary of type 'int'}}
   template<typename T> constexpr T postinc(T &&r) { return (r++, r); }
   template<typename T> constexpr T postdec(T &&r) { return (r--, r); }
 
+  template int &ref<int>(int &&);
+  // cxx2b-note@-1  {{in instantiation of function template specialization}}
+
+  static_assert(postinc(0) == 1, "");
+  static_assert(postdec(0) == -1, "");
+#if __cplusplus <= 202002L
   static_assert(++ref(0) == 1, "");
   static_assert(ref(0)++ == 0, "");
-  static_assert(postinc(0) == 1, "");
   static_assert(--ref(0) == -1, "");
   static_assert(ref(0)-- == 0, "");
-  static_assert(postdec(0) == -1, "");
+#endif
 
+#if __cplusplus <= 202002L
   constexpr int overflow_int_inc_1 = ref(0x7fffffff)++; // expected-error {{constant}} expected-note {{2147483648}}
   constexpr int overflow_int_inc_1_ok = ref(0x7ffffffe)++;
   constexpr int overflow_int_inc_2 = ++ref(0x7fffffff); // expected-error {{constant}} expected-note {{2147483648}}
@@ -293,33 +300,38 @@
   // cxx20_2b-error@-1 {{ISO C++17 does not allow incrementing expression of type bool}}
   static_assert(++ref(true), ""); // cxx14-warning {{deprecated}}
   // cxx20_2b-error@-1 {{ISO C++17 does not allow incrementing expression of type bool}}
+#endif
 
   int arr[10];
+  static_assert(postinc(&arr[0]) == &arr[1], "");
+  static_assert(postdec(&arr[1]) == &arr[0], "");
+#if __cplusplus <= 202002L
   static_assert(++ref(&arr[0]) == &arr[1], "");
   static_assert(++ref(&arr[9]) == &arr[10], "");
   static_assert(++ref(&arr[10]) == &arr[11], ""); // expected-error {{constant}} expected-note {{cannot refer to element 11}}
   static_assert(ref(&arr[0])++ == &arr[0], "");
   static_assert(ref(&arr[10])++ == &arr[10], ""); // expected-error {{constant}} expected-note {{cannot refer to element 11}}
-  static_assert(postinc(&arr[0]) == &arr[1], "");
   static_assert(--ref(&arr[10]) == &arr[9], "");
   static_assert(--ref(&arr[1]) == &arr[0], "");
   static_assert(--ref(&arr[0]) != &arr[0], ""); // expected-error {{constant}} expected-note {{cannot refer to element -1}}
   static_assert(ref(&arr[1])-- == &arr[1], "");
   static_assert(ref(&arr[0])-- == &arr[0], ""); // expected-error {{constant}} expected-note {{cannot refer to element -1}}
-  static_assert(postdec(&arr[1]) == &arr[0], "");
+#endif
 
+  static_assert(postinc(0.0) == 1.0, "");
+  static_assert(postdec(0.0) == -1.0, "");
+#if __cplusplus <= 202002L
   int x;
   static_assert(++ref(&x) == &x + 1, "");
 
   static_assert(++ref(0.0) == 1.0, "");
   static_assert(ref(0.0)++ == 0.0, "");
-  static_assert(postinc(0.0) == 1.0, "");
   static_assert(--ref(0.0) == -1.0, "");
   static_assert(ref(0.0)-- == 0.0, "");
-  static_assert(postdec(0.0) == -1.0, "");
 
   static_assert(++ref(1e100) == 1e100, "");
   static_assert(--ref(1e100) == 1e100, "");
+#endif
 
   union U {
     int a, b;
@@ -861,9 +873,13 @@
 
 namespace Lifetime {
   constexpr int &get(int &&r) { return r; }
+  // cxx2b-error@-1 {{non-const lvalue reference to type 'int' cannot bind to a temporary of type 'int'}}
+  // cxx2b-error@-2 {{no return statement in constexpr function}} See PR40598
   constexpr int f() {
     int &r = get(123);
-    return r; // expected-note {{read of object outside its lifetime}}
+    return r;
+    // cxx2b-note@-1 {{use of reference outside its lifetime is not allowed in a constant expression}}
+    // cxx14_20-note@-2 {{read of object outside its lifetime}}
   }
   static_assert(f() == 123, ""); // expected-error {{constant expression}} expected-note {{in call}}
 
Index: clang/test/SemaCXX/constant-expression-cxx11.cpp
===================================================================
--- clang/test/SemaCXX/constant-expression-cxx11.cpp
+++ clang/test/SemaCXX/constant-expression-cxx11.cpp
@@ -1,6 +1,6 @@
-// RUN: %clang_cc1 -std=c++2b -fsyntax-only -verify=expected,cxx20_2b -triple x86_64-linux -Wno-string-plus-int -Wno-pointer-arith -Wno-zero-length-array -Wno-c99-designator -fcxx-exceptions -pedantic %s -Wno-comment -Wno-tautological-pointer-compare -Wno-bool-conversion
-// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify=expected,cxx20_2b -triple x86_64-linux -Wno-string-plus-int -Wno-pointer-arith -Wno-zero-length-array -Wno-c99-designator -fcxx-exceptions -pedantic %s -Wno-comment -Wno-tautological-pointer-compare -Wno-bool-conversion
-// RUN: %clang_cc1 -std=c++11 -fsyntax-only -verify=expected,cxx11 -triple x86_64-linux -Wno-string-plus-int -Wno-pointer-arith -Wno-zero-length-array -Wno-c99-designator -fcxx-exceptions -pedantic %s -Wno-comment -Wno-tautological-pointer-compare -Wno-bool-conversion
+// RUN: %clang_cc1 -std=c++2b -fsyntax-only -verify=expected,cxx20_2b,cxx2b    -triple x86_64-linux -Wno-string-plus-int -Wno-pointer-arith -Wno-zero-length-array -Wno-c99-designator -fcxx-exceptions -pedantic %s -Wno-comment -Wno-tautological-pointer-compare -Wno-bool-conversion
+// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify=expected,cxx11_20,cxx20_2b -triple x86_64-linux -Wno-string-plus-int -Wno-pointer-arith -Wno-zero-length-array -Wno-c99-designator -fcxx-exceptions -pedantic %s -Wno-comment -Wno-tautological-pointer-compare -Wno-bool-conversion
+// RUN: %clang_cc1 -std=c++11 -fsyntax-only -verify=expected,cxx11_20,cxx11    -triple x86_64-linux -Wno-string-plus-int -Wno-pointer-arith -Wno-zero-length-array -Wno-c99-designator -fcxx-exceptions -pedantic %s -Wno-comment -Wno-tautological-pointer-compare -Wno-bool-conversion
 
 namespace StaticAssertFoldTest {
 
@@ -1936,13 +1936,18 @@
   }
 
   constexpr int &get(int &&n) { return n; }
+  // cxx2b-error@-1 {{non-const lvalue reference to type 'int' cannot bind to a temporary of type 'int'}}
+  // cxx2b-error@-2 {{no return statement in constexpr function}} See PR40598
   constexpr int &&get_rv(int &&n) { return static_cast<int&&>(n); }
   struct S {
     int &&r;
     int &s;
     int t;
     constexpr S() : r(get_rv(0)), s(get(0)), t(r) {} // expected-note {{read of object outside its lifetime}}
-    constexpr S(int) : r(get_rv(0)), s(get(0)), t(s) {} // expected-note {{read of object outside its lifetime}}
+    constexpr S(int) : r(get_rv(0)), s(get(0)), t(s) {}
+    // cxx2b-warning@-1 {{reference 's' is not yet bound to a value when used here}}
+    // cxx2b-note@-2    {{read of uninitialized object is not allowed in a constant expression}}
+    // cxx11_20-note@-3 {{read of object outside its lifetime}}
   };
   constexpr int k1 = S().t; // expected-error {{constant expression}} expected-note {{in call}}
   constexpr int k2 = S(0).t; // expected-error {{constant expression}} expected-note {{in call}}
Index: clang/test/CXX/temp/temp.decls/temp.mem/p5.cpp
===================================================================
--- clang/test/CXX/temp/temp.decls/temp.mem/p5.cpp
+++ clang/test/CXX/temp/temp.decls/temp.mem/p5.cpp
@@ -1,6 +1,6 @@
-// RUN: %clang_cc1 -std=c++2b -fsyntax-only -verify %s
-// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify %s
-// RUN: %clang_cc1 -std=c++98 -fsyntax-only -verify %s
+// RUN: %clang_cc1 -std=c++2b -fsyntax-only -verify=expected,cxx2b    %s
+// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify=expected,cxx98_20 %s
+// RUN: %clang_cc1 -std=c++98 -fsyntax-only -verify=expected,cxx98_20 %s
 
 struct A {
   template <class T> operator T*();
@@ -66,8 +66,10 @@
 
   template<typename T> operator const T*() const {
     T x = T();
-    return x; // expected-error{{cannot initialize return object of type 'const char *' with an lvalue of type 'char'}} \
-    // expected-error{{cannot initialize return object of type 'const int *' with an lvalue of type 'int'}}
+    return x; // cxx98_20-error{{cannot initialize return object of type 'const char *' with an lvalue of type 'char'}} \
+    // cxx98_20-error{{cannot initialize return object of type 'const int *' with an lvalue of type 'int'}} \
+    // cxx2b-error{{cannot initialize return object of type 'const char *' with an rvalue of type 'char'}} \
+    // cxx2b-error{{cannot initialize return object of type 'const int *' with an rvalue of type 'int'}}
   }
 };
 
Index: clang/test/CXX/expr/expr.prim/expr.prim.lambda/p4-cxx14.cpp
===================================================================
--- clang/test/CXX/expr/expr.prim/expr.prim.lambda/p4-cxx14.cpp
+++ clang/test/CXX/expr/expr.prim/expr.prim.lambda/p4-cxx14.cpp
@@ -1,6 +1,6 @@
-// RUN: %clang_cc1 -std=c++2b -fsyntax-only -verify %s
-// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify %s
-// RUN: %clang_cc1 -std=c++14 -fsyntax-only -verify %s
+// RUN: %clang_cc1 -std=c++2b -fsyntax-only -verify=expected,cxx2b    %s
+// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify=expected,cxx14_20 %s
+// RUN: %clang_cc1 -std=c++14 -fsyntax-only -verify=expected,cxx14_20 %s
 
 int a;
 int &b = [] (int &r) -> decltype(auto) { return r; } (a);
@@ -9,13 +9,15 @@
 int &e = [] (int &r) -> auto { return r; } (a); // expected-error {{cannot bind to a temporary}}
 int &f = [] (int r) -> decltype(auto) { return r; } (a); // expected-error {{cannot bind to a temporary}}
 int &g = [] (int r) -> decltype(auto) { return (r); } (a); // expected-warning {{reference to stack}}
+// cxx2b-error@-1 {{on-const lvalue reference to type 'int' cannot bind to a temporary of type 'int'}}
 
 int test_explicit_auto_return()
 {
     struct X {};
     auto L = [](auto F, auto a) { return F(a); };
     auto M = [](auto a) -> auto { return a; }; // OK
-    auto MRef = [](auto b) -> auto& { return b; }; //expected-warning{{reference to stack}}
+    auto MRef = [](auto b) -> auto & { return b; }; //cxx14_20-warning{{reference to stack}}
+    // cxx2b-error@-1 {{non-const lvalue reference to type 'X' cannot bind to a temporary of type 'X'}}
     auto MPtr = [](auto c) -> auto* { return &c; }; //expected-warning{{address of stack}}
     auto MDeclType = [](auto&& d) -> decltype(auto) { return static_cast<decltype(d)>(d); }; //OK
     M(3);
Index: clang/test/CXX/drs/dr3xx.cpp
===================================================================
--- clang/test/CXX/drs/dr3xx.cpp
+++ clang/test/CXX/drs/dr3xx.cpp
@@ -1,9 +1,9 @@
-// RUN: %clang_cc1 -std=c++2b -verify=expected,cxx20_2b -triple %itanium_abi_triple %s -fexceptions -fcxx-exceptions -pedantic-errors
-// RUN: %clang_cc1 -std=c++20 -verify=expected,cxx20_2b -triple %itanium_abi_triple %s -fexceptions -fcxx-exceptions -pedantic-errors
-// RUN: %clang_cc1 -std=c++17 -verify=expected,cxx98_17 -triple %itanium_abi_triple %s -fexceptions -fcxx-exceptions -pedantic-errors
-// RUN: %clang_cc1 -std=c++14 -verify=expected,cxx98_17 -triple %itanium_abi_triple %s -fexceptions -fcxx-exceptions -pedantic-errors
-// RUN: %clang_cc1 -std=c++11 -verify=expected,cxx98_17 -triple %itanium_abi_triple %s -fexceptions -fcxx-exceptions -pedantic-errors
-// RUN: %clang_cc1 -std=c++98 -verify=expected,cxx98_17 -triple %itanium_abi_triple %s -fexceptions -fcxx-exceptions -pedantic-errors
+// RUN: %clang_cc1 -std=c++2b -verify=expected,cxx20_2b,cxx2b    -triple %itanium_abi_triple %s -fexceptions -fcxx-exceptions -pedantic-errors
+// RUN: %clang_cc1 -std=c++20 -verify=expected,cxx98_20,cxx20_2b -triple %itanium_abi_triple %s -fexceptions -fcxx-exceptions -pedantic-errors
+// RUN: %clang_cc1 -std=c++17 -verify=expected,cxx98_17,cxx98_20 -triple %itanium_abi_triple %s -fexceptions -fcxx-exceptions -pedantic-errors
+// RUN: %clang_cc1 -std=c++14 -verify=expected,cxx98_17,cxx98_20 -triple %itanium_abi_triple %s -fexceptions -fcxx-exceptions -pedantic-errors
+// RUN: %clang_cc1 -std=c++11 -verify=expected,cxx98_17,cxx98_20 -triple %itanium_abi_triple %s -fexceptions -fcxx-exceptions -pedantic-errors
+// RUN: %clang_cc1 -std=c++98 -verify=expected,cxx98_17,cxx98_20 -triple %itanium_abi_triple %s -fexceptions -fcxx-exceptions -pedantic-errors
 
 namespace dr300 { // dr300: yes
   template<typename R, typename A> void f(R (&)(A)) {}
@@ -628,7 +628,8 @@
   struct A {
     template <class T> operator T ***() {
       int ***p = 0;
-      return p; // expected-error {{cannot initialize return object of type 'const int ***' with an lvalue of type 'int ***'}}
+      return p; // cxx98_20-error {{cannot initialize return object of type 'const int ***' with an lvalue of type 'int ***'}}
+      // cxx2b-error@-1 {{cannot initialize return object of type 'const int ***' with an rvalue of type 'int ***'}}
     }
   };
 
Index: clang/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p7-cxx14.cpp
===================================================================
--- clang/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p7-cxx14.cpp
+++ clang/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p7-cxx14.cpp
@@ -1,6 +1,6 @@
-// RUN: %clang_cc1 -verify -std=c++2b -verify %s
-// RUN: %clang_cc1 -verify -std=c++20 -verify %s
-// RUN: %clang_cc1 -verify -std=c++14 -verify %s
+// RUN: %clang_cc1 -verify -std=c++2b -verify=expected,cxx2b    %s
+// RUN: %clang_cc1 -verify -std=c++20 -verify=expected,cxx14_20 %s
+// RUN: %clang_cc1 -verify -std=c++14 -verify=expected,cxx14_20 %s
 
 namespace std {
   template<typename T> struct initializer_list {
@@ -30,7 +30,7 @@
 auto x4a = (i);
 decltype(auto) x4d = (i);
 using Int = decltype(x4a);
-using IntLRef = decltype(x4d);
+using IntLRef = decltype(x4d); // cxx2b-note {{previous definition is here}}
 
 auto x5a = f();
 decltype(auto) x5d = f();
@@ -81,7 +81,7 @@
 auto f3a(int n) { return (n); }
 decltype(auto) f3d(int n) { return (n); } // expected-warning {{reference to stack memory}}
 using Int = decltype(f3a(0));
-using IntLRef = decltype(f3d(0));
+using IntLRef = decltype(f3d(0)); // cxx2b-error {{type alias redefinition with different types ('decltype(f3d(0))' (aka 'int &&') vs 'decltype(x4d)' (aka 'int &'))}}
 
 auto f4a(int n) { return f(); }
 decltype(auto) f4d(int n) { return f(); }
@@ -91,7 +91,7 @@
 auto f5aa(int n) { auto x = f(); return x; }
 auto f5ad(int n) { decltype(auto) x = f(); return x; }
 decltype(auto) f5da(int n) { auto x = f(); return x; }
-decltype(auto) f5dd(int n) { decltype(auto) x = f(); return x; } // expected-error {{rvalue reference to type 'int' cannot bind to lvalue}}
+decltype(auto) f5dd(int n) { decltype(auto) x = f(); return x; } // cxx14_20-error {{rvalue reference to type 'int' cannot bind to lvalue}}
 using Int = decltype(f5aa(0));
 using Int = decltype(f5ad(0));
 using Int = decltype(f5da(0));
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
@@ -1,8 +1,8 @@
-// RUN: %clang_cc1 -std=c++2b -fsyntax-only -fcxx-exceptions -verify=expected,cxx20_2b %s
-// RUN: %clang_cc1 -std=c++20 -fsyntax-only -fcxx-exceptions -verify=expected,cxx20_2b %s
-// RUN: %clang_cc1 -std=c++17 -fsyntax-only -fcxx-exceptions -verify=expected,cxx11_17 %s
-// RUN: %clang_cc1 -std=c++14 -fsyntax-only -fcxx-exceptions -verify=expected,cxx11_17 %s
-// RUN: %clang_cc1 -std=c++11 -fsyntax-only -fcxx-exceptions -verify=expected,cxx11_17 %s
+// RUN: %clang_cc1 -std=c++2b -fsyntax-only -fcxx-exceptions -verify=expected,cxx20_2b,cxx2b %s
+// RUN: %clang_cc1 -std=c++20 -fsyntax-only -fcxx-exceptions -verify=expected,cxx11_20,cxx20_2b %s
+// RUN: %clang_cc1 -std=c++17 -fsyntax-only -fcxx-exceptions -verify=expected,cxx11_17,cxx11_20 %s
+// RUN: %clang_cc1 -std=c++14 -fsyntax-only -fcxx-exceptions -verify=expected,cxx11_17,cxx11_20 %s
+// RUN: %clang_cc1 -std=c++11 -fsyntax-only -fcxx-exceptions -verify=expected,cxx11_17,cxx11_20 %s
 
 namespace test_delete_function {
 struct A1 {
@@ -409,8 +409,10 @@
 namespace test_simpler_implicit_move {
 
 struct CopyOnly {
-  CopyOnly();
-  CopyOnly(CopyOnly &);
+  CopyOnly(); // cxx2b-note {{candidate constructor not viable: requires 0 arguments, but 1 was provided}}
+  // cxx2b-note@-1 {{candidate constructor not viable: requires 0 arguments, but 1 was provided}}
+  CopyOnly(CopyOnly &); // cxx2b-note {{candidate constructor not viable: expects an lvalue for 1st argument}}
+  // cxx2b-note@-1 {{candidate constructor not viable: expects an lvalue for 1st argument}}
 };
 struct MoveOnly {
   MoveOnly();
@@ -419,7 +421,7 @@
 MoveOnly &&rref();
 
 MoveOnly &&test1(MoveOnly &&w) {
-  return w; // expected-error {{cannot bind to lvalue of type}}
+  return w; // cxx11_20-error {{cannot bind to lvalue of type}}
 }
 
 CopyOnly test2(bool b) {
@@ -428,22 +430,22 @@
   if (b) {
     return w1;
   } else {
-    return w2;
+    return w2; // cxx2b-error {{no matching constructor for initialization}}
   }
 }
 
-template <class T> T &&test3(T &&x) { return x; } // expected-error {{cannot bind to lvalue of type}}
+template <class T> T &&test3(T &&x) { return x; } // cxx11_20-error {{cannot bind to lvalue of type}}
 template MoveOnly& test3<MoveOnly&>(MoveOnly&);
-template MoveOnly&& test3<MoveOnly>(MoveOnly&&); // expected-note {{in instantiation of function template specialization}}
+template MoveOnly &&test3<MoveOnly>(MoveOnly &&); // cxx11_20-note {{in instantiation of function template specialization}}
 
 MoveOnly &&test4() {
   MoveOnly &&x = rref();
-  return x; // expected-error {{cannot bind to lvalue of type}}
+  return x; // cxx11_20-error {{cannot bind to lvalue of type}}
 }
 
 void test5() try {
   CopyOnly x;
-  throw x;
+  throw x; // cxx2b-error {{no matching constructor for initialization}}
 } catch (...) {
 }
 
Index: clang/lib/Sema/SemaType.cpp
===================================================================
--- clang/lib/Sema/SemaType.cpp
+++ clang/lib/Sema/SemaType.cpp
@@ -8837,6 +8837,10 @@
   if (E->isTypeDependent())
     return S.Context.DependentTy;
 
+  Expr *IDExpr = E;
+  if (auto *ImplCastExpr = dyn_cast<ImplicitCastExpr>(E))
+    IDExpr = ImplCastExpr->getSubExpr();
+
   // C++11 [dcl.type.simple]p4:
   //   The type denoted by decltype(e) is defined as follows:
 
@@ -8847,7 +8851,7 @@
   // Note that this does not pick up the implicit 'const' for a template
   // parameter object. This rule makes no difference before C++20 so we apply
   // it unconditionally.
-  if (const auto *SNTTPE = dyn_cast<SubstNonTypeTemplateParmExpr>(E))
+  if (const auto *SNTTPE = dyn_cast<SubstNonTypeTemplateParmExpr>(IDExpr))
     return SNTTPE->getParameterType(S.Context);
 
   //     - if e is an unparenthesized id-expression or an unparenthesized class
@@ -8856,21 +8860,22 @@
   //       functions, the program is ill-formed;
   //
   // We apply the same rules for Objective-C ivar and property references.
-  if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E)) {
+  if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(IDExpr)) {
     const ValueDecl *VD = DRE->getDecl();
     if (auto *TPO = dyn_cast<TemplateParamObjectDecl>(VD))
       return TPO->getType().getUnqualifiedType();
     return VD->getType();
-  } else if (const MemberExpr *ME = dyn_cast<MemberExpr>(E)) {
+  } else if (const MemberExpr *ME = dyn_cast<MemberExpr>(IDExpr)) {
     if (const ValueDecl *VD = ME->getMemberDecl())
       if (isa<FieldDecl>(VD) || isa<VarDecl>(VD))
         return VD->getType();
-  } else if (const ObjCIvarRefExpr *IR = dyn_cast<ObjCIvarRefExpr>(E)) {
+  } else if (const ObjCIvarRefExpr *IR = dyn_cast<ObjCIvarRefExpr>(IDExpr)) {
     return IR->getDecl()->getType();
-  } else if (const ObjCPropertyRefExpr *PR = dyn_cast<ObjCPropertyRefExpr>(E)) {
+  } else if (const ObjCPropertyRefExpr *PR =
+                 dyn_cast<ObjCPropertyRefExpr>(IDExpr)) {
     if (PR->isExplicitProperty())
       return PR->getExplicitProperty()->getType();
-  } else if (auto *PE = dyn_cast<PredefinedExpr>(E)) {
+  } else if (auto *PE = dyn_cast<PredefinedExpr>(IDExpr)) {
     return PE->getType();
   }
 
@@ -8883,8 +8888,8 @@
   //   entity.
   using namespace sema;
   if (S.getCurLambda()) {
-    if (isa<ParenExpr>(E)) {
-      if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E->IgnoreParens())) {
+    if (isa<ParenExpr>(IDExpr)) {
+      if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(IDExpr->IgnoreParens())) {
         if (VarDecl *Var = dyn_cast<VarDecl>(DRE->getDecl())) {
           QualType T = S.getCapturedDeclRefType(Var, DRE->getLocation());
           if (!T.isNull())
Index: clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
===================================================================
--- clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -1052,10 +1052,19 @@
                                      StartingScope, InstantiatingVarTemplate);
 
   if (D->isNRVOVariable()) {
-    QualType ReturnType = cast<FunctionDecl>(DC)->getReturnType();
-    if (SemaRef.isCopyElisionCandidate(ReturnType, Var, Sema::CES_Strict))
-      Var->setNRVOVariable(true);
+    QualType ReturnType;
+    if (auto *F = dyn_cast<FunctionDecl>(DC))
+      ReturnType = F->getReturnType();
+    else if (auto *F = dyn_cast<BlockDecl>(DC))
+      goto unimplemented; // FIXME: get the return type here somehow...
+    else
+      assert(false && "Unknown context type");
+
+    Sema::NRVOResult Res = SemaRef.getNRVOResult(Var);
+    SemaRef.getNRVOResultFnRetType(Res, ReturnType);
+    Var->setNRVOVariable(Res.isCopyElidable());
   }
+unimplemented:
 
   Var->setImplicit(D->isImplicit());
 
Index: clang/lib/Sema/SemaStmt.cpp
===================================================================
--- clang/lib/Sema/SemaStmt.cpp
+++ clang/lib/Sema/SemaStmt.cpp
@@ -3026,99 +3026,134 @@
   return new (Context) BreakStmt(BreakLoc);
 }
 
-/// Determine whether the given expression is a candidate for
-/// copy elision in either a return statement or a throw expression.
-///
-/// \param ReturnType If we're determining the copy elision candidate for
-/// a return statement, this is the return type of the function. If we're
-/// determining the copy elision candidate for a throw expression, this will
-/// be a NULL type.
+static void downgradeNRVOResult(Sema::NRVOResult &Res, bool CanMove) {
+  Res.S = CanMove ? std::min(Res.S, Sema::NRVOResult::MoveEligible)
+                  : Sema::NRVOResult::None;
+}
+
+/// Determine whether the given NRVO candidate variable is move-eligible or
+/// copy-elision eligible, without considering function return type.
 ///
-/// \param E The expression being returned from the function or block, or
-/// being thrown.
+/// \param VD The NRVO candidate variable.
 ///
-/// \param CESK Whether we allow function parameters or
-/// id-expressions that could be moved out of the function to be considered NRVO
-/// candidates. C++ prohibits these for NRVO itself, but we re-use this logic to
-/// determine whether we should try to move as part of a return or throw (which
-/// does allow function parameters).
+/// \param ForceCXX20 Overrides detection of current language mode
+/// and uses the rules for C++20.
 ///
-/// \returns The NRVO candidate variable, if the return statement may use the
-/// NRVO, or NULL if there is no such candidate.
-VarDecl *Sema::getCopyElisionCandidate(QualType ReturnType, Expr *E,
-                                       CopyElisionSemanticsKind CESK) {
-  // - in a return statement in a function [where] ...
-  // ... the expression is the name of a non-volatile automatic object ...
-  DeclRefExpr *DR = dyn_cast<DeclRefExpr>(E->IgnoreParens());
-  if (!DR || DR->refersToEnclosingVariableOrCapture())
-    return nullptr;
-  VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl());
+/// \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::NRVOResult Sema::getNRVOResult(const VarDecl *VD, bool ForceCXX20) {
   if (!VD)
-    return nullptr;
+    return NRVOResult();
 
-  if (isCopyElisionCandidate(ReturnType, VD, CESK))
-    return VD;
-  return nullptr;
-}
+  bool hasCXX11 = getLangOpts().CPlusPlus11 || ForceCXX20,
+       hasCXX20 = getLangOpts().CPlusPlus20 || ForceCXX20;
+  NRVOResult Res{VD, NRVOResult::MoveEligibleAndCopyElidable};
 
-bool Sema::isCopyElisionCandidate(QualType ReturnType, const VarDecl *VD,
-                                  CopyElisionSemanticsKind CESK) {
-  QualType VDType = VD->getType();
-  // - in a return statement in a function with ...
-  // ... a class return type ...
-  if (!ReturnType.isNull() && !ReturnType->isDependentType()) {
-    if (!ReturnType->isRecordType())
-      return false;
-    // ... the same cv-unqualified type as the function return type ...
-    // When considering moving this expression out, allow dissimilar types.
-    if (!(CESK & CES_AllowDifferentTypes) && !VDType->isDependentType() &&
-        !Context.hasSameUnqualifiedType(ReturnType, VDType))
-      return false;
+  // (other than a function ... parameter)
+  if (VD->getKind() == Decl::ParmVar) {
+    downgradeNRVOResult(Res, hasCXX11);
+  } else if (VD->getKind() != Decl::Var) {
+    return NRVOResult();
   }
 
-  // ...object (other than a function or catch-clause parameter)...
-  if (VD->getKind() != Decl::Var &&
-      !((CESK & CES_AllowParameters) && VD->getKind() == Decl::ParmVar))
-    return false;
-  if (!(CESK & CES_AllowExceptionVariables) && VD->isExceptionVariable())
-    return false;
+  // (other than ... a catch-clause parameter)
+  if (VD->isExceptionVariable())
+    downgradeNRVOResult(Res, hasCXX20);
 
   // ...automatic...
-  if (!VD->hasLocalStorage()) return false;
+  if (!VD->hasLocalStorage())
+    return NRVOResult();
 
   // Return false if VD is a __block variable. We don't want to implicitly move
   // out of a __block variable during a return because we cannot assume the
   // variable will no longer be used.
   if (VD->hasAttr<BlocksAttr>())
-    return false;
+    return NRVOResult();
 
+  QualType VDType = VD->getType();
   if (VDType->isObjectType()) {
     // C++17 [class.copy.elision]p3:
     // ...non-volatile automatic object...
     if (VDType.isVolatileQualified())
-      return false;
+      return NRVOResult();
   } else if (VDType->isRValueReferenceType()) {
     // C++20 [class.copy.elision]p3:
-    // ...either a non-volatile object or an rvalue reference to a non-volatile object type...
-    if (!(CESK & CES_AllowRValueReferenceType))
-      return false;
+    // ...either a non-volatile object or an rvalue reference to a non-volatile
+    // object type...
     QualType VDReferencedType = VDType.getNonReferenceType();
-    if (VDReferencedType.isVolatileQualified() || !VDReferencedType->isObjectType())
-      return false;
+    if (VDReferencedType.isVolatileQualified() ||
+        !VDReferencedType->isObjectType())
+      return NRVOResult();
+    downgradeNRVOResult(Res, hasCXX20);
   } else {
-    return false;
+    return NRVOResult();
   }
 
-  if (CESK & CES_AllowDifferentTypes)
-    return true;
-
   // Variables with higher required alignment than their type's ABI
   // alignment cannot use NRVO.
   if (!VDType->isDependentType() && VD->hasAttr<AlignedAttr>() &&
       Context.getDeclAlign(VD) > Context.getTypeAlignInChars(VDType))
-    return false;
+    downgradeNRVOResult(Res, hasCXX11);
 
-  return true;
+  return Res;
+}
+
+/// Determine whether the given expression might be move-eligible or
+/// copy-elidable in either a (co_)return statement or throw expression,
+/// without considering function return type, if applicable.
+///
+/// \param E The expression being returned from the function or block,
+/// being thrown, or being co_returned from a coroutine. This expression
+/// might be modified by the implementation.
+///
+/// \param ForceCXX2b Overrides detection of current language mode
+/// and uses the rules for C++2b.
+///
+/// \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::NRVOResult Sema::getNRVOCandidate(Expr *&E, bool ForceCXX2b) {
+  if (!E)
+    return NRVOResult();
+  // - in a return statement in a function [where] ...
+  // ... the expression is the name of a non-volatile automatic object ...
+  DeclRefExpr *DR = dyn_cast<DeclRefExpr>(E->IgnoreParens());
+  if (!DR || DR->refersToEnclosingVariableOrCapture())
+    return NRVOResult();
+  VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl());
+  NRVOResult Res = getNRVOResult(VD, /*ForceCXX20=*/ForceCXX2b);
+  if (Res.Candidate && E->getValueKind() != VK_XValue &&
+      (ForceCXX2b || getLangOpts().CPlusPlus2b)) {
+    E = ImplicitCastExpr::Create(Context, VD->getType().getNonReferenceType(),
+                                 CK_NoOp, E, nullptr, VK_XValue,
+                                 FPOptionsOverride());
+  }
+  return Res;
+}
+
+/// Updates given NRVOResult's move-eligible and
+/// copy-elidable statuses, considering the function return type criteria
+/// as applicable to return statements.
+///
+/// \param Res The NRVOResult object to update.
+///
+/// \param ReturnType This is the return type of the function.
+void Sema::getNRVOResultFnRetType(NRVOResult &Res, QualType ReturnType) {
+  if (!Res.Candidate || ReturnType->isDependentType())
+    return;
+  // - in a return statement in a function with ...
+  // ... a class return type ...
+  if (!ReturnType->isRecordType()) {
+    Res = NRVOResult();
+    return;
+  }
+  QualType VDType = Res.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))
+    downgradeNRVOResult(Res, getLangOpts().CPlusPlus11);
 }
 
 /// Try to perform the initialization of a potentially-movable value,
@@ -3229,61 +3264,42 @@
 /// construction), then falls back to treating them as lvalues if that failed.
 ExprResult Sema::PerformMoveOrCopyInitialization(
     const InitializedEntity &Entity, const VarDecl *NRVOCandidate,
-    QualType ResultType, Expr *Value, bool AllowNRVO) {
-  ExprResult Res = ExprError();
-  bool NeedSecondOverloadResolution = true;
-
-  if (AllowNRVO) {
-    CopyElisionSemanticsKind CESK = CES_Strict;
-    if (getLangOpts().CPlusPlus20) {
-      CESK = CES_ImplicitlyMovableCXX20;
-    } else if (getLangOpts().CPlusPlus11) {
-      CESK = CES_ImplicitlyMovableCXX11CXX14CXX17;
-    }
-
-    if (!NRVOCandidate) {
-      NRVOCandidate = getCopyElisionCandidate(ResultType, Value, CESK);
-    }
-
-    if (NRVOCandidate) {
-      NeedSecondOverloadResolution =
-          TryMoveInitialization(*this, Entity, NRVOCandidate, ResultType, Value,
-                                !getLangOpts().CPlusPlus20, false, Res);
+    QualType ResultType, Expr *Value, bool isMoveEligible) {
+
+  if (NRVOCandidate && !getLangOpts().CPlusPlus2b) {
+    if (isMoveEligible) {
+      ExprResult Res;
+      if (!TryMoveInitialization(*this, Entity, NRVOCandidate, ResultType,
+                                 Value, !getLangOpts().CPlusPlus20, false, Res))
+        return Res;
     }
-
-    if (!getLangOpts().CPlusPlus20 && NeedSecondOverloadResolution &&
-        !getDiagnostics().isIgnored(diag::warn_return_std_move,
+    if (!getDiagnostics().isIgnored(diag::warn_return_std_move,
                                     Value->getExprLoc())) {
-      const VarDecl *FakeNRVOCandidate = getCopyElisionCandidate(
-          QualType(), Value, CES_ImplicitlyMovableCXX20);
-      if (FakeNRVOCandidate) {
-        QualType QT = FakeNRVOCandidate->getType();
-        if (QT->isLValueReferenceType()) {
-          // Adding 'std::move' around an lvalue reference variable's name is
-          // dangerous. Don't suggest it.
-        } else 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, FakeNRVOCandidate, ResultType,
-                                FakeValue, false, true, FakeRes);
-          if (!FakeRes.isInvalid()) {
-            bool IsThrow =
-                (Entity.getKind() == InitializedEntity::EK_Exception);
-            SmallString<32> Str;
-            Str += "std::move(";
-            Str += FakeNRVOCandidate->getDeclName().getAsString();
-            Str += ")";
-            Diag(Value->getExprLoc(), diag::warn_return_std_move)
-                << Value->getSourceRange()
-                << FakeNRVOCandidate->getDeclName() << IsThrow;
-            Diag(Value->getExprLoc(), diag::note_add_std_move)
-                << FixItHint::CreateReplacement(Value->getSourceRange(), Str);
-          }
+      QualType QT = NRVOCandidate->getType();
+      if (QT->isLValueReferenceType()) {
+        // Adding 'std::move' around an lvalue reference variable's name is
+        // dangerous. Don't suggest it.
+      } else 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, NRVOCandidate, ResultType,
+                              FakeValue, false, true, FakeRes);
+        if (!FakeRes.isInvalid()) {
+          bool IsThrow = (Entity.getKind() == InitializedEntity::EK_Exception);
+          SmallString<32> Str;
+          Str += "std::move(";
+          Str += NRVOCandidate->getDeclName().getAsString();
+          Str += ")";
+          Diag(Value->getExprLoc(), diag::warn_return_std_move)
+              << Value->getSourceRange() << NRVOCandidate->getDeclName()
+              << IsThrow;
+          Diag(Value->getExprLoc(), diag::note_add_std_move)
+              << FixItHint::CreateReplacement(Value->getSourceRange(), Str);
         }
       }
     }
@@ -3292,10 +3308,7 @@
   // 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.
-  if (NeedSecondOverloadResolution)
-    Res = PerformCopyInitialization(Entity, SourceLocation(), Value);
-
-  return Res;
+  return PerformCopyInitialization(Entity, SourceLocation(), Value);
 }
 
 /// Determine whether the declared return type of the specified function
@@ -3309,8 +3322,8 @@
 /// ActOnCapScopeReturnStmt - Utility routine to type-check return statements
 /// for capturing scopes.
 ///
-StmtResult
-Sema::ActOnCapScopeReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp) {
+StmtResult Sema::ActOnCapScopeReturnStmt(SourceLocation ReturnLoc,
+                                         Expr *RetValExp, NRVOResult &NRVORes) {
   // 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());
@@ -3390,6 +3403,7 @@
       CurCap->ReturnType = FnRetType;
   }
   assert(!FnRetType.isNull());
+  getNRVOResultFnRetType(NRVORes, FnRetType);
 
   if (auto *CurBlock = dyn_cast<BlockScopeInfo>(CurCap)) {
     if (CurBlock->FunctionType->castAs<FunctionType>()->getNoReturnAttr()) {
@@ -3412,7 +3426,6 @@
   // Otherwise, verify that this result type matches the previous one.  We are
   // pickier with blocks than for normal functions because we don't have GCC
   // compatibility to worry about here.
-  const VarDecl *NRVOCandidate = nullptr;
   if (FnRetType->isDependentType()) {
     // Delay processing for now.  TODO: there are lots of dependent
     // types we can conclusively prove aren't void.
@@ -3440,20 +3453,17 @@
 
     // In C++ the return statement is handled via a copy initialization.
     // the C version of which boils down to CheckSingleAssignmentConstraints.
-    NRVOCandidate = getCopyElisionCandidate(FnRetType, RetValExp, CES_Strict);
-    InitializedEntity Entity = InitializedEntity::InitializeResult(ReturnLoc,
-                                                                   FnRetType,
-                                                      NRVOCandidate != nullptr);
-    ExprResult Res = PerformMoveOrCopyInitialization(Entity, NRVOCandidate,
-                                                     FnRetType, RetValExp);
+    InitializedEntity Entity = InitializedEntity::InitializeResult(
+        ReturnLoc, FnRetType, NRVORes.isCopyElidable());
+    ExprResult Res =
+        PerformMoveOrCopyInitialization(Entity, NRVORes.Candidate, FnRetType,
+                                        RetValExp, NRVORes.isMoveEligible());
     if (Res.isInvalid()) {
       // FIXME: Cleanup temporaries here, anyway?
       return StmtError();
     }
     RetValExp = Res.get();
     CheckReturnValExpr(RetValExp, FnRetType, ReturnLoc);
-  } else {
-    NRVOCandidate = getCopyElisionCandidate(FnRetType, RetValExp, CES_Strict);
   }
 
   if (RetValExp) {
@@ -3463,13 +3473,14 @@
       return StmtError();
     RetValExp = ER.get();
   }
-  auto *Result =
-      ReturnStmt::Create(Context, ReturnLoc, RetValExp, NRVOCandidate);
+  auto *Result = ReturnStmt::Create(Context, ReturnLoc, RetValExp,
+                                    NRVORes.isCopyElidable() ? NRVORes.Candidate
+                                                             : nullptr);
 
   // If we need to check for the named return value optimization,
   // or if we need to infer the return type,
   // save the return statement in our scope for later processing.
-  if (CurCap->HasImplicitReturnType || NRVOCandidate)
+  if (CurCap->HasImplicitReturnType || NRVORes.isCopyElidable())
     FunctionScopes.back()->Returns.push_back(Result);
 
   if (FunctionScopes.back()->FirstReturnLoc.isInvalid())
@@ -3662,8 +3673,10 @@
   if (RetValExp && DiagnoseUnexpandedParameterPack(RetValExp))
     return StmtError();
 
+  NRVOResult NRVORes = getNRVOCandidate(RetValExp);
+
   if (isa<CapturingScopeInfo>(getCurFunction()))
-    return ActOnCapScopeReturnStmt(ReturnLoc, RetValExp);
+    return ActOnCapScopeReturnStmt(ReturnLoc, RetValExp, NRVORes);
 
   QualType FnRetType;
   QualType RelatedRetType;
@@ -3735,6 +3748,7 @@
       }
     }
   }
+  getNRVOResultFnRetType(NRVORes, FnRetType);
 
   bool HasDependentReturnType = FnRetType->isDependentType();
 
@@ -3841,8 +3855,6 @@
                                 /* NRVOCandidate=*/nullptr);
   } else {
     assert(RetValExp || HasDependentReturnType);
-    const VarDecl *NRVOCandidate = nullptr;
-
     QualType RetType = RelatedRetType.isNull() ? FnRetType : RelatedRetType;
 
     // C99 6.8.6.4p3(136): The return statement is not an assignment. The
@@ -3851,15 +3863,13 @@
 
     // In C++ the return statement is handled via a copy initialization,
     // the C version of which boils down to CheckSingleAssignmentConstraints.
-    if (RetValExp)
-      NRVOCandidate = getCopyElisionCandidate(FnRetType, RetValExp, CES_Strict);
     if (!HasDependentReturnType && !RetValExp->isTypeDependent()) {
       // we have a non-void function with an expression, continue checking
-      InitializedEntity Entity = InitializedEntity::InitializeResult(ReturnLoc,
-                                                                     RetType,
-                                                      NRVOCandidate != nullptr);
-      ExprResult Res = PerformMoveOrCopyInitialization(Entity, NRVOCandidate,
-                                                       RetType, RetValExp);
+      InitializedEntity Entity = InitializedEntity::InitializeResult(
+          ReturnLoc, RetType, NRVORes.isCopyElidable());
+      ExprResult Res =
+          PerformMoveOrCopyInitialization(Entity, NRVORes.Candidate, RetType,
+                                          RetValExp, NRVORes.isMoveEligible());
       if (Res.isInvalid()) {
         // FIXME: Clean up temporaries here anyway?
         return StmtError();
@@ -3892,7 +3902,9 @@
         return StmtError();
       RetValExp = ER.get();
     }
-    Result = ReturnStmt::Create(Context, ReturnLoc, RetValExp, NRVOCandidate);
+    Result = ReturnStmt::Create(Context, ReturnLoc, RetValExp,
+                                NRVORes.isCopyElidable() ? NRVORes.Candidate
+                                                         : nullptr);
   }
 
   // If we need to check for the named return value optimization, save the
Index: clang/lib/Sema/SemaExprCXX.cpp
===================================================================
--- clang/lib/Sema/SemaExprCXX.cpp
+++ clang/lib/Sema/SemaExprCXX.cpp
@@ -851,6 +851,9 @@
     Diag(OpLoc, diag::err_omp_simd_region_cannot_use_stmt) << "throw";
 
   if (Ex && !Ex->isTypeDependent()) {
+    NRVOResult NRVORes =
+        IsThrownVarInScope ? getNRVOCandidate(Ex) : NRVOResult();
+
     QualType ExceptionObjectTy = Context.getExceptionObjectType(Ex->getType());
     if (CheckCXXThrowOperand(OpLoc, ExceptionObjectTy, Ex))
       return ExprError();
@@ -870,15 +873,12 @@
     //       operation from the operand to the exception object (15.1) can be
     //       omitted by constructing the automatic object directly into the
     //       exception object
-    const VarDecl *NRVOVariable = nullptr;
-    if (IsThrownVarInScope)
-      NRVOVariable = getCopyElisionCandidate(QualType(), Ex, CES_Strict);
 
     InitializedEntity Entity = InitializedEntity::InitializeException(
         OpLoc, ExceptionObjectTy,
-        /*NRVO=*/NRVOVariable != nullptr);
+        /*NRVO=*/NRVORes.isCopyElidable());
     ExprResult Res = PerformMoveOrCopyInitialization(
-        Entity, NRVOVariable, QualType(), Ex, IsThrownVarInScope);
+        Entity, NRVORes.Candidate, QualType(), Ex, NRVORes.isMoveEligible());
     if (Res.isInvalid())
       return ExprError();
     Ex = Res.get();
Index: clang/lib/Sema/SemaCoroutine.cpp
===================================================================
--- clang/lib/Sema/SemaCoroutine.cpp
+++ clang/lib/Sema/SemaCoroutine.cpp
@@ -994,26 +994,10 @@
     E = R.get();
   }
 
-  // Move the return value if we can
-  if (E) {
-    const VarDecl *NRVOCandidate = this->getCopyElisionCandidate(
-        E->getType(), E, CES_ImplicitlyMovableCXX20);
-    if (NRVOCandidate) {
-      InitializedEntity Entity =
-          InitializedEntity::InitializeResult(Loc, E->getType(), NRVOCandidate);
-      ExprResult MoveResult = this->PerformMoveOrCopyInitialization(
-          Entity, NRVOCandidate, E->getType(), E);
-      if (MoveResult.get())
-        E = MoveResult.get();
-    }
-  }
-
-  // FIXME: If the operand is a reference to a variable that's about to go out
-  // of scope, we should treat the operand as an xvalue for this overload
-  // resolution.
   VarDecl *Promise = FSI->CoroutinePromise;
   ExprResult PC;
   if (E && (isa<InitListExpr>(E) || !E->getType()->isVoidType())) {
+    getNRVOCandidate(E, /*ForceCXX2b=*/true);
     PC = buildPromiseCall(*this, Promise, Loc, "return_value", E);
   } else {
     E = MakeFullDiscardedValueExpr(E).get();
@@ -1570,7 +1554,7 @@
     // Trigger a nice error message.
     InitializedEntity Entity =
         InitializedEntity::InitializeResult(Loc, FnRetType, false);
-    S.PerformMoveOrCopyInitialization(Entity, nullptr, FnRetType, ReturnValue);
+    S.PerformCopyInitialization(Entity, SourceLocation(), ReturnValue);
     noteMemberDeclaredHere(S, ReturnValue, Fn);
     return false;
   }
@@ -1586,8 +1570,8 @@
     return false;
 
   InitializedEntity Entity = InitializedEntity::InitializeVariable(GroDecl);
-  ExprResult Res = S.PerformMoveOrCopyInitialization(Entity, nullptr, GroType,
-                                                     this->ReturnValue);
+  ExprResult Res =
+      S.PerformCopyInitialization(Entity, SourceLocation(), ReturnValue);
   if (Res.isInvalid())
     return false;
 
Index: clang/lib/Sema/Sema.cpp
===================================================================
--- clang/lib/Sema/Sema.cpp
+++ clang/lib/Sema/Sema.cpp
@@ -1952,9 +1952,10 @@
   SourceLocation Loc = VD->getLocation();
   Expr *VarRef =
       new (S.Context) DeclRefExpr(S.Context, VD, false, T, VK_LValue, Loc);
-  ExprResult Result = S.PerformMoveOrCopyInitialization(
-      InitializedEntity::InitializeBlock(Loc, T, false), VD, VD->getType(),
-      VarRef, /*AllowNRVO=*/true);
+  ExprResult Result = S.PerformCopyInitialization(
+      InitializedEntity::InitializeBlock(Loc, T, false), SourceLocation(),
+      VarRef);
+
   if (!Result.isInvalid()) {
     Result = S.MaybeCreateExprWithCleanups(Result);
     Expr *Init = Result.getAs<Expr>();
Index: clang/include/clang/Sema/Sema.h
===================================================================
--- clang/include/clang/Sema/Sema.h
+++ clang/include/clang/Sema/Sema.h
@@ -3436,9 +3436,8 @@
 
   ExprResult PerformMoveOrCopyInitialization(const InitializedEntity &Entity,
                                              const VarDecl *NRVOCandidate,
-                                             QualType ResultType,
-                                             Expr *Value,
-                                             bool AllowNRVO = true);
+                                             QualType ResultType, Expr *Value,
+                                             bool isMoveEligible);
 
   bool CanPerformAggregateInitializationForOverloadResolution(
       const InitializedEntity &Entity, InitListExpr *From);
@@ -4736,28 +4735,24 @@
                                            SourceLocation Loc,
                                            unsigned NumParams);
 
-  enum CopyElisionSemanticsKind {
-    CES_Strict = 0,
-    CES_AllowParameters = 1,
-    CES_AllowDifferentTypes = 2,
-    CES_AllowExceptionVariables = 4,
-    CES_AllowRValueReferenceType = 8,
-    CES_ImplicitlyMovableCXX11CXX14CXX17 =
-        (CES_AllowParameters | CES_AllowDifferentTypes),
-    CES_ImplicitlyMovableCXX20 =
-        (CES_AllowParameters | CES_AllowDifferentTypes |
-         CES_AllowExceptionVariables | CES_AllowRValueReferenceType),
-  };
+  struct NRVOResult {
+    const VarDecl *Candidate;
+
+    enum Status { None, MoveEligible, MoveEligibleAndCopyElidable };
+    Status S;
 
-  VarDecl *getCopyElisionCandidate(QualType ReturnType, Expr *E,
-                                   CopyElisionSemanticsKind CESK);
-  bool isCopyElisionCandidate(QualType ReturnType, const VarDecl *VD,
-                              CopyElisionSemanticsKind CESK);
+    bool isMoveEligible() { return S >= MoveEligible; };
+    bool isCopyElidable() { return S == MoveEligibleAndCopyElidable; }
+  };
+  NRVOResult getNRVOCandidate(Expr *&E, bool ForceCXX2b = false);
+  NRVOResult getNRVOResult(const VarDecl *VD, bool ForceCXX20 = false);
+  void getNRVOResultFnRetType(NRVOResult &Res, QualType ReturnType);
 
   StmtResult ActOnReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp,
                              Scope *CurScope);
   StmtResult BuildReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp);
-  StmtResult ActOnCapScopeReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp);
+  StmtResult ActOnCapScopeReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp,
+                                     NRVOResult &NRVORes);
 
   StmtResult ActOnGCCAsmStmt(SourceLocation AsmLoc, bool IsSimple,
                              bool IsVolatile, unsigned NumOutputs,
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to