diff --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h --- a/clang/include/clang/AST/Expr.h +++ b/clang/include/clang/AST/Expr.h @@ -272,6 +272,7 @@ bool isLValue() const { return getValueKind() == VK_LValue; } bool isRValue() const { return getValueKind() == VK_RValue; } bool isXValue() const { return getValueKind() == VK_XValue; } + bool isYValue() const { return getValueKind() == VK_YValue; } bool isGLValue() const { return getValueKind() != VK_RValue; } enum LValueClassification { 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 @@ -6378,7 +6378,8 @@ def note_remove_move : Note<"remove std::move call here">; def warn_return_std_move : Warning< - "local variable %0 will be copied despite being %select{returned|thrown}1 by name">, + "local variable %0 will be copied despite being " + "%select{returned|co_returned|thrown}1 by name">, InGroup, DefaultIgnore; def note_add_std_move : Note< "call 'std::move' explicitly to avoid copying">; diff --git a/clang/include/clang/Basic/Specifiers.h b/clang/include/clang/Basic/Specifiers.h --- a/clang/include/clang/Basic/Specifiers.h +++ b/clang/include/clang/Basic/Specifiers.h @@ -116,7 +116,11 @@ /// An x-value expression is a reference to an object with /// independent storage but which can be "moved", i.e. /// efficiently cannibalized for its resources. - VK_XValue + VK_XValue, + + // Is just like an x-value, but will bind to a + // l-value reference, at a lower rank. + VK_YValue, }; /// A further classification of the kind of object referenced by an 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 @@ -4733,24 +4733,19 @@ enum Status : uint8_t { None, MoveEligible, MoveEligibleAndCopyElidable }; Status S; - bool isMoveEligible() const { return S != None; }; + bool isMoveEligible() const { return S >= MoveEligible; }; bool isCopyElidable() const { return S == MoveEligibleAndCopyElidable; } }; - NamedReturnInfo getNamedReturnInfo(Expr *&E, bool ForceCXX2b = false); - NamedReturnInfo getNamedReturnInfo(const VarDecl *VD, - bool ForceCXX20 = false); - const VarDecl *getCopyElisionCandidate(NamedReturnInfo &Info, - QualType ReturnType); - - ExprResult PerformMoveOrCopyInitialization(const InitializedEntity &Entity, - const NamedReturnInfo &NRInfo, - Expr *Value); + NamedReturnInfo getNamedReturnInfo(const VarDecl *VD, QualType ReturnType, + bool Parenthesized); + enum class NRVOExprContext { Returned, Co_Returned, Thrown }; + const VarDecl *getCopyElisionCandidate(Expr *&E, NRVOExprContext C, + QualType ReturnType = QualType()); StmtResult ActOnReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp, Scope *CurScope); StmtResult BuildReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp); - StmtResult ActOnCapScopeReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp, - NamedReturnInfo &NRInfo); + StmtResult ActOnCapScopeReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp); StmtResult ActOnGCCAsmStmt(SourceLocation AsmLoc, bool IsSimple, bool IsVolatile, unsigned NumOutputs, diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -5446,6 +5446,7 @@ return getRValueReferenceType(T); // - otherwise, if e is an lvalue, decltype(e) is T&, where T is the // type of e; + case VK_YValue: case VK_LValue: return getLValueReferenceType(T); // - otherwise, decltype(e) is the type of e. diff --git a/clang/lib/AST/ExprClassification.cpp b/clang/lib/AST/ExprClassification.cpp --- a/clang/lib/AST/ExprClassification.cpp +++ b/clang/lib/AST/ExprClassification.cpp @@ -57,7 +57,7 @@ assert(isLValue()); break; case Cl::CL_XValue: - assert(isXValue()); + assert(isXValue() || isYValue()); break; case Cl::CL_Function: case Cl::CL_Void: @@ -99,6 +99,7 @@ return Lang.CPlusPlus ? ClassifyTemporary(E->getType()) : Cl::CL_PRValue; case VK_LValue: return Cl::CL_LValue; + case VK_YValue: case VK_XValue: return Cl::CL_XValue; } diff --git a/clang/lib/AST/JSONNodeDumper.cpp b/clang/lib/AST/JSONNodeDumper.cpp --- a/clang/lib/AST/JSONNodeDumper.cpp +++ b/clang/lib/AST/JSONNodeDumper.cpp @@ -59,6 +59,9 @@ switch (E->getValueKind()) { case VK_LValue: Category = "lvalue"; break; case VK_XValue: Category = "xvalue"; break; + case VK_YValue: + Category = "yvalue"; + break; case VK_RValue: Category = "rvalue"; break; } JOS.attribute("valueCategory", Category); diff --git a/clang/lib/AST/TextNodeDumper.cpp b/clang/lib/AST/TextNodeDumper.cpp --- a/clang/lib/AST/TextNodeDumper.cpp +++ b/clang/lib/AST/TextNodeDumper.cpp @@ -152,6 +152,9 @@ case VK_XValue: OS << " xvalue"; break; + case VK_YValue: + OS << " yvalue"; + break; } } diff --git a/clang/lib/CodeGen/CGDecl.cpp b/clang/lib/CodeGen/CGDecl.cpp --- a/clang/lib/CodeGen/CGDecl.cpp +++ b/clang/lib/CodeGen/CGDecl.cpp @@ -711,7 +711,7 @@ if (srcExpr->isLValue()) { CGF.EmitARCCopyWeak(destLV.getAddress(CGF), srcAddr); } else { - assert(srcExpr->isXValue()); + assert(srcExpr->isXValue() || srcExpr->isYValue()); CGF.EmitARCMoveWeak(destLV.getAddress(CGF), srcAddr); } return true; diff --git a/clang/lib/CodeGen/CGObjC.cpp b/clang/lib/CodeGen/CGObjC.cpp --- a/clang/lib/CodeGen/CGObjC.cpp +++ b/clang/lib/CodeGen/CGObjC.cpp @@ -2887,8 +2887,7 @@ // If we're loading retained from a __strong xvalue, we can avoid // an extra retain/release pair by zeroing out the source of this // "move" operation. - if (e->isXValue() && - !type.isConstQualified() && + if ((e->isXValue() || e->isYValue()) && !type.isConstQualified() && type.getObjCLifetime() == Qualifiers::OCL_Strong) { // Emit the lvalue. LValue lv = CGF.EmitLValue(e); diff --git a/clang/lib/Sema/SemaCodeComplete.cpp b/clang/lib/Sema/SemaCodeComplete.cpp --- a/clang/lib/Sema/SemaCodeComplete.cpp +++ b/clang/lib/Sema/SemaCodeComplete.cpp @@ -1241,7 +1241,7 @@ // For xvalue objects, we prefer the rvalue overload even if we have to // add qualifiers (which is rare, because const&& is rare). - if (ObjectKind == clang::VK_XValue) + if (ObjectKind == clang::VK_XValue || ObjectKind == clang::VK_YValue) return CandidateRef == RQ_RValue ? OverloadCompare::Dominates : OverloadCompare::Dominated; } 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 @@ -997,7 +997,7 @@ VarDecl *Promise = FSI->CoroutinePromise; ExprResult PC; if (E && (isa(E) || !E->getType()->isVoidType())) { - getNamedReturnInfo(E, /*ForceCXX2b=*/true); + getCopyElisionCandidate(E, NRVOExprContext::Co_Returned); PC = buildPromiseCall(*this, Promise, Loc, "return_value", E); } else { E = MakeFullDiscardedValueExpr(E).get(); 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 @@ -866,17 +866,18 @@ // operation from the operand to the exception object (15.1) can be // omitted by constructing the automatic object directly into the // exception object - NamedReturnInfo NRInfo = - IsThrownVarInScope ? getNamedReturnInfo(Ex) : NamedReturnInfo(); + bool CopyElidable = + IsThrownVarInScope && + getCopyElisionCandidate(Ex, NRVOExprContext::Thrown) != nullptr; QualType ExceptionObjectTy = Context.getExceptionObjectType(Ex->getType()); if (CheckCXXThrowOperand(OpLoc, ExceptionObjectTy, Ex)) return ExprError(); - InitializedEntity Entity = InitializedEntity::InitializeException( - OpLoc, ExceptionObjectTy, - /*NRVO=*/NRInfo.isCopyElidable()); - ExprResult Res = PerformMoveOrCopyInitialization(Entity, NRInfo, Ex); + InitializedEntity Entity = + InitializedEntity::InitializeException(OpLoc, ExceptionObjectTy, + /*NRVO=*/CopyElidable); + ExprResult Res = PerformCopyInitialization(Entity, SourceLocation(), Ex); if (Res.isInvalid()) return ExprError(); Ex = Res.get(); diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp --- a/clang/lib/Sema/SemaInit.cpp +++ b/clang/lib/Sema/SemaInit.cpp @@ -3585,6 +3585,7 @@ Step S; switch (VK) { case VK_RValue: S.Kind = SK_CastDerivedToBaseRValue; break; + case VK_YValue: case VK_XValue: S.Kind = SK_CastDerivedToBaseXValue; break; case VK_LValue: S.Kind = SK_CastDerivedToBaseLValue; break; } @@ -3636,6 +3637,7 @@ case VK_RValue: S.Kind = SK_QualificationConversionRValue; break; + case VK_YValue: case VK_XValue: S.Kind = SK_QualificationConversionXValue; break; @@ -4806,7 +4808,8 @@ OverloadingResult ConvOvlResult = OR_Success; bool T1Function = T1->isFunctionType(); if (isLValueRef || T1Function) { - if (InitCategory.isLValue() && !isNonReferenceableGLValue(Initializer) && + if ((InitCategory.isLValue() || Initializer->isYValue()) && + !isNonReferenceableGLValue(Initializer) && (RefRelationship == Sema::Ref_Compatible || (Kind.isCStyleOrFunctionalCast() && RefRelationship == Sema::Ref_Related))) { diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -4768,10 +4768,11 @@ // FIXME: As a speculative fix to a defect introduced by CWG2352, we rank // a reference binding that performs a non-top-level qualification // conversion as a qualification conversion, not as an identity conversion. - ICS.Standard.Third = (RefConv & - Sema::ReferenceConversions::NestedQualification) - ? ICK_Qualification - : ICK_Identity; + ICS.Standard.Third = + (RefConv & Sema::ReferenceConversions::NestedQualification) || + (Init->isYValue() && !isRValRef) + ? ICK_Qualification + : ICK_Identity; ICS.Standard.setFromType(T2); ICS.Standard.setToType(0, T2); ICS.Standard.setToType(1, T1); @@ -4830,15 +4831,16 @@ AllowExplicit)) return ICS; } - } - // -- Otherwise, the reference shall be an lvalue reference to a - // non-volatile const type (i.e., cv1 shall be const), or the reference - // shall be an rvalue reference. - if (!isRValRef && (!T1.isConstQualified() || T1.isVolatileQualified())) { - if (InitCategory.isRValue() && RefRelationship != Sema::Ref_Incompatible) - ICS.setBad(BadConversionSequence::lvalue_ref_to_rvalue, Init, DeclType); - return ICS; + // -- Otherwise, the reference shall be an lvalue reference to a + // non-volatile const type (i.e., cv1 shall be const), or the + // reference shall be an rvalue reference. + if (!Init->isYValue() && + (!T1.isConstQualified() || T1.isVolatileQualified())) { + if (InitCategory.isRValue() && RefRelationship != Sema::Ref_Incompatible) + ICS.setBad(BadConversionSequence::lvalue_ref_to_rvalue, Init, DeclType); + return ICS; + } } // -- If the initializer expression 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 @@ -3050,37 +3050,49 @@ /// \returns An aggregate which contains the Candidate and isMoveEligible /// and isCopyElidable methods. If Candidate is non-null, it means /// isMoveEligible() would be true under the most permissive language standard. -Sema::NamedReturnInfo Sema::getNamedReturnInfo(Expr *&E, bool ForceCXX2b) { +const VarDecl *Sema::getCopyElisionCandidate(Expr *&E, NRVOExprContext C, + QualType ReturnType) { if (!E) - return NamedReturnInfo(); + return nullptr; // - in a return statement in a function [where] ... // ... the expression is the name of a non-volatile automatic object ... const auto *DR = dyn_cast(E->IgnoreParens()); if (!DR || DR->refersToEnclosingVariableOrCapture()) - return NamedReturnInfo(); + return nullptr; const auto *VD = dyn_cast(DR->getDecl()); if (!VD) - return NamedReturnInfo(); - NamedReturnInfo Res = getNamedReturnInfo(VD, /*ForceCXX20=*/ForceCXX2b); - if (Res.Candidate && !E->isXValue() && - (ForceCXX2b || getLangOpts().CPlusPlus2b)) { + return nullptr; + + NamedReturnInfo Info = getNamedReturnInfo(VD, ReturnType, isa(E)); + if (Info.isMoveEligible() && (E->isLValue() || E->isRValue())) { + ExprValueKind CastToKind = + getLangOpts().CPlusPlus2b ? VK_XValue : VK_YValue; E = ImplicitCastExpr::Create(Context, VD->getType().getNonReferenceType(), - CK_NoOp, E, nullptr, VK_XValue, + CK_NoOp, E, nullptr, CastToKind, FPOptionsOverride()); + return Info.isCopyElidable() ? Info.Candidate : nullptr; } - return Res; -} -/// Updates the status in the given NamedReturnInfo object to disallow -/// copy elision, and optionally also implicit move. -/// -/// \param Info The NamedReturnInfo object to update. -/// -/// \param CanMove If true, disallow only copy elision. -/// If false, also disallow implcit move. -static void disallowNRVO(Sema::NamedReturnInfo &Info, bool CanMove) { - Info.S = std::min(Info.S, CanMove ? Sema::NamedReturnInfo::MoveEligible - : Sema::NamedReturnInfo::None); + if (Info.Candidate && !getDiagnostics().isIgnored(diag::warn_return_std_move, + E->getExprLoc())) { + QualType QT = Info.Candidate->getType(); + if (QT.getNonReferenceType().getUnqualifiedType().isTriviallyCopyableType( + Context)) { + // Adding 'std::move' around a trivially copyable variable is probably + // pointless. Don't suggest it. + } else { + Diag(E->getExprLoc(), diag::warn_return_std_move) + << E->getSourceRange() << Info.Candidate->getDeclName() << int(C); + + SmallString<32> Str; + Str += "std::move("; + Str += Info.Candidate->getDeclName().getAsString(); + Str += ")"; + Diag(E->getExprLoc(), diag::note_add_std_move) + << FixItHint::CreateReplacement(E->getSourceRange(), Str); + } + } + return nullptr; } /// Determine whether the given NRVO candidate variable is move-eligible or @@ -3095,22 +3107,28 @@ /// and isCopyElidable methods. If Candidate is non-null, it means /// isMoveEligible() would be true under the most permissive language standard. Sema::NamedReturnInfo Sema::getNamedReturnInfo(const VarDecl *VD, - bool ForceCXX20) { - bool hasCXX11 = getLangOpts().CPlusPlus11 || ForceCXX20; - bool hasCXX20 = getLangOpts().CPlusPlus20 || ForceCXX20; + QualType ReturnType, + bool Parenthesized) { NamedReturnInfo Info{VD, NamedReturnInfo::MoveEligibleAndCopyElidable}; + // Updates the status in Info to disallow copy elision. + // Optionally also disallows implicit move. + auto disallowNRVO = [&Info](bool CanMove) { + Info.S = std::min(Info.S, CanMove ? NamedReturnInfo::MoveEligible + : NamedReturnInfo::None); + }; + // C++20 [class.copy.elision]p3: // - in a return statement in a function with ... // (other than a function ... parameter) if (VD->getKind() == Decl::ParmVar) - disallowNRVO(Info, hasCXX11); + disallowNRVO(getLangOpts().CPlusPlus11); else if (VD->getKind() != Decl::Var) return NamedReturnInfo(); // (other than ... a catch-clause parameter) if (VD->isExceptionVariable()) - disallowNRVO(Info, hasCXX20); + disallowNRVO(getLangOpts().CPlusPlus20); // ...automatic... if (!VD->hasLocalStorage()) @@ -3135,7 +3153,7 @@ if (VDReferencedType.isVolatileQualified() || !VDReferencedType->isObjectType()) return NamedReturnInfo(); - disallowNRVO(Info, hasCXX20); + disallowNRVO(getLangOpts().CPlusPlus20); } else { return NamedReturnInfo(); } @@ -3144,204 +3162,48 @@ // alignment cannot use NRVO. if (!VDType->isDependentType() && VD->hasAttr() && Context.getDeclAlign(VD) > Context.getTypeAlignInChars(VDType)) - disallowNRVO(Info, hasCXX11); - - return Info; -} - -/// Updates given NamedReturnInfo's move-eligible and -/// copy-elidable statuses, considering the function -/// return type criteria as applicable to return statements. -/// -/// \param Info The NamedReturnInfo object to update. -/// -/// \param ReturnType This is the return type of the function. -/// \returns The copy elision candidate, in case the initial return expression -/// was copy elidable, or nullptr otherwise. -const VarDecl *Sema::getCopyElisionCandidate(NamedReturnInfo &Info, - QualType ReturnType) { - if (!Info.Candidate) - return nullptr; - - auto invalidNRVO = [&] { - Info = NamedReturnInfo(); - return nullptr; - }; - - // If we got a non-deduced auto ReturnType, we are in a dependent context and - // there is no point in allowing copy elision since we won't have it deduced - // by the point the VardDecl is instantiated, which is the last chance we have - // of deciding if the candidate is really copy elidable. - if ((ReturnType->getTypeClass() == Type::TypeClass::Auto && - ReturnType->isCanonicalUnqualified()) || - ReturnType->isSpecificBuiltinType(BuiltinType::Dependent)) - return invalidNRVO(); - - if (!ReturnType->isDependentType()) { - // - in a return statement in a function with ... - // ... a class return type ... - if (!ReturnType->isRecordType()) - return invalidNRVO(); - - QualType VDType = Info.Candidate->getType(); - // ... the same cv-unqualified type as the function return type ... - // When considering moving this expression out, allow dissimilar types. - if (!VDType->isDependentType() && - !Context.hasSameUnqualifiedType(ReturnType, VDType)) - disallowNRVO(Info, getLangOpts().CPlusPlus11); - } - return Info.isCopyElidable() ? Info.Candidate : nullptr; -} - -/// Try to perform the initialization of a potentially-movable value, -/// which is the operand to a return or throw statement. -/// -/// 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.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. -/// If move-initialization is not possible, such that we must fall back to -/// treating the operand as an lvalue, we will leave Res in its original -/// invalid state. -/// -/// \returns Whether we need to do the second overload resolution. If the first -/// overload resolution fails, or if the first overload resolution succeeds but -/// the selected constructor/operator doesn't match the additional criteria, we -/// need to do the second overload resolution. -static bool TryMoveInitialization(Sema &S, const InitializedEntity &Entity, - const VarDecl *NRVOCandidate, Expr *&Value, - bool ConvertingConstructorsOnly, - bool IsDiagnosticsCheck, ExprResult &Res) { - ImplicitCastExpr AsRvalue(ImplicitCastExpr::OnStack, Value->getType(), - CK_NoOp, Value, VK_XValue, FPOptionsOverride()); - - Expr *InitExpr = &AsRvalue; - - InitializationKind Kind = InitializationKind::CreateCopy( - Value->getBeginLoc(), Value->getBeginLoc()); - - InitializationSequence Seq(S, Entity, Kind, InitExpr); - - bool NeedSecondOverloadResolution = true; - if (!Seq && - (IsDiagnosticsCheck || Seq.getFailedOverloadResult() != OR_Deleted)) { - return NeedSecondOverloadResolution; - } - - for (const InitializationSequence::Step &Step : Seq.steps()) { - if (Step.Kind != InitializationSequence::SK_ConstructorInitialization && - Step.Kind != InitializationSequence::SK_UserConversion) - continue; - - FunctionDecl *FD = Step.Function.Function; - if (ConvertingConstructorsOnly) { - if (isa(FD)) { - // C++11 [class.copy]p32: - // C++14 [class.copy]p32: - // 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. - const RValueReferenceType *RRefType = - FD->getParamDecl(0)->getType()->getAs(); - if (!RRefType) - break; - if (!S.Context.hasSameUnqualifiedType(RRefType->getPointeeType(), - NRVOCandidate->getType())) - break; - } else { - continue; + disallowNRVO(getLangOpts().CPlusPlus11); + + if (!ReturnType.isNull()) { + if (ReturnType->isDependentType()) { + if ((ReturnType->getTypeClass() == Type::TypeClass::Auto && + ReturnType->isCanonicalUnqualified()) || + ReturnType->isSpecificBuiltinType(BuiltinType::Dependent)) { + return NamedReturnInfo(); } } else { - if (isa(FD)) { - // 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 (IsDiagnosticsCheck && - !isa(FD->getParamDecl(0)->getType())) - break; - } else if (isa(FD)) { - // Check that overload resolution selected a conversion operator - // taking an rvalue reference. - if (cast(FD)->getRefQualifier() != RQ_RValue) - break; - } else { - continue; + if (ReturnType->isSpecificBuiltinType(BuiltinType::Dependent)) { + ReturnType = Context.getAutoDeductType(); } - } - - NeedSecondOverloadResolution = false; - // Promote "AsRvalue" to the heap, since we now need this - // expression node to persist. - Value = - ImplicitCastExpr::Create(S.Context, Value->getType(), CK_NoOp, Value, - nullptr, VK_XValue, FPOptionsOverride()); - - // Complete type-checking the initialization of the return type - // using the constructor we found. - Res = Seq.Perform(S, Entity, Kind, Value); - } - - return NeedSecondOverloadResolution; -} - -/// Perform the initialization of a potentially-movable value, which -/// is the result of return value. -/// -/// This routine implements C++20 [class.copy.elision]p3, which attempts to -/// treat returned lvalues as rvalues in certain cases (to prefer move -/// construction), then falls back to treating them as lvalues if that failed. -ExprResult -Sema::PerformMoveOrCopyInitialization(const InitializedEntity &Entity, - const NamedReturnInfo &NRInfo, - Expr *Value) { - - if (NRInfo.Candidate && !getLangOpts().CPlusPlus2b) { - if (NRInfo.isMoveEligible()) { - ExprResult Res; - if (!TryMoveInitialization(*this, Entity, NRInfo.Candidate, Value, - !getLangOpts().CPlusPlus20, false, Res)) - return Res; - } - if (!getDiagnostics().isIgnored(diag::warn_return_std_move, - Value->getExprLoc())) { - QualType QT = NRInfo.Candidate->getType(); - if (QT.getNonReferenceType().getUnqualifiedType().isTriviallyCopyableType( - Context)) { - // Adding 'std::move' around a trivially copyable variable is probably - // pointless. Don't suggest it. - } else { - ExprResult FakeRes = ExprError(); - Expr *FakeValue = Value; - TryMoveInitialization(*this, Entity, NRInfo.Candidate, FakeValue, false, - true, FakeRes); - if (!FakeRes.isInvalid()) { - bool IsThrow = (Entity.getKind() == InitializedEntity::EK_Exception); - SmallString<32> Str; - Str += "std::move("; - Str += NRInfo.Candidate->getDeclName().getAsString(); - Str += ")"; - Diag(Value->getExprLoc(), diag::warn_return_std_move) - << Value->getSourceRange() << NRInfo.Candidate->getDeclName() - << IsThrow; - Diag(Value->getExprLoc(), diag::note_add_std_move) - << FixItHint::CreateReplacement(Value->getSourceRange(), Str); + if (ReturnType->isCanonicalUnqualified()) { + if (AutoType *AT = ReturnType->getContainedAutoType()) { + QualType T = VDType.getNonReferenceType().getUnqualifiedType(); + if (AT->isDecltypeAuto()) + ReturnType = Parenthesized ? Context.getLValueReferenceType(T) : T; + else if (ReturnType->isLValueReferenceType()) + ReturnType = Context.getLValueReferenceType(T); + else if (ReturnType->isRValueReferenceType()) + ReturnType = Context.getRValueReferenceType(T); + else + ReturnType = T; } } + + // - in a return statement in a function with ... + // ... a class return type ... + if (!ReturnType->isRecordType()) + disallowNRVO(getLangOpts().CPlusPlus2b); + + // ... the same cv-unqualified type as the function return type ... + // When considering moving this expression out, allow dissimilar + // types. + if (!VDType->isDependentType() && + !Context.hasSameUnqualifiedType(ReturnType, VDType)) + disallowNRVO(getLangOpts().CPlusPlus11); } } - // Either we didn't meet the criteria for treating an lvalue as an rvalue, - // above, or overload resolution failed. Either way, we need to try - // (again) now with the return value expression as written. - return PerformCopyInitialization(Entity, SourceLocation(), Value); + return Info; } /// Determine whether the declared return type of the specified function @@ -3356,8 +3218,7 @@ /// for capturing scopes. /// StmtResult Sema::ActOnCapScopeReturnStmt(SourceLocation ReturnLoc, - Expr *RetValExp, - NamedReturnInfo &NRInfo) { + Expr *RetValExp) { // If this is the first return we've seen, infer the return type. // [expr.prim.lambda]p4 in C++11; block literals follow the same rules. CapturingScopeInfo *CurCap = cast(getCurFunction()); @@ -3380,6 +3241,7 @@ /* NRVOCandidate=*/nullptr); } + const VarDecl *NRVOCandidate = nullptr; if (HasDeducedReturnType) { FunctionDecl *FD = CurLambda->CallOperator; // If we've already decided this lambda is invalid, e.g. because @@ -3392,6 +3254,9 @@ if (CurCap->ReturnType.isNull()) CurCap->ReturnType = FD->getReturnType(); + NRVOCandidate = getCopyElisionCandidate( + RetValExp, NRVOExprContext::Returned, CurCap->ReturnType); + AutoType *AT = CurCap->ReturnType->getContainedAutoType(); assert(AT && "lost auto type from lambda return type"); if (DeduceFunctionTypeFromReturnExpr(FD, ReturnLoc, RetValExp, AT)) { @@ -3400,43 +3265,47 @@ return StmtError(); } CurCap->ReturnType = FnRetType = FD->getReturnType(); - } else if (CurCap->HasImplicitReturnType) { - // For blocks/lambdas with implicit return types, we check each return - // statement individually, and deduce the common return type when the block - // or lambda is completed. - // FIXME: Fold this into the 'auto' codepath above. - if (RetValExp && !isa(RetValExp)) { - ExprResult Result = DefaultFunctionArrayLvalueConversion(RetValExp); - if (Result.isInvalid()) - return StmtError(); - RetValExp = Result.get(); - - // DR1048: even prior to C++14, we should use the 'auto' deduction rules - // when deducing a return type for a lambda-expression (or by extension - // for a block). These rules differ from the stated C++11 rules only in - // that they remove top-level cv-qualifiers. - if (!CurContext->isDependentContext()) - FnRetType = RetValExp->getType().getUnqualifiedType(); - else - FnRetType = CurCap->ReturnType = Context.DependentTy; - } else { - if (RetValExp) { - // C++11 [expr.lambda.prim]p4 bans inferring the result from an - // initializer list, because it is not an expression (even - // though we represent it as one). We still deduce 'void'. - Diag(ReturnLoc, diag::err_lambda_return_init_list) - << RetValExp->getSourceRange(); + } else { + if (CurCap->HasImplicitReturnType) { + // For blocks/lambdas with implicit return types, we check each return + // statement individually, and deduce the common return type when the + // block or lambda is completed. + // FIXME: Fold this into the 'auto' codepath above. + if (RetValExp && !isa(RetValExp)) { + ExprResult Result = DefaultFunctionArrayLvalueConversion(RetValExp); + if (Result.isInvalid()) + return StmtError(); + RetValExp = Result.get(); + + // DR1048: even prior to C++14, we should use the 'auto' deduction rules + // when deducing a return type for a lambda-expression (or by extension + // for a block). These rules differ from the stated C++11 rules only in + // that they remove top-level cv-qualifiers. + if (!CurContext->isDependentContext()) + FnRetType = RetValExp->getType().getUnqualifiedType(); + else + FnRetType = CurCap->ReturnType = Context.DependentTy; + } else { + if (RetValExp) { + // C++11 [expr.lambda.prim]p4 bans inferring the result from an + // initializer list, because it is not an expression (even + // though we represent it as one). We still deduce 'void'. + Diag(ReturnLoc, diag::err_lambda_return_init_list) + << RetValExp->getSourceRange(); + } + + FnRetType = Context.VoidTy; } - FnRetType = Context.VoidTy; + // Although we'll properly infer the type of the block once it's + // completed, make sure we provide a return type now for better error + // recovery. + if (CurCap->ReturnType.isNull()) + CurCap->ReturnType = FnRetType; } - - // Although we'll properly infer the type of the block once it's completed, - // make sure we provide a return type now for better error recovery. - if (CurCap->ReturnType.isNull()) - CurCap->ReturnType = FnRetType; + NRVOCandidate = getCopyElisionCandidate( + RetValExp, NRVOExprContext::Returned, CurCap->ReturnType); } - const VarDecl *NRVOCandidate = getCopyElisionCandidate(NRInfo, FnRetType); if (auto *CurBlock = dyn_cast(CurCap)) { if (CurBlock->FunctionType->castAs()->getNoReturnAttr()) { @@ -3488,7 +3357,8 @@ // the C version of which boils down to CheckSingleAssignmentConstraints. InitializedEntity Entity = InitializedEntity::InitializeResult( ReturnLoc, FnRetType, NRVOCandidate != nullptr); - ExprResult Res = PerformMoveOrCopyInitialization(Entity, NRInfo, RetValExp); + ExprResult Res = + PerformCopyInitialization(Entity, SourceLocation(), RetValExp); if (Res.isInvalid()) { // FIXME: Cleanup temporaries here, anyway? return StmtError(); @@ -3703,10 +3573,8 @@ if (RetValExp && DiagnoseUnexpandedParameterPack(RetValExp)) return StmtError(); - NamedReturnInfo NRInfo = getNamedReturnInfo(RetValExp); - if (isa(getCurFunction())) - return ActOnCapScopeReturnStmt(ReturnLoc, RetValExp, NRInfo); + return ActOnCapScopeReturnStmt(ReturnLoc, RetValExp); QualType FnRetType; QualType RelatedRetType; @@ -3760,6 +3628,8 @@ /* NRVOCandidate=*/nullptr); } + const VarDecl *NRVOCandidate = + getCopyElisionCandidate(RetValExp, NRVOExprContext::Returned, FnRetType); // FIXME: Add a flag to the ScopeInfo to indicate whether we're performing // deduction. if (getLangOpts().CPlusPlus14) { @@ -3778,7 +3648,6 @@ } } } - const VarDecl *NRVOCandidate = getCopyElisionCandidate(NRInfo, FnRetType); bool HasDependentReturnType = FnRetType->isDependentType(); @@ -3898,7 +3767,7 @@ InitializedEntity Entity = InitializedEntity::InitializeResult( ReturnLoc, RetType, NRVOCandidate != nullptr); ExprResult Res = - PerformMoveOrCopyInitialization(Entity, NRInfo, RetValExp); + PerformCopyInitialization(Entity, SourceLocation(), RetValExp); if (Res.isInvalid()) { // FIXME: Clean up temporaries here anyway? return StmtError(); diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -1071,10 +1071,9 @@ // 'optimization'. Notably, functions with 'auto' return types won't have it // deduced by this point. Coupled with the limitation described // previously, this makes it very hard to support copy elision for these. - Sema::NamedReturnInfo Info = SemaRef.getNamedReturnInfo(Var); - bool NRVO = SemaRef.getCopyElisionCandidate( - Info, cast(FT)->getReturnType()) != nullptr; - Var->setNRVOVariable(NRVO); + Sema::NamedReturnInfo Info = SemaRef.getNamedReturnInfo( + Var, cast(FT)->getReturnType(), /*Parenthesized=*/false); + Var->setNRVOVariable(Info.isCopyElidable()); } Var->setImplicit(D->isImplicit()); 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 @@ -136,42 +136,42 @@ struct A1 { operator C() &&; - operator C() const & = delete; // cxx11_17-note {{'operator C' has been explicitly marked deleted here}} + operator C() const & = delete; }; C test1() { A1 a; - return a; // cxx11_17-error {{conversion function from 'test_non_ctor_conversion::A1' to 'test_non_ctor_conversion::C' invokes a deleted function}} + return a; } struct A2 { operator C() &&; private: - operator C() const &; // cxx11_17-note {{declared private here}} + operator C() const &; }; C test2() { A2 a; - return a; // cxx11_17-error {{'operator C' is a private member of 'test_non_ctor_conversion::A2'}} + return a; } struct B1 { operator C() const &; - operator C() && = delete; // cxx20_2b-note {{'operator C' has been explicitly marked deleted here}} + operator C() && = delete; // expected-note {{'operator C' has been explicitly marked deleted here}} }; C test3() { B1 b; - return b; // cxx20_2b-error {{conversion function from 'test_non_ctor_conversion::B1' to 'test_non_ctor_conversion::C' invokes a deleted function}} + return b; // expected-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_2b-note {{declared private here}} + operator C() &&; // expected-note {{declared private here}} }; C test4() { B2 b; - return b; // cxx20_2b-error {{'operator C' is a private member of 'test_non_ctor_conversion::B2'}} + return b; // expected-error {{'operator C' is a private member of 'test_non_ctor_conversion::B2'}} } } // namespace test_non_ctor_conversion @@ -190,35 +190,35 @@ NeedRvalueRef(B2 &&); }; struct NeedValue { - NeedValue(A1); // cxx11_17-note 2 {{passing argument to parameter here}} + NeedValue(A1); NeedValue(A2); - NeedValue(B1); // cxx20_2b-note 2 {{passing argument to parameter here}} + NeedValue(B1); // expected-note 2 {{passing argument to parameter here}} NeedValue(B2); }; struct A1 { A1(); A1(A1 &&); - A1(const A1 &) = delete; // cxx11_17-note 3 {{'A1' has been explicitly marked deleted here}} + A1(const A1 &) = delete; }; NeedValue test_1_1() { // not rvalue reference // same type A1 a; - return a; // cxx11_17-error {{call to deleted constructor of 'test_ctor_param_rvalue_ref::A1'}} + return a; } class DerivedA1 : public A1 {}; A1 test_1_2() { // rvalue reference // not same type DerivedA1 a; - return a; // cxx11_17-error {{call to deleted constructor of 'test_ctor_param_rvalue_ref::A1'}} + return a; } NeedValue test_1_3() { // not rvalue reference // not same type DerivedA1 a; - return a; // cxx11_17-error {{call to deleted constructor of 'test_ctor_param_rvalue_ref::A1'}} + return a; } struct A2 { @@ -226,51 +226,51 @@ A2(A2 &&); private: - A2(const A2 &); // cxx11_17-note 3 {{declared private here}} + A2(const A2 &); }; NeedValue test_2_1() { // not rvalue reference // same type A2 a; - return a; // cxx11_17-error {{calling a private constructor of class 'test_ctor_param_rvalue_ref::A2'}} + return a; } class DerivedA2 : public A2 {}; A2 test_2_2() { // rvalue reference // not same type DerivedA2 a; - return a; // cxx11_17-error {{calling a private constructor of class 'test_ctor_param_rvalue_ref::A2'}} + return a; } NeedValue test_2_3() { // not rvalue reference // not same type DerivedA2 a; - return a; // cxx11_17-error {{calling a private constructor of class 'test_ctor_param_rvalue_ref::A2'}} + return a; } struct B1 { B1(); B1(const B1 &); - B1(B1 &&) = delete; // cxx20_2b-note 3 {{'B1' has been explicitly marked deleted here}} + B1(B1 &&) = delete; // expected-note 3 {{'B1' has been explicitly marked deleted here}} }; NeedValue test_3_1() { // not rvalue reference // same type B1 b; - return b; // cxx20_2b-error {{call to deleted constructor of 'test_ctor_param_rvalue_ref::B1'}} + return b; // expected-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_2b-error {{call to deleted constructor of 'test_ctor_param_rvalue_ref::B1'}} + return b; // expected-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_2b-error {{call to deleted constructor of 'test_ctor_param_rvalue_ref::B1'}} + return b; // expected-error {{call to deleted constructor of 'test_ctor_param_rvalue_ref::B1'}} } struct B2 { @@ -278,26 +278,26 @@ B2(const B2 &); private: - B2(B2 &&); // cxx20_2b-note 3 {{declared private here}} + B2(B2 &&); // expected-note 3 {{declared private here}} }; NeedValue test_4_1() { // not rvalue reference // same type B2 b; - return b; // cxx20_2b-error {{calling a private constructor of class 'test_ctor_param_rvalue_ref::B2'}} + return b; // expected-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_2b-error {{calling a private constructor of class 'test_ctor_param_rvalue_ref::B2'}} + return b; // expected-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_2b-error {{calling a private constructor of class 'test_ctor_param_rvalue_ref::B2'}} + return b; // expected-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 --- a/clang/test/SemaCXX/P1155.cpp +++ b/clang/test/SemaCXX/P1155.cpp @@ -1,9 +1,9 @@ -// RUN: %clang_cc1 -std=c++2b -fsyntax-only -fcxx-exceptions -verify=cxx20_2b %s -// RUN: %clang_cc1 -std=c++20 -fsyntax-only -fcxx-exceptions -verify=cxx20_2b %s -// RUN: %clang_cc1 -std=c++17 -fsyntax-only -fcxx-exceptions -verify=cxx11_17 %s -// RUN: %clang_cc1 -std=c++14 -fsyntax-only -fcxx-exceptions -verify=cxx11_17 %s -// RUN: %clang_cc1 -std=c++11 -fsyntax-only -fcxx-exceptions -verify=cxx11_17 %s -// cxx20_2b-no-diagnostics +// RUN: %clang_cc1 -std=c++2b -fsyntax-only -fcxx-exceptions -verify %s +// RUN: %clang_cc1 -std=c++20 -fsyntax-only -fcxx-exceptions -verify %s +// RUN: %clang_cc1 -std=c++17 -fsyntax-only -fcxx-exceptions -verify %s +// RUN: %clang_cc1 -std=c++14 -fsyntax-only -fcxx-exceptions -verify %s +// RUN: %clang_cc1 -std=c++11 -fsyntax-only -fcxx-exceptions -verify %s +// expected-no-diagnostics // Throwing namespace test_throwing { @@ -23,13 +23,13 @@ class Widget {}; struct To { - operator Widget() const & = delete; // cxx11_17-note {{'operator Widget' has been explicitly marked deleted here}} + operator Widget() const & = delete; operator Widget() &&; }; Widget nine() { To t; - return t; // cxx11_17-error {{conversion function from 'test_non_constructor_conversion::To' to 'test_non_constructor_conversion::Widget' invokes a deleted function}} + return t; } } // namespace test_non_constructor_conversion @@ -39,16 +39,16 @@ public: Widget(); Widget(Widget &&); - Widget(const Widget &) = delete; // cxx11_17-note {{'Widget' has been explicitly marked deleted here}} + Widget(const Widget &) = delete; }; struct Fowl { - Fowl(Widget); // cxx11_17-note {{passing argument to parameter here}} + Fowl(Widget); }; Fowl eleven() { Widget w; - return w; // cxx11_17-error {{call to deleted constructor of 'test_by_value_sinks::Widget'}} + return w; } } // namespace test_by_value_sinks @@ -58,13 +58,13 @@ public: Base(); Base(Base &&); - Base(Base const &) = delete; // cxx11_17-note {{'Base' has been explicitly marked deleted here}} + Base(Base const &) = delete; }; class Derived : public Base {}; Base thirteen() { Derived result; - return result; // cxx11_17-error {{call to deleted constructor of 'test_slicing::Base'}} + return result; } } // namespace test_slicing diff --git a/clang/test/SemaCXX/conversion-function.cpp b/clang/test/SemaCXX/conversion-function.cpp --- a/clang/test/SemaCXX/conversion-function.cpp +++ b/clang/test/SemaCXX/conversion-function.cpp @@ -1,9 +1,9 @@ -// RUN: %clang_cc1 -std=c++2b -fsyntax-only -verify=expected -triple %itanium_abi_triple -Wbind-to-temporary-copy %s -// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify=expected -triple %itanium_abi_triple -Wbind-to-temporary-copy %s -// RUN: %clang_cc1 -std=c++14 -fsyntax-only -verify=expected,cxx98_14 -triple %itanium_abi_triple -Wbind-to-temporary-copy %s -// RUN: %clang_cc1 -std=c++11 -fsyntax-only -verify=expected,cxx98_14 -triple %itanium_abi_triple -Wbind-to-temporary-copy %s -// RUN: %clang_cc1 -std=c++98 -fsyntax-only -verify=expected,cxx98_14 -triple %itanium_abi_triple -Wbind-to-temporary-copy %s -// RUN: %clang_cc1 -fsyntax-only -verify=expected,cxx98_14 -triple %itanium_abi_triple -Wbind-to-temporary-copy %s +// RUN: %clang_cc1 -std=c++2b -fsyntax-only -verify=expected -triple %itanium_abi_triple -Wbind-to-temporary-copy %s +// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify=expected,cxx98_20 -triple %itanium_abi_triple -Wbind-to-temporary-copy %s +// RUN: %clang_cc1 -std=c++14 -fsyntax-only -verify=expected,cxx98_14,cxx98_20 -triple %itanium_abi_triple -Wbind-to-temporary-copy %s +// RUN: %clang_cc1 -std=c++11 -fsyntax-only -verify=expected,cxx98_14,cxx98_20 -triple %itanium_abi_triple -Wbind-to-temporary-copy %s +// RUN: %clang_cc1 -std=c++98 -fsyntax-only -verify=expected,cxx98_14,cxx98_20 -triple %itanium_abi_triple -Wbind-to-temporary-copy %s +// RUN: %clang_cc1 -fsyntax-only -verify=expected,cxx98_14,cxx98_20 -triple %itanium_abi_triple -Wbind-to-temporary-copy %s class X { public: @@ -126,7 +126,7 @@ class AutoPtrRef { }; class AutoPtr { - AutoPtr(AutoPtr &); // cxx98_14-note{{declared private here}} + AutoPtr(AutoPtr &); // cxx98_20-note{{declared private here}} public: AutoPtr(); @@ -142,7 +142,7 @@ AutoPtr p; if (Cond) - return p; // cxx98_14-error{{calling a private constructor}} + return p; // cxx98_20-error{{calling a private constructor}} return AutoPtr(); } diff --git a/clang/test/SemaCXX/coroutine-rvo.cpp b/clang/test/SemaCXX/coroutine-rvo.cpp --- a/clang/test/SemaCXX/coroutine-rvo.cpp +++ b/clang/test/SemaCXX/coroutine-rvo.cpp @@ -1,4 +1,6 @@ -// RUN: %clang_cc1 -verify -std=c++17 -fcoroutines-ts -fsyntax-only %s +// RUN: %clang_cc1 -std=c++2b -fsyntax-only -verify=expected,cxx2b %s +// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify=expected %s +// RUN: %clang_cc1 -std=c++17 -fcoroutines-ts -fsyntax-only -verify=expected,cxx17 %s namespace std::experimental { template struct coroutine_handle { @@ -56,7 +58,10 @@ auto final_suspend() noexcept { return suspend_never{}; } auto get_return_object() { return task{}; } static void unhandled_exception() {} - void return_value(T &&value) {} // expected-note 4{{passing argument}} + void return_value(T &&value) {} + // cxx2b-note@-1 2{{passing argument}} + // cxx17-note@-2 {{passing argument}} + // expected-note@-3 2{{passing argument}} }; }; @@ -67,7 +72,7 @@ task local2ref() { NoCopyNoMove value; - co_return value; // expected-error {{non-const lvalue reference to type 'NoCopyNoMove' cannot bind to a temporary of type 'NoCopyNoMove'}} + co_return value; // cxx2b-error {{non-const lvalue reference to type 'NoCopyNoMove' cannot bind to a temporary of type 'NoCopyNoMove'}} } // We need the move constructor for construction of the coroutine. @@ -80,7 +85,7 @@ } task rvalue2val(NoCopyNoMove &&value) { - co_return value; + co_return value; // cxx17-error {{rvalue reference to type 'NoCopyNoMove' cannot bind to lvalue of type 'NoCopyNoMove'}} } task lvalue2ref(NoCopyNoMove &value) { @@ -88,7 +93,7 @@ } task rvalue2ref(NoCopyNoMove &&value) { - co_return value; // expected-error {{non-const lvalue reference to type 'NoCopyNoMove' cannot bind to a temporary of type 'NoCopyNoMove'}} + co_return value; // cxx2b-error {{non-const lvalue reference to type 'NoCopyNoMove' cannot bind to a temporary of type 'NoCopyNoMove'}} } struct To { 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,12 @@ -// RUN: %clang_cc1 -std=c++2b -fsyntax-only -verify=cxx20_2b,cxx2b -fcxx-exceptions -Wreturn-std-move %s -// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify=cxx20_2b -fcxx-exceptions -Wreturn-std-move %s -// RUN: %clang_cc1 -std=c++17 -fsyntax-only -verify=cxx11_17 -fcxx-exceptions -Wreturn-std-move %s -// RUN: %clang_cc1 -std=c++14 -fsyntax-only -verify=cxx11_17 -fcxx-exceptions -Wreturn-std-move %s -// RUN: %clang_cc1 -std=c++11 -fsyntax-only -verify=cxx11_17 -fcxx-exceptions -Wreturn-std-move %s +// RUN: %clang_cc1 -std=c++2b -fsyntax-only -verify=expected,cxx20_2b,cxx2b -fcxx-exceptions -Wreturn-std-move %s +// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify=expected,cxx20_2b,cxx11_20 -fcxx-exceptions -Wreturn-std-move %s +// RUN: %clang_cc1 -std=c++17 -fsyntax-only -verify=expected,cxx11_17,cxx11_20 -fcxx-exceptions -Wreturn-std-move %s +// RUN: %clang_cc1 -std=c++14 -fsyntax-only -verify=expected,cxx11_17,cxx11_20 -fcxx-exceptions -Wreturn-std-move %s +// RUN: %clang_cc1 -std=c++11 -fsyntax-only -verify=expected,cxx11_17,cxx11_20 -fcxx-exceptions -Wreturn-std-move %s -// RUN: %clang_cc1 -std=c++17 -fsyntax-only -fcxx-exceptions -Wreturn-std-move -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s -check-prefix=CHECK -// RUN: %clang_cc1 -std=c++14 -fsyntax-only -fcxx-exceptions -Wreturn-std-move -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s -check-prefix=CHECK -// RUN: %clang_cc1 -std=c++11 -fsyntax-only -fcxx-exceptions -Wreturn-std-move -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s -check-prefix=CHECK +// RUN: %clang_cc1 -std=c++17 -fsyntax-only -fcxx-exceptions -Wreturn-std-move -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s -check-prefixes=CHECK,CHECK_14_17 +// RUN: %clang_cc1 -std=c++14 -fsyntax-only -fcxx-exceptions -Wreturn-std-move -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s -check-prefixes=CHECK,CHECK_14_17 +// RUN: %clang_cc1 -std=c++11 -fsyntax-only -fcxx-exceptions -Wreturn-std-move -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s -check-prefixes=CHECK // definitions for std::move namespace std { @@ -78,9 +78,6 @@ Base test2() { Derived d2; return d2; // e1 - // cxx11_17-warning@-1{{will be copied despite being returned by name}} - // cxx11_17-note@-2{{to avoid copying}} - // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:14}:"std::move(d2)" } ConstructFromDerived test3() { Derived d3; @@ -89,23 +86,14 @@ ConstructFromBase test4() { Derived d4; return d4; // e3 - // cxx11_17-warning@-1{{will be copied despite being returned by name}} - // cxx11_17-note@-2{{to avoid copying}} - // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:14}:"std::move(d4)" } ConvertFromDerived test5() { Derived d5; return d5; // e4 - // cxx11_17-warning@-1{{will be copied despite being returned by name}} - // cxx11_17-note@-2{{to avoid copying}} - // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:14}:"std::move(d5)" } ConvertFromBase test6() { Derived d6; return d6; // e5 - // cxx11_17-warning@-1{{will be copied despite being returned by name}} - // cxx11_17-note@-2{{to avoid copying}} - // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:14}:"std::move(d6)" } // These test cases should not produce the warning. @@ -152,30 +140,18 @@ Derived testParam1(Derived d) { return d; } Base testParam2(Derived d) { return d; // e6 - // cxx11_17-warning@-1{{will be copied despite being returned by name}} - // cxx11_17-note@-2{{to avoid copying}} - // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:13}:"std::move(d)" } ConstructFromDerived testParam3(Derived d) { return d; // ok } ConstructFromBase testParam4(Derived d) { return d; // e8 - // cxx11_17-warning@-1{{will be copied despite being returned by name}} - // cxx11_17-note@-2{{to avoid copying}} - // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:13}:"std::move(d)" } ConvertFromDerived testParam5(Derived d) { return d; // e9 - // cxx11_17-warning@-1{{will be copied despite being returned by name}} - // cxx11_17-note@-2{{to avoid copying}} - // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:13}:"std::move(d)" } ConvertFromBase testParam6(Derived d) { return d; // e10 - // cxx11_17-warning@-1{{will be copied despite being returned by name}} - // cxx11_17-note@-2{{to avoid copying}} - // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:13}:"std::move(d)" } // If the target is an rvalue reference parameter, do apply the diagnostic. @@ -216,21 +192,37 @@ // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:13}:"std::move(d)" } +// FIXME: For the next four tests, under cxx11_20, we give good advice, but in a bad way. + // But if the return type is a reference type, then moving would be wrong. -Derived &testRetRef1(Derived &&d) { return d; } // cxx2b-error {{non-const lvalue reference to type 'Derived' cannot bind to a temporary of type 'Derived'}} -Base &testRetRef2(Derived &&d) { return d; } // cxx2b-error {{non-const lvalue reference to type 'Base' cannot bind to a temporary of type 'Derived'}} +Derived &testRetRef1(Derived &&d) { return d; } +// cxx2b-error@-1 {{non-const lvalue reference to type 'Derived' cannot bind to a temporary of type 'Derived'}} +// cxx11_20-warning@-2{{will be copied despite being returned by name}} +// cxx11_20-note@-3{{to avoid copying}} +// CHECK: fix-it:"{{.*}}":{[[@LINE-4]]:44-[[@LINE-4]]:45}:"std::move(d)" + +Base &testRetRef2(Derived &&d) { return d; } +// cxx2b-error@-1 {{non-const lvalue reference to type 'Base' cannot bind to a temporary of type 'Derived'}} +// cxx11_20-warning@-2{{will be copied despite being returned by name}} +// cxx11_20-note@-3{{to avoid copying}} +// CHECK: fix-it:"{{.*}}":{[[@LINE-4]]:41-[[@LINE-4]]:42}:"std::move(d)" + #if __cplusplus >= 201402L auto&& testRetRef3(Derived&& d) { return d; } +// cxx11_20-warning@-1{{will be copied despite being returned by name}} +// cxx11_20-note@-2{{to avoid copying}} +// CHECK_14_17: fix-it:"{{.*}}":{[[@LINE-3]]:42-[[@LINE-3]]:43}:"std::move(d)" + decltype(auto) testRetRef4(Derived&& d) { return (d); } +// cxx11_20-warning@-1{{will be copied despite being returned by name}} +// cxx11_20-note@-2{{to avoid copying}} +// CHECK_14_17: fix-it:"{{.*}}":{[[@LINE-3]]:50-[[@LINE-3]]:53}:"std::move(d)" #endif // As long as we're checking parentheses, make sure parentheses don't disable the warning. Base testParens1() { Derived d; return (d); // e17 - // cxx11_17-warning@-1{{will be copied despite being returned by name}} - // cxx11_17-note@-2{{to avoid copying}} - // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:15}:"std::move(d)" } ConstructFromDerived testParens2() { Derived d; @@ -334,18 +326,3 @@ void ok_throw6(Derived &d) { throw static_cast(d); } void ok_throw7(TriviallyCopyable d) { throw d; } void ok_throw8(OnlyCopyable d) { throw d; } - -namespace test_delete { -struct Base { - Base(); - Base(Base &&) = delete; // cxx20_2b-note {{'Base' has been explicitly marked deleted here}} - Base(Base const &); -}; - -struct Derived : public Base {}; - -Base test_ok() { - Derived d; - return d; // cxx20_2b-error {{call to deleted constructor of 'test_delete::Base'}} -} -} // namespace test_delete