[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