[clang] fbee4a0 - [C++20] [P1825] More implicit moves

Arthur O'Dwyer via cfe-commits cfe-commits at lists.llvm.org
Tue Feb 16 14:24:34 PST 2021


Author: Yang Fan
Date: 2021-02-16T17:24:20-05:00
New Revision: fbee4a0c79cc4ee87c34e51342742a5bc6fcf872

URL: https://github.com/llvm/llvm-project/commit/fbee4a0c79cc4ee87c34e51342742a5bc6fcf872
DIFF: https://github.com/llvm/llvm-project/commit/fbee4a0c79cc4ee87c34e51342742a5bc6fcf872.diff

LOG: [C++20] [P1825] More implicit moves

Implement all of P1825R0:

- implicitly movable entity can be an rvalue reference to non-volatile
    automatic object.
- operand of throw-expression can be a function or catch-clause parameter
    (support for function parameter has already been implemented).
- in the first overload resolution, the selected function no need to be
    a constructor.
- in the first overload resolution, the first parameter of the selected
    function no need to be an rvalue reference to the object's type.

This patch also removes the diagnostic `-Wreturn-std-move-in-c++11`.

Differential Revision: https://reviews.llvm.org/D88220

Added: 
    clang/test/SemaCXX/P1155.cpp

Modified: 
    clang/include/clang/Basic/DiagnosticGroups.td
    clang/include/clang/Basic/DiagnosticSemaKinds.td
    clang/include/clang/Sema/Sema.h
    clang/lib/Sema/SemaCoroutine.cpp
    clang/lib/Sema/SemaStmt.cpp
    clang/test/CXX/class/class.init/class.copy.elision/p3.cpp
    clang/test/SemaCXX/warn-return-std-move.cpp
    clang/www/cxx_status.html

Removed: 
    


################################################################################
diff  --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td
index fcac6f44aa9a..21fc7d4cb82b 100644
--- a/clang/include/clang/Basic/DiagnosticGroups.td
+++ b/clang/include/clang/Basic/DiagnosticGroups.td
@@ -499,7 +499,6 @@ def Packed : DiagGroup<"packed">;
 def Padded : DiagGroup<"padded">;
 
 def PessimizingMove : DiagGroup<"pessimizing-move">;
-def ReturnStdMoveInCXX11 : DiagGroup<"return-std-move-in-c++11">;
 def ReturnStdMove : DiagGroup<"return-std-move">;
 
 def PointerArith : DiagGroup<"pointer-arith">;

diff  --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 782140f1d62e..f69ef3286975 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -6370,13 +6370,6 @@ def warn_return_std_move : Warning<
   InGroup<ReturnStdMove>, DefaultIgnore;
 def note_add_std_move : Note<
   "call 'std::move' explicitly to avoid copying">;
-def warn_return_std_move_in_cxx11 : Warning<
-  "prior to the resolution of a defect report against ISO C++11, "
-  "local variable %0 would have been copied despite being returned by name, "
-  "due to its not matching the function return type%
diff { ($ vs $)|}1,2">,
-  InGroup<ReturnStdMoveInCXX11>, DefaultIgnore;
-def note_add_std_move_in_cxx11 : Note<
-  "call 'std::move' explicitly to avoid copying on older compilers">;
 
 def warn_string_plus_int : Warning<
   "adding %0 to a string does not append to the string">,

diff  --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 162c0b472bd3..b6fd6640a835 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -4724,10 +4724,12 @@ class Sema final {
     CES_AllowParameters = 1,
     CES_AllowDifferentTypes = 2,
     CES_AllowExceptionVariables = 4,
-    CES_FormerDefault = (CES_AllowParameters),
-    CES_Default = (CES_AllowParameters | CES_AllowDifferentTypes),
-    CES_AsIfByStdMove = (CES_AllowParameters | CES_AllowDifferentTypes |
-                         CES_AllowExceptionVariables),
+    CES_AllowRValueReferenceType = 8,
+    CES_ImplicitlyMovableCXX11CXX14CXX17 =
+        (CES_AllowParameters | CES_AllowDifferentTypes),
+    CES_ImplicitlyMovableCXX20 =
+        (CES_AllowParameters | CES_AllowDifferentTypes |
+         CES_AllowExceptionVariables | CES_AllowRValueReferenceType),
   };
 
   VarDecl *getCopyElisionCandidate(QualType ReturnType, Expr *E,

diff  --git a/clang/lib/Sema/SemaCoroutine.cpp b/clang/lib/Sema/SemaCoroutine.cpp
index 7a48bfa429e9..cd3b0db6b824 100644
--- a/clang/lib/Sema/SemaCoroutine.cpp
+++ b/clang/lib/Sema/SemaCoroutine.cpp
@@ -996,7 +996,8 @@ StmtResult Sema::BuildCoreturnStmt(SourceLocation Loc, Expr *E,
 
   // Move the return value if we can
   if (E) {
-    auto NRVOCandidate = this->getCopyElisionCandidate(E->getType(), E, CES_AsIfByStdMove);
+    const VarDecl *NRVOCandidate = this->getCopyElisionCandidate(
+        E->getType(), E, CES_ImplicitlyMovableCXX20);
     if (NRVOCandidate) {
       InitializedEntity Entity =
           InitializedEntity::InitializeResult(Loc, E->getType(), NRVOCandidate);

diff  --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp
index b24a8ab110b2..e25d69931538 100644
--- a/clang/lib/Sema/SemaStmt.cpp
+++ b/clang/lib/Sema/SemaStmt.cpp
@@ -3083,12 +3083,20 @@ bool Sema::isCopyElisionCandidate(QualType ReturnType, const VarDecl *VD,
   // 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;
+  if (VD->hasAttr<BlocksAttr>())
+    return false;
 
   // ...non-volatile...
   if (VD->getType().isVolatileQualified())
     return false;
 
+  // C++20 [class.copy.elision]p3:
+  // ...rvalue reference to a non-volatile...
+  if (VD->getType()->isRValueReferenceType() &&
+      (!(CESK & CES_AllowRValueReferenceType) ||
+       VD->getType().getNonReferenceType().isVolatileQualified()))
+    return false;
+
   if (CESK & CES_AllowDifferentTypes)
     return true;
 
@@ -3104,13 +3112,13 @@ bool Sema::isCopyElisionCandidate(QualType ReturnType, const VarDecl *VD,
 /// Try to perform the initialization of a potentially-movable value,
 /// which is the operand to a return or throw statement.
 ///
-/// This routine implements C++14 [class.copy]p32, 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.
+/// This routine implements C++20 [class.copy.elision]p3, which attempts to
+/// treat returned lvalues as rvalues in certain cases (to prefer move
+/// construction), then falls back to treating them as lvalues if that failed.
 ///
-/// \param ConvertingConstructorsOnly If true, follow [class.copy]p32 and reject
-/// resolutions that find non-constructors, such as derived-to-base conversions
-/// or `operator T()&&` member functions. If false, do consider such
+/// \param ConvertingConstructorsOnly If true, follow [class.copy.elision]p3 and
+/// reject resolutions that find non-constructors, such as derived-to-base
+/// conversions or `operator T()&&` member functions. If false, do consider such
 /// conversion sequences.
 ///
 /// \param Res We will fill this in if move-initialization was possible.
@@ -3151,9 +3159,10 @@ static bool TryMoveInitialization(Sema &S, const InitializedEntity &Entity,
     FunctionDecl *FD = Step.Function.Function;
     if (ConvertingConstructorsOnly) {
       if (isa<CXXConstructorDecl>(FD)) {
+        // C++11 [class.copy]p32:
         // C++14 [class.copy]p32:
-        // [...] If the first overload resolution fails or was not performed,
-        // or if the type of the first parameter of the selected constructor
+        // C++17 [class.copy.elision]p3:
+        // [...] if the type of the first parameter of the selected constructor
         // is not an rvalue reference to the object's type (possibly
         // cv-qualified), overload resolution is performed again, considering
         // the object as an lvalue.
@@ -3172,7 +3181,8 @@ static bool TryMoveInitialization(Sema &S, const InitializedEntity &Entity,
         // Check that overload resolution selected a constructor taking an
         // rvalue reference. If it selected an lvalue reference, then we
         // didn't need to cast this thing to an rvalue in the first place.
-        if (!isa<RValueReferenceType>(FD->getParamDecl(0)->getType()))
+        if (IsDiagnosticsCheck &&
+            !isa<RValueReferenceType>(FD->getParamDecl(0)->getType()))
           break;
       } else if (isa<CXXMethodDecl>(FD)) {
         // Check that overload resolution selected a conversion operator
@@ -3202,73 +3212,38 @@ static bool TryMoveInitialization(Sema &S, const InitializedEntity &Entity,
 /// Perform the initialization of a potentially-movable value, which
 /// is the result of return value.
 ///
-/// This routine implements C++14 [class.copy]p32, 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 VarDecl *NRVOCandidate,
-                                      QualType ResultType,
-                                      Expr *Value,
-                                      bool AllowNRVO) {
-  // C++14 [class.copy]p32:
-  // When the criteria for elision of a copy/move operation are met, but not for
-  // an exception-declaration, and the object to be copied is designated by an
-  // lvalue, or when the expression in a return statement is a (possibly
-  // parenthesized) id-expression that names an object with automatic storage
-  // duration declared in the body or parameter-declaration-clause of the
-  // innermost enclosing function or lambda-expression, overload resolution to
-  // select the constructor for the copy is first performed as if the object
-  // were designated by an rvalue.
+/// 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 VarDecl *NRVOCandidate,
+    QualType ResultType, Expr *Value, bool AllowNRVO) {
   ExprResult Res = ExprError();
   bool NeedSecondOverloadResolution = true;
 
   if (AllowNRVO) {
-    bool AffectedByCWG1579 = false;
+    CopyElisionSemanticsKind CESK = CES_Strict;
+    if (getLangOpts().CPlusPlus20) {
+      CESK = CES_ImplicitlyMovableCXX20;
+    } else if (getLangOpts().CPlusPlus11) {
+      CESK = CES_ImplicitlyMovableCXX11CXX14CXX17;
+    }
 
     if (!NRVOCandidate) {
-      NRVOCandidate = getCopyElisionCandidate(ResultType, Value, CES_Default);
-      if (NRVOCandidate &&
-          !getDiagnostics().isIgnored(diag::warn_return_std_move_in_cxx11,
-                                      Value->getExprLoc())) {
-        const VarDecl *NRVOCandidateInCXX11 =
-            getCopyElisionCandidate(ResultType, Value, CES_FormerDefault);
-        AffectedByCWG1579 = (!NRVOCandidateInCXX11);
-      }
+      NRVOCandidate = getCopyElisionCandidate(ResultType, Value, CESK);
     }
 
     if (NRVOCandidate) {
-      NeedSecondOverloadResolution = TryMoveInitialization(
-          *this, Entity, NRVOCandidate, ResultType, Value, true, false, Res);
+      NeedSecondOverloadResolution =
+          TryMoveInitialization(*this, Entity, NRVOCandidate, ResultType, Value,
+                                !getLangOpts().CPlusPlus20, false, Res);
     }
 
-    if (!NeedSecondOverloadResolution && AffectedByCWG1579) {
-      QualType QT = NRVOCandidate->getType();
-      if (QT.getNonReferenceType().getUnqualifiedType().isTriviallyCopyableType(
-              Context)) {
-        // Adding 'std::move' around a trivially copyable variable is probably
-        // pointless. Don't suggest it.
-      } else {
-        // Common cases for this are returning unique_ptr<Derived> from a
-        // function of return type unique_ptr<Base>, or returning T from a
-        // function of return type Expected<T>. This is totally fine in a
-        // post-CWG1579 world, but was not fine before.
-        assert(!ResultType.isNull());
-        SmallString<32> Str;
-        Str += "std::move(";
-        Str += NRVOCandidate->getDeclName().getAsString();
-        Str += ")";
-        Diag(Value->getExprLoc(), diag::warn_return_std_move_in_cxx11)
-            << Value->getSourceRange() << NRVOCandidate->getDeclName()
-            << ResultType << QT;
-        Diag(Value->getExprLoc(), diag::note_add_std_move_in_cxx11)
-            << FixItHint::CreateReplacement(Value->getSourceRange(), Str);
-      }
-    } else if (NeedSecondOverloadResolution &&
-               !getDiagnostics().isIgnored(diag::warn_return_std_move,
-                                           Value->getExprLoc())) {
-      const VarDecl *FakeNRVOCandidate =
-          getCopyElisionCandidate(QualType(), Value, CES_AsIfByStdMove);
+    if (!getLangOpts().CPlusPlus20 && NeedSecondOverloadResolution &&
+        !getDiagnostics().isIgnored(diag::warn_return_std_move,
+                                    Value->getExprLoc())) {
+      const VarDecl *FakeNRVOCandidate = getCopyElisionCandidate(
+          QualType(), Value, CES_ImplicitlyMovableCXX20);
       if (FakeNRVOCandidate) {
         QualType QT = FakeNRVOCandidate->getType();
         if (QT->isLValueReferenceType()) {

diff  --git a/clang/test/CXX/class/class.init/class.copy.elision/p3.cpp b/clang/test/CXX/class/class.init/class.copy.elision/p3.cpp
index b3427243bd61..29d818602537 100644
--- a/clang/test/CXX/class/class.init/class.copy.elision/p3.cpp
+++ b/clang/test/CXX/class/class.init/class.copy.elision/p3.cpp
@@ -1,7 +1,7 @@
-// RUN: %clang_cc1 -std=c++20 -fsyntax-only -fcxx-exceptions -verify=expected %s
-// RUN: %clang_cc1 -std=c++17 -fsyntax-only -fcxx-exceptions -verify=expected %s
-// RUN: %clang_cc1 -std=c++14 -fsyntax-only -fcxx-exceptions -verify=expected %s
-// RUN: %clang_cc1 -std=c++11 -fsyntax-only -fcxx-exceptions -verify=expected %s
+// RUN: %clang_cc1 -std=c++20 -fsyntax-only -fcxx-exceptions -verify=expected,cxx20 %s
+// RUN: %clang_cc1 -std=c++17 -fsyntax-only -fcxx-exceptions -verify=expected,cxx11_14_17 %s
+// RUN: %clang_cc1 -std=c++14 -fsyntax-only -fcxx-exceptions -verify=expected,cxx11_14_17 %s
+// RUN: %clang_cc1 -std=c++11 -fsyntax-only -fcxx-exceptions -verify=expected,cxx11_14_17 %s
 
 namespace test_delete_function {
 struct A1 {
@@ -48,3 +48,247 @@ B2 test4() {
   return c; // expected-error {{calling a private constructor of class 'test_delete_function::B2'}}
 }
 } // namespace test_delete_function
+
+// In C++20, implicitly movable entity can be rvalue reference to non-volatile
+// automatic object.
+namespace test_implicitly_movable_rvalue_ref {
+struct A1 {
+  A1(A1 &&);
+  A1(const A1 &) = delete; // cxx11_14_17-note {{'A1' has been explicitly marked deleted here}}
+};
+A1 test1(A1 &&a) {
+  return a; // cxx11_14_17-error {{call to deleted constructor of 'test_implicitly_movable_rvalue_ref::A1'}}
+}
+
+struct A2 {
+  A2(A2 &&);
+
+private:
+  A2(const A2 &); // cxx11_14_17-note {{declared private here}}
+};
+A2 test2(A2 &&a) {
+  return a; // cxx11_14_17-error {{calling a private constructor of class 'test_implicitly_movable_rvalue_ref::A2'}}
+}
+
+struct B1 {
+  B1(const B1 &);
+  B1(B1 &&) = delete; // cxx20-note {{'B1' has been explicitly marked deleted here}}
+};
+B1 test3(B1 &&b) {
+  return b; // cxx20-error {{call to deleted constructor of 'test_implicitly_movable_rvalue_ref::B1'}}
+}
+
+struct B2 {
+  B2(const B2 &);
+
+private:
+  B2(B2 &&); // cxx20-note {{declared private here}}
+};
+B2 test4(B2 &&b) {
+  return b; // cxx20-error {{calling a private constructor of class 'test_implicitly_movable_rvalue_ref::B2'}}
+}
+} // namespace test_implicitly_movable_rvalue_ref
+
+// In C++20, operand of throw-expression can be function parameter or
+// catch-clause parameter.
+namespace test_throw_parameter {
+void func();
+
+struct A1 {
+  A1(const A1 &);
+  A1(A1 &&) = delete; // cxx20-note {{'A1' has been explicitly marked deleted here}}
+};
+void test1() {
+  try {
+    func();
+  } catch (A1 a) {
+    throw a; // cxx20-error {{call to deleted constructor of 'test_throw_parameter::A1'}}
+  }
+}
+
+struct A2 {
+  A2(const A2 &);
+
+private:
+  A2(A2 &&); // cxx20-note {{declared private here}}
+};
+void test2() {
+  try {
+    func();
+  } catch (A2 a) {
+    throw a; // cxx20-error {{calling a private constructor of class 'test_throw_parameter::A2'}}
+  }
+}
+} // namespace test_throw_parameter
+
+// In C++20, during the first overload resolution, the selected function no
+// need to be a constructor.
+namespace test_non_ctor_conversion {
+class C {};
+
+struct A1 {
+  operator C() &&;
+  operator C() const & = delete; // cxx11_14_17-note {{'operator C' has been explicitly marked deleted here}}
+};
+C test1() {
+  A1 a;
+  return a; // cxx11_14_17-error {{conversion function from 'test_non_ctor_conversion::A1' to 'test_non_ctor_conversion::C' invokes a deleted function}}
+}
+
+struct A2 {
+  operator C() &&;
+
+private:
+  operator C() const &; // cxx11_14_17-note {{declared private here}}
+};
+C test2() {
+  A2 a;
+  return a; // cxx11_14_17-error {{'operator C' is a private member of 'test_non_ctor_conversion::A2'}}
+}
+
+struct B1 {
+  operator C() const &;
+  operator C() && = delete; // cxx20-note {{'operator C' has been explicitly marked deleted here}}
+};
+C test3() {
+  B1 b;
+  return b; // cxx20-error {{conversion function from 'test_non_ctor_conversion::B1' to 'test_non_ctor_conversion::C' invokes a deleted function}}
+}
+
+struct B2 {
+  operator C() const &;
+
+private:
+  operator C() &&; // cxx20-note {{declared private here}}
+};
+C test4() {
+  B2 b;
+  return b; // cxx20-error {{'operator C' is a private member of 'test_non_ctor_conversion::B2'}}
+}
+} // namespace test_non_ctor_conversion
+
+// In C++20, during the first overload resolution, the first parameter of the
+// selected function no need to be an rvalue reference to the object's type.
+namespace test_ctor_param_rvalue_ref {
+struct A1;
+struct A2;
+struct B1;
+struct B2;
+
+struct NeedRvalueRef {
+  NeedRvalueRef(A1 &&);
+  NeedRvalueRef(A2 &&);
+  NeedRvalueRef(B1 &&);
+  NeedRvalueRef(B2 &&);
+};
+struct NeedValue {
+  NeedValue(A1); // cxx11_14_17-note 2 {{passing argument to parameter here}}
+  NeedValue(A2);
+  NeedValue(B1); // cxx20-note 2 {{passing argument to parameter here}}
+  NeedValue(B2);
+};
+
+struct A1 {
+  A1();
+  A1(A1 &&);
+  A1(const A1 &) = delete; // cxx11_14_17-note 3 {{'A1' has been explicitly marked deleted here}}
+};
+NeedValue test_1_1() {
+  // not rvalue reference
+  // same type
+  A1 a;
+  return a; // cxx11_14_17-error {{call to deleted constructor of 'test_ctor_param_rvalue_ref::A1'}}
+}
+class DerivedA1 : public A1 {};
+A1 test_1_2() {
+  // rvalue reference
+  // not same type
+  DerivedA1 a;
+  return a; // cxx11_14_17-error {{call to deleted constructor of 'test_ctor_param_rvalue_ref::A1'}}
+}
+NeedValue test_1_3() {
+  // not rvalue reference
+  // not same type
+  DerivedA1 a;
+  return a; // cxx11_14_17-error {{call to deleted constructor of 'test_ctor_param_rvalue_ref::A1'}}
+}
+
+struct A2 {
+  A2();
+  A2(A2 &&);
+
+private:
+  A2(const A2 &); // cxx11_14_17-note 3 {{declared private here}}
+};
+NeedValue test_2_1() {
+  // not rvalue reference
+  // same type
+  A2 a;
+  return a; // cxx11_14_17-error {{calling a private constructor of class 'test_ctor_param_rvalue_ref::A2'}}
+}
+class DerivedA2 : public A2 {};
+A2 test_2_2() {
+  // rvalue reference
+  // not same type
+  DerivedA2 a;
+  return a; // cxx11_14_17-error {{calling a private constructor of class 'test_ctor_param_rvalue_ref::A2'}}
+}
+NeedValue test_2_3() {
+  // not rvalue reference
+  // not same type
+  DerivedA2 a;
+  return a; // cxx11_14_17-error {{calling a private constructor of class 'test_ctor_param_rvalue_ref::A2'}}
+}
+
+struct B1 {
+  B1();
+  B1(const B1 &);
+  B1(B1 &&) = delete; // cxx20-note 3 {{'B1' has been explicitly marked deleted here}}
+};
+NeedValue test_3_1() {
+  // not rvalue reference
+  // same type
+  B1 b;
+  return b; // cxx20-error {{call to deleted constructor of 'test_ctor_param_rvalue_ref::B1'}}
+}
+class DerivedB1 : public B1 {};
+B1 test_3_2() {
+  // rvalue reference
+  // not same type
+  DerivedB1 b;
+  return b; // cxx20-error {{call to deleted constructor of 'test_ctor_param_rvalue_ref::B1'}}
+}
+NeedValue test_3_3() {
+  // not rvalue reference
+  // not same type
+  DerivedB1 b;
+  return b; // cxx20-error {{call to deleted constructor of 'test_ctor_param_rvalue_ref::B1'}}
+}
+
+struct B2 {
+  B2();
+  B2(const B2 &);
+
+private:
+  B2(B2 &&); // cxx20-note 3 {{declared private here}}
+};
+NeedValue test_4_1() {
+  // not rvalue reference
+  // same type
+  B2 b;
+  return b; // cxx20-error {{calling a private constructor of class 'test_ctor_param_rvalue_ref::B2'}}
+}
+class DerivedB2 : public B2 {};
+B2 test_4_2() {
+  // rvalue reference
+  // not same type
+  DerivedB2 b;
+  return b; // cxx20-error {{calling a private constructor of class 'test_ctor_param_rvalue_ref::B2'}}
+}
+NeedValue test_4_3() {
+  // not rvalue reference
+  // not same type
+  DerivedB2 b;
+  return b; // cxx20-error {{calling a private constructor of class 'test_ctor_param_rvalue_ref::B2'}}
+}
+} // namespace test_ctor_param_rvalue_ref

diff  --git a/clang/test/SemaCXX/P1155.cpp b/clang/test/SemaCXX/P1155.cpp
new file mode 100644
index 000000000000..f38f4586f6af
--- /dev/null
+++ b/clang/test/SemaCXX/P1155.cpp
@@ -0,0 +1,69 @@
+// RUN: %clang_cc1 -std=c++20 -fsyntax-only -fcxx-exceptions -verify=cxx20 %s
+// RUN: %clang_cc1 -std=c++17 -fsyntax-only -fcxx-exceptions -verify=cxx11_14_17 %s
+// RUN: %clang_cc1 -std=c++14 -fsyntax-only -fcxx-exceptions -verify=cxx11_14_17 %s
+// RUN: %clang_cc1 -std=c++11 -fsyntax-only -fcxx-exceptions -verify=cxx11_14_17 %s
+// cxx20-no-diagnostics
+
+// Throwing
+namespace test_throwing {
+class Widget {
+public:
+  Widget(Widget &&);
+  Widget(const Widget &) = delete;
+};
+
+void seven(Widget w) {
+  throw w; // Clang already do this implicit move before -std=c++20
+}
+} // namespace test_throwing
+
+// Non-constructor conversion
+namespace test_non_constructor_conversion {
+class Widget {};
+
+struct To {
+  operator Widget() const & = delete; // cxx11_14_17-note {{'operator Widget' has been explicitly marked deleted here}}
+  operator Widget() &&;
+};
+
+Widget nine() {
+  To t;
+  return t; // cxx11_14_17-error {{conversion function from 'test_non_constructor_conversion::To' to 'test_non_constructor_conversion::Widget' invokes a deleted function}}
+}
+} // namespace test_non_constructor_conversion
+
+// By-value sinks
+namespace test_by_value_sinks {
+class Widget {
+public:
+  Widget();
+  Widget(Widget &&);
+  Widget(const Widget &) = delete; // cxx11_14_17-note {{'Widget' has been explicitly marked deleted here}}
+};
+
+struct Fowl {
+  Fowl(Widget); // cxx11_14_17-note {{passing argument to parameter here}}
+};
+
+Fowl eleven() {
+  Widget w;
+  return w; // cxx11_14_17-error {{call to deleted constructor of 'test_by_value_sinks::Widget'}}
+}
+} // namespace test_by_value_sinks
+
+// Slicing
+namespace test_slicing {
+class Base {
+public:
+  Base();
+  Base(Base &&);
+  Base(Base const &) = delete; // cxx11_14_17-note {{'Base' has been explicitly marked deleted here}}
+};
+
+class Derived : public Base {};
+
+Base thirteen() {
+  Derived result;
+  return result; // cxx11_14_17-error {{call to deleted constructor of 'test_slicing::Base'}}
+}
+} // namespace test_slicing

diff  --git a/clang/test/SemaCXX/warn-return-std-move.cpp b/clang/test/SemaCXX/warn-return-std-move.cpp
index 9669c7d282d9..647b5ab9e4b0 100644
--- a/clang/test/SemaCXX/warn-return-std-move.cpp
+++ b/clang/test/SemaCXX/warn-return-std-move.cpp
@@ -1,12 +1,17 @@
-// RUN: %clang_cc1 -fsyntax-only -fcxx-exceptions -Wreturn-std-move -Wreturn-std-move-in-c++11 -std=c++14 -verify %s
-// RUN: %clang_cc1 -fsyntax-only -fcxx-exceptions -Wreturn-std-move -Wreturn-std-move-in-c++11 -std=c++14 -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s
+// RUN: %clang_cc1 -fsyntax-only -fcxx-exceptions -Wreturn-std-move -std=c++20 -verify=cxx20 %s
+// RUN: %clang_cc1 -fsyntax-only -fcxx-exceptions -Wreturn-std-move -std=c++17 -verify=expected %s
+// RUN: %clang_cc1 -fsyntax-only -fcxx-exceptions -Wreturn-std-move -std=c++14 -verify=expected %s
+// RUN: %clang_cc1 -fsyntax-only -fcxx-exceptions -Wreturn-std-move -std=c++11 -verify=expected %s
+// RUN: %clang_cc1 -fsyntax-only -fcxx-exceptions -Wreturn-std-move -std=c++17 -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s -check-prefix=CHECK
+// RUN: %clang_cc1 -fsyntax-only -fcxx-exceptions -Wreturn-std-move -std=c++14 -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s -check-prefix=CHECK
+// RUN: %clang_cc1 -fsyntax-only -fcxx-exceptions -Wreturn-std-move -std=c++11 -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s -check-prefix=CHECK
 
 // definitions for std::move
 namespace std {
 inline namespace foo {
 template <class T> struct remove_reference { typedef T type; };
-template <class T> struct remove_reference<T&> { typedef T type; };
-template <class T> struct remove_reference<T&&> { typedef T type; };
+template <class T> struct remove_reference<T &> { typedef T type; };
+template <class T> struct remove_reference<T &&> { typedef T type; };
 
 template <class T> typename remove_reference<T>::type &&move(T &&t);
 } // namespace foo
@@ -76,11 +81,8 @@ Base test2() {
     // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:14}:"std::move(d2)"
 }
 ConstructFromDerived test3() {
-    Derived d3;
-    return d3;  // e2-cxx11
-    // expected-warning at -1{{would have been copied despite being returned by name}}
-    // expected-note at -2{{to avoid copying on older compilers}}
-    // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:14}:"std::move(d3)"
+  Derived d3;
+  return d3; // ok
 }
 ConstructFromBase test4() {
     Derived d4;
@@ -153,10 +155,7 @@ Base testParam2(Derived d) {
     // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:13}:"std::move(d)"
 }
 ConstructFromDerived testParam3(Derived d) {
-    return d;  // e7-cxx11
-    // expected-warning at -1{{would have been copied despite being returned by name}}
-    // expected-note at -2{{to avoid copying on older compilers}}
-    // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:13}:"std::move(d)"
+  return d; // ok
 }
 ConstructFromBase testParam4(Derived d) {
     return d;  // e8
@@ -218,8 +217,10 @@ ConvertFromBase testRParam6(Derived&& d) {
 // 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; }
+#if __cplusplus >= 201402L
 auto&& testRetRef3(Derived&& d) { return d; }
 decltype(auto) testRetRef4(Derived&& d) { return (d); }
+#endif
 
 // As long as we're checking parentheses, make sure parentheses don't disable the warning.
 Base testParens1() {
@@ -230,14 +231,10 @@ Base testParens1() {
     // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:15}:"std::move(d)"
 }
 ConstructFromDerived testParens2() {
-    Derived d;
-    return (d);  // e18-cxx11
-    // expected-warning at -1{{would have been copied despite being returned by name}}
-    // expected-note at -2{{to avoid copying}}
-    // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:15}:"std::move(d)"
+  Derived d;
+  return (d); // ok
 }
 
-
 // If the target is a catch-handler parameter, do apply the diagnostic.
 void throw_derived();
 Derived testEParam1() {
@@ -339,7 +336,7 @@ void ok_throw8(OnlyCopyable d) { throw d; }
 namespace test_delete {
 struct Base {
   Base();
-  Base(Base &&) = delete;
+  Base(Base &&) = delete; // cxx20-note {{'Base' has been explicitly marked deleted here}}
   Base(Base const &);
 };
 
@@ -347,6 +344,6 @@ struct Derived : public Base {};
 
 Base test_ok() {
   Derived d;
-  return d;
+  return d; // cxx20-error {{call to deleted constructor of 'test_delete::Base'}}
 }
 } // namespace test_delete

diff  --git a/clang/www/cxx_status.html b/clang/www/cxx_status.html
index fc3340ec9d96..1c55ec982289 100755
--- a/clang/www/cxx_status.html
+++ b/clang/www/cxx_status.html
@@ -79,7 +79,7 @@ <h2 id="cxx11">C++11 implementation status</h2>
       <td class="full" align="center">Clang 2.9</td>
       <tr> <!-- from Kona 2019-->
         <td><a href="https://wg21.link/p1825r0">P1825R0</a> (<a href="#dr">DR</a>)</td>
-        <td class="none" align="center">No</td>
+        <td class="unreleased" align="center">Clang 13</td>
       </tr>
     </tr>
     <tr>
@@ -1203,6 +1203,11 @@ <h2 id="cxx20">C++20 implementation status</h2>
       <td><a href="https://wg21.link/p0593r6">P0593R6</a> (<a href="#dr">DR</a>)</td>
       <td class="unreleased" align="center">Clang 11</td>
     </tr>
+    <tr>
+      <td>More implicit moves</td>
+      <td><a href="https://wg21.link/p1825r0">P1825R0</a> (<a href="#dr">DR</a>)</td>
+      <td class="unreleased" align="center">Clang 13</td>
+    </tr>
 </table>
 
 <p>


        


More information about the cfe-commits mailing list