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

So it turns out there was a pre-existing bug where ObjC++ blocks
with dependent return type were not getting copy elision.

My latest refactoring accidentally fixed this in the return statement
sema action, but unfortunately the template instantiator still expects
only FunctionDecl and crashes otherwise.

I am not sure there is an easy way to fix this, as BlockDecl
doesn't even carry a type unlike FunctionDecl, so there is no easy way
to get the return type.

This might require some surgery to fix, so for now I am just
skipping it and putting a FIXME.


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,9 +1052,18 @@
                                      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,137 @@
   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.
+/// Determine whether the given NRVO candidate variable is move-eligible or
+/// copy-elision eligible, without considering function return type.
 ///
-/// \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.
+/// \param VD The NRVO candidate variable.
 ///
-/// \param E The expression being returned from the function or block, or
-/// being thrown.
+/// \param ForceCXX20 If true, perform the check considering C++20 rules,
+/// as opposed to considering the rules for current language mode.
 ///
-/// \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).
-///
-/// \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 A NRVOResult struct, where Candidate, if non-null, is the same
+/// as param VD and is at least move-eligible, without considering function
+/// return type, if applicable.
+Sema::NRVOResult Sema::getNRVOResult(const VarDecl *VD, bool ForceCXX20) {
   if (!VD)
-    return nullptr;
-
-  if (isCopyElisionCandidate(ReturnType, VD, CESK))
-    return VD;
-  return nullptr;
-}
-
-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;
+    return NRVOResult();
+
+  bool hasCXX11 = getLangOpts().CPlusPlus11 || ForceCXX20,
+       hasCXX20 = getLangOpts().CPlusPlus20 || ForceCXX20;
+  NRVOResult Res{VD, true};
+  if (VD->getKind() != Decl::Var) {
+    // (other than a function ... parameter)
+    if (!hasCXX11 || VD->getKind() != Decl::ParmVar)
+      return NRVOResult();
+    Res.isCopyElidable = false;
+  }
+  // (other than ... a catch-clause parameter)
+  if (VD->isExceptionVariable()) {
+    if (!hasCXX20)
+      return NRVOResult();
+    Res.isCopyElidable = false;
   }
-
-  // ...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;
 
   // ...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...
+    Res.isCopyElidable = false;
+    if (!hasCXX20)
+      return NRVOResult();
     QualType VDReferencedType = VDType.getNonReferenceType();
-    if (VDReferencedType.isVolatileQualified() || !VDReferencedType->isObjectType())
-      return false;
+    if (VDReferencedType.isVolatileQualified() ||
+        !VDReferencedType->isObjectType())
+      return NRVOResult();
   } else {
-    return false;
+    return NRVOResult();
+  }
+  if (!VDType->isDependentType() && VD->hasAttr<AlignedAttr>() &&
+      Context.getDeclAlign(VD) > Context.getTypeAlignInChars(VDType)) {
+    // Variables with higher required alignment than their type's ABI
+    // alignment cannot use NRVO.
+    if (!hasCXX11)
+      return NRVOResult();
+    Res.isCopyElidable = false;
   }
 
-  if (CESK & CES_AllowDifferentTypes)
-    return true;
+  return Res;
+}
 
-  // 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;
+/// Determine whether the given expression might be move-eligible or
+/// copy-elidable in either a (co_)return statement or throw expression.
+///
+/// \param E The expression being returned from the function or block,
+/// being thrown, or being co_returned from a coroutine.
+///
+/// \param Mode The language standard mode to use,
+/// or Auto (default) for the current mode.
+///
+/// \returns A NRVOResult struct, where Candidate, if non-null, means
+/// the expression is at least move eligible, without considering function
+/// return type, if applicable.
+Sema::NRVOResult Sema::getNRVOCandidate(Expr *&E, NRVOMode Mode) {
+  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, Mode != NRVOMode::Auto);
+  if (Res.Candidate && E->getValueKind() != VK_XValue &&
+      (Mode == NRVOMode::CXX2b || getLangOpts().CPlusPlus2b)) {
+    E = ImplicitCastExpr::Create(Context, VD->getType().getNonReferenceType(),
+                                 CK_NoOp, E, nullptr, VK_XValue,
+                                 FPOptionsOverride());
+  }
+  return Res;
+}
 
-  return true;
+/// 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)) {
+    if (!getLangOpts().CPlusPlus11) {
+      Res = NRVOResult();
+      return;
+    }
+    Res.isCopyElidable = false;
+  }
 }
 
 /// Try to perform the initialization of a potentially-movable value,
@@ -3230,34 +3268,20 @@
 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 (AllowNRVO && !getLangOpts().CPlusPlus2b) {
     if (NRVOCandidate) {
-      NeedSecondOverloadResolution =
-          TryMoveInitialization(*this, Entity, NRVOCandidate, ResultType, Value,
-                                !getLangOpts().CPlusPlus20, false, Res);
+      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();
+      NRVOResult FakeNRVORes = getNRVOCandidate(Value, NRVOMode::CXX20);
+      if (FakeNRVORes.Candidate) {
+        QualType QT = FakeNRVORes.Candidate->getType();
         if (QT->isLValueReferenceType()) {
           // Adding 'std::move' around an lvalue reference variable's name is
           // dangerous. Don't suggest it.
@@ -3269,18 +3293,18 @@
         } else {
           ExprResult FakeRes = ExprError();
           Expr *FakeValue = Value;
-          TryMoveInitialization(*this, Entity, FakeNRVOCandidate, ResultType,
-                                FakeValue, false, true, FakeRes);
+          TryMoveInitialization(*this, Entity, FakeNRVORes.Candidate,
+                                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 += FakeNRVORes.Candidate->getDeclName().getAsString();
             Str += ")";
             Diag(Value->getExprLoc(), diag::warn_return_std_move)
                 << Value->getSourceRange()
-                << FakeNRVOCandidate->getDeclName() << IsThrow;
+                << FakeNRVORes.Candidate->getDeclName() << IsThrow;
             Diag(Value->getExprLoc(), diag::note_add_std_move)
                 << FixItHint::CreateReplacement(Value->getSourceRange(), Str);
           }
@@ -3292,10 +3316,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 +3330,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 +3411,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 +3434,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,11 +3461,9 @@
 
     // 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,
+    InitializedEntity Entity = InitializedEntity::InitializeResult(
+        ReturnLoc, FnRetType, NRVORes.isCopyElidable);
+    ExprResult Res = PerformMoveOrCopyInitialization(Entity, NRVORes.Candidate,
                                                      FnRetType, RetValExp);
     if (Res.isInvalid()) {
       // FIXME: Cleanup temporaries here, anyway?
@@ -3452,8 +3471,6 @@
     }
     RetValExp = Res.get();
     CheckReturnValExpr(RetValExp, FnRetType, ReturnLoc);
-  } else {
-    NRVOCandidate = getCopyElisionCandidate(FnRetType, RetValExp, CES_Strict);
   }
 
   if (RetValExp) {
@@ -3464,12 +3481,13 @@
     RetValExp = ER.get();
   }
   auto *Result =
-      ReturnStmt::Create(Context, ReturnLoc, RetValExp, NRVOCandidate);
+      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 +3680,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 +3755,7 @@
       }
     }
   }
+  getNRVOResultFnRetType(NRVORes, FnRetType);
 
   bool HasDependentReturnType = FnRetType->isDependentType();
 
@@ -3841,8 +3862,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 +3870,12 @@
 
     // 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);
       if (Res.isInvalid()) {
         // FIXME: Clean up temporaries here anyway?
         return StmtError();
@@ -3892,7 +3908,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);
+    InitializedEntity Entity =
+        InitializedEntity::InitializeException(OpLoc, ExceptionObjectTy,
+                                               /*NRVO=*/NRVORes.isCopyElidable);
     ExprResult Res = PerformMoveOrCopyInitialization(
-        Entity, NRVOVariable, QualType(), Ex, IsThrownVarInScope);
+        Entity, NRVORes.Candidate, QualType(), Ex, IsThrownVarInScope);
     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, NRVOMode::CXX2b);
     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
@@ -4736,28 +4736,20 @@
                                            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;
+    bool isCopyElidable;
   };
-
-  VarDecl *getCopyElisionCandidate(QualType ReturnType, Expr *E,
-                                   CopyElisionSemanticsKind CESK);
-  bool isCopyElisionCandidate(QualType ReturnType, const VarDecl *VD,
-                              CopyElisionSemanticsKind CESK);
+  enum class NRVOMode { Auto, CXX20, CXX2b };
+  NRVOResult getNRVOCandidate(Expr *&E, NRVOMode Mode = NRVOMode::Auto);
+  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