From c60dd3b2626a4d9eefd9f82f9a406b0d28d3fd72 Mon Sep 17 00:00:00 2001 From: Hans Wennborg Date: Mon, 14 Jun 2021 16:39:32 +0200 Subject: [PATCH] Revert "[clang] NRVO: Improvements and handling of more cases." This change caused build errors related to move-only __block variables, see discussion on https://reviews.llvm.org/D99696 > This expands NRVO propagation for more cases: > > Parse analysis improvement: > * Lambdas and Blocks with dependent return type can have their variables > marked as NRVO Candidates. > > Variable instantiation improvements: > * Fixes crash when instantiating NRVO variables in Blocks. > * Functions, Lambdas, and Blocks which have auto return type have their > variables' NRVO status propagated. For Blocks with non-auto return type, > as a limitation, this propagation does not consider the actual return > type. > > This also implements exclusion of VarDecls which are references to > dependent types. > > Signed-off-by: Matheus Izvekov > > Reviewed By: Quuxplusone > > Differential Revision: https://reviews.llvm.org/D99696 This also reverts the follow-on change which was hard to tease apart form the one above: > "[clang] Implement P2266 Simpler implicit move" > > This Implements [[http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p2266r1.html|P2266 Simpler implicit move]]. > > Signed-off-by: Matheus Izvekov > > Reviewed By: Quuxplusone > > Differential Revision: https://reviews.llvm.org/D99005 This reverts commits 1e50c3d785f4563873ab1ce86559f2a1285b5678 and bf20631782183cd19e0bb7219e908c2bbb01a75f. --- clang/include/clang/Sema/Sema.h | 40 +-- clang/lib/Sema/Sema.cpp | 7 +- clang/lib/Sema/SemaCoroutine.cpp | 24 +- clang/lib/Sema/SemaExprCXX.cpp | 18 +- clang/lib/Sema/SemaStmt.cpp | 317 ++++++++++----------- clang/lib/Sema/SemaTemplateInstantiateDecl.cpp | 28 +- clang/lib/Sema/SemaType.cpp | 21 +- .../CXX/class/class.init/class.copy.elision/p3.cpp | 28 +- .../dcl.spec/dcl.type/dcl.spec.auto/p7-cxx14.cpp | 12 +- clang/test/CXX/drs/dr3xx.cpp | 15 +- .../expr/expr.prim/expr.prim.lambda/p4-cxx14.cpp | 10 +- clang/test/CXX/temp/temp.decls/temp.mem/p5.cpp | 14 +- clang/test/CodeGen/nrvo-tracking.cpp | 31 +- clang/test/SemaCXX/constant-expression-cxx11.cpp | 13 +- clang/test/SemaCXX/constant-expression-cxx14.cpp | 40 +-- clang/test/SemaCXX/coroutine-rvo.cpp | 17 +- clang/test/SemaCXX/coroutines.cpp | 9 +- clang/test/SemaCXX/deduced-return-type-cxx14.cpp | 23 +- clang/test/SemaCXX/return-stack-addr.cpp | 15 +- clang/test/SemaCXX/warn-return-std-move.cpp | 14 +- 20 files changed, 304 insertions(+), 392 deletions(-) diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index db38992..6ade9d7 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -3455,6 +3455,12 @@ public: bool DiagnoseMultipleUserDefinedConversion(Expr *From, QualType ToType); bool isSameOrCompatibleFunctionType(CanQualType Param, CanQualType Arg); + ExprResult PerformMoveOrCopyInitialization(const InitializedEntity &Entity, + const VarDecl *NRVOCandidate, + QualType ResultType, + Expr *Value, + bool AllowNRVO = true); + bool CanPerformAggregateInitializationForOverloadResolution( const InitializedEntity &Entity, InitListExpr *From); @@ -4754,30 +4760,28 @@ public: SourceLocation Loc, unsigned NumParams); - struct NamedReturnInfo { - const VarDecl *Candidate; - - enum Status : uint8_t { None, MoveEligible, MoveEligibleAndCopyElidable }; - Status S; - - bool isMoveEligible() const { return S != None; }; - bool isCopyElidable() const { return S == MoveEligibleAndCopyElidable; } + enum CopyElisionSemanticsKind { + CES_Strict = 0, + CES_AllowParameters = 1, + CES_AllowDifferentTypes = 2, + CES_AllowExceptionVariables = 4, + CES_AllowRValueReferenceType = 8, + CES_ImplicitlyMovableCXX11CXX14CXX17 = + (CES_AllowParameters | CES_AllowDifferentTypes), + CES_ImplicitlyMovableCXX20 = + (CES_AllowParameters | CES_AllowDifferentTypes | + CES_AllowExceptionVariables | CES_AllowRValueReferenceType), }; - 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); + VarDecl *getCopyElisionCandidate(QualType ReturnType, Expr *E, + CopyElisionSemanticsKind CESK); + bool isCopyElisionCandidate(QualType ReturnType, const VarDecl *VD, + CopyElisionSemanticsKind CESK); 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/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp index cdde9a8..850c189 100644 --- a/clang/lib/Sema/Sema.cpp +++ b/clang/lib/Sema/Sema.cpp @@ -1962,10 +1962,9 @@ static void checkEscapingByref(VarDecl *VD, Sema &S) { SourceLocation Loc = VD->getLocation(); Expr *VarRef = new (S.Context) DeclRefExpr(S.Context, VD, false, T, VK_LValue, Loc); - ExprResult Result = S.PerformCopyInitialization( - InitializedEntity::InitializeBlock(Loc, T, false), SourceLocation(), - VarRef); - + ExprResult Result = S.PerformMoveOrCopyInitialization( + InitializedEntity::InitializeBlock(Loc, T, false), VD, VD->getType(), + VarRef, /*AllowNRVO=*/true); 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 index cec8043..67c6478 100644 --- a/clang/lib/Sema/SemaCoroutine.cpp +++ b/clang/lib/Sema/SemaCoroutine.cpp @@ -994,10 +994,26 @@ StmtResult Sema::BuildCoreturnStmt(SourceLocation Loc, Expr *E, E = R.get(); } + // Move the return value if we can + if (E) { + const VarDecl *NRVOCandidate = this->getCopyElisionCandidate( + E->getType(), E, CES_ImplicitlyMovableCXX20); + if (NRVOCandidate) { + InitializedEntity Entity = + InitializedEntity::InitializeResult(Loc, E->getType(), NRVOCandidate); + ExprResult MoveResult = this->PerformMoveOrCopyInitialization( + Entity, NRVOCandidate, E->getType(), E); + if (MoveResult.get()) + E = MoveResult.get(); + } + } + + // FIXME: If the operand is a reference to a variable that's about to go out + // of scope, we should treat the operand as an xvalue for this overload + // resolution. VarDecl *Promise = FSI->CoroutinePromise; ExprResult PC; if (E && (isa(E) || !E->getType()->isVoidType())) { - getNamedReturnInfo(E, /*ForceCXX2b=*/true); PC = buildPromiseCall(*this, Promise, Loc, "return_value", E); } else { E = MakeFullDiscardedValueExpr(E).get(); @@ -1554,7 +1570,7 @@ bool CoroutineStmtBuilder::makeGroDeclAndReturnStmt() { // Trigger a nice error message. InitializedEntity Entity = InitializedEntity::InitializeResult(Loc, FnRetType, false); - S.PerformCopyInitialization(Entity, SourceLocation(), ReturnValue); + S.PerformMoveOrCopyInitialization(Entity, nullptr, FnRetType, ReturnValue); noteMemberDeclaredHere(S, ReturnValue, Fn); return false; } @@ -1570,8 +1586,8 @@ bool CoroutineStmtBuilder::makeGroDeclAndReturnStmt() { return false; InitializedEntity Entity = InitializedEntity::InitializeVariable(GroDecl); - ExprResult Res = - S.PerformCopyInitialization(Entity, SourceLocation(), ReturnValue); + ExprResult Res = S.PerformMoveOrCopyInitialization(Entity, nullptr, GroType, + this->ReturnValue); if (Res.isInvalid()) return false; diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp index a57c5ad..2bf575e 100644 --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -854,6 +854,10 @@ ExprResult Sema::BuildCXXThrow(SourceLocation OpLoc, Expr *Ex, Diag(OpLoc, diag::err_omp_simd_region_cannot_use_stmt) << "throw"; if (Ex && !Ex->isTypeDependent()) { + QualType ExceptionObjectTy = Context.getExceptionObjectType(Ex->getType()); + if (CheckCXXThrowOperand(OpLoc, ExceptionObjectTy, Ex)) + return ExprError(); + // Initialize the exception result. This implicitly weeds out // abstract types or types with inaccessible copy constructors. @@ -869,17 +873,15 @@ ExprResult Sema::BuildCXXThrow(SourceLocation OpLoc, Expr *Ex, // 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(); - - QualType ExceptionObjectTy = Context.getExceptionObjectType(Ex->getType()); - if (CheckCXXThrowOperand(OpLoc, ExceptionObjectTy, Ex)) - return ExprError(); + const VarDecl *NRVOVariable = nullptr; + if (IsThrownVarInScope) + NRVOVariable = getCopyElisionCandidate(QualType(), Ex, CES_Strict); InitializedEntity Entity = InitializedEntity::InitializeException( OpLoc, ExceptionObjectTy, - /*NRVO=*/NRInfo.isCopyElidable()); - ExprResult Res = PerformMoveOrCopyInitialization(Entity, NRInfo, Ex); + /*NRVO=*/NRVOVariable != nullptr); + ExprResult Res = PerformMoveOrCopyInitialization( + Entity, NRVOVariable, QualType(), Ex, IsThrownVarInScope); if (Res.isInvalid()) return ExprError(); Ex = Res.get(); diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp index afea878..8bb2e0e 100644 --- a/clang/lib/Sema/SemaStmt.cpp +++ b/clang/lib/Sema/SemaStmt.cpp @@ -3307,161 +3307,99 @@ Sema::ActOnBreakStmt(SourceLocation BreakLoc, Scope *CurScope) { return new (Context) BreakStmt(BreakLoc); } -/// Determine whether the given expression might be move-eligible or -/// copy-elidable in either a (co_)return statement or throw expression, -/// without considering function return type, if applicable. +/// Determine whether the given expression is a candidate for +/// copy elision in either a return statement or a throw expression. /// -/// \param E The expression being returned from the function or block, -/// being thrown, or being co_returned from a coroutine. This expression -/// might be modified by the implementation. +/// \param ReturnType If we're determining the copy elision candidate for +/// a return statement, this is the return type of the function. If we're +/// determining the copy elision candidate for a throw expression, this will +/// be a NULL type. /// -/// \param ForceCXX2b Overrides detection of current language mode -/// and uses the rules for C++2b. +/// \param E The expression being returned from the function or block, or +/// being thrown. /// -/// \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) { - if (!E) - return NamedReturnInfo(); +/// \param CESK Whether we allow function parameters or +/// id-expressions that could be moved out of the function to be considered NRVO +/// candidates. C++ prohibits these for NRVO itself, but we re-use this logic to +/// determine whether we should try to move as part of a return or throw (which +/// does allow function parameters). +/// +/// \returns The NRVO candidate variable, if the return statement may use the +/// NRVO, or NULL if there is no such candidate. +VarDecl *Sema::getCopyElisionCandidate(QualType ReturnType, Expr *E, + CopyElisionSemanticsKind CESK) { // - 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()); + DeclRefExpr *DR = dyn_cast(E->IgnoreParens()); if (!DR || DR->refersToEnclosingVariableOrCapture()) - return NamedReturnInfo(); - const auto *VD = dyn_cast(DR->getDecl()); + return nullptr; + VarDecl *VD = dyn_cast(DR->getDecl()); if (!VD) - return NamedReturnInfo(); - NamedReturnInfo Res = getNamedReturnInfo(VD, /*ForceCXX20=*/ForceCXX2b); - if (Res.Candidate && !E->isXValue() && - (ForceCXX2b || getLangOpts().CPlusPlus2b)) { - E = ImplicitCastExpr::Create(Context, VD->getType().getNonReferenceType(), - CK_NoOp, E, nullptr, VK_XValue, - FPOptionsOverride()); - } - return Res; -} + return nullptr; -/// 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 (isCopyElisionCandidate(ReturnType, VD, CESK)) + return VD; + return nullptr; } -/// Determine whether the given NRVO candidate variable is move-eligible or -/// copy-elidable, without considering function return type. -/// -/// \param VD The NRVO candidate variable. -/// -/// \param ForceCXX20 Overrides detection of current language mode -/// and uses the rules for C++20. -/// -/// \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, - bool ForceCXX20) { - bool hasCXX11 = getLangOpts().CPlusPlus11 || ForceCXX20; - bool hasCXX20 = getLangOpts().CPlusPlus20 || ForceCXX20; - NamedReturnInfo Info{VD, NamedReturnInfo::MoveEligibleAndCopyElidable}; - - // C++20 [class.copy.elision]p3: +bool Sema::isCopyElisionCandidate(QualType ReturnType, const VarDecl *VD, + CopyElisionSemanticsKind CESK) { + QualType VDType = VD->getType(); // - in a return statement in a function with ... - // (other than a function ... parameter) - if (VD->getKind() == Decl::ParmVar) - disallowNRVO(Info, hasCXX11); - else if (VD->getKind() != Decl::Var) - return NamedReturnInfo(); + // ... a class return type ... + if (!ReturnType.isNull() && !ReturnType->isDependentType()) { + if (!ReturnType->isRecordType()) + return false; + // ... the same cv-unqualified type as the function return type ... + // When considering moving this expression out, allow dissimilar types. + if (!(CESK & CES_AllowDifferentTypes) && !VDType->isDependentType() && + !Context.hasSameUnqualifiedType(ReturnType, VDType)) + return false; + } - // (other than ... a catch-clause parameter) - if (VD->isExceptionVariable()) - disallowNRVO(Info, hasCXX20); + // ...object (other than a function or catch-clause parameter)... + if (VD->getKind() != Decl::Var && + !((CESK & CES_AllowParameters) && VD->getKind() == Decl::ParmVar)) + return false; + if (!(CESK & CES_AllowExceptionVariables) && VD->isExceptionVariable()) + return false; // ...automatic... - if (!VD->hasLocalStorage()) - return NamedReturnInfo(); + if (!VD->hasLocalStorage()) return false; - // We don't want to implicitly move out of a __block variable during a return - // because we cannot assume the variable will no longer be used. + // Return false if VD is a __block variable. We don't want to implicitly move + // out of a __block variable during a return because we cannot assume the + // variable will no longer be used. if (VD->hasAttr()) - return NamedReturnInfo(); + return false; - QualType VDType = VD->getType(); if (VDType->isObjectType()) { // C++17 [class.copy.elision]p3: // ...non-volatile automatic object... if (VDType.isVolatileQualified()) - return NamedReturnInfo(); + return false; } else if (VDType->isRValueReferenceType()) { // C++20 [class.copy.elision]p3: - // ...either a non-volatile object or an rvalue reference to a non-volatile - // object type... + // ...either a non-volatile object or an rvalue reference to a non-volatile object type... + if (!(CESK & CES_AllowRValueReferenceType)) + return false; QualType VDReferencedType = VDType.getNonReferenceType(); - if (VDReferencedType.isVolatileQualified() || - !VDReferencedType->isObjectType()) - return NamedReturnInfo(); - disallowNRVO(Info, hasCXX20); + if (VDReferencedType.isVolatileQualified() || !VDReferencedType->isObjectType()) + return false; } else { - return NamedReturnInfo(); + return false; } + if (CESK & CES_AllowDifferentTypes) + return true; + // Variables with higher required alignment than their type's ABI // 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(); + return false; - 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; + return true; } /// Try to perform the initialization of a potentially-movable value, @@ -3486,7 +3424,8 @@ const VarDecl *Sema::getCopyElisionCandidate(NamedReturnInfo &Info, /// 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, + const VarDecl *NRVOCandidate, + QualType ResultType, Expr *&Value, bool ConvertingConstructorsOnly, bool IsDiagnosticsCheck, ExprResult &Res) { ImplicitCastExpr AsRvalue(ImplicitCastExpr::OnStack, Value->getType(), @@ -3569,41 +3508,63 @@ static bool TryMoveInitialization(Sema &S, const InitializedEntity &Entity, /// 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; +ExprResult Sema::PerformMoveOrCopyInitialization( + const InitializedEntity &Entity, const VarDecl *NRVOCandidate, + QualType ResultType, Expr *Value, bool AllowNRVO) { + ExprResult Res = ExprError(); + bool NeedSecondOverloadResolution = true; + + if (AllowNRVO) { + CopyElisionSemanticsKind CESK = CES_Strict; + if (getLangOpts().CPlusPlus20) { + CESK = CES_ImplicitlyMovableCXX20; + } else if (getLangOpts().CPlusPlus11) { + CESK = CES_ImplicitlyMovableCXX11CXX14CXX17; + } + + if (!NRVOCandidate) { + NRVOCandidate = getCopyElisionCandidate(ResultType, Value, CESK); } - if (!getDiagnostics().isIgnored(diag::warn_return_std_move, + + if (NRVOCandidate) { + NeedSecondOverloadResolution = + TryMoveInitialization(*this, Entity, NRVOCandidate, ResultType, Value, + !getLangOpts().CPlusPlus20, false, Res); + } + + if (!getLangOpts().CPlusPlus20 && NeedSecondOverloadResolution && + !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); + const VarDecl *FakeNRVOCandidate = getCopyElisionCandidate( + QualType(), Value, CES_ImplicitlyMovableCXX20); + if (FakeNRVOCandidate) { + QualType QT = FakeNRVOCandidate->getType(); + if (QT->isLValueReferenceType()) { + // Adding 'std::move' around an lvalue reference variable's name is + // dangerous. Don't suggest it. + } else 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, FakeNRVOCandidate, ResultType, + FakeValue, false, true, FakeRes); + if (!FakeRes.isInvalid()) { + bool IsThrow = + (Entity.getKind() == InitializedEntity::EK_Exception); + SmallString<32> Str; + Str += "std::move("; + Str += FakeNRVOCandidate->getDeclName().getAsString(); + Str += ")"; + Diag(Value->getExprLoc(), diag::warn_return_std_move) + << Value->getSourceRange() + << FakeNRVOCandidate->getDeclName() << IsThrow; + Diag(Value->getExprLoc(), diag::note_add_std_move) + << FixItHint::CreateReplacement(Value->getSourceRange(), Str); + } } } } @@ -3612,7 +3573,10 @@ Sema::PerformMoveOrCopyInitialization(const InitializedEntity &Entity, // 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); + if (NeedSecondOverloadResolution) + Res = PerformCopyInitialization(Entity, SourceLocation(), Value); + + return Res; } /// Determine whether the declared return type of the specified function @@ -3626,9 +3590,8 @@ static bool hasDeducedReturnType(FunctionDecl *FD) { /// ActOnCapScopeReturnStmt - Utility routine to type-check return statements /// for capturing scopes. /// -StmtResult Sema::ActOnCapScopeReturnStmt(SourceLocation ReturnLoc, - Expr *RetValExp, - NamedReturnInfo &NRInfo) { +StmtResult +Sema::ActOnCapScopeReturnStmt(SourceLocation ReturnLoc, 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()); @@ -3707,7 +3670,7 @@ StmtResult Sema::ActOnCapScopeReturnStmt(SourceLocation ReturnLoc, if (CurCap->ReturnType.isNull()) CurCap->ReturnType = FnRetType; } - const VarDecl *NRVOCandidate = getCopyElisionCandidate(NRInfo, FnRetType); + assert(!FnRetType.isNull()); if (auto *CurBlock = dyn_cast(CurCap)) { if (CurBlock->FunctionType->castAs()->getNoReturnAttr()) { @@ -3730,6 +3693,7 @@ StmtResult Sema::ActOnCapScopeReturnStmt(SourceLocation ReturnLoc, // Otherwise, verify that this result type matches the previous one. We are // pickier with blocks than for normal functions because we don't have GCC // compatibility to worry about here. + const VarDecl *NRVOCandidate = nullptr; if (FnRetType->isDependentType()) { // Delay processing for now. TODO: there are lots of dependent // types we can conclusively prove aren't void. @@ -3757,15 +3721,20 @@ StmtResult Sema::ActOnCapScopeReturnStmt(SourceLocation ReturnLoc, // In C++ the return statement is handled via a copy initialization. // the C version of which boils down to CheckSingleAssignmentConstraints. - InitializedEntity Entity = InitializedEntity::InitializeResult( - ReturnLoc, FnRetType, NRVOCandidate != nullptr); - ExprResult Res = PerformMoveOrCopyInitialization(Entity, NRInfo, RetValExp); + NRVOCandidate = getCopyElisionCandidate(FnRetType, RetValExp, CES_Strict); + InitializedEntity Entity = InitializedEntity::InitializeResult(ReturnLoc, + FnRetType, + NRVOCandidate != nullptr); + ExprResult Res = PerformMoveOrCopyInitialization(Entity, NRVOCandidate, + FnRetType, RetValExp); if (Res.isInvalid()) { // FIXME: Cleanup temporaries here, anyway? return StmtError(); } RetValExp = Res.get(); CheckReturnValExpr(RetValExp, FnRetType, ReturnLoc); + } else { + NRVOCandidate = getCopyElisionCandidate(FnRetType, RetValExp, CES_Strict); } if (RetValExp) { @@ -3974,10 +3943,8 @@ StmtResult Sema::BuildReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp) { 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; @@ -4049,7 +4016,6 @@ StmtResult Sema::BuildReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp) { } } } - const VarDecl *NRVOCandidate = getCopyElisionCandidate(NRInfo, FnRetType); bool HasDependentReturnType = FnRetType->isDependentType(); @@ -4156,6 +4122,8 @@ StmtResult Sema::BuildReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp) { /* NRVOCandidate=*/nullptr); } else { assert(RetValExp || HasDependentReturnType); + const VarDecl *NRVOCandidate = nullptr; + QualType RetType = RelatedRetType.isNull() ? FnRetType : RelatedRetType; // C99 6.8.6.4p3(136): The return statement is not an assignment. The @@ -4164,12 +4132,15 @@ StmtResult Sema::BuildReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp) { // In C++ the return statement is handled via a copy initialization, // the C version of which boils down to CheckSingleAssignmentConstraints. + if (RetValExp) + NRVOCandidate = getCopyElisionCandidate(FnRetType, RetValExp, CES_Strict); if (!HasDependentReturnType && !RetValExp->isTypeDependent()) { // we have a non-void function with an expression, continue checking - InitializedEntity Entity = InitializedEntity::InitializeResult( - ReturnLoc, RetType, NRVOCandidate != nullptr); - ExprResult Res = - PerformMoveOrCopyInitialization(Entity, NRInfo, RetValExp); + InitializedEntity Entity = InitializedEntity::InitializeResult(ReturnLoc, + RetType, + NRVOCandidate != nullptr); + ExprResult Res = PerformMoveOrCopyInitialization(Entity, NRVOCandidate, + RetType, 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 index 1327e08..8de09de 100644 --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -23,7 +23,6 @@ #include "clang/Basic/TargetInfo.h" #include "clang/Sema/Initialization.h" #include "clang/Sema/Lookup.h" -#include "clang/Sema/ScopeInfo.h" #include "clang/Sema/SemaInternal.h" #include "clang/Sema/Template.h" #include "clang/Sema/TemplateInstCallback.h" @@ -1086,30 +1085,11 @@ Decl *TemplateDeclInstantiator::VisitVarDecl(VarDecl *D, SemaRef.BuildVariableInstantiation(Var, D, TemplateArgs, LateAttrs, Owner, StartingScope, InstantiatingVarTemplate); + if (D->isNRVOVariable()) { - QualType RT; - if (auto *F = dyn_cast(DC)) - RT = F->getReturnType(); - else if (isa(DC)) - RT = cast(SemaRef.getCurBlock()->FunctionType) - ->getReturnType(); - else - llvm_unreachable("Unknown context type"); - - // This is the last chance we have of checking copy elision eligibility - // for functions in depdendent contexts. The sema actions for building - // the return statement during template instantiation will have no effect - // regarding copy elision, since NRVO propagation runs on the scope exit - // actions, and these are not run on instantiation. - // This might run through some VarDecls which were returned from non-taken - // 'if constexpr' branches, and these will end up being constructed on the - // return slot even if they will never be returned, as a sort of accidental - // '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); + QualType ReturnType = cast(DC)->getReturnType(); + if (SemaRef.isCopyElisionCandidate(ReturnType, Var, Sema::CES_Strict)) + Var->setNRVOVariable(true); } Var->setImplicit(D->isImplicit()); diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp index b0ed636..4c5e651 100644 --- a/clang/lib/Sema/SemaType.cpp +++ b/clang/lib/Sema/SemaType.cpp @@ -8899,10 +8899,6 @@ static QualType getDecltypeForExpr(Sema &S, Expr *E) { 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: @@ -8913,7 +8909,7 @@ static QualType getDecltypeForExpr(Sema &S, Expr *E) { // 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(IDExpr)) + if (const auto *SNTTPE = dyn_cast(E)) return SNTTPE->getParameterType(S.Context); // - if e is an unparenthesized id-expression or an unparenthesized class @@ -8922,22 +8918,21 @@ static QualType getDecltypeForExpr(Sema &S, Expr *E) { // functions, the program is ill-formed; // // We apply the same rules for Objective-C ivar and property references. - if (const DeclRefExpr *DRE = dyn_cast(IDExpr)) { + if (const DeclRefExpr *DRE = dyn_cast(E)) { 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(IDExpr)) { + } else if (const MemberExpr *ME = dyn_cast(E)) { if (const ValueDecl *VD = ME->getMemberDecl()) if (isa(VD) || isa(VD)) return VD->getType(); - } else if (const ObjCIvarRefExpr *IR = dyn_cast(IDExpr)) { + } else if (const ObjCIvarRefExpr *IR = dyn_cast(E)) { return IR->getDecl()->getType(); - } else if (const ObjCPropertyRefExpr *PR = - dyn_cast(IDExpr)) { + } else if (const ObjCPropertyRefExpr *PR = dyn_cast(E)) { if (PR->isExplicitProperty()) return PR->getExplicitProperty()->getType(); - } else if (auto *PE = dyn_cast(IDExpr)) { + } else if (auto *PE = dyn_cast(E)) { return PE->getType(); } @@ -8950,8 +8945,8 @@ static QualType getDecltypeForExpr(Sema &S, Expr *E) { // entity. using namespace sema; if (S.getCurLambda()) { - if (isa(IDExpr)) { - if (DeclRefExpr *DRE = dyn_cast(IDExpr->IgnoreParens())) { + if (isa(E)) { + if (DeclRefExpr *DRE = dyn_cast(E->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 index bf79ac9..9d1d7d9 100644 --- a/clang/test/CXX/class/class.init/class.copy.elision/p3.cpp +++ b/clang/test/CXX/class/class.init/class.copy.elision/p3.cpp @@ -1,8 +1,8 @@ -// 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_20,cxx20_2b %s -// RUN: %clang_cc1 -std=c++17 -fsyntax-only -fcxx-exceptions -verify=expected,cxx11_17,cxx11_20 %s -// RUN: %clang_cc1 -std=c++14 -fsyntax-only -fcxx-exceptions -verify=expected,cxx11_17,cxx11_20 %s -// RUN: %clang_cc1 -std=c++11 -fsyntax-only -fcxx-exceptions -verify=expected,cxx11_17,cxx11_20 %s +// RUN: %clang_cc1 -std=c++2b -fsyntax-only -fcxx-exceptions -verify=expected,cxx20_2b %s +// RUN: %clang_cc1 -std=c++20 -fsyntax-only -fcxx-exceptions -verify=expected,cxx20_2b %s +// RUN: %clang_cc1 -std=c++17 -fsyntax-only -fcxx-exceptions -verify=expected,cxx11_17 %s +// RUN: %clang_cc1 -std=c++14 -fsyntax-only -fcxx-exceptions -verify=expected,cxx11_17 %s +// RUN: %clang_cc1 -std=c++11 -fsyntax-only -fcxx-exceptions -verify=expected,cxx11_17 %s namespace test_delete_function { struct A1 { @@ -409,10 +409,8 @@ Target t4() { namespace test_simpler_implicit_move { struct CopyOnly { - CopyOnly(); // cxx2b-note {{candidate constructor not viable: requires 0 arguments, but 1 was provided}} - // cxx2b-note@-1 {{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}} - // cxx2b-note@-1 {{candidate constructor not viable: expects an lvalue for 1st argument}} + CopyOnly(); + CopyOnly(CopyOnly &); }; struct MoveOnly { MoveOnly(); @@ -421,7 +419,7 @@ struct MoveOnly { MoveOnly &&rref(); MoveOnly &&test1(MoveOnly &&w) { - return w; // cxx11_20-error {{cannot bind to lvalue of type}} + return w; // expected-error {{cannot bind to lvalue of type}} } CopyOnly test2(bool b) { @@ -430,22 +428,22 @@ CopyOnly test2(bool b) { if (b) { return w1; } else { - return w2; // cxx2b-error {{no matching constructor for initialization}} + return w2; } } -template T &&test3(T &&x) { return x; } // cxx11_20-error {{cannot bind to lvalue of type}} +template T &&test3(T &&x) { return x; } // expected-error {{cannot bind to lvalue of type}} template MoveOnly& test3(MoveOnly&); -template MoveOnly &&test3(MoveOnly &&); // cxx11_20-note {{in instantiation of function template specialization}} +template MoveOnly&& test3(MoveOnly&&); // expected-note {{in instantiation of function template specialization}} MoveOnly &&test4() { MoveOnly &&x = rref(); - return x; // cxx11_20-error {{cannot bind to lvalue of type}} + return x; // expected-error {{cannot bind to lvalue of type}} } void test5() try { CopyOnly x; - throw x; // cxx2b-error {{no matching constructor for initialization}} + throw x; } catch (...) { } diff --git a/clang/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p7-cxx14.cpp b/clang/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p7-cxx14.cpp index 609ab8c..018e5b9 100644 --- a/clang/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p7-cxx14.cpp +++ b/clang/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p7-cxx14.cpp @@ -1,6 +1,6 @@ -// RUN: %clang_cc1 -verify -std=c++2b -verify=expected,cxx2b %s -// RUN: %clang_cc1 -verify -std=c++20 -verify=expected,cxx14_20 %s -// RUN: %clang_cc1 -verify -std=c++14 -verify=expected,cxx14_20 %s +// RUN: %clang_cc1 -verify -std=c++2b -verify %s +// RUN: %clang_cc1 -verify -std=c++20 -verify %s +// RUN: %clang_cc1 -verify -std=c++14 -verify %s namespace std { template struct initializer_list { @@ -30,7 +30,7 @@ using Int = decltype(x3d); auto x4a = (i); decltype(auto) x4d = (i); using Int = decltype(x4a); -using IntLRef = decltype(x4d); // cxx2b-note {{previous definition is here}} +using IntLRef = decltype(x4d); auto x5a = f(); decltype(auto) x5d = f(); @@ -81,7 +81,7 @@ using Int = decltype(f2d(0)); auto f3a(int n) { return (n); } decltype(auto) f3d(int n) { return (n); } // expected-warning {{reference to stack memory}} using Int = decltype(f3a(0)); -using IntLRef = decltype(f3d(0)); // cxx2b-error {{type alias redefinition with different types ('decltype(f3d(0))' (aka 'int &&') vs 'decltype(x4d)' (aka 'int &'))}} +using IntLRef = decltype(f3d(0)); auto f4a(int n) { return f(); } decltype(auto) f4d(int n) { return f(); } @@ -91,7 +91,7 @@ using IntRRef = decltype(f4d(0)); auto f5aa(int n) { auto x = f(); return x; } auto f5ad(int n) { decltype(auto) x = f(); return x; } decltype(auto) f5da(int n) { auto x = f(); return x; } -decltype(auto) f5dd(int n) { decltype(auto) x = f(); return x; } // cxx14_20-error {{rvalue reference to type 'int' cannot bind to lvalue}} +decltype(auto) f5dd(int n) { decltype(auto) x = f(); return x; } // expected-error {{rvalue reference to type 'int' cannot bind to lvalue}} using Int = decltype(f5aa(0)); using Int = decltype(f5ad(0)); using Int = decltype(f5da(0)); diff --git a/clang/test/CXX/drs/dr3xx.cpp b/clang/test/CXX/drs/dr3xx.cpp index 5c4c755..d91ad0c 100644 --- a/clang/test/CXX/drs/dr3xx.cpp +++ b/clang/test/CXX/drs/dr3xx.cpp @@ -1,9 +1,9 @@ -// RUN: %clang_cc1 -std=c++2b -verify=expected,cxx20_2b,cxx2b -triple %itanium_abi_triple %s -fexceptions -fcxx-exceptions -pedantic-errors -// RUN: %clang_cc1 -std=c++20 -verify=expected,cxx98_20,cxx20_2b -triple %itanium_abi_triple %s -fexceptions -fcxx-exceptions -pedantic-errors -// RUN: %clang_cc1 -std=c++17 -verify=expected,cxx98_17,cxx98_20 -triple %itanium_abi_triple %s -fexceptions -fcxx-exceptions -pedantic-errors -// RUN: %clang_cc1 -std=c++14 -verify=expected,cxx98_17,cxx98_20 -triple %itanium_abi_triple %s -fexceptions -fcxx-exceptions -pedantic-errors -// RUN: %clang_cc1 -std=c++11 -verify=expected,cxx98_17,cxx98_20 -triple %itanium_abi_triple %s -fexceptions -fcxx-exceptions -pedantic-errors -// RUN: %clang_cc1 -std=c++98 -verify=expected,cxx98_17,cxx98_20 -triple %itanium_abi_triple %s -fexceptions -fcxx-exceptions -pedantic-errors +// RUN: %clang_cc1 -std=c++2b -verify=expected,cxx20_2b -triple %itanium_abi_triple %s -fexceptions -fcxx-exceptions -pedantic-errors +// RUN: %clang_cc1 -std=c++20 -verify=expected,cxx20_2b -triple %itanium_abi_triple %s -fexceptions -fcxx-exceptions -pedantic-errors +// RUN: %clang_cc1 -std=c++17 -verify=expected,cxx98_17 -triple %itanium_abi_triple %s -fexceptions -fcxx-exceptions -pedantic-errors +// RUN: %clang_cc1 -std=c++14 -verify=expected,cxx98_17 -triple %itanium_abi_triple %s -fexceptions -fcxx-exceptions -pedantic-errors +// RUN: %clang_cc1 -std=c++11 -verify=expected,cxx98_17 -triple %itanium_abi_triple %s -fexceptions -fcxx-exceptions -pedantic-errors +// RUN: %clang_cc1 -std=c++98 -verify=expected,cxx98_17 -triple %itanium_abi_triple %s -fexceptions -fcxx-exceptions -pedantic-errors namespace dr300 { // dr300: yes template void f(R (&)(A)) {} @@ -628,8 +628,7 @@ namespace dr349 { // dr349: no struct A { template operator T ***() { int ***p = 0; - return p; // cxx98_20-error {{cannot initialize return object of type 'const int ***' with an lvalue of type 'int ***'}} - // cxx2b-error@-1 {{cannot initialize return object of type 'const int ***' with an rvalue of type 'int ***'}} + return p; // expected-error {{cannot initialize return object of type 'const int ***' with an lvalue of type 'int ***'}} } }; diff --git a/clang/test/CXX/expr/expr.prim/expr.prim.lambda/p4-cxx14.cpp b/clang/test/CXX/expr/expr.prim/expr.prim.lambda/p4-cxx14.cpp index f780bf7..8a37910 100644 --- a/clang/test/CXX/expr/expr.prim/expr.prim.lambda/p4-cxx14.cpp +++ b/clang/test/CXX/expr/expr.prim/expr.prim.lambda/p4-cxx14.cpp @@ -1,6 +1,6 @@ -// RUN: %clang_cc1 -std=c++2b -fsyntax-only -verify=expected,cxx2b %s -// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify=expected,cxx14_20 %s -// RUN: %clang_cc1 -std=c++14 -fsyntax-only -verify=expected,cxx14_20 %s +// RUN: %clang_cc1 -std=c++2b -fsyntax-only -verify %s +// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify %s +// RUN: %clang_cc1 -std=c++14 -fsyntax-only -verify %s int a; int &b = [] (int &r) -> decltype(auto) { return r; } (a); @@ -9,15 +9,13 @@ int &d = [] (int &r) -> auto & { return r; } (a); int &e = [] (int &r) -> auto { return r; } (a); // expected-error {{cannot bind to a temporary}} int &f = [] (int r) -> decltype(auto) { return r; } (a); // expected-error {{cannot bind to a temporary}} int &g = [] (int r) -> decltype(auto) { return (r); } (a); // expected-warning {{reference to stack}} -// cxx2b-error@-1 {{non-const lvalue reference to type 'int' cannot bind to a temporary of type 'int'}} int test_explicit_auto_return() { struct X {}; auto L = [](auto F, auto a) { return F(a); }; auto M = [](auto a) -> auto { return a; }; // OK - auto MRef = [](auto b) -> auto & { return b; }; //cxx14_20-warning{{reference to stack}} - // cxx2b-error@-1 {{non-const lvalue reference to type 'X' cannot bind to a temporary of type 'X'}} + auto MRef = [](auto b) -> auto& { return b; }; //expected-warning{{reference to stack}} auto MPtr = [](auto c) -> auto* { return &c; }; //expected-warning{{address of stack}} auto MDeclType = [](auto&& d) -> decltype(auto) { return static_cast(d); }; //OK M(3); diff --git a/clang/test/CXX/temp/temp.decls/temp.mem/p5.cpp b/clang/test/CXX/temp/temp.decls/temp.mem/p5.cpp index 24a70a8..3ca9f5e 100644 --- a/clang/test/CXX/temp/temp.decls/temp.mem/p5.cpp +++ b/clang/test/CXX/temp/temp.decls/temp.mem/p5.cpp @@ -1,7 +1,7 @@ -// RUN: %clang_cc1 -std=c++2b -fsyntax-only -verify=expected,cxx2b %s -// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify=expected,cxx98_20 %s -// RUN: %clang_cc1 -std=c++98 -fsyntax-only -verify=expected,cxx98_20 %s -// RUN: %clang_cc1 -fsyntax-only -verify=expected,cxx98_20 %s +// RUN: %clang_cc1 -std=c++2b -fsyntax-only -verify %s +// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify %s +// RUN: %clang_cc1 -std=c++98 -fsyntax-only -verify %s +// RUN: %clang_cc1 -fsyntax-only -verify %s struct A { template operator T*(); @@ -67,10 +67,8 @@ struct X0 { template operator const T*() const { T x = T(); - return x; // cxx98_20-error{{cannot initialize return object of type 'const char *' with an lvalue of type 'char'}} \ - // cxx98_20-error{{cannot initialize return object of type 'const int *' with an lvalue of type 'int'}} \ - // cxx2b-error{{cannot initialize return object of type 'const char *' with an rvalue of type 'char'}} \ - // cxx2b-error{{cannot initialize return object of type 'const int *' with an rvalue of type 'int'}} + return x; // expected-error{{cannot initialize return object of type 'const char *' with an lvalue of type 'char'}} \ + // expected-error{{cannot initialize return object of type 'const int *' with an lvalue of type 'int'}} } }; diff --git a/clang/test/CodeGen/nrvo-tracking.cpp b/clang/test/CodeGen/nrvo-tracking.cpp index b1aa673..db5aa19 100644 --- a/clang/test/CodeGen/nrvo-tracking.cpp +++ b/clang/test/CodeGen/nrvo-tracking.cpp @@ -29,6 +29,8 @@ L(2, t, X&); // CHECK-LABEL: define{{.*}} void @_Z2l3v // CHECK: call {{.*}} @_ZN1XC1Ev +// CHECK-NEXT: call {{.*}} @_ZN1XC1EOS_ +// CHECK-NEXT: call void @llvm.lifetime.end // CHECK-NEXT: call void @llvm.lifetime.end // CHECK-NEXT: ret void L(3, t, T); @@ -150,11 +152,7 @@ F(8, (t), decltype(auto)); }; }()(); \ } -// CHECK-LABEL: define{{.*}} void @_Z2b1v -// CHECK: call {{.*}} @_ZN1XC1Ev -// CHECK-NEXT: call void @llvm.lifetime.end -// CHECK-NEXT: ret void -B(1, X); +//B(1, X); // Uncomment this line at your own peril ;) // CHECK-LABEL: define{{.*}} void @_Z2b2v // CHECK: call {{.*}} @_ZN1XC1Ev @@ -166,6 +164,8 @@ B(2, X&); // CHECK-LABEL: define{{.*}} void @_Z2b3v // CHECK: call {{.*}} @_ZN1XC1Ev +// CHECK-NEXT: call {{.*}} @_ZN1XC1EOS_ +// CHECK-NEXT: call void @llvm.lifetime.end // CHECK-NEXT: call void @llvm.lifetime.end // CHECK-NEXT: ret void B(3, T); @@ -187,24 +187,3 @@ B(4, T&); B(5, ); #undef B - -// CHECK-LABEL: define{{.*}} void @_Z6f_attrv -// CHECK: call {{.*}} @_ZN1XC1Ev -// CHECK-NEXT: call void @llvm.lifetime.end -// CHECK-NEXT: ret void -template [[gnu::cdecl]] static inline auto tf_attr() -> X { - T t; - return t; -} -void f_attr() { auto t = tf_attr(); } - -// CHECK-LABEL: define{{.*}} void @_Z6b_attrv -// CHECK: call {{.*}} @_ZN1XC1Ev -// CHECK-NEXT: call void @llvm.lifetime.end -// CHECK-NEXT: ret void -void b_attr() { - auto t = []() { return ^ X () [[clang::vectorcall]] { - T t; - return t; - }; }()(); -} diff --git a/clang/test/SemaCXX/constant-expression-cxx11.cpp b/clang/test/SemaCXX/constant-expression-cxx11.cpp index 8d7e460..5a992b6 100644 --- a/clang/test/SemaCXX/constant-expression-cxx11.cpp +++ b/clang/test/SemaCXX/constant-expression-cxx11.cpp @@ -1,6 +1,6 @@ -// RUN: %clang_cc1 -std=c++2b -fsyntax-only -verify=expected,cxx20_2b,cxx2b -triple x86_64-linux -Wno-string-plus-int -Wno-pointer-arith -Wno-zero-length-array -Wno-c99-designator -fcxx-exceptions -pedantic %s -Wno-comment -Wno-tautological-pointer-compare -Wno-bool-conversion -// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify=expected,cxx11_20,cxx20_2b -triple x86_64-linux -Wno-string-plus-int -Wno-pointer-arith -Wno-zero-length-array -Wno-c99-designator -fcxx-exceptions -pedantic %s -Wno-comment -Wno-tautological-pointer-compare -Wno-bool-conversion -// RUN: %clang_cc1 -std=c++11 -fsyntax-only -verify=expected,cxx11_20,cxx11 -triple x86_64-linux -Wno-string-plus-int -Wno-pointer-arith -Wno-zero-length-array -Wno-c99-designator -fcxx-exceptions -pedantic %s -Wno-comment -Wno-tautological-pointer-compare -Wno-bool-conversion +// RUN: %clang_cc1 -std=c++2b -fsyntax-only -verify=expected,cxx20_2b -triple x86_64-linux -Wno-string-plus-int -Wno-pointer-arith -Wno-zero-length-array -Wno-c99-designator -fcxx-exceptions -pedantic %s -Wno-comment -Wno-tautological-pointer-compare -Wno-bool-conversion +// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify=expected,cxx20_2b -triple x86_64-linux -Wno-string-plus-int -Wno-pointer-arith -Wno-zero-length-array -Wno-c99-designator -fcxx-exceptions -pedantic %s -Wno-comment -Wno-tautological-pointer-compare -Wno-bool-conversion +// RUN: %clang_cc1 -std=c++11 -fsyntax-only -verify=expected,cxx11 -triple x86_64-linux -Wno-string-plus-int -Wno-pointer-arith -Wno-zero-length-array -Wno-c99-designator -fcxx-exceptions -pedantic %s -Wno-comment -Wno-tautological-pointer-compare -Wno-bool-conversion namespace StaticAssertFoldTest { @@ -1938,18 +1938,13 @@ namespace Lifetime { } constexpr int &get(int &&n) { return n; } - // cxx2b-error@-1 {{non-const lvalue reference to type 'int' cannot bind to a temporary of type 'int'}} - // cxx2b-error@-2 {{no return statement in constexpr function}} See PR40598 constexpr int &&get_rv(int &&n) { return static_cast(n); } struct S { int &&r; int &s; int t; constexpr S() : r(get_rv(0)), s(get(0)), t(r) {} // expected-note {{read of object outside its lifetime}} - constexpr S(int) : r(get_rv(0)), s(get(0)), t(s) {} - // cxx2b-warning@-1 {{reference 's' is not yet bound to a value when used here}} - // cxx2b-note@-2 {{read of uninitialized object is not allowed in a constant expression}} - // cxx11_20-note@-3 {{read of object outside its lifetime}} + constexpr S(int) : r(get_rv(0)), s(get(0)), t(s) {} // expected-note {{read of object outside its lifetime}} }; constexpr int k1 = S().t; // expected-error {{constant expression}} expected-note {{in call}} constexpr int k2 = S(0).t; // expected-error {{constant expression}} expected-note {{in call}} diff --git a/clang/test/SemaCXX/constant-expression-cxx14.cpp b/clang/test/SemaCXX/constant-expression-cxx14.cpp index ee6d796..6d213ba 100644 --- a/clang/test/SemaCXX/constant-expression-cxx14.cpp +++ b/clang/test/SemaCXX/constant-expression-cxx14.cpp @@ -1,6 +1,6 @@ -// RUN: %clang_cc1 -std=c++2b -fsyntax-only -verify=expected,cxx20_2b,cxx2b %s -fcxx-exceptions -triple=x86_64-linux-gnu -// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify=expected,cxx14_20,cxx20_2b,cxx20 %s -fcxx-exceptions -triple=x86_64-linux-gnu -// RUN: %clang_cc1 -std=c++14 -fsyntax-only -verify=expected,cxx14_20,cxx14 %s -fcxx-exceptions -triple=x86_64-linux-gnu +// RUN: %clang_cc1 -std=c++2b -fsyntax-only -verify=expected,cxx20_2b %s -fcxx-exceptions -triple=x86_64-linux-gnu +// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify=expected,cxx20_2b %s -fcxx-exceptions -triple=x86_64-linux-gnu +// RUN: %clang_cc1 -std=c++14 -fsyntax-only -verify=expected,cxx14 %s -fcxx-exceptions -triple=x86_64-linux-gnu struct S { // dummy ctor to make this a literal type @@ -269,23 +269,16 @@ namespace null { namespace incdec { template constexpr T &ref(T &&r) { return r; } - // cxx2b-error@-1 {{non-const lvalue reference to type 'int' cannot bind to a temporary of type 'int'}} template constexpr T postinc(T &&r) { return (r++, r); } template constexpr T postdec(T &&r) { return (r--, r); } - template int &ref(int &&); - // cxx2b-note@-1 {{in instantiation of function template specialization}} - - static_assert(postinc(0) == 1, ""); - static_assert(postdec(0) == -1, ""); -#if __cplusplus <= 202002L static_assert(++ref(0) == 1, ""); static_assert(ref(0)++ == 0, ""); + static_assert(postinc(0) == 1, ""); static_assert(--ref(0) == -1, ""); static_assert(ref(0)-- == 0, ""); -#endif + static_assert(postdec(0) == -1, ""); -#if __cplusplus <= 202002L constexpr int overflow_int_inc_1 = ref(0x7fffffff)++; // expected-error {{constant}} expected-note {{2147483648}} constexpr int overflow_int_inc_1_ok = ref(0x7ffffffe)++; constexpr int overflow_int_inc_2 = ++ref(0x7fffffff); // expected-error {{constant}} expected-note {{2147483648}} @@ -298,42 +291,37 @@ namespace incdec { // inc on bool sets to true static_assert(++ref(false), ""); // cxx14-warning@-1 {{incrementing expression of type bool}} - // cxx20-error@-2 {{incrementing expression of type bool}} + // cxx20_2b-error@-2 {{incrementing expression of type bool}} static_assert(++ref(true), ""); // cxx14-warning@-1 {{incrementing expression of type bool}} - // cxx20-error@-2 {{incrementing expression of type bool}} -#endif + // cxx20_2b-error@-2 {{incrementing expression of type bool}} int arr[10]; - static_assert(postinc(&arr[0]) == &arr[1], ""); - static_assert(postdec(&arr[1]) == &arr[0], ""); -#if __cplusplus <= 202002L static_assert(++ref(&arr[0]) == &arr[1], ""); static_assert(++ref(&arr[9]) == &arr[10], ""); static_assert(++ref(&arr[10]) == &arr[11], ""); // expected-error {{constant}} expected-note {{cannot refer to element 11}} static_assert(ref(&arr[0])++ == &arr[0], ""); static_assert(ref(&arr[10])++ == &arr[10], ""); // expected-error {{constant}} expected-note {{cannot refer to element 11}} + static_assert(postinc(&arr[0]) == &arr[1], ""); static_assert(--ref(&arr[10]) == &arr[9], ""); static_assert(--ref(&arr[1]) == &arr[0], ""); static_assert(--ref(&arr[0]) != &arr[0], ""); // expected-error {{constant}} expected-note {{cannot refer to element -1}} static_assert(ref(&arr[1])-- == &arr[1], ""); static_assert(ref(&arr[0])-- == &arr[0], ""); // expected-error {{constant}} expected-note {{cannot refer to element -1}} -#endif + static_assert(postdec(&arr[1]) == &arr[0], ""); - static_assert(postinc(0.0) == 1.0, ""); - static_assert(postdec(0.0) == -1.0, ""); -#if __cplusplus <= 202002L int x; static_assert(++ref(&x) == &x + 1, ""); static_assert(++ref(0.0) == 1.0, ""); static_assert(ref(0.0)++ == 0.0, ""); + static_assert(postinc(0.0) == 1.0, ""); static_assert(--ref(0.0) == -1.0, ""); static_assert(ref(0.0)-- == 0.0, ""); + static_assert(postdec(0.0) == -1.0, ""); static_assert(++ref(1e100) == 1e100, ""); static_assert(--ref(1e100) == 1e100, ""); -#endif union U { int a, b; @@ -875,13 +863,9 @@ namespace VirtualFromBase { namespace Lifetime { constexpr int &get(int &&r) { return r; } - // cxx2b-error@-1 {{non-const lvalue reference to type 'int' cannot bind to a temporary of type 'int'}} - // cxx2b-error@-2 {{no return statement in constexpr function}} See PR40598 constexpr int f() { int &r = get(123); - return r; - // cxx2b-note@-1 {{use of reference outside its lifetime is not allowed in a constant expression}} - // cxx14_20-note@-2 {{read of object outside its lifetime}} + return r; // expected-note {{read of object outside its lifetime}} } static_assert(f() == 123, ""); // expected-error {{constant expression}} expected-note {{in call}} diff --git a/clang/test/SemaCXX/coroutine-rvo.cpp b/clang/test/SemaCXX/coroutine-rvo.cpp index 2c4bb07..2e4ba47 100644 --- a/clang/test/SemaCXX/coroutine-rvo.cpp +++ b/clang/test/SemaCXX/coroutine-rvo.cpp @@ -39,14 +39,15 @@ struct suspend_never { }; struct MoveOnly { - MoveOnly() = default; + MoveOnly() {}; MoveOnly(const MoveOnly&) = delete; - MoveOnly(MoveOnly &&) = default; + MoveOnly(MoveOnly&&) noexcept {}; + ~MoveOnly() {}; }; struct NoCopyNoMove { NoCopyNoMove() = default; - NoCopyNoMove(const NoCopyNoMove &) = delete; + NoCopyNoMove(const NoCopyNoMove &) = delete; // expected-note 4{{'NoCopyNoMove' has been explicitly marked deleted here}} }; template @@ -62,12 +63,13 @@ struct task { task local2val() { NoCopyNoMove value; - co_return value; + co_return value; // expected-error {{call to deleted constructor of 'NoCopyNoMove'}} + // expected-error@-1 {{value reference to type 'NoCopyNoMove' cannot bind to lvalue of type 'NoCopyNoMove'}} } 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; // expected-error {{call to deleted constructor of 'NoCopyNoMove'}} } // We need the move constructor for construction of the coroutine. @@ -80,7 +82,8 @@ task lvalue2val(NoCopyNoMove &value) { } task rvalue2val(NoCopyNoMove &&value) { - co_return value; + co_return value; // expected-error {{rvalue reference to type 'NoCopyNoMove' cannot bind to lvalue of type 'NoCopyNoMove'}} + // expected-error@-1 {{call to deleted constructor of 'NoCopyNoMove'}} } task lvalue2ref(NoCopyNoMove &value) { @@ -88,7 +91,7 @@ task lvalue2ref(NoCopyNoMove &value) { } 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; // expected-error {{call to deleted constructor of 'NoCopyNoMove'}} } struct To { diff --git a/clang/test/SemaCXX/coroutines.cpp b/clang/test/SemaCXX/coroutines.cpp index 681af0a..897f279 100644 --- a/clang/test/SemaCXX/coroutines.cpp +++ b/clang/test/SemaCXX/coroutines.cpp @@ -1,9 +1,9 @@ // This file contains references to sections of the Coroutines TS, which can be // found at http://wg21.link/coroutines. -// RUN: %clang_cc1 -std=c++2b -fsyntax-only -verify=expected,cxx20_2b,cxx2b %s -fcxx-exceptions -fexceptions -Wunused-result -// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify=expected,cxx14_20,cxx20_2b %s -fcxx-exceptions -fexceptions -Wunused-result -// RUN: %clang_cc1 -std=c++14 -fcoroutines-ts -fsyntax-only -verify=expected,cxx14_20 %s -fcxx-exceptions -fexceptions -Wunused-result +// RUN: %clang_cc1 -std=c++2b -fsyntax-only -verify=expected,cxx20_2b %s -fcxx-exceptions -fexceptions -Wunused-result +// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify=expected,cxx20_2b %s -fcxx-exceptions -fexceptions -Wunused-result +// RUN: %clang_cc1 -std=c++14 -fcoroutines-ts -fsyntax-only -verify=expected %s -fcxx-exceptions -fexceptions -Wunused-result void no_coroutine_traits_bad_arg_await() { co_await a; // expected-error {{include }} @@ -934,8 +934,7 @@ struct std::experimental::coroutine_traits { }; extern "C" int f(mismatch_gro_type_tag2) { - // cxx2b-error@-1 {{cannot initialize return object of type 'int' with an rvalue of type 'void *'}} - // cxx14_20-error@-2 {{cannot initialize return object of type 'int' with an lvalue of type 'void *'}} + // expected-error@-1 {{cannot initialize return object of type 'int' with an lvalue of type 'void *'}} co_return; //expected-note {{function is a coroutine due to use of 'co_return' here}} } diff --git a/clang/test/SemaCXX/deduced-return-type-cxx14.cpp b/clang/test/SemaCXX/deduced-return-type-cxx14.cpp index 71439c6..4d6700c 100644 --- a/clang/test/SemaCXX/deduced-return-type-cxx14.cpp +++ b/clang/test/SemaCXX/deduced-return-type-cxx14.cpp @@ -1,11 +1,11 @@ -// RUN: %clang_cc1 -std=c++2b -fsyntax-only -verify=expected,cxx20_2b,cxx2b %s -// RUN: %clang_cc1 -std=c++2b -fsyntax-only -verify=expected,cxx20_2b,cxx2b %s -fdelayed-template-parsing -DDELAYED_TEMPLATE_PARSING +// RUN: %clang_cc1 -std=c++2b -fsyntax-only -verify=expected,cxx20_2b %s +// RUN: %clang_cc1 -std=c++2b -fsyntax-only -verify=expected,cxx20_2b %s -fdelayed-template-parsing -DDELAYED_TEMPLATE_PARSING -// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify=expected,cxx14_20,cxx20_2b %s -// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify=expected,cxx14_20,cxx20_2b %s -fdelayed-template-parsing -DDELAYED_TEMPLATE_PARSING +// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify=expected,cxx20_2b %s +// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify=expected,cxx20_2b %s -fdelayed-template-parsing -DDELAYED_TEMPLATE_PARSING -// RUN: %clang_cc1 -std=c++14 -fsyntax-only -verify=expected,cxx14_20,cxx14 %s -// RUN: %clang_cc1 -std=c++14 -fsyntax-only -verify=expected,cxx14_20,cxx14 %s -fdelayed-template-parsing -DDELAYED_TEMPLATE_PARSING +// RUN: %clang_cc1 -std=c++14 -fsyntax-only -verify=expected,cxx14 %s +// RUN: %clang_cc1 -std=c++14 -fsyntax-only -verify=expected,cxx14 %s -fdelayed-template-parsing -DDELAYED_TEMPLATE_PARSING auto f(); // expected-note {{previous}} int f(); // expected-error {{differ only in their return type}} @@ -129,14 +129,10 @@ namespace Templates { return T() + 1; } template auto &f2(T &&v) { return v; } - // cxx2b-error@-1 {{non-const lvalue reference to type 'int' cannot bind to a temporary of type 'int'}} - // cxx2b-error@-2 {{non-const lvalue reference to type 'double' cannot bind to a temporary of type 'double'}} - // cxx2b-note@-3 {{candidate template ignored: substitution failure [with T = double]}} int a = f1(); - const int &b = f2(0); // cxx2b-note {{in instantiation of function template specialization 'Templates::f2' requested here}} + const int &b = f2(0); double d; float &c = f2(0.0); // expected-error {{non-const lvalue reference to type 'float' cannot bind to a value of unrelated type 'double'}} - // cxx2b-note@-1 {{in instantiation of function template specialization 'Templates::f2' requested here}} template auto fwd_decl(); // expected-note {{declared here}} int e = fwd_decl(); // expected-error {{cannot be used before it is defined}} @@ -149,9 +145,8 @@ namespace Templates { auto (*p)() = f1; // expected-error {{incompatible initializer}} auto (*q)() = f1; // ok - typedef decltype(f2(1.2)) dbl; // cxx14_20-note {{previous}} - // cxx2b-error@-1 {{no matching function for call to 'f2'}} - typedef float dbl; // cxx14_20-error {{typedef redefinition with different types ('float' vs 'decltype(f2(1.2))' (aka 'double &'))}} + typedef decltype(f2(1.2)) dbl; // expected-note {{previous}} + typedef float dbl; // expected-error {{typedef redefinition with different types ('float' vs 'decltype(f2(1.2))' (aka 'double &'))}} extern template auto fwd_decl(); int k1 = fwd_decl(); diff --git a/clang/test/SemaCXX/return-stack-addr.cpp b/clang/test/SemaCXX/return-stack-addr.cpp index 2f3b5b5..87ee57f 100644 --- a/clang/test/SemaCXX/return-stack-addr.cpp +++ b/clang/test/SemaCXX/return-stack-addr.cpp @@ -1,6 +1,6 @@ -// RUN: %clang_cc1 -std=c++2b -fsyntax-only -verify=expected,cxx2b %s -// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify=expected,cxx11_20 %s -// RUN: %clang_cc1 -std=c++11 -fsyntax-only -verify=expected,cxx11_20,cxx11 %s +// RUN: %clang_cc1 -std=c++2b -fsyntax-only -verify=expected %s +// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify=expected %s +// RUN: %clang_cc1 -std=c++11 -fsyntax-only -verify=expected,cxx11 %s int* ret_local() { int x = 1; @@ -29,8 +29,7 @@ int* ret_local_array_element_const_index() { int& ret_local_ref() { int x = 1; - return x; // cxx11_20-warning {{reference to stack memory}} - // cxx2b-error@-1 {{non-const lvalue reference to type 'int' cannot bind to a temporary of type 'int'}} + return x; // expected-warning {{reference to stack memory}} } int* ret_local_addrOf() { @@ -155,10 +154,8 @@ void ret_from_lambda() { (void) [&]() -> int& { return b; }; (void) [=]() mutable -> int& { return a; }; (void) [=]() mutable -> int& { return b; }; - (void) [&]() -> int& { int a; return a; }; // cxx11_20-warning {{reference to stack}} - // cxx2b-error@-1 {{non-const lvalue reference to type 'int' cannot bind to a temporary of type 'int'}} - (void) [=]() -> int& { int a; return a; }; // cxx11_20-warning {{reference to stack}} - // cxx2b-error@-1 {{non-const lvalue reference to type 'int' cannot bind to a temporary of type 'int'}} + (void) [&]() -> int& { int a; return a; }; // expected-warning {{reference to stack}} + (void) [=]() -> int& { int a; return a; }; // expected-warning {{reference to stack}} (void) [&]() -> int& { int &a = b; return a; }; (void) [=]() mutable -> int& { int &a = b; return a; }; diff --git a/clang/test/SemaCXX/warn-return-std-move.cpp b/clang/test/SemaCXX/warn-return-std-move.cpp index 3dc81bc..d9888cf 100644 --- a/clang/test/SemaCXX/warn-return-std-move.cpp +++ b/clang/test/SemaCXX/warn-return-std-move.cpp @@ -1,8 +1,8 @@ -// 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=cxx20_2b -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++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 @@ -217,8 +217,8 @@ ConvertFromBase testRParam6(Derived&& d) { } // 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; } +Base& testRetRef2(Derived&& d) { return d; } #if __cplusplus >= 201402L auto&& testRetRef3(Derived&& d) { return d; } decltype(auto) testRetRef4(Derived&& d) { return (d); } -- 2.7.4