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

Patch now in workable state. Is Missing a few cleanups in the implementation.
Updates coroutine implicit move tests, as now they can finally match C++20 
behavior and don't need P2266 <https://reviews.llvm.org/P2266> retroactively 
applied.

There are some test result changes.
These all match current GCC behaviour though: We are now broken in the same way 
as GCC is for these.
TBD what to do about these. The ambiguous overload resolution one might be easy 
to solve,
the other ones have to be studied more carefully.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D100000

Files:
  clang/include/clang/AST/Expr.h
  clang/include/clang/Basic/Specifiers.h
  clang/include/clang/Sema/Overload.h
  clang/include/clang/Sema/Sema.h
  clang/lib/AST/ASTContext.cpp
  clang/lib/AST/ExprClassification.cpp
  clang/lib/AST/JSONNodeDumper.cpp
  clang/lib/AST/TextNodeDumper.cpp
  clang/lib/CodeGen/CGDecl.cpp
  clang/lib/CodeGen/CGObjC.cpp
  clang/lib/Sema/Sema.cpp
  clang/lib/Sema/SemaCodeComplete.cpp
  clang/lib/Sema/SemaCoroutine.cpp
  clang/lib/Sema/SemaExprCXX.cpp
  clang/lib/Sema/SemaInit.cpp
  clang/lib/Sema/SemaOverload.cpp
  clang/lib/Sema/SemaStmt.cpp
  clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
  clang/test/CXX/class/class.init/class.copy.elision/p3.cpp
  clang/test/SemaCXX/conversion-function.cpp
  clang/test/SemaCXX/coroutine-rvo.cpp
  clang/test/SemaObjCXX/block-capture.mm

Index: clang/test/SemaObjCXX/block-capture.mm
===================================================================
--- clang/test/SemaObjCXX/block-capture.mm
+++ clang/test/SemaObjCXX/block-capture.mm
@@ -1,7 +1,7 @@
-// RUN: %clang_cc1 -std=c++2b -fsyntax-only -fobjc-arc -fblocks                       -verify=cxx98_2b,cxx11_2b,cxx2b %s
-// RUN: %clang_cc1 -std=c++20 -fsyntax-only -fobjc-arc -fblocks                       -verify=cxx98_2b,cxx11_2b       %s
-// RUN: %clang_cc1 -std=c++11 -fsyntax-only -fobjc-arc -fblocks                       -verify=cxx98_2b,cxx11_2b       %s
-// RUN: %clang_cc1 -std=c++98 -fsyntax-only -fobjc-arc -fblocks -Wno-c++11-extensions -verify=cxx98_2b,cxx98          %s
+// RUN: %clang_cc1 -std=c++2b -fsyntax-only -fobjc-arc -fblocks                       -verify=cxx98_2b,cxx11_2b,cxx2b    %s
+// RUN: %clang_cc1 -std=c++20 -fsyntax-only -fobjc-arc -fblocks                       -verify=cxx98_2b,cxx98_20,cxx11_2b %s
+// RUN: %clang_cc1 -std=c++11 -fsyntax-only -fobjc-arc -fblocks                       -verify=cxx98_2b,cxx98_20,cxx11_2b %s
+// RUN: %clang_cc1 -std=c++98 -fsyntax-only -fobjc-arc -fblocks -Wno-c++11-extensions -verify=cxx98_2b,cxx98_20,cxx98    %s
 
 #define TEST(T) void test_##T() { \
   __block T x;                    \
@@ -51,31 +51,31 @@
 
 struct ConvertingRVRef {
   ConvertingRVRef();
-  ConvertingRVRef(ConvertingRVRef &) = delete; // cxx98-note {{marked deleted here}}
+  ConvertingRVRef(ConvertingRVRef &) = delete; // cxx98_20-note {{marked deleted here}}
 
   struct X {};
   ConvertingRVRef(X &&);
   operator X() const & = delete;
   operator X() &&;
 };
-TEST(ConvertingRVRef); // cxx98-error {{call to deleted constructor}}
+TEST(ConvertingRVRef); // cxx98_20-error {{call to deleted constructor}}
 
 struct ConvertingCLVRef {
   ConvertingCLVRef();
   ConvertingCLVRef(ConvertingCLVRef &);
 
   struct X {};
-  ConvertingCLVRef(X &&); // cxx11_2b-note {{passing argument to parameter here}}
+  ConvertingCLVRef(X &&); // cxx2b-note {{passing argument to parameter here}}
   operator X() const &;
-  operator X() && = delete; // cxx11_2b-note {{marked deleted here}}
+  operator X() && = delete; // cxx2b-note {{marked deleted here}}
 };
-TEST(ConvertingCLVRef); // cxx11_2b-error {{invokes a deleted function}}
+TEST(ConvertingCLVRef); // cxx2b-error {{invokes a deleted function}}
 
 struct SubSubMove {};
 struct SubMove : SubSubMove {
   SubMove();
-  SubMove(SubMove &) = delete; // cxx98-note {{marked deleted here}}
+  SubMove(SubMove &) = delete; // cxx98_20-note {{marked deleted here}}
 
   SubMove(SubSubMove &&);
 };
-TEST(SubMove); // cxx98-error {{call to deleted constructor}}
+TEST(SubMove); // cxx98_20-error {{call to deleted constructor}}
Index: clang/test/SemaCXX/coroutine-rvo.cpp
===================================================================
--- clang/test/SemaCXX/coroutine-rvo.cpp
+++ clang/test/SemaCXX/coroutine-rvo.cpp
@@ -1,4 +1,5 @@
-// RUN: %clang_cc1 -verify -std=c++17 -fcoroutines-ts -fsyntax-only %s
+// RUN: %clang_cc1 -std=c++2b                 -fsyntax-only -verify=expected,cxx2b %s
+// RUN: %clang_cc1 -std=c++20                 -fsyntax-only -verify=expected       %s
 
 namespace std::experimental {
 template <class Promise = void> struct coroutine_handle {
@@ -56,7 +57,9 @@
     auto final_suspend() noexcept { return suspend_never{}; }
     auto get_return_object() { return task{}; }
     static void unhandled_exception() {}
-    void return_value(T &&value) {} // expected-note 4{{passing argument}}
+    void return_value(T &&value) {}
+    // cxx2b-note@-1    2{{passing argument}}
+    // expected-note@-2 2{{passing argument}}
   };
 };
 
@@ -67,7 +70,7 @@
 
 task<NoCopyNoMove &> local2ref() {
   NoCopyNoMove value;
-  co_return value; // expected-error {{non-const lvalue reference to type 'NoCopyNoMove' cannot bind to a temporary of type 'NoCopyNoMove'}}
+  co_return value; // cxx2b-error {{non-const lvalue reference to type 'NoCopyNoMove' cannot bind to a temporary of type 'NoCopyNoMove'}}
 }
 
 // We need the move constructor for construction of the coroutine.
@@ -88,7 +91,7 @@
 }
 
 task<NoCopyNoMove &> rvalue2ref(NoCopyNoMove &&value) {
-  co_return value; // expected-error {{non-const lvalue reference to type 'NoCopyNoMove' cannot bind to a temporary of type 'NoCopyNoMove'}}
+  co_return value; // cxx2b-error {{non-const lvalue reference to type 'NoCopyNoMove' cannot bind to a temporary of type 'NoCopyNoMove'}}
 }
 
 struct To {
Index: clang/test/SemaCXX/conversion-function.cpp
===================================================================
--- clang/test/SemaCXX/conversion-function.cpp
+++ clang/test/SemaCXX/conversion-function.cpp
@@ -1,7 +1,7 @@
-// RUN: %clang_cc1 -std=c++2b -fsyntax-only -verify=expected                -triple %itanium_abi_triple -Wbind-to-temporary-copy %s
-// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify=expected                -triple %itanium_abi_triple -Wbind-to-temporary-copy %s
-// RUN: %clang_cc1 -std=c++11 -fsyntax-only -verify=expected,cxx98_11,cxx11 -triple %itanium_abi_triple -Wbind-to-temporary-copy %s
-// RUN: %clang_cc1 -std=c++98 -fsyntax-only -verify=expected,cxx98_11,cxx98 -triple %itanium_abi_triple -Wbind-to-temporary-copy %s
+// RUN: %clang_cc1 -std=c++2b -fsyntax-only -verify=expected                         -triple %itanium_abi_triple -Wbind-to-temporary-copy %s
+// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify=expected,cxx98_20                -triple %itanium_abi_triple -Wbind-to-temporary-copy %s
+// RUN: %clang_cc1 -std=c++11 -fsyntax-only -verify=expected,cxx98_20,cxx98_11,cxx11 -triple %itanium_abi_triple -Wbind-to-temporary-copy %s
+// RUN: %clang_cc1 -std=c++98 -fsyntax-only -verify=expected,cxx98_20,cxx98_11,cxx98 -triple %itanium_abi_triple -Wbind-to-temporary-copy %s
 
 class X {
 public:
@@ -124,7 +124,7 @@
 class AutoPtrRef { };
 
 class AutoPtr {
-  AutoPtr(AutoPtr &); // cxx98-note {{declared private here}}
+  AutoPtr(AutoPtr &); // cxx98_20-note {{declared private here}}
 
 public:
   AutoPtr();
@@ -140,7 +140,7 @@
 
   AutoPtr p;
   if (Cond)
-    return p; // cxx98-error {{calling a private constructor}}
+    return p; // cxx98_20-error {{calling a private constructor}}
 
   return AutoPtr();
 }
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
@@ -427,12 +427,12 @@
 
 struct B;
 struct A {
-  A(B &) = delete; // cxx98-note {{has been explicitly deleted}}
+  A(B &) = delete; // cxx98_20-note {{has been explicitly deleted}}
 };
 struct B {
-  operator A(); // cxx98-note {{candidate function}}
+  operator A(); // cxx98_20-note {{candidate function}}
 };
-A test1(B x) { return x; } // cxx98-error {{conversion}} {{is ambiguous}}
+A test1(B x) { return x; } // cxx98_20-error {{conversion}} {{is ambiguous}}
 
 struct C {};
 struct D {
Index: clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
===================================================================
--- clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -1108,9 +1108,9 @@
     // 'optimization'. Notably, functions with 'auto' return types won't have it
     // deduced by this point. Coupled with the limitation described
     // previously, this makes it very hard to support copy elision for these.
-    Sema::NamedReturnInfo Info = SemaRef.getNamedReturnInfo(Var);
-    bool NRVO = SemaRef.getCopyElisionCandidate(Info, RT) != nullptr;
-    Var->setNRVOVariable(NRVO);
+    Sema::NamedReturnInfo Info =
+        SemaRef.getNamedReturnInfo(Var, RT, /*Parenthesized=*/false);
+    Var->setNRVOVariable(Info.isCopyElidable());
   }
 
   Var->setImplicit(D->isImplicit());
Index: clang/lib/Sema/SemaStmt.cpp
===================================================================
--- clang/lib/Sema/SemaStmt.cpp
+++ clang/lib/Sema/SemaStmt.cpp
@@ -3321,25 +3321,28 @@
 /// \returns An aggregate which contains the Candidate and isMoveEligible
 /// and isCopyElidable methods. If Candidate is non-null, it means
 /// isMoveEligible() would be true under the most permissive language standard.
-Sema::NamedReturnInfo Sema::getNamedReturnInfo(Expr *&E, bool ForceCXX2b) {
+const VarDecl *Sema::getCopyElisionCandidate(Expr *&E, NRVOExprContext C,
+                                             QualType ReturnType) {
   if (!E)
-    return NamedReturnInfo();
+    return nullptr;
   // - in a return statement in a function [where] ...
   // ... the expression is the name of a non-volatile automatic object ...
   const auto *DR = dyn_cast<DeclRefExpr>(E->IgnoreParens());
   if (!DR || DR->refersToEnclosingVariableOrCapture())
-    return NamedReturnInfo();
+    return nullptr;
   const auto *VD = dyn_cast<VarDecl>(DR->getDecl());
   if (!VD)
-    return NamedReturnInfo();
-  NamedReturnInfo Res = getNamedReturnInfo(VD);
-  if (Res.Candidate && !E->isXValue() &&
-      (ForceCXX2b || getLangOpts().CPlusPlus2b)) {
+    return nullptr;
+
+  NamedReturnInfo Info = getNamedReturnInfo(VD, ReturnType, isa<ParenExpr>(E));
+  if (getLangOpts().CPlusPlus11 && Info.isMoveEligible() && !E->isXValue()) {
+    ExprValueKind CastToKind =
+        getLangOpts().CPlusPlus2b ? VK_XValue : VK_YValue;
     E = ImplicitCastExpr::Create(Context, VD->getType().getNonReferenceType(),
-                                 CK_NoOp, E, nullptr, VK_XValue,
+                                 CK_NoOp, E, nullptr, CastToKind,
                                  FPOptionsOverride());
   }
-  return Res;
+  return Info.isCopyElidable() ? Info.Candidate : nullptr;
 }
 
 /// Determine whether the given NRVO candidate variable is move-eligible or
@@ -3350,9 +3353,18 @@
 /// \returns An aggregate which contains the Candidate and isMoveEligible
 /// and isCopyElidable methods. If Candidate is non-null, it means
 /// isMoveEligible() would be true under the most permissive language standard.
-Sema::NamedReturnInfo Sema::getNamedReturnInfo(const VarDecl *VD) {
+Sema::NamedReturnInfo Sema::getNamedReturnInfo(const VarDecl *VD,
+                                               QualType ReturnType,
+                                               bool Parenthesized) {
   NamedReturnInfo Info{VD, NamedReturnInfo::MoveEligibleAndCopyElidable};
 
+  // Updates the status in Info to disallow copy elision.
+  // Optionally also disallows implicit move.
+  auto disallowNRVO = [&Info](bool CanMove) {
+    Info.S = std::min(Info.S, CanMove ? NamedReturnInfo::MoveEligible
+                                      : NamedReturnInfo::None);
+  };
+
   // C++20 [class.copy.elision]p3:
   // - in a return statement in a function with ...
   // (other than a function ... parameter)
@@ -3399,87 +3411,47 @@
       Context.getDeclAlign(VD) > Context.getTypeAlignInChars(VDType))
     Info.S = NamedReturnInfo::MoveEligible;
 
-  return Info;
-}
-
-/// Updates given NamedReturnInfo's move-eligible and
-/// copy-elidable statuses, considering the function
-/// return type criteria as applicable to return statements.
-///
-/// \param Info The NamedReturnInfo object to update.
-///
-/// \param ReturnType This is the return type of the function.
-/// \returns The copy elision candidate, in case the initial return expression
-/// was copy elidable, or nullptr otherwise.
-const VarDecl *Sema::getCopyElisionCandidate(NamedReturnInfo &Info,
-                                             QualType ReturnType) {
-  if (!Info.Candidate)
-    return nullptr;
-
-  auto invalidNRVO = [&] {
-    Info = NamedReturnInfo();
-    return nullptr;
-  };
-
-  // If we got a non-deduced auto ReturnType, we are in a dependent context and
-  // there is no point in allowing copy elision since we won't have it deduced
-  // by the point the VardDecl is instantiated, which is the last chance we have
-  // of deciding if the candidate is really copy elidable.
-  if ((ReturnType->getTypeClass() == Type::TypeClass::Auto &&
-       ReturnType->isCanonicalUnqualified()) ||
-      ReturnType->isSpecificBuiltinType(BuiltinType::Dependent))
-    return invalidNRVO();
-
-  if (!ReturnType->isDependentType()) {
-    // - in a return statement in a function with ...
-    // ... a class return type ...
-    if (!ReturnType->isRecordType())
-      return invalidNRVO();
-
-    QualType VDType = Info.Candidate->getType();
-    // ... the same cv-unqualified type as the function return type ...
-    // When considering moving this expression out, allow dissimilar types.
-    if (!VDType->isDependentType() &&
-        !Context.hasSameUnqualifiedType(ReturnType, VDType))
-      Info.S = NamedReturnInfo::MoveEligible;
-  }
-  return Info.isCopyElidable() ? Info.Candidate : nullptr;
-}
+  if (!ReturnType.isNull()) {
+    if (ReturnType->isDependentType()) {
+      if ((ReturnType->getTypeClass() == Type::TypeClass::Auto &&
+           ReturnType->isCanonicalUnqualified()) ||
+          ReturnType->isSpecificBuiltinType(BuiltinType::Dependent)) {
+        return NamedReturnInfo();
+      }
+    } else {
+      if (ReturnType->isSpecificBuiltinType(BuiltinType::Dependent)) {
+        ReturnType = Context.getAutoDeductType();
+      }
+      if (ReturnType->isCanonicalUnqualified()) {
+        if (AutoType *AT = ReturnType->getContainedAutoType()) {
+          QualType T = VDType.getNonReferenceType().getUnqualifiedType();
+          if (AT->isDecltypeAuto())
+            ReturnType = Parenthesized ? Context.getLValueReferenceType(T) : T;
+          else if (ReturnType->isLValueReferenceType())
+            ReturnType = Context.getLValueReferenceType(T);
+          else if (ReturnType->isRValueReferenceType())
+            ReturnType = Context.getRValueReferenceType(T);
+          else
+            ReturnType = T;
+        }
+      }
 
-/// Perform the initialization of a potentially-movable value, which
-/// is the result of return value.
-///
-/// This routine implements C++20 [class.copy.elision]p3, which attempts to
-/// treat returned lvalues as rvalues in certain cases (to prefer move
-/// construction), then falls back to treating them as lvalues if that failed.
-ExprResult
-Sema::PerformMoveOrCopyInitialization(const InitializedEntity &Entity,
-                                      const NamedReturnInfo &NRInfo,
-                                      Expr *Value) {
-  if (getLangOpts().CPlusPlus11 && !getLangOpts().CPlusPlus2b &&
-      NRInfo.isMoveEligible()) {
-    ImplicitCastExpr AsRvalue(ImplicitCastExpr::OnStack, Value->getType(),
-                              CK_NoOp, Value, VK_XValue, FPOptionsOverride());
-    Expr *InitExpr = &AsRvalue;
-    auto Kind = InitializationKind::CreateCopy(Value->getBeginLoc(),
-                                               Value->getBeginLoc());
-    InitializationSequence Seq(*this, Entity, Kind, InitExpr);
-    auto Res = Seq.getFailedOverloadResult();
-    if (Res == OR_Success || Res == OR_Deleted) {
-      // Promote "AsRvalue" to the heap, since we now need this
-      // expression node to persist.
-      Value =
-          ImplicitCastExpr::Create(Context, Value->getType(), CK_NoOp, Value,
-                                   nullptr, VK_XValue, FPOptionsOverride());
-      // Complete type-checking the initialization of the return type
-      // using the constructor we found.
-      return Seq.Perform(*this, Entity, Kind, Value);
+      // - in a return statement in a function with ...
+      // ... a class return type ...
+      if (!ReturnType->isRecordType())
+        Info.S = getLangOpts().CPlusPlus2b ? NamedReturnInfo::MoveEligible
+                                           : NamedReturnInfo::None;
+
+      // ... 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))
+        Info.S = std::min(Info.S, NamedReturnInfo::MoveEligible);
     }
   }
-  // Either we didn't meet the criteria for treating an lvalue as an rvalue,
-  // above, or overload resolution failed. Either way, we need to try
-  // (again) now with the return value expression as written.
-  return PerformCopyInitialization(Entity, SourceLocation(), Value);
+
+  return Info;
 }
 
 /// Determine whether the declared return type of the specified function
@@ -3494,8 +3466,7 @@
 /// for capturing scopes.
 ///
 StmtResult Sema::ActOnCapScopeReturnStmt(SourceLocation ReturnLoc,
-                                         Expr *RetValExp,
-                                         NamedReturnInfo &NRInfo) {
+                                         Expr *RetValExp) {
   // If this is the first return we've seen, infer the return type.
   // [expr.prim.lambda]p4 in C++11; block literals follow the same rules.
   CapturingScopeInfo *CurCap = cast<CapturingScopeInfo>(getCurFunction());
@@ -3518,6 +3489,7 @@
                               /* NRVOCandidate=*/nullptr);
   }
 
+  const VarDecl *NRVOCandidate = nullptr;
   if (HasDeducedReturnType) {
     FunctionDecl *FD = CurLambda->CallOperator;
     // If we've already decided this lambda is invalid, e.g. because
@@ -3530,6 +3502,9 @@
     if (CurCap->ReturnType.isNull())
       CurCap->ReturnType = FD->getReturnType();
 
+    NRVOCandidate = getCopyElisionCandidate(
+        RetValExp, NRVOExprContext::Returned, CurCap->ReturnType);
+
     AutoType *AT = CurCap->ReturnType->getContainedAutoType();
     assert(AT && "lost auto type from lambda return type");
     if (DeduceFunctionTypeFromReturnExpr(FD, ReturnLoc, RetValExp, AT)) {
@@ -3538,43 +3513,47 @@
       return StmtError();
     }
     CurCap->ReturnType = FnRetType = FD->getReturnType();
-  } else if (CurCap->HasImplicitReturnType) {
-    // For blocks/lambdas with implicit return types, we check each return
-    // statement individually, and deduce the common return type when the block
-    // or lambda is completed.
-    // FIXME: Fold this into the 'auto' codepath above.
-    if (RetValExp && !isa<InitListExpr>(RetValExp)) {
-      ExprResult Result = DefaultFunctionArrayLvalueConversion(RetValExp);
-      if (Result.isInvalid())
-        return StmtError();
-      RetValExp = Result.get();
-
-      // DR1048: even prior to C++14, we should use the 'auto' deduction rules
-      // when deducing a return type for a lambda-expression (or by extension
-      // for a block). These rules differ from the stated C++11 rules only in
-      // that they remove top-level cv-qualifiers.
-      if (!CurContext->isDependentContext())
-        FnRetType = RetValExp->getType().getUnqualifiedType();
-      else
-        FnRetType = CurCap->ReturnType = Context.DependentTy;
-    } else {
-      if (RetValExp) {
-        // C++11 [expr.lambda.prim]p4 bans inferring the result from an
-        // initializer list, because it is not an expression (even
-        // though we represent it as one). We still deduce 'void'.
-        Diag(ReturnLoc, diag::err_lambda_return_init_list)
-          << RetValExp->getSourceRange();
+  } else {
+    if (CurCap->HasImplicitReturnType) {
+      // For blocks/lambdas with implicit return types, we check each return
+      // statement individually, and deduce the common return type when the
+      // block or lambda is completed.
+      // FIXME: Fold this into the 'auto' codepath above.
+      if (RetValExp && !isa<InitListExpr>(RetValExp)) {
+        ExprResult Result = DefaultFunctionArrayLvalueConversion(RetValExp);
+        if (Result.isInvalid())
+          return StmtError();
+        RetValExp = Result.get();
+
+        // DR1048: even prior to C++14, we should use the 'auto' deduction rules
+        // when deducing a return type for a lambda-expression (or by extension
+        // for a block). These rules differ from the stated C++11 rules only in
+        // that they remove top-level cv-qualifiers.
+        if (!CurContext->isDependentContext())
+          FnRetType = RetValExp->getType().getUnqualifiedType();
+        else
+          FnRetType = CurCap->ReturnType = Context.DependentTy;
+      } else {
+        if (RetValExp) {
+          // C++11 [expr.lambda.prim]p4 bans inferring the result from an
+          // initializer list, because it is not an expression (even
+          // though we represent it as one). We still deduce 'void'.
+          Diag(ReturnLoc, diag::err_lambda_return_init_list)
+              << RetValExp->getSourceRange();
+        }
+
+        FnRetType = Context.VoidTy;
       }
 
-      FnRetType = Context.VoidTy;
+      // Although we'll properly infer the type of the block once it's
+      // completed, make sure we provide a return type now for better error
+      // recovery.
+      if (CurCap->ReturnType.isNull())
+        CurCap->ReturnType = FnRetType;
     }
-
-    // Although we'll properly infer the type of the block once it's completed,
-    // make sure we provide a return type now for better error recovery.
-    if (CurCap->ReturnType.isNull())
-      CurCap->ReturnType = FnRetType;
+    NRVOCandidate = getCopyElisionCandidate(
+        RetValExp, NRVOExprContext::Returned, CurCap->ReturnType);
   }
-  const VarDecl *NRVOCandidate = getCopyElisionCandidate(NRInfo, FnRetType);
 
   if (auto *CurBlock = dyn_cast<BlockScopeInfo>(CurCap)) {
     if (CurBlock->FunctionType->castAs<FunctionType>()->getNoReturnAttr()) {
@@ -3626,7 +3605,8 @@
     // the C version of which boils down to CheckSingleAssignmentConstraints.
     InitializedEntity Entity = InitializedEntity::InitializeResult(
         ReturnLoc, FnRetType, NRVOCandidate != nullptr);
-    ExprResult Res = PerformMoveOrCopyInitialization(Entity, NRInfo, RetValExp);
+    ExprResult Res =
+        PerformCopyInitialization(Entity, SourceLocation(), RetValExp);
     if (Res.isInvalid()) {
       // FIXME: Cleanup temporaries here, anyway?
       return StmtError();
@@ -3841,10 +3821,8 @@
   if (RetValExp && DiagnoseUnexpandedParameterPack(RetValExp))
     return StmtError();
 
-  NamedReturnInfo NRInfo = getNamedReturnInfo(RetValExp);
-
   if (isa<CapturingScopeInfo>(getCurFunction()))
-    return ActOnCapScopeReturnStmt(ReturnLoc, RetValExp, NRInfo);
+    return ActOnCapScopeReturnStmt(ReturnLoc, RetValExp);
 
   QualType FnRetType;
   QualType RelatedRetType;
@@ -3898,6 +3876,8 @@
                               /* NRVOCandidate=*/nullptr);
   }
 
+  const VarDecl *NRVOCandidate =
+      getCopyElisionCandidate(RetValExp, NRVOExprContext::Returned, FnRetType);
   // FIXME: Add a flag to the ScopeInfo to indicate whether we're performing
   // deduction.
   if (getLangOpts().CPlusPlus14) {
@@ -3916,7 +3896,6 @@
       }
     }
   }
-  const VarDecl *NRVOCandidate = getCopyElisionCandidate(NRInfo, FnRetType);
 
   bool HasDependentReturnType = FnRetType->isDependentType();
 
@@ -4036,7 +4015,7 @@
       InitializedEntity Entity = InitializedEntity::InitializeResult(
           ReturnLoc, RetType, NRVOCandidate != nullptr);
       ExprResult Res =
-          PerformMoveOrCopyInitialization(Entity, NRInfo, RetValExp);
+          PerformCopyInitialization(Entity, SourceLocation(), RetValExp);
       if (Res.isInvalid()) {
         // FIXME: Clean up temporaries here anyway?
         return StmtError();
Index: clang/lib/Sema/SemaOverload.cpp
===================================================================
--- clang/lib/Sema/SemaOverload.cpp
+++ clang/lib/Sema/SemaOverload.cpp
@@ -3905,10 +3905,15 @@
       SCS2.BindsImplicitObjectArgumentWithoutRefQualifier)
     return false;
 
-  return (!SCS1.IsLvalueReference && SCS1.BindsToRvalue &&
-          SCS2.IsLvalueReference) ||
-         (SCS1.IsLvalueReference && SCS1.BindsToFunctionLvalue &&
-          !SCS2.IsLvalueReference && SCS2.BindsToFunctionLvalue);
+  if (!SCS1.IsLvalueReference && SCS1.BindsToRvalue && SCS2.IsLvalueReference)
+    return true;
+
+  if (SCS1.IsLvalueReference && SCS1.NonVolatileConstBindsToYvalue &&
+      SCS2.IsLvalueReference)
+    return true;
+
+  return SCS1.IsLvalueReference && SCS1.BindsToFunctionLvalue &&
+         !SCS2.IsLvalueReference && SCS2.BindsToFunctionLvalue;
 }
 
 enum class FixedEnumPromotion {
@@ -4769,10 +4774,10 @@
     // FIXME: As a speculative fix to a defect introduced by CWG2352, we rank
     // a reference binding that performs a non-top-level qualification
     // conversion as a qualification conversion, not as an identity conversion.
-    ICS.Standard.Third = (RefConv &
-                              Sema::ReferenceConversions::NestedQualification)
-                             ? ICK_Qualification
-                             : ICK_Identity;
+    ICS.Standard.Third =
+        (RefConv & Sema::ReferenceConversions::NestedQualification)
+            ? ICK_Qualification
+            : ICK_Identity;
     ICS.Standard.setFromType(T2);
     ICS.Standard.setToType(0, T2);
     ICS.Standard.setToType(1, T1);
@@ -4782,6 +4787,8 @@
     ICS.Standard.IsLvalueReference = !isRValRef;
     ICS.Standard.BindsToFunctionLvalue = T2->isFunctionType();
     ICS.Standard.BindsToRvalue = InitCategory.isRValue();
+    ICS.Standard.NonVolatileConstBindsToYvalue =
+        Init->isYValue() && T1.isConstQualified() && !T1.isVolatileQualified();
     ICS.Standard.BindsImplicitObjectArgumentWithoutRefQualifier = false;
     ICS.Standard.ObjCLifetimeConversionBinding =
         (RefConv & Sema::ReferenceConversions::ObjCLifetime) != 0;
@@ -4799,7 +4806,8 @@
     //        reference-compatible with "cv2 T2," or
     //
     // Per C++ [over.ics.ref]p4, we don't check the bit-field property here.
-    if (InitCategory.isLValue() && RefRelationship == Sema::Ref_Compatible) {
+    if ((InitCategory.isLValue() || Init->isYValue()) &&
+        RefRelationship == Sema::Ref_Compatible) {
       // C++ [over.ics.ref]p1:
       //   When a parameter of reference type binds directly (8.5.3)
       //   to an argument expression, the implicit conversion sequence
@@ -4831,15 +4839,15 @@
                                    AllowExplicit))
         return ICS;
     }
-  }
 
-  //     -- Otherwise, the reference shall be an lvalue reference to a
-  //        non-volatile const type (i.e., cv1 shall be const), or the reference
-  //        shall be an rvalue reference.
-  if (!isRValRef && (!T1.isConstQualified() || T1.isVolatileQualified())) {
-    if (InitCategory.isRValue() && RefRelationship != Sema::Ref_Incompatible)
-      ICS.setBad(BadConversionSequence::lvalue_ref_to_rvalue, Init, DeclType);
-    return ICS;
+    //     -- Otherwise, the reference shall be an lvalue reference to a
+    //        non-volatile const type (i.e., cv1 shall be const), or the
+    //        reference shall be an rvalue reference.
+    if (!T1.isConstQualified() || T1.isVolatileQualified()) {
+      if (InitCategory.isRValue() && RefRelationship != Sema::Ref_Incompatible)
+        ICS.setBad(BadConversionSequence::lvalue_ref_to_rvalue, Init, DeclType);
+      return ICS;
+    }
   }
 
   //       -- If the initializer expression
Index: clang/lib/Sema/SemaInit.cpp
===================================================================
--- clang/lib/Sema/SemaInit.cpp
+++ clang/lib/Sema/SemaInit.cpp
@@ -3587,6 +3587,7 @@
   case VK_PRValue:
     S.Kind = SK_CastDerivedToBasePRValue;
     break;
+  case VK_YValue:
   case VK_XValue: S.Kind = SK_CastDerivedToBaseXValue; break;
   case VK_LValue: S.Kind = SK_CastDerivedToBaseLValue; break;
   }
@@ -3638,6 +3639,7 @@
   case VK_PRValue:
     S.Kind = SK_QualificationConversionPRValue;
     break;
+  case VK_YValue:
   case VK_XValue:
     S.Kind = SK_QualificationConversionXValue;
     break;
@@ -4808,7 +4810,8 @@
   OverloadingResult ConvOvlResult = OR_Success;
   bool T1Function = T1->isFunctionType();
   if (isLValueRef || T1Function) {
-    if (InitCategory.isLValue() && !isNonReferenceableGLValue(Initializer) &&
+    if ((InitCategory.isLValue() || Initializer->isYValue()) &&
+        !isNonReferenceableGLValue(Initializer) &&
         (RefRelationship == Sema::Ref_Compatible ||
          (Kind.isCStyleOrFunctionalCast() &&
           RefRelationship == Sema::Ref_Related))) {
Index: clang/lib/Sema/SemaExprCXX.cpp
===================================================================
--- clang/lib/Sema/SemaExprCXX.cpp
+++ clang/lib/Sema/SemaExprCXX.cpp
@@ -886,17 +886,18 @@
     //       operation from the operand to the exception object (15.1) can be
     //       omitted by constructing the automatic object directly into the
     //       exception object
-    NamedReturnInfo NRInfo =
-        IsThrownVarInScope ? getNamedReturnInfo(Ex) : NamedReturnInfo();
+    bool CopyElidable =
+        IsThrownVarInScope &&
+        getCopyElisionCandidate(Ex, NRVOExprContext::Thrown) != nullptr;
 
     QualType ExceptionObjectTy = Context.getExceptionObjectType(Ex->getType());
     if (CheckCXXThrowOperand(OpLoc, ExceptionObjectTy, Ex))
       return ExprError();
 
-    InitializedEntity Entity = InitializedEntity::InitializeException(
-        OpLoc, ExceptionObjectTy,
-        /*NRVO=*/NRInfo.isCopyElidable());
-    ExprResult Res = PerformMoveOrCopyInitialization(Entity, NRInfo, Ex);
+    InitializedEntity Entity =
+        InitializedEntity::InitializeException(OpLoc, ExceptionObjectTy,
+                                               /*NRVO=*/CopyElidable);
+    ExprResult Res = PerformCopyInitialization(Entity, SourceLocation(), Ex);
     if (Res.isInvalid())
       return ExprError();
     Ex = Res.get();
Index: clang/lib/Sema/SemaCoroutine.cpp
===================================================================
--- clang/lib/Sema/SemaCoroutine.cpp
+++ clang/lib/Sema/SemaCoroutine.cpp
@@ -997,7 +997,7 @@
   VarDecl *Promise = FSI->CoroutinePromise;
   ExprResult PC;
   if (E && (isa<InitListExpr>(E) || !E->getType()->isVoidType())) {
-    getNamedReturnInfo(E, /*ForceCXX2b=*/true);
+    getCopyElisionCandidate(E, NRVOExprContext::Co_Returned);
     PC = buildPromiseCall(*this, Promise, Loc, "return_value", E);
   } else {
     E = MakeFullDiscardedValueExpr(E).get();
Index: clang/lib/Sema/SemaCodeComplete.cpp
===================================================================
--- clang/lib/Sema/SemaCodeComplete.cpp
+++ clang/lib/Sema/SemaCodeComplete.cpp
@@ -1240,7 +1240,7 @@
 
     // For xvalue objects, we prefer the rvalue overload even if we have to
     // add qualifiers (which is rare, because const&& is rare).
-    if (ObjectKind == clang::VK_XValue)
+    if (ObjectKind == clang::VK_XValue || ObjectKind == clang::VK_YValue)
       return CandidateRef == RQ_RValue ? OverloadCompare::Dominates
                                        : OverloadCompare::Dominated;
   }
Index: clang/lib/Sema/Sema.cpp
===================================================================
--- clang/lib/Sema/Sema.cpp
+++ clang/lib/Sema/Sema.cpp
@@ -1965,17 +1965,14 @@
   SourceLocation Loc = VD->getLocation();
   Expr *VarRef =
       new (S.Context) DeclRefExpr(S.Context, VD, false, T, VK_LValue, Loc);
-  ExprResult Result;
   auto IE = InitializedEntity::InitializeBlock(Loc, T, false);
-  if (S.getLangOpts().CPlusPlus2b) {
-    auto *E = ImplicitCastExpr::Create(S.Context, T, CK_NoOp, VarRef, nullptr,
-                                       VK_XValue, FPOptionsOverride());
-    Result = S.PerformCopyInitialization(IE, SourceLocation(), E);
-  } else {
-    Result = S.PerformMoveOrCopyInitialization(
-        IE, Sema::NamedReturnInfo{VD, Sema::NamedReturnInfo::MoveEligible},
-        VarRef);
-  }
+  auto *E = S.getLangOpts().CPlusPlus11
+                ? ImplicitCastExpr::Create(
+                      S.Context, T, CK_NoOp, VarRef, nullptr,
+                      S.getLangOpts().CPlusPlus2b ? VK_XValue : VK_YValue,
+                      FPOptionsOverride())
+                : VarRef;
+  ExprResult Result = S.PerformCopyInitialization(IE, SourceLocation(), E);
 
   if (!Result.isInvalid()) {
     Result = S.MaybeCreateExprWithCleanups(Result);
Index: clang/lib/CodeGen/CGObjC.cpp
===================================================================
--- clang/lib/CodeGen/CGObjC.cpp
+++ clang/lib/CodeGen/CGObjC.cpp
@@ -2887,8 +2887,7 @@
   // If we're loading retained from a __strong xvalue, we can avoid
   // an extra retain/release pair by zeroing out the source of this
   // "move" operation.
-  if (e->isXValue() &&
-      !type.isConstQualified() &&
+  if ((e->isXValue() || e->isYValue()) && !type.isConstQualified() &&
       type.getObjCLifetime() == Qualifiers::OCL_Strong) {
     // Emit the lvalue.
     LValue lv = CGF.EmitLValue(e);
Index: clang/lib/CodeGen/CGDecl.cpp
===================================================================
--- clang/lib/CodeGen/CGDecl.cpp
+++ clang/lib/CodeGen/CGDecl.cpp
@@ -717,7 +717,7 @@
       if (srcExpr->isLValue()) {
         CGF.EmitARCCopyWeak(destLV.getAddress(CGF), srcAddr);
       } else {
-        assert(srcExpr->isXValue());
+        assert(srcExpr->isXValue() || srcExpr->isYValue());
         CGF.EmitARCMoveWeak(destLV.getAddress(CGF), srcAddr);
       }
       return true;
Index: clang/lib/AST/TextNodeDumper.cpp
===================================================================
--- clang/lib/AST/TextNodeDumper.cpp
+++ clang/lib/AST/TextNodeDumper.cpp
@@ -153,6 +153,9 @@
       case VK_XValue:
         OS << " xvalue";
         break;
+      case VK_YValue:
+        OS << " yvalue";
+        break;
       }
     }
 
Index: clang/lib/AST/JSONNodeDumper.cpp
===================================================================
--- clang/lib/AST/JSONNodeDumper.cpp
+++ clang/lib/AST/JSONNodeDumper.cpp
@@ -62,6 +62,9 @@
     case VK_PRValue:
       Category = "prvalue";
       break;
+    case VK_YValue:
+      Category = "yvalue";
+      break;
     }
     JOS.attribute("valueCategory", Category);
   }
Index: clang/lib/AST/ExprClassification.cpp
===================================================================
--- clang/lib/AST/ExprClassification.cpp
+++ clang/lib/AST/ExprClassification.cpp
@@ -57,7 +57,7 @@
     assert(isLValue());
     break;
   case Cl::CL_XValue:
-    assert(isXValue());
+    assert(isXValue() || isYValue());
     break;
   case Cl::CL_Function:
   case Cl::CL_Void:
@@ -99,6 +99,7 @@
     return Lang.CPlusPlus ? ClassifyTemporary(E->getType()) : Cl::CL_PRValue;
   case VK_LValue:
     return Cl::CL_LValue;
+  case VK_YValue:
   case VK_XValue:
     return Cl::CL_XValue;
   }
Index: clang/lib/AST/ASTContext.cpp
===================================================================
--- clang/lib/AST/ASTContext.cpp
+++ clang/lib/AST/ASTContext.cpp
@@ -5471,6 +5471,7 @@
     return getRValueReferenceType(T);
   //     - otherwise, if e is an lvalue, decltype(e) is T&, where T is the
   //       type of e;
+  case VK_YValue:
   case VK_LValue:
     return getLValueReferenceType(T);
   //  - otherwise, decltype(e) is the type of e.
Index: clang/include/clang/Sema/Sema.h
===================================================================
--- clang/include/clang/Sema/Sema.h
+++ clang/include/clang/Sema/Sema.h
@@ -4776,23 +4776,19 @@
     enum Status : uint8_t { None, MoveEligible, MoveEligibleAndCopyElidable };
     Status S;
 
-    bool isMoveEligible() const { return S != None; };
+    bool isMoveEligible() const { return S >= MoveEligible; };
     bool isCopyElidable() const { return S == MoveEligibleAndCopyElidable; }
   };
-  NamedReturnInfo getNamedReturnInfo(Expr *&E, bool ForceCXX2b = false);
-  NamedReturnInfo getNamedReturnInfo(const VarDecl *VD);
-  const VarDecl *getCopyElisionCandidate(NamedReturnInfo &Info,
-                                         QualType ReturnType);
-
-  ExprResult PerformMoveOrCopyInitialization(const InitializedEntity &Entity,
-                                             const NamedReturnInfo &NRInfo,
-                                             Expr *Value);
+  NamedReturnInfo getNamedReturnInfo(const VarDecl *VD, QualType ReturnType,
+                                     bool Parenthesized);
+  enum class NRVOExprContext { Returned, Co_Returned, Thrown };
+  const VarDecl *getCopyElisionCandidate(Expr *&E, NRVOExprContext C,
+                                         QualType ReturnType = QualType());
 
   StmtResult ActOnReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp,
                              Scope *CurScope);
   StmtResult BuildReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp);
-  StmtResult ActOnCapScopeReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp,
-                                     NamedReturnInfo &NRInfo);
+  StmtResult ActOnCapScopeReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp);
 
   StmtResult ActOnGCCAsmStmt(SourceLocation AsmLoc, bool IsSimple,
                              bool IsVolatile, unsigned NumOutputs,
Index: clang/include/clang/Sema/Overload.h
===================================================================
--- clang/include/clang/Sema/Overload.h
+++ clang/include/clang/Sema/Overload.h
@@ -304,6 +304,10 @@
     /// Whether we're binding to an rvalue.
     unsigned BindsToRvalue : 1;
 
+    /// Whether this is a non-volatile const qualified
+    /// reference binding to an yvalue.
+    unsigned NonVolatileConstBindsToYvalue : 1;
+
     /// Whether this binds an implicit object argument to a
     /// non-static member function without a ref-qualifier.
     unsigned BindsImplicitObjectArgumentWithoutRefQualifier : 1;
Index: clang/include/clang/Basic/Specifiers.h
===================================================================
--- clang/include/clang/Basic/Specifiers.h
+++ clang/include/clang/Basic/Specifiers.h
@@ -116,7 +116,11 @@
     /// An x-value expression is a reference to an object with
     /// independent storage but which can be "moved", i.e.
     /// efficiently cannibalized for its resources.
-    VK_XValue
+    VK_XValue,
+
+    // Is just like an x-value, but will bind to a
+    // l-value reference, at a lower rank.
+    VK_YValue,
   };
 
   /// A further classification of the kind of object referenced by an
Index: clang/include/clang/AST/Expr.h
===================================================================
--- clang/include/clang/AST/Expr.h
+++ clang/include/clang/AST/Expr.h
@@ -270,6 +270,7 @@
   bool isLValue() const { return getValueKind() == VK_LValue; }
   bool isPRValue() const { return getValueKind() == VK_PRValue; }
   bool isXValue() const { return getValueKind() == VK_XValue; }
+  bool isYValue() const { return getValueKind() == VK_YValue; }
   bool isGLValue() const { return getValueKind() != VK_PRValue; }
 
   enum LValueClassification {
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to