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 @@ -4736,28 +4736,20 @@ SourceLocation Loc, unsigned NumParams); - 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), + struct NRVOResult { + const VarDecl *Candidate; + bool isCopyElidable; }; - - VarDecl *getCopyElisionCandidate(QualType ReturnType, Expr *E, - CopyElisionSemanticsKind CESK); - bool isCopyElisionCandidate(QualType ReturnType, const VarDecl *VD, - CopyElisionSemanticsKind CESK); + enum class NRVOMode { Auto, CXX20, CXX2b }; + NRVOResult getNRVOCandidate(Expr *&E, NRVOMode Mode = NRVOMode::Auto); + NRVOResult getNRVOResult(const VarDecl *VD, bool ForceCXX20 = false); + void getNRVOResultFnRetType(NRVOResult &Res, QualType ReturnType); StmtResult ActOnReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp, Scope *CurScope); StmtResult BuildReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp); - StmtResult ActOnCapScopeReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp); + StmtResult ActOnCapScopeReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp, + NRVOResult &NRVORes); StmtResult ActOnGCCAsmStmt(SourceLocation AsmLoc, bool IsSimple, bool IsVolatile, unsigned NumOutputs, 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 @@ -1952,9 +1952,10 @@ SourceLocation Loc = VD->getLocation(); Expr *VarRef = new (S.Context) DeclRefExpr(S.Context, VD, false, T, VK_LValue, Loc); - ExprResult Result = S.PerformMoveOrCopyInitialization( - InitializedEntity::InitializeBlock(Loc, T, false), VD, VD->getType(), - VarRef, /*AllowNRVO=*/true); + ExprResult Result = S.PerformCopyInitialization( + InitializedEntity::InitializeBlock(Loc, T, false), SourceLocation(), + VarRef); + if (!Result.isInvalid()) { Result = S.MaybeCreateExprWithCleanups(Result); Expr *Init = Result.getAs(); diff --git a/clang/lib/Sema/SemaCoroutine.cpp b/clang/lib/Sema/SemaCoroutine.cpp --- a/clang/lib/Sema/SemaCoroutine.cpp +++ b/clang/lib/Sema/SemaCoroutine.cpp @@ -994,26 +994,10 @@ 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())) { + getNRVOCandidate(E, NRVOMode::CXX2b); PC = buildPromiseCall(*this, Promise, Loc, "return_value", E); } else { E = MakeFullDiscardedValueExpr(E).get(); @@ -1570,7 +1554,7 @@ // Trigger a nice error message. InitializedEntity Entity = InitializedEntity::InitializeResult(Loc, FnRetType, false); - S.PerformMoveOrCopyInitialization(Entity, nullptr, FnRetType, ReturnValue); + S.PerformCopyInitialization(Entity, SourceLocation(), ReturnValue); noteMemberDeclaredHere(S, ReturnValue, Fn); return false; } @@ -1586,8 +1570,8 @@ return false; InitializedEntity Entity = InitializedEntity::InitializeVariable(GroDecl); - ExprResult Res = S.PerformMoveOrCopyInitialization(Entity, nullptr, GroType, - this->ReturnValue); + ExprResult Res = + S.PerformCopyInitialization(Entity, SourceLocation(), ReturnValue); if (Res.isInvalid()) return false; diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -851,6 +851,9 @@ Diag(OpLoc, diag::err_omp_simd_region_cannot_use_stmt) << "throw"; if (Ex && !Ex->isTypeDependent()) { + NRVOResult NRVORes = + IsThrownVarInScope ? getNRVOCandidate(Ex) : NRVOResult(); + QualType ExceptionObjectTy = Context.getExceptionObjectType(Ex->getType()); if (CheckCXXThrowOperand(OpLoc, ExceptionObjectTy, Ex)) return ExprError(); @@ -870,15 +873,12 @@ // operation from the operand to the exception object (15.1) can be // omitted by constructing the automatic object directly into the // exception object - const VarDecl *NRVOVariable = nullptr; - if (IsThrownVarInScope) - NRVOVariable = getCopyElisionCandidate(QualType(), Ex, CES_Strict); - InitializedEntity Entity = InitializedEntity::InitializeException( - OpLoc, ExceptionObjectTy, - /*NRVO=*/NRVOVariable != nullptr); + InitializedEntity Entity = + InitializedEntity::InitializeException(OpLoc, ExceptionObjectTy, + /*NRVO=*/NRVORes.isCopyElidable); ExprResult Res = PerformMoveOrCopyInitialization( - Entity, NRVOVariable, QualType(), Ex, IsThrownVarInScope); + Entity, NRVORes.Candidate, 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 --- a/clang/lib/Sema/SemaStmt.cpp +++ b/clang/lib/Sema/SemaStmt.cpp @@ -3026,99 +3026,137 @@ return new (Context) BreakStmt(BreakLoc); } -/// Determine whether the given expression is a candidate for -/// copy elision in either a return statement or a throw expression. +/// Determine whether the given NRVO candidate variable is move-eligible or +/// copy-elision eligible, without considering function return type. /// -/// \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 VD The NRVO candidate variable. /// -/// \param E The expression being returned from the function or block, or -/// being thrown. +/// \param ForceCXX20 If true, perform the check considering C++20 rules, +/// as opposed to considering the rules for current language mode. /// -/// \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 ... - DeclRefExpr *DR = dyn_cast(E->IgnoreParens()); - if (!DR || DR->refersToEnclosingVariableOrCapture()) - return nullptr; - VarDecl *VD = dyn_cast(DR->getDecl()); +/// \returns A NRVOResult struct, where Candidate, if non-null, is the same +/// as param VD and is at least move-eligible, without considering function +/// return type, if applicable. +Sema::NRVOResult Sema::getNRVOResult(const VarDecl *VD, bool ForceCXX20) { if (!VD) - return nullptr; - - if (isCopyElisionCandidate(ReturnType, VD, CESK)) - return VD; - return nullptr; -} - -bool Sema::isCopyElisionCandidate(QualType ReturnType, const VarDecl *VD, - CopyElisionSemanticsKind CESK) { - QualType VDType = VD->getType(); - // - in a return statement in a function with ... - // ... 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; + return NRVOResult(); + + bool hasCXX11 = getLangOpts().CPlusPlus11 || ForceCXX20, + hasCXX20 = getLangOpts().CPlusPlus20 || ForceCXX20; + NRVOResult Res{VD, true}; + if (VD->getKind() != Decl::Var) { + // (other than a function ... parameter) + if (!hasCXX11 || VD->getKind() != Decl::ParmVar) + return NRVOResult(); + Res.isCopyElidable = false; + } + // (other than ... a catch-clause parameter) + if (VD->isExceptionVariable()) { + if (!hasCXX20) + return NRVOResult(); + Res.isCopyElidable = false; } - - // ...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 false; + if (!VD->hasLocalStorage()) + return NRVOResult(); // Return false if VD is a __block variable. We don't want to implicitly move // out of a __block variable during a return because we cannot assume the // variable will no longer be used. if (VD->hasAttr()) - return false; + return NRVOResult(); + QualType VDType = VD->getType(); if (VDType->isObjectType()) { // C++17 [class.copy.elision]p3: // ...non-volatile automatic object... if (VDType.isVolatileQualified()) - return false; + return NRVOResult(); } 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... - if (!(CESK & CES_AllowRValueReferenceType)) - return false; + // ...either a non-volatile object or an rvalue reference to a non-volatile + // object type... + Res.isCopyElidable = false; + if (!hasCXX20) + return NRVOResult(); QualType VDReferencedType = VDType.getNonReferenceType(); - if (VDReferencedType.isVolatileQualified() || !VDReferencedType->isObjectType()) - return false; + if (VDReferencedType.isVolatileQualified() || + !VDReferencedType->isObjectType()) + return NRVOResult(); } else { - return false; + return NRVOResult(); + } + if (!VDType->isDependentType() && VD->hasAttr() && + Context.getDeclAlign(VD) > Context.getTypeAlignInChars(VDType)) { + // Variables with higher required alignment than their type's ABI + // alignment cannot use NRVO. + if (!hasCXX11) + return NRVOResult(); + Res.isCopyElidable = false; } - if (CESK & CES_AllowDifferentTypes) - return true; + return Res; +} - // 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)) - return false; +/// Determine whether the given expression might be move-eligible or +/// copy-elidable in either a (co_)return statement or throw expression. +/// +/// \param E The expression being returned from the function or block, +/// being thrown, or being co_returned from a coroutine. +/// +/// \param Mode The language standard mode to use, +/// or Auto (default) for the current mode. +/// +/// \returns A NRVOResult struct, where Candidate, if non-null, means +/// the expression is at least move eligible, without considering function +/// return type, if applicable. +Sema::NRVOResult Sema::getNRVOCandidate(Expr *&E, NRVOMode Mode) { + if (!E) + return NRVOResult(); + // - in a return statement in a function [where] ... + // ... the expression is the name of a non-volatile automatic object ... + DeclRefExpr *DR = dyn_cast(E->IgnoreParens()); + if (!DR || DR->refersToEnclosingVariableOrCapture()) + return NRVOResult(); + VarDecl *VD = dyn_cast(DR->getDecl()); + NRVOResult Res = getNRVOResult(VD, Mode != NRVOMode::Auto); + if (Res.Candidate && E->getValueKind() != VK_XValue && + (Mode == NRVOMode::CXX2b || getLangOpts().CPlusPlus2b)) { + E = ImplicitCastExpr::Create(Context, VD->getType().getNonReferenceType(), + CK_NoOp, E, nullptr, VK_XValue, + FPOptionsOverride()); + } + return Res; +} - return true; +/// Updates given NRVOResult's move-eligible and +/// copy-elidable statuses, considering the function return type criteria +/// as applicable to return statements. +/// +/// \param Res The NRVOResult object to update. +/// +/// \param ReturnType This is the return type of the function. +void Sema::getNRVOResultFnRetType(NRVOResult &Res, QualType ReturnType) { + if (!Res.Candidate || ReturnType->isDependentType()) + return; + // - in a return statement in a function with ... + // ... a class return type ... + if (!ReturnType->isRecordType()) { + Res = NRVOResult(); + return; + } + QualType VDType = Res.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)) { + if (!getLangOpts().CPlusPlus11) { + Res = NRVOResult(); + return; + } + Res.isCopyElidable = false; + } } /// Try to perform the initialization of a potentially-movable value, @@ -3230,34 +3268,20 @@ 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 (AllowNRVO && !getLangOpts().CPlusPlus2b) { if (NRVOCandidate) { - NeedSecondOverloadResolution = - TryMoveInitialization(*this, Entity, NRVOCandidate, ResultType, Value, - !getLangOpts().CPlusPlus20, false, Res); + ExprResult Res; + if (!TryMoveInitialization(*this, Entity, NRVOCandidate, ResultType, + Value, !getLangOpts().CPlusPlus20, false, Res)) + return Res; } - if (!getLangOpts().CPlusPlus20 && NeedSecondOverloadResolution && - !getDiagnostics().isIgnored(diag::warn_return_std_move, + if (!getDiagnostics().isIgnored(diag::warn_return_std_move, Value->getExprLoc())) { - const VarDecl *FakeNRVOCandidate = getCopyElisionCandidate( - QualType(), Value, CES_ImplicitlyMovableCXX20); - if (FakeNRVOCandidate) { - QualType QT = FakeNRVOCandidate->getType(); + NRVOResult FakeNRVORes = getNRVOCandidate(Value, NRVOMode::CXX20); + if (FakeNRVORes.Candidate) { + QualType QT = FakeNRVORes.Candidate->getType(); if (QT->isLValueReferenceType()) { // Adding 'std::move' around an lvalue reference variable's name is // dangerous. Don't suggest it. @@ -3269,18 +3293,18 @@ } else { ExprResult FakeRes = ExprError(); Expr *FakeValue = Value; - TryMoveInitialization(*this, Entity, FakeNRVOCandidate, ResultType, - FakeValue, false, true, FakeRes); + TryMoveInitialization(*this, Entity, FakeNRVORes.Candidate, + 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 += FakeNRVORes.Candidate->getDeclName().getAsString(); Str += ")"; Diag(Value->getExprLoc(), diag::warn_return_std_move) << Value->getSourceRange() - << FakeNRVOCandidate->getDeclName() << IsThrow; + << FakeNRVORes.Candidate->getDeclName() << IsThrow; Diag(Value->getExprLoc(), diag::note_add_std_move) << FixItHint::CreateReplacement(Value->getSourceRange(), Str); } @@ -3292,10 +3316,7 @@ // 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. - if (NeedSecondOverloadResolution) - Res = PerformCopyInitialization(Entity, SourceLocation(), Value); - - return Res; + return PerformCopyInitialization(Entity, SourceLocation(), Value); } /// Determine whether the declared return type of the specified function @@ -3309,8 +3330,8 @@ /// ActOnCapScopeReturnStmt - Utility routine to type-check return statements /// for capturing scopes. /// -StmtResult -Sema::ActOnCapScopeReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp) { +StmtResult Sema::ActOnCapScopeReturnStmt(SourceLocation ReturnLoc, + Expr *RetValExp, NRVOResult &NRVORes) { // 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()); @@ -3390,6 +3411,7 @@ CurCap->ReturnType = FnRetType; } assert(!FnRetType.isNull()); + getNRVOResultFnRetType(NRVORes, FnRetType); if (auto *CurBlock = dyn_cast(CurCap)) { if (CurBlock->FunctionType->castAs()->getNoReturnAttr()) { @@ -3412,7 +3434,6 @@ // 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. @@ -3440,11 +3461,9 @@ // In C++ the return statement is handled via a copy initialization. // the C version of which boils down to CheckSingleAssignmentConstraints. - NRVOCandidate = getCopyElisionCandidate(FnRetType, RetValExp, CES_Strict); - InitializedEntity Entity = InitializedEntity::InitializeResult(ReturnLoc, - FnRetType, - NRVOCandidate != nullptr); - ExprResult Res = PerformMoveOrCopyInitialization(Entity, NRVOCandidate, + InitializedEntity Entity = InitializedEntity::InitializeResult( + ReturnLoc, FnRetType, NRVORes.isCopyElidable); + ExprResult Res = PerformMoveOrCopyInitialization(Entity, NRVORes.Candidate, FnRetType, RetValExp); if (Res.isInvalid()) { // FIXME: Cleanup temporaries here, anyway? @@ -3452,8 +3471,6 @@ } RetValExp = Res.get(); CheckReturnValExpr(RetValExp, FnRetType, ReturnLoc); - } else { - NRVOCandidate = getCopyElisionCandidate(FnRetType, RetValExp, CES_Strict); } if (RetValExp) { @@ -3464,12 +3481,13 @@ RetValExp = ER.get(); } auto *Result = - ReturnStmt::Create(Context, ReturnLoc, RetValExp, NRVOCandidate); + ReturnStmt::Create(Context, ReturnLoc, RetValExp, + NRVORes.isCopyElidable ? NRVORes.Candidate : nullptr); // If we need to check for the named return value optimization, // or if we need to infer the return type, // save the return statement in our scope for later processing. - if (CurCap->HasImplicitReturnType || NRVOCandidate) + if (CurCap->HasImplicitReturnType || NRVORes.isCopyElidable) FunctionScopes.back()->Returns.push_back(Result); if (FunctionScopes.back()->FirstReturnLoc.isInvalid()) @@ -3662,8 +3680,10 @@ if (RetValExp && DiagnoseUnexpandedParameterPack(RetValExp)) return StmtError(); + NRVOResult NRVORes = getNRVOCandidate(RetValExp); + if (isa(getCurFunction())) - return ActOnCapScopeReturnStmt(ReturnLoc, RetValExp); + return ActOnCapScopeReturnStmt(ReturnLoc, RetValExp, NRVORes); QualType FnRetType; QualType RelatedRetType; @@ -3735,6 +3755,7 @@ } } } + getNRVOResultFnRetType(NRVORes, FnRetType); bool HasDependentReturnType = FnRetType->isDependentType(); @@ -3841,8 +3862,6 @@ /* 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 @@ -3851,15 +3870,12 @@ // 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, NRVOCandidate, - RetType, RetValExp); + InitializedEntity Entity = InitializedEntity::InitializeResult( + ReturnLoc, RetType, NRVORes.isCopyElidable); + ExprResult Res = PerformMoveOrCopyInitialization( + Entity, NRVORes.Candidate, RetType, RetValExp); if (Res.isInvalid()) { // FIXME: Clean up temporaries here anyway? return StmtError(); @@ -3892,7 +3908,9 @@ return StmtError(); RetValExp = ER.get(); } - Result = ReturnStmt::Create(Context, ReturnLoc, RetValExp, NRVOCandidate); + Result = ReturnStmt::Create(Context, ReturnLoc, RetValExp, + NRVORes.isCopyElidable ? NRVORes.Candidate + : nullptr); } // If we need to check for the named return value optimization, save the 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 @@ -1052,9 +1052,10 @@ StartingScope, InstantiatingVarTemplate); if (D->isNRVOVariable()) { - QualType ReturnType = cast(DC)->getReturnType(); - if (SemaRef.isCopyElisionCandidate(ReturnType, Var, Sema::CES_Strict)) - Var->setNRVOVariable(true); + Sema::NRVOResult Res = SemaRef.getNRVOResult(Var); + SemaRef.getNRVOResultFnRetType(Res, + cast(DC)->getReturnType()); + Var->setNRVOVariable(Res.isCopyElidable); } Var->setImplicit(D->isImplicit()); diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp --- a/clang/lib/Sema/SemaType.cpp +++ b/clang/lib/Sema/SemaType.cpp @@ -8837,6 +8837,10 @@ if (E->isTypeDependent()) return S.Context.DependentTy; + Expr *IDExpr = E; + if (auto *ImplCastExpr = dyn_cast(E)) + IDExpr = ImplCastExpr->getSubExpr(); + // C++11 [dcl.type.simple]p4: // The type denoted by decltype(e) is defined as follows: @@ -8847,7 +8851,7 @@ // Note that this does not pick up the implicit 'const' for a template // parameter object. This rule makes no difference before C++20 so we apply // it unconditionally. - if (const auto *SNTTPE = dyn_cast(E)) + if (const auto *SNTTPE = dyn_cast(IDExpr)) return SNTTPE->getParameterType(S.Context); // - if e is an unparenthesized id-expression or an unparenthesized class @@ -8856,21 +8860,22 @@ // functions, the program is ill-formed; // // We apply the same rules for Objective-C ivar and property references. - if (const DeclRefExpr *DRE = dyn_cast(E)) { + if (const DeclRefExpr *DRE = dyn_cast(IDExpr)) { const ValueDecl *VD = DRE->getDecl(); if (auto *TPO = dyn_cast(VD)) return TPO->getType().getUnqualifiedType(); return VD->getType(); - } else if (const MemberExpr *ME = dyn_cast(E)) { + } else if (const MemberExpr *ME = dyn_cast(IDExpr)) { if (const ValueDecl *VD = ME->getMemberDecl()) if (isa(VD) || isa(VD)) return VD->getType(); - } else if (const ObjCIvarRefExpr *IR = dyn_cast(E)) { + } else if (const ObjCIvarRefExpr *IR = dyn_cast(IDExpr)) { return IR->getDecl()->getType(); - } else if (const ObjCPropertyRefExpr *PR = dyn_cast(E)) { + } else if (const ObjCPropertyRefExpr *PR = + dyn_cast(IDExpr)) { if (PR->isExplicitProperty()) return PR->getExplicitProperty()->getType(); - } else if (auto *PE = dyn_cast(E)) { + } else if (auto *PE = dyn_cast(IDExpr)) { return PE->getType(); } @@ -8883,8 +8888,8 @@ // entity. using namespace sema; if (S.getCurLambda()) { - if (isa(E)) { - if (DeclRefExpr *DRE = dyn_cast(E->IgnoreParens())) { + if (isa(IDExpr)) { + if (DeclRefExpr *DRE = dyn_cast(IDExpr->IgnoreParens())) { if (VarDecl *Var = dyn_cast(DRE->getDecl())) { QualType T = S.getCapturedDeclRefType(Var, DRE->getLocation()); if (!T.isNull()) diff --git a/clang/test/CXX/class/class.init/class.copy.elision/p3.cpp b/clang/test/CXX/class/class.init/class.copy.elision/p3.cpp --- a/clang/test/CXX/class/class.init/class.copy.elision/p3.cpp +++ b/clang/test/CXX/class/class.init/class.copy.elision/p3.cpp @@ -1,8 +1,8 @@ -// 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 +// 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 namespace test_delete_function { struct A1 { @@ -409,8 +409,10 @@ namespace test_simpler_implicit_move { struct CopyOnly { - CopyOnly(); - CopyOnly(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}} }; struct MoveOnly { MoveOnly(); @@ -419,7 +421,7 @@ MoveOnly &&rref(); MoveOnly &&test1(MoveOnly &&w) { - return w; // expected-error {{cannot bind to lvalue of type}} + return w; // cxx11_20-error {{cannot bind to lvalue of type}} } CopyOnly test2(bool b) { @@ -428,22 +430,22 @@ if (b) { return w1; } else { - return w2; + return w2; // cxx2b-error {{no matching constructor for initialization}} } } -template T &&test3(T &&x) { return x; } // expected-error {{cannot bind to lvalue of type}} +template T &&test3(T &&x) { return x; } // cxx11_20-error {{cannot bind to lvalue of type}} template MoveOnly& test3(MoveOnly&); -template MoveOnly&& test3(MoveOnly&&); // expected-note {{in instantiation of function template specialization}} +template MoveOnly&& test3(MoveOnly&&); // cxx11_20-note {{in instantiation of function template specialization}} MoveOnly &&test4() { MoveOnly &&x = rref(); - return x; // expected-error {{cannot bind to lvalue of type}} + return x; // cxx11_20-error {{cannot bind to lvalue of type}} } void test5() try { CopyOnly x; - throw x; + throw x; // cxx2b-error {{no matching constructor for initialization}} } 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 --- 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 %s -// RUN: %clang_cc1 -verify -std=c++20 -verify %s -// RUN: %clang_cc1 -verify -std=c++14 -verify %s +// 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 namespace std { template struct initializer_list { @@ -30,7 +30,7 @@ auto x4a = (i); decltype(auto) x4d = (i); using Int = decltype(x4a); -using IntLRef = decltype(x4d); +using IntLRef = decltype(x4d); // cxx2b-note {{previous definition is here}} auto x5a = f(); decltype(auto) x5d = f(); @@ -81,7 +81,7 @@ 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)); +using IntLRef = decltype(f3d(0)); // cxx2b-error {{type alias redefinition with different types ('decltype(f3d(0))' (aka 'int &&') vs 'decltype(x4d)' (aka 'int &'))}} auto f4a(int n) { return f(); } decltype(auto) f4d(int n) { return f(); } @@ -91,7 +91,7 @@ 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; } // expected-error {{rvalue reference to type 'int' cannot bind to lvalue}} +decltype(auto) f5dd(int n) { decltype(auto) x = f(); return x; } // cxx14_20-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 --- 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 -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 +// 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 namespace dr300 { // dr300: yes template void f(R (&)(A)) {} @@ -628,7 +628,8 @@ struct A { template operator T ***() { int ***p = 0; - return p; // expected-error {{cannot initialize return object of type 'const int ***' with an lvalue of type 'int ***'}} + 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 ***'}} } }; 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 --- 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 %s -// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify %s -// RUN: %clang_cc1 -std=c++14 -fsyntax-only -verify %s +// 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 int a; int &b = [] (int &r) -> decltype(auto) { return r; } (a); @@ -9,13 +9,15 @@ 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 {{on-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; }; //expected-warning{{reference to stack}} + 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 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 --- a/clang/test/CXX/temp/temp.decls/temp.mem/p5.cpp +++ b/clang/test/CXX/temp/temp.decls/temp.mem/p5.cpp @@ -1,6 +1,6 @@ -// 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 -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 struct A { template operator T*(); @@ -66,8 +66,10 @@ template operator const T*() const { T x = T(); - 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'}} + 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'}} } }; diff --git a/clang/test/SemaCXX/constant-expression-cxx11.cpp b/clang/test/SemaCXX/constant-expression-cxx11.cpp --- 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 -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 +// 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 namespace StaticAssertFoldTest { @@ -1936,13 +1936,18 @@ } 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) {} // 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 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 --- 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 %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 +// 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 %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 struct S { // dummy ctor to make this a literal type @@ -269,16 +269,23 @@ 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, ""); - static_assert(postdec(0) == -1, ""); +#endif +#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}} @@ -293,33 +300,38 @@ // cxx20_2b-error@-1 {{ISO C++17 does not allow incrementing expression of type bool}} static_assert(++ref(true), ""); // cxx14-warning {{deprecated}} // cxx20_2b-error@-1 {{ISO C++17 does not allow incrementing expression of type bool}} +#endif 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}} - static_assert(postdec(&arr[1]) == &arr[0], ""); +#endif + 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; @@ -861,9 +873,13 @@ 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; // expected-note {{read of object outside its lifetime}} + 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}} } 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 --- a/clang/test/SemaCXX/coroutine-rvo.cpp +++ b/clang/test/SemaCXX/coroutine-rvo.cpp @@ -46,7 +46,7 @@ struct NoCopyNoMove { NoCopyNoMove() = default; - NoCopyNoMove(const NoCopyNoMove &) = delete; // expected-note 4{{'NoCopyNoMove' has been explicitly marked deleted here}} + NoCopyNoMove(const NoCopyNoMove &) = delete; }; template @@ -62,13 +62,12 @@ task local2val() { NoCopyNoMove 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'}} + co_return value; } task local2ref() { NoCopyNoMove value; - co_return value; // expected-error {{call to deleted constructor of 'NoCopyNoMove'}} + co_return value; // expected-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. @@ -81,8 +80,7 @@ } task rvalue2val(NoCopyNoMove &&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'}} + co_return value; } task lvalue2ref(NoCopyNoMove &value) { @@ -90,7 +88,7 @@ } task rvalue2ref(NoCopyNoMove &&value) { - co_return value; // expected-error {{call to deleted constructor of 'NoCopyNoMove'}} + co_return value; // expected-error {{non-const lvalue reference to type 'NoCopyNoMove' cannot bind to a temporary of type 'NoCopyNoMove'}} } struct To { diff --git a/clang/test/SemaCXX/coroutines.cpp b/clang/test/SemaCXX/coroutines.cpp --- 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 %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 +// 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 void no_coroutine_traits_bad_arg_await() { co_await a; // expected-error {{include }} @@ -934,7 +934,8 @@ }; extern "C" int f(mismatch_gro_type_tag2) { - // expected-error@-1 {{cannot initialize return object of type 'int' with an lvalue of type 'void *'}} + // 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 *'}} 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 --- 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 %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++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++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++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++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 +// 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 auto f(); // expected-note {{previous}} int f(); // expected-error {{differ only in their return type}} @@ -129,10 +129,14 @@ 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); + const int &b = f2(0); // cxx2b-note {{in instantiation of function template specialization 'Templates::f2' requested here}} 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}} @@ -145,8 +149,9 @@ auto (*p)() = f1; // expected-error {{incompatible initializer}} auto (*q)() = f1; // ok - 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 &'))}} + 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 &'))}} 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 --- 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 %s -// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify=expected %s -// RUN: %clang_cc1 -std=c++11 -fsyntax-only -verify=expected,cxx11 %s +// 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 int* ret_local() { int x = 1; @@ -29,7 +29,8 @@ int& ret_local_ref() { int x = 1; - return x; // expected-warning {{reference to stack memory}} + 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'}} } int* ret_local_addrOf() { @@ -154,8 +155,10 @@ (void) [&]() -> int& { return b; }; (void) [=]() mutable -> int& { return a; }; (void) [=]() mutable -> int& { return b; }; - (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; 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 = 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 --- 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 -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,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++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 @@ } // But if the return type is a reference type, then moving would be wrong. -Derived& testRetRef1(Derived&& d) { return d; } -Base& testRetRef2(Derived&& d) { return d; } +Derived& testRetRef1(Derived&& d) { return d; } // cxx2b-error {{on-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'}} #if __cplusplus >= 201402L auto&& testRetRef3(Derived&& d) { return d; } decltype(auto) testRetRef4(Derived&& d) { return (d); }