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 @@ -270,6 +270,7 @@ bool isLValue() const { return getValueKind() == VK_LValue; } bool isPRValue() const { return getValueKind() == VK_PRValue; } bool isXValue() const { return getValueKind() == VK_XValue; } + bool isYValue() const { return getValueKind() == VK_YValue; } bool isGLValue() const { return getValueKind() != VK_PRValue; } enum LValueClassification { 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/Overload.h b/clang/include/clang/Sema/Overload.h --- a/clang/include/clang/Sema/Overload.h +++ b/clang/include/clang/Sema/Overload.h @@ -304,6 +304,10 @@ /// Whether we're binding to an rvalue. unsigned BindsToRvalue : 1; + /// Whether this is a non-volatile const qualified + /// reference binding to an yvalue. + unsigned NonVolatileConstBindsToYvalue : 1; + /// Whether this binds an implicit object argument to a /// non-static member function without a ref-qualifier. unsigned BindsImplicitObjectArgumentWithoutRefQualifier : 1; 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 @@ -4776,23 +4776,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); - 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 @@ -5471,6 +5471,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 @@ -62,6 +62,9 @@ case VK_PRValue: Category = "prvalue"; break; + case VK_YValue: + Category = "yvalue"; + 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 @@ -153,6 +153,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 @@ -717,7 +717,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/Sema.cpp b/clang/lib/Sema/Sema.cpp --- a/clang/lib/Sema/Sema.cpp +++ b/clang/lib/Sema/Sema.cpp @@ -1965,17 +1965,14 @@ SourceLocation Loc = VD->getLocation(); Expr *VarRef = new (S.Context) DeclRefExpr(S.Context, VD, false, T, VK_LValue, Loc); - ExprResult Result; auto IE = InitializedEntity::InitializeBlock(Loc, T, false); - if (S.getLangOpts().CPlusPlus2b) { - auto *E = ImplicitCastExpr::Create(S.Context, T, CK_NoOp, VarRef, nullptr, - VK_XValue, FPOptionsOverride()); - Result = S.PerformCopyInitialization(IE, SourceLocation(), E); - } else { - Result = S.PerformMoveOrCopyInitialization( - IE, Sema::NamedReturnInfo{VD, Sema::NamedReturnInfo::MoveEligible}, - VarRef); - } + auto *E = S.getLangOpts().CPlusPlus11 + ? ImplicitCastExpr::Create( + S.Context, T, CK_NoOp, VarRef, nullptr, + S.getLangOpts().CPlusPlus2b ? VK_XValue : VK_YValue, + FPOptionsOverride()) + : VarRef; + ExprResult Result = S.PerformCopyInitialization(IE, SourceLocation(), E); if (!Result.isInvalid()) { Result = S.MaybeCreateExprWithCleanups(Result); 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 @@ -1240,7 +1240,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 @@ -886,17 +886,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 @@ -3587,6 +3587,7 @@ case VK_PRValue: S.Kind = SK_CastDerivedToBasePRValue; break; + case VK_YValue: case VK_XValue: S.Kind = SK_CastDerivedToBaseXValue; break; case VK_LValue: S.Kind = SK_CastDerivedToBaseLValue; break; } @@ -3638,6 +3639,7 @@ case VK_PRValue: S.Kind = SK_QualificationConversionPRValue; break; + case VK_YValue: case VK_XValue: S.Kind = SK_QualificationConversionXValue; break; @@ -4808,7 +4810,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 @@ -3905,10 +3905,15 @@ SCS2.BindsImplicitObjectArgumentWithoutRefQualifier) return false; - return (!SCS1.IsLvalueReference && SCS1.BindsToRvalue && - SCS2.IsLvalueReference) || - (SCS1.IsLvalueReference && SCS1.BindsToFunctionLvalue && - !SCS2.IsLvalueReference && SCS2.BindsToFunctionLvalue); + if (!SCS1.IsLvalueReference && SCS1.BindsToRvalue && SCS2.IsLvalueReference) + return true; + + if (SCS1.IsLvalueReference && SCS1.NonVolatileConstBindsToYvalue && + SCS2.IsLvalueReference) + return true; + + return SCS1.IsLvalueReference && SCS1.BindsToFunctionLvalue && + !SCS2.IsLvalueReference && SCS2.BindsToFunctionLvalue; } enum class FixedEnumPromotion { @@ -4769,10 +4774,10 @@ // 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) + ? ICK_Qualification + : ICK_Identity; ICS.Standard.setFromType(T2); ICS.Standard.setToType(0, T2); ICS.Standard.setToType(1, T1); @@ -4782,6 +4787,8 @@ ICS.Standard.IsLvalueReference = !isRValRef; ICS.Standard.BindsToFunctionLvalue = T2->isFunctionType(); ICS.Standard.BindsToRvalue = InitCategory.isRValue(); + ICS.Standard.NonVolatileConstBindsToYvalue = + Init->isYValue() && T1.isConstQualified() && !T1.isVolatileQualified(); ICS.Standard.BindsImplicitObjectArgumentWithoutRefQualifier = false; ICS.Standard.ObjCLifetimeConversionBinding = (RefConv & Sema::ReferenceConversions::ObjCLifetime) != 0; @@ -4799,7 +4806,8 @@ // reference-compatible with "cv2 T2," or // // Per C++ [over.ics.ref]p4, we don't check the bit-field property here. - if (InitCategory.isLValue() && RefRelationship == Sema::Ref_Compatible) { + if ((InitCategory.isLValue() || Init->isYValue()) && + RefRelationship == Sema::Ref_Compatible) { // C++ [over.ics.ref]p1: // When a parameter of reference type binds directly (8.5.3) // to an argument expression, the implicit conversion sequence @@ -4831,15 +4839,15 @@ 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 (!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 @@ -3321,25 +3321,28 @@ /// \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); - if (Res.Candidate && !E->isXValue() && - (ForceCXX2b || getLangOpts().CPlusPlus2b)) { + return nullptr; + + NamedReturnInfo Info = getNamedReturnInfo(VD, ReturnType, isa(E)); + if (getLangOpts().CPlusPlus11 && Info.isMoveEligible() && !E->isXValue()) { + 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 Res; + return Info.isCopyElidable() ? Info.Candidate : nullptr; } /// Determine whether the given NRVO candidate variable is move-eligible or @@ -3350,9 +3353,18 @@ /// \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(const VarDecl *VD) { +Sema::NamedReturnInfo Sema::getNamedReturnInfo(const VarDecl *VD, + 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) @@ -3399,87 +3411,47 @@ Context.getDeclAlign(VD) > Context.getTypeAlignInChars(VDType)) Info.S = NamedReturnInfo::MoveEligible; - 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)) - Info.S = NamedReturnInfo::MoveEligible; - } - return Info.isCopyElidable() ? Info.Candidate : nullptr; -} + if (!ReturnType.isNull()) { + if (ReturnType->isDependentType()) { + if ((ReturnType->getTypeClass() == Type::TypeClass::Auto && + ReturnType->isCanonicalUnqualified()) || + ReturnType->isSpecificBuiltinType(BuiltinType::Dependent)) { + return NamedReturnInfo(); + } + } else { + if (ReturnType->isSpecificBuiltinType(BuiltinType::Dependent)) { + ReturnType = Context.getAutoDeductType(); + } + 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; + } + } -/// 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 (getLangOpts().CPlusPlus11 && !getLangOpts().CPlusPlus2b && - NRInfo.isMoveEligible()) { - ImplicitCastExpr AsRvalue(ImplicitCastExpr::OnStack, Value->getType(), - CK_NoOp, Value, VK_XValue, FPOptionsOverride()); - Expr *InitExpr = &AsRvalue; - auto Kind = InitializationKind::CreateCopy(Value->getBeginLoc(), - Value->getBeginLoc()); - InitializationSequence Seq(*this, Entity, Kind, InitExpr); - auto Res = Seq.getFailedOverloadResult(); - if (Res == OR_Success || Res == OR_Deleted) { - // Promote "AsRvalue" to the heap, since we now need this - // expression node to persist. - Value = - ImplicitCastExpr::Create(Context, Value->getType(), CK_NoOp, Value, - nullptr, VK_XValue, FPOptionsOverride()); - // Complete type-checking the initialization of the return type - // using the constructor we found. - return Seq.Perform(*this, Entity, Kind, Value); + // - in a return statement in a function with ... + // ... a class return type ... + if (!ReturnType->isRecordType()) + Info.S = getLangOpts().CPlusPlus2b ? NamedReturnInfo::MoveEligible + : NamedReturnInfo::None; + + // ... 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)) + Info.S = std::min(Info.S, NamedReturnInfo::MoveEligible); } } - // 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 @@ -3494,8 +3466,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()); @@ -3518,6 +3489,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 @@ -3530,6 +3502,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)) { @@ -3538,43 +3513,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()) { @@ -3626,7 +3605,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(); @@ -3841,10 +3821,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; @@ -3898,6 +3876,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) { @@ -3916,7 +3896,6 @@ } } } - const VarDecl *NRVOCandidate = getCopyElisionCandidate(NRInfo, FnRetType); bool HasDependentReturnType = FnRetType->isDependentType(); @@ -4036,7 +4015,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 @@ -1108,9 +1108,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, RT) != nullptr; - Var->setNRVOVariable(NRVO); + Sema::NamedReturnInfo Info = + SemaRef.getNamedReturnInfo(Var, RT, /*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 @@ -427,12 +427,12 @@ struct B; struct A { - A(B &) = delete; // cxx98-note {{has been explicitly deleted}} + A(B &) = delete; // cxx98_20-note {{has been explicitly deleted}} }; struct B { - operator A(); // cxx98-note {{candidate function}} + operator A(); // cxx98_20-note {{candidate function}} }; -A test1(B x) { return x; } // cxx98-error {{conversion}} {{is ambiguous}} +A test1(B x) { return x; } // cxx98_20-error {{conversion}} {{is ambiguous}} struct C {}; struct D { 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,7 +1,7 @@ -// 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++11 -fsyntax-only -verify=expected,cxx98_11,cxx11 -triple %itanium_abi_triple -Wbind-to-temporary-copy %s -// RUN: %clang_cc1 -std=c++98 -fsyntax-only -verify=expected,cxx98_11,cxx98 -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++11 -fsyntax-only -verify=expected,cxx98_20,cxx98_11,cxx11 -triple %itanium_abi_triple -Wbind-to-temporary-copy %s +// RUN: %clang_cc1 -std=c++98 -fsyntax-only -verify=expected,cxx98_20,cxx98_11,cxx98 -triple %itanium_abi_triple -Wbind-to-temporary-copy %s class X { public: @@ -124,7 +124,7 @@ class AutoPtrRef { }; class AutoPtr { - AutoPtr(AutoPtr &); // cxx98-note {{declared private here}} + AutoPtr(AutoPtr &); // cxx98_20-note {{declared private here}} public: AutoPtr(); @@ -140,7 +140,7 @@ AutoPtr p; if (Cond) - return p; // cxx98-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,5 @@ -// 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 namespace std::experimental { template struct coroutine_handle { @@ -56,7 +57,9 @@ 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}} + // expected-note@-2 2{{passing argument}} }; }; @@ -67,7 +70,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. @@ -88,7 +91,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/SemaObjCXX/block-capture.mm b/clang/test/SemaObjCXX/block-capture.mm --- a/clang/test/SemaObjCXX/block-capture.mm +++ b/clang/test/SemaObjCXX/block-capture.mm @@ -1,7 +1,7 @@ -// RUN: %clang_cc1 -std=c++2b -fsyntax-only -fobjc-arc -fblocks -verify=cxx98_2b,cxx11_2b,cxx2b %s -// RUN: %clang_cc1 -std=c++20 -fsyntax-only -fobjc-arc -fblocks -verify=cxx98_2b,cxx11_2b %s -// RUN: %clang_cc1 -std=c++11 -fsyntax-only -fobjc-arc -fblocks -verify=cxx98_2b,cxx11_2b %s -// RUN: %clang_cc1 -std=c++98 -fsyntax-only -fobjc-arc -fblocks -Wno-c++11-extensions -verify=cxx98_2b,cxx98 %s +// RUN: %clang_cc1 -std=c++2b -fsyntax-only -fobjc-arc -fblocks -verify=cxx98_2b,cxx11_2b,cxx2b %s +// RUN: %clang_cc1 -std=c++20 -fsyntax-only -fobjc-arc -fblocks -verify=cxx98_2b,cxx98_20,cxx11_2b %s +// RUN: %clang_cc1 -std=c++11 -fsyntax-only -fobjc-arc -fblocks -verify=cxx98_2b,cxx98_20,cxx11_2b %s +// RUN: %clang_cc1 -std=c++98 -fsyntax-only -fobjc-arc -fblocks -Wno-c++11-extensions -verify=cxx98_2b,cxx98_20,cxx98 %s #define TEST(T) void test_##T() { \ __block T x; \ @@ -51,31 +51,31 @@ struct ConvertingRVRef { ConvertingRVRef(); - ConvertingRVRef(ConvertingRVRef &) = delete; // cxx98-note {{marked deleted here}} + ConvertingRVRef(ConvertingRVRef &) = delete; // cxx98_20-note {{marked deleted here}} struct X {}; ConvertingRVRef(X &&); operator X() const & = delete; operator X() &&; }; -TEST(ConvertingRVRef); // cxx98-error {{call to deleted constructor}} +TEST(ConvertingRVRef); // cxx98_20-error {{call to deleted constructor}} struct ConvertingCLVRef { ConvertingCLVRef(); ConvertingCLVRef(ConvertingCLVRef &); struct X {}; - ConvertingCLVRef(X &&); // cxx11_2b-note {{passing argument to parameter here}} + ConvertingCLVRef(X &&); // cxx2b-note {{passing argument to parameter here}} operator X() const &; - operator X() && = delete; // cxx11_2b-note {{marked deleted here}} + operator X() && = delete; // cxx2b-note {{marked deleted here}} }; -TEST(ConvertingCLVRef); // cxx11_2b-error {{invokes a deleted function}} +TEST(ConvertingCLVRef); // cxx2b-error {{invokes a deleted function}} struct SubSubMove {}; struct SubMove : SubSubMove { SubMove(); - SubMove(SubMove &) = delete; // cxx98-note {{marked deleted here}} + SubMove(SubMove &) = delete; // cxx98_20-note {{marked deleted here}} SubMove(SubSubMove &&); }; -TEST(SubMove); // cxx98-error {{call to deleted constructor}} +TEST(SubMove); // cxx98_20-error {{call to deleted constructor}}