diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td --- a/clang/include/clang/Basic/DiagnosticGroups.td +++ b/clang/include/clang/Basic/DiagnosticGroups.td @@ -499,7 +499,6 @@ 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 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -6370,13 +6370,6 @@ InGroup, 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, 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 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -4724,10 +4724,12 @@ 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 --- a/clang/lib/Sema/SemaCoroutine.cpp +++ b/clang/lib/Sema/SemaCoroutine.cpp @@ -996,7 +996,8 @@ // 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 --- a/clang/lib/Sema/SemaStmt.cpp +++ b/clang/lib/Sema/SemaStmt.cpp @@ -3083,12 +3083,20 @@ // 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()) return false; + if (VD->hasAttr()) + 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 @@ /// 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 @@ FunctionDecl *FD = Step.Function.Function; if (ConvertingConstructorsOnly) { if (isa(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 @@ // 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(FD->getParamDecl(0)->getType())) + if (IsDiagnosticsCheck && + !isa(FD->getParamDecl(0)->getType())) break; } else if (isa(FD)) { // Check that overload resolution selected a conversion operator @@ -3202,73 +3212,38 @@ /// 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 from a - // function of return type unique_ptr, or returning T from a - // function of return type Expected. 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 --- 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 @@ 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 --- /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 --- 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 struct remove_reference { typedef T type; }; -template struct remove_reference { typedef T type; }; -template struct remove_reference { typedef T type; }; +template struct remove_reference { typedef T type; }; +template struct remove_reference { typedef T type; }; template typename remove_reference::type &&move(T &&t); } // namespace foo @@ -76,11 +81,8 @@ // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:14}:"std::move(d2)" } ConstructFromDerived test3() { - Derived d3; - return d3; // e2-cxx11 - // expected-warning@-1{{would have been copied despite being returned by name}} - // expected-note@-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 @@ // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:13}:"std::move(d)" } ConstructFromDerived testParam3(Derived d) { - return d; // e7-cxx11 - // expected-warning@-1{{would have been copied despite being returned by name}} - // expected-note@-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 @@ // 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 @@ // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:15}:"std::move(d)" } ConstructFromDerived testParens2() { - Derived d; - return (d); // e18-cxx11 - // expected-warning@-1{{would have been copied despite being returned by name}} - // expected-note@-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 @@ 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 @@ 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 --- a/clang/www/cxx_status.html +++ b/clang/www/cxx_status.html @@ -79,7 +79,7 @@ Clang 2.9 P1825R0 (DR) - No + Clang 13 @@ -1203,6 +1203,11 @@ P0593R6 (DR) Clang 11 + + More implicit moves + P1825R0 (DR) + Clang 13 +