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 @@ -3428,9 +3428,8 @@ ExprResult PerformMoveOrCopyInitialization(const InitializedEntity &Entity, const VarDecl *NRVOCandidate, - QualType ResultType, - Expr *Value, - bool AllowNRVO = true); + QualType ResultType, Expr *Value, + bool AllowNRVO); bool CanPerformAggregateInitializationForOverloadResolution( const InitializedEntity &Entity, InitListExpr *From); diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp --- a/clang/lib/Sema/Sema.cpp +++ b/clang/lib/Sema/Sema.cpp @@ -1954,7 +1954,7 @@ new (S.Context) DeclRefExpr(S.Context, VD, false, T, VK_LValue, Loc); ExprResult Result = S.PerformMoveOrCopyInitialization( InitializedEntity::InitializeBlock(Loc, T, false), VD, VD->getType(), - VarRef, /*AllowNRVO=*/true); + VarRef, /*AllowNRVO=*/!S.getLangOpts().CPlusPlus2b); if (!Result.isInvalid()) { Result = S.MaybeCreateExprWithCleanups(Result); Expr *Init = Result.getAs(); 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 @@ -999,10 +999,14 @@ const VarDecl *NRVOCandidate = this->getCopyElisionCandidate( E->getType(), E, CES_ImplicitlyMovableCXX20); if (NRVOCandidate) { + QualType T = NRVOCandidate->getType(); + E = ImplicitCastExpr::Create(Context, T.getNonReferenceType(), CK_NoOp, E, + nullptr, VK_XValue, FPOptionsOverride()); InitializedEntity Entity = - InitializedEntity::InitializeResult(Loc, E->getType(), NRVOCandidate); - ExprResult MoveResult = this->PerformMoveOrCopyInitialization( - Entity, NRVOCandidate, E->getType(), E); + InitializedEntity::InitializeResult(Loc, T, NRVOCandidate); + ExprResult MoveResult = + this->PerformMoveOrCopyInitialization(Entity, NRVOCandidate, T, E, + /*AllowNRVO=*/false); if (MoveResult.get()) E = MoveResult.get(); } @@ -1570,7 +1574,8 @@ // Trigger a nice error message. InitializedEntity Entity = InitializedEntity::InitializeResult(Loc, FnRetType, false); - S.PerformMoveOrCopyInitialization(Entity, nullptr, FnRetType, ReturnValue); + S.PerformMoveOrCopyInitialization(Entity, nullptr, FnRetType, ReturnValue, + /*AllowNRVO=*/false); noteMemberDeclaredHere(S, ReturnValue, Fn); return false; } @@ -1587,7 +1592,8 @@ InitializedEntity Entity = InitializedEntity::InitializeVariable(GroDecl); ExprResult Res = S.PerformMoveOrCopyInitialization(Entity, nullptr, GroType, - this->ReturnValue); + this->ReturnValue, + /*AllowNRVO=*/false); if (Res.isInvalid()) return false; diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -851,6 +851,15 @@ Diag(OpLoc, diag::err_omp_simd_region_cannot_use_stmt) << "throw"; if (Ex && !Ex->isTypeDependent()) { + if (getLangOpts().CPlusPlus2b) { + if (VarDecl *VD = getCopyElisionCandidate(QualType{}, Ex, + CES_ImplicitlyMovableCXX20)) { + Ex = ImplicitCastExpr::Create( + Context, VD->getType().getNonReferenceType(), CK_NoOp, Ex, nullptr, + VK_XValue, FPOptionsOverride()); + } + } + QualType ExceptionObjectTy = Context.getExceptionObjectType(Ex->getType()); if (CheckCXXThrowOperand(OpLoc, ExceptionObjectTy, Ex)) return ExprError(); @@ -878,7 +887,8 @@ OpLoc, ExceptionObjectTy, /*NRVO=*/NRVOVariable != nullptr); ExprResult Res = PerformMoveOrCopyInitialization( - Entity, NRVOVariable, QualType(), Ex, IsThrownVarInScope); + Entity, NRVOVariable, QualType(), Ex, + /*AllowNRVO=*/!getLangOpts().CPlusPlus2b && IsThrownVarInScope); if (Res.isInvalid()) return ExprError(); Ex = Res.get(); 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 @@ -3041,6 +3041,8 @@ /// NRVO, or NULL if there is no such candidate. VarDecl *Sema::getCopyElisionCandidate(QualType ReturnType, Expr *E, CopyElisionSemanticsKind CESK) { + if (auto ImplCastExpr = dyn_cast(E)) + E = ImplCastExpr->getSubExpr(); // - in a return statement in a function [where] ... // ... the expression is the name of a non-volatile automatic object ... DeclRefExpr *DR = dyn_cast(E->IgnoreParens()); @@ -3086,15 +3088,14 @@ 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())) + QualType VDNonRefType = VDType; + if (VDType->isReferenceType()) { + if (!(CESK & CES_AllowRValueReferenceType) || + !VDType->isRValueReferenceType()) + return false; + VDNonRefType = VDType.getNonReferenceType(); + } + if (!VDNonRefType->isObjectType() || VDNonRefType.isVolatileQualified()) return false; if (CESK & CES_AllowDifferentTypes) @@ -3102,8 +3103,8 @@ // Variables with higher required alignment than their type's ABI // alignment cannot use NRVO. - if (!VD->getType()->isDependentType() && VD->hasAttr() && - Context.getDeclAlign(VD) > Context.getTypeAlignInChars(VD->getType())) + if (!VDNonRefType->isDependentType() && VD->hasAttr() && + Context.getDeclAlign(VD) > Context.getTypeAlignInChars(VDNonRefType)) return false; return true; @@ -3222,14 +3223,13 @@ bool NeedSecondOverloadResolution = true; if (AllowNRVO) { - CopyElisionSemanticsKind CESK = CES_Strict; - if (getLangOpts().CPlusPlus20) { - CESK = CES_ImplicitlyMovableCXX20; - } else if (getLangOpts().CPlusPlus11) { - CESK = CES_ImplicitlyMovableCXX11CXX14CXX17; - } - if (!NRVOCandidate) { + CopyElisionSemanticsKind CESK = CES_Strict; + if (getLangOpts().CPlusPlus20) { + CESK = CES_ImplicitlyMovableCXX20; + } else if (getLangOpts().CPlusPlus11) { + CESK = CES_ImplicitlyMovableCXX11CXX14CXX17; + } NRVOCandidate = getCopyElisionCandidate(ResultType, Value, CESK); } @@ -3432,8 +3432,9 @@ InitializedEntity Entity = InitializedEntity::InitializeResult(ReturnLoc, FnRetType, NRVOCandidate != nullptr); - ExprResult Res = PerformMoveOrCopyInitialization(Entity, NRVOCandidate, - FnRetType, RetValExp); + ExprResult Res = PerformMoveOrCopyInitialization( + Entity, NRVOCandidate, FnRetType, RetValExp, + /*AllowNRVO=*/!getLangOpts().CPlusPlus2b); if (Res.isInvalid()) { // FIXME: Cleanup temporaries here, anyway? return StmtError(); @@ -3650,6 +3651,15 @@ if (RetValExp && DiagnoseUnexpandedParameterPack(RetValExp)) return StmtError(); + if (getLangOpts().CPlusPlus2b && RetValExp) { + if (VarDecl *VD = getCopyElisionCandidate(QualType{}, RetValExp, + CES_ImplicitlyMovableCXX20)) { + RetValExp = ImplicitCastExpr::Create( + Context, VD->getType().getNonReferenceType(), CK_NoOp, RetValExp, + nullptr, VK_XValue, FPOptionsOverride()); + } + } + if (isa(getCurFunction())) return ActOnCapScopeReturnStmt(ReturnLoc, RetValExp); @@ -3846,8 +3856,9 @@ InitializedEntity Entity = InitializedEntity::InitializeResult(ReturnLoc, RetType, NRVOCandidate != nullptr); - ExprResult Res = PerformMoveOrCopyInitialization(Entity, NRVOCandidate, - RetType, RetValExp); + ExprResult Res = PerformMoveOrCopyInitialization( + Entity, NRVOCandidate, RetType, RetValExp, + /*AllowNRVO=*/!getLangOpts().CPlusPlus2b); if (Res.isInvalid()) { // FIXME: Clean up temporaries here anyway? return StmtError(); diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp --- a/clang/lib/Sema/SemaType.cpp +++ b/clang/lib/Sema/SemaType.cpp @@ -8839,6 +8839,10 @@ if (E->isTypeDependent()) return S.Context.DependentTy; + Expr *IDExpr = E; + if (auto *ImplCastExpr = dyn_cast(E)) + IDExpr = ImplCastExpr->getSubExpr(); + // C++11 [dcl.type.simple]p4: // The type denoted by decltype(e) is defined as follows: @@ -8849,7 +8853,7 @@ // Note that this does not pick up the implicit 'const' for a template // parameter object. This rule makes no difference before C++20 so we apply // it unconditionally. - if (const auto *SNTTPE = dyn_cast(E)) + if (const auto *SNTTPE = dyn_cast(IDExpr)) return SNTTPE->getParameterType(S.Context); // - if e is an unparenthesized id-expression or an unparenthesized class @@ -8858,21 +8862,22 @@ // functions, the program is ill-formed; // // We apply the same rules for Objective-C ivar and property references. - if (const DeclRefExpr *DRE = dyn_cast(E)) { + if (const DeclRefExpr *DRE = dyn_cast(IDExpr)) { const ValueDecl *VD = DRE->getDecl(); if (auto *TPO = dyn_cast(VD)) return TPO->getType().getUnqualifiedType(); return VD->getType(); - } else if (const MemberExpr *ME = dyn_cast(E)) { + } else if (const MemberExpr *ME = dyn_cast(IDExpr)) { if (const ValueDecl *VD = ME->getMemberDecl()) if (isa(VD) || isa(VD)) return VD->getType(); - } else if (const ObjCIvarRefExpr *IR = dyn_cast(E)) { + } else if (const ObjCIvarRefExpr *IR = dyn_cast(IDExpr)) { return IR->getDecl()->getType(); - } else if (const ObjCPropertyRefExpr *PR = dyn_cast(E)) { + } else if (const ObjCPropertyRefExpr *PR = + dyn_cast(IDExpr)) { if (PR->isExplicitProperty()) return PR->getExplicitProperty()->getType(); - } else if (auto *PE = dyn_cast(E)) { + } else if (auto *PE = dyn_cast(IDExpr)) { return PE->getType(); } @@ -8885,8 +8890,8 @@ // entity. using namespace sema; if (S.getCurLambda()) { - if (isa(E)) { - if (DeclRefExpr *DRE = dyn_cast(E->IgnoreParens())) { + if (isa(IDExpr)) { + if (DeclRefExpr *DRE = dyn_cast(IDExpr->IgnoreParens())) { if (VarDecl *Var = dyn_cast(DRE->getDecl())) { QualType T = S.getCapturedDeclRefType(Var, DRE->getLocation()); if (!T.isNull()) 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,8 @@ -// 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 +// RUN: %clang_cc1 -std=c++2b -fsyntax-only -fcxx-exceptions -verify=expected,cxx20_2b,cxx2b %s +// RUN: %clang_cc1 -std=c++20 -fsyntax-only -fcxx-exceptions -verify=expected,cxx11_14_17_20,cxx20_2b %s +// RUN: %clang_cc1 -std=c++17 -fsyntax-only -fcxx-exceptions -verify=expected,cxx11_14_17_20,cxx11_14_17 %s +// RUN: %clang_cc1 -std=c++14 -fsyntax-only -fcxx-exceptions -verify=expected,cxx11_14_17_20,cxx11_14_17 %s +// RUN: %clang_cc1 -std=c++11 -fsyntax-only -fcxx-exceptions -verify=expected,cxx11_14_17_20,cxx11_14_17 %s namespace test_delete_function { struct A1 { @@ -72,20 +73,20 @@ struct B1 { B1(const B1 &); - B1(B1 &&) = delete; // cxx20-note {{'B1' has been explicitly marked deleted here}} + B1(B1 &&) = delete; // cxx20_2b-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'}} + return b; // cxx20_2b-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(B2 &&); // cxx20_2b-note {{declared private here}} }; B2 test4(B2 &&b) { - return b; // cxx20-error {{calling a private constructor of class 'test_implicitly_movable_rvalue_ref::B2'}} + return b; // cxx20_2b-error {{calling a private constructor of class 'test_implicitly_movable_rvalue_ref::B2'}} } } // namespace test_implicitly_movable_rvalue_ref @@ -96,13 +97,13 @@ struct A1 { A1(const A1 &); - A1(A1 &&) = delete; // cxx20-note {{'A1' has been explicitly marked deleted here}} + A1(A1 &&) = delete; // cxx20_2b-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'}} + throw a; // cxx20_2b-error {{call to deleted constructor of 'test_throw_parameter::A1'}} } } @@ -110,13 +111,13 @@ A2(const A2 &); private: - A2(A2 &&); // cxx20-note {{declared private here}} + A2(A2 &&); // cxx20_2b-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'}} + throw a; // cxx20_2b-error {{calling a private constructor of class 'test_throw_parameter::A2'}} } } } // namespace test_throw_parameter @@ -148,22 +149,22 @@ struct B1 { operator C() const &; - operator C() && = delete; // cxx20-note {{'operator C' has been explicitly marked deleted here}} + operator C() && = delete; // cxx20_2b-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}} + return b; // cxx20_2b-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}} + operator C() &&; // cxx20_2b-note {{declared private here}} }; C test4() { B2 b; - return b; // cxx20-error {{'operator C' is a private member of 'test_non_ctor_conversion::B2'}} + return b; // cxx20_2b-error {{'operator C' is a private member of 'test_non_ctor_conversion::B2'}} } } // namespace test_non_ctor_conversion @@ -184,7 +185,7 @@ 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(B1); // cxx20_2b-note 2 {{passing argument to parameter here}} NeedValue(B2); }; @@ -243,26 +244,26 @@ struct B1 { B1(); B1(const B1 &); - B1(B1 &&) = delete; // cxx20-note 3 {{'B1' has been explicitly marked deleted here}} + B1(B1 &&) = delete; // cxx20_2b-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'}} + return b; // cxx20_2b-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'}} + return b; // cxx20_2b-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'}} + return b; // cxx20_2b-error {{call to deleted constructor of 'test_ctor_param_rvalue_ref::B1'}} } struct B2 { @@ -270,25 +271,68 @@ B2(const B2 &); private: - B2(B2 &&); // cxx20-note 3 {{declared private here}} + B2(B2 &&); // cxx20_2b-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'}} + return b; // cxx20_2b-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'}} + return b; // cxx20_2b-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'}} + return b; // cxx20_2b-error {{calling a private constructor of class 'test_ctor_param_rvalue_ref::B2'}} } } // namespace test_ctor_param_rvalue_ref +namespace test_simpler_implicit_move { +struct CopyOnly { + CopyOnly(); // cxx2b-note {{candidate constructor not viable: requires 0 arguments, but 1 was provided}} + CopyOnly(CopyOnly&); // cxx2b-note {{candidate constructor not viable: expects an lvalue for 1st argument}} +}; +struct MoveOnly { + MoveOnly(); + MoveOnly(MoveOnly&&); // cxx11_14_17_20-note {{copy constructor is implicitly deleted}} +}; +MoveOnly&& rref(); + +MoveOnly&& test_1(MoveOnly&& w) { + return w; // cxx11_14_17_20-error {{cannot bind to lvalue of type}} +} + +CopyOnly test_2(bool b) { + static CopyOnly w1; + CopyOnly w2; + if (b) { + return w1; + } else { + return w2; // cxx2b-error {{no matching constructor for initialization}} + } +} + +template T&& test_3a(T&& x) { return x; } // cxx11_14_17_20-error {{cannot bind to lvalue of type}} +void test_3b(MoveOnly w) { + MoveOnly& r = test_3a(w); + MoveOnly&& rr = test_3a(static_cast(w)); // cxx11_14_17_20-note {{in instantiation of function template specialization}} +} + +MoveOnly&& test_4() { + MoveOnly &&x = rref(); + return x; // cxx11_14_17_20-error {{cannot bind to lvalue of type}} +} + +void test_5() { + MoveOnly x; + try { + throw x; // cxx11_14_17_20-error {{call to implicitly-deleted copy constructor}} + } catch(...) {} +} +} // namespace test_simpler_implicit_move diff --git a/clang/test/CXX/special/class.copy/p33-0x.cpp b/clang/test/CXX/special/class.copy/p33-0x.cpp --- a/clang/test/CXX/special/class.copy/p33-0x.cpp +++ b/clang/test/CXX/special/class.copy/p33-0x.cpp @@ -1,4 +1,6 @@ -// RUN: %clang_cc1 -fcxx-exceptions -fexceptions -std=c++11 -fsyntax-only -verify %s +// RUN: %clang_cc1 -fcxx-exceptions -fexceptions -std=c++2b -fsyntax-only -verify=expected,cxx2b %s +// RUN: %clang_cc1 -fcxx-exceptions -fexceptions -std=c++11 -fsyntax-only -verify=expected,cxx11 %s +// cxx2b-no-diagnostics class X { X(const X&); @@ -27,7 +29,7 @@ struct X { X(); X(X&&); - X(const X&) = delete; // expected-note 2{{'X' has been explicitly marked deleted here}} + X(const X&) = delete; // cxx11-note 2{{'X' has been explicitly marked deleted here}} }; void f(int i) { @@ -36,7 +38,7 @@ X x2; if (i) throw x2; // okay - throw x; // expected-error{{call to deleted constructor of 'PR10142::X'}} + throw x; // cxx11-error{{call to deleted constructor of 'PR10142::X'}} } catch (...) { } } @@ -48,10 +50,10 @@ T x2; if (i) throw x2; // okay - throw x; // expected-error{{call to deleted constructor of 'PR10142::X'}} + throw x; // cxx11-error{{call to deleted constructor of 'PR10142::X'}} } catch (...) { } } - template void f2(int); // expected-note{{in instantiation of function template specialization 'PR10142::f2' requested here}} + template void f2(int); // cxx11-note{{in instantiation of function template specialization 'PR10142::f2' requested here}} }