mizvekov created this revision.
Herald added a subscriber: lxfind.
mizvekov requested review of this revision.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.
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 improvement:
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.
Signed-off-by: Matheus Izvekov <[email protected]>
Repository:
rG LLVM Github Monorepo
https://reviews.llvm.org/D99696
Files:
clang/include/clang/Sema/Sema.h
clang/lib/Sema/Sema.cpp
clang/lib/Sema/SemaCoroutine.cpp
clang/lib/Sema/SemaExprCXX.cpp
clang/lib/Sema/SemaStmt.cpp
clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
Index: clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
===================================================================
--- clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -1052,9 +1052,20 @@
StartingScope, InstantiatingVarTemplate);
if (D->isNRVOVariable()) {
- QualType ReturnType = cast<FunctionDecl>(DC)->getReturnType();
- if (SemaRef.isCopyElisionCandidate(ReturnType, Var, Sema::CES_Strict))
- Var->setNRVOVariable(true);
+ Sema::NRVOResult Res = SemaRef.getNRVOResult(Var);
+
+ QualType ReturnType;
+ if (auto *F = dyn_cast<FunctionDecl>(DC))
+ ReturnType = F->getReturnType();
+ else if (auto *F = dyn_cast<BlockDecl>(DC))
+ // FIXME: get the return type here somehow...
+ ReturnType = SemaRef.Context.getAutoDeductType();
+ else
+ assert(false && "Unknown context type");
+
+ if (!ReturnType->isUndeducedType())
+ SemaRef.updNRVOResultWithRetType(Res, ReturnType);
+ Var->setNRVOVariable(Res.isCopyElidable());
}
Var->setImplicit(D->isImplicit());
Index: clang/lib/Sema/SemaStmt.cpp
===================================================================
--- clang/lib/Sema/SemaStmt.cpp
+++ clang/lib/Sema/SemaStmt.cpp
@@ -3026,99 +3026,128 @@
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.
-///
-/// \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.
+static void downgradeNRVOResult(Sema::NRVOResult &Res, bool CanMove) {
+ Res.S = CanMove ? std::min(Res.S, Sema::NRVOResult::MoveEligible)
+ : Sema::NRVOResult::None;
+}
+
+/// Determine whether the given NRVO candidate variable is move-eligible or
+/// copy-elision eligible, without considering function return type.
///
-/// \param E The expression being returned from the function or block, or
-/// being thrown.
+/// \param VD The NRVO candidate variable.
///
-/// \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).
+/// \param ForceCXX20 Overrides detection of current language mode
+/// and uses the rules for C++20.
///
-/// \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<DeclRefExpr>(E->IgnoreParens());
- if (!DR || DR->refersToEnclosingVariableOrCapture())
- return nullptr;
- VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl());
+/// \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::NRVOResult Sema::getNRVOResult(const VarDecl *VD, bool ForceCXX20) {
if (!VD)
- return nullptr;
+ return NRVOResult();
- if (isCopyElisionCandidate(ReturnType, VD, CESK))
- return VD;
- return nullptr;
-}
+ bool hasCXX11 = getLangOpts().CPlusPlus11 || ForceCXX20,
+ hasCXX20 = getLangOpts().CPlusPlus20 || ForceCXX20;
+ NRVOResult Res{VD, NRVOResult::MoveEligibleAndCopyElidable};
-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;
+ // (other than a function ... parameter)
+ if (VD->getKind() == Decl::ParmVar) {
+ downgradeNRVOResult(Res, hasCXX11);
+ } else if (VD->getKind() != Decl::Var) {
+ return NRVOResult();
}
- // ...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;
+ // (other than ... a catch-clause parameter)
+ if (VD->isExceptionVariable())
+ downgradeNRVOResult(Res, hasCXX20);
// ...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<BlocksAttr>())
- 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...
QualType VDReferencedType = VDType.getNonReferenceType();
- if (VDReferencedType.isVolatileQualified() || !VDReferencedType->isObjectType())
- return false;
+ if (VDReferencedType.isVolatileQualified() ||
+ !VDReferencedType->isObjectType())
+ return NRVOResult();
+ downgradeNRVOResult(Res, hasCXX20);
} else {
- return false;
+ return NRVOResult();
}
- 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<AlignedAttr>() &&
Context.getDeclAlign(VD) > Context.getTypeAlignInChars(VDType))
- return false;
+ downgradeNRVOResult(Res, hasCXX11);
- return true;
+ return Res;
+}
+
+/// 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.
+///
+/// \param E The expression being returned from the function or block,
+/// being thrown, or being co_returned from a coroutine.
+///
+/// \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::NRVOResult Sema::getNRVOResult(const Expr *E, bool ForceCXX20) {
+ if (!E)
+ return NRVOResult();
+ // - in a return statement in a function [where] ...
+ // ... the expression is the name of a non-volatile automatic object ...
+ auto DR = dyn_cast<DeclRefExpr>(E->IgnoreParens());
+ if (!DR || DR->refersToEnclosingVariableOrCapture())
+ return NRVOResult();
+ return getNRVOResult(dyn_cast<VarDecl>(DR->getDecl()),
+ /*ForceCXX20=*/ForceCXX20);
+}
+
+/// 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::updNRVOResultWithRetType(NRVOResult &Res, QualType ReturnType) {
+ assert(!ReturnType->isUndeducedType());
+
+ 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))
+ downgradeNRVOResult(Res, getLangOpts().CPlusPlus11);
}
/// Try to perform the initialization of a potentially-movable value,
@@ -3143,8 +3172,7 @@
/// 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,
- QualType ResultType, Expr *&Value,
+ const VarDecl *NRVOCandidate, Expr *&Value,
bool ConvertingConstructorsOnly,
bool IsDiagnosticsCheck, ExprResult &Res) {
ImplicitCastExpr AsRvalue(ImplicitCastExpr::OnStack, Value->getType(),
@@ -3227,63 +3255,44 @@
/// 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 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 (NRVOCandidate) {
- NeedSecondOverloadResolution =
- TryMoveInitialization(*this, Entity, NRVOCandidate, ResultType, Value,
- !getLangOpts().CPlusPlus20, false, Res);
+ExprResult
+Sema::PerformMoveOrCopyInitialization(const InitializedEntity &Entity,
+ const NRVOResult &NRVORes, Expr *Value) {
+
+ if (NRVORes.Candidate) {
+ if (NRVORes.isMoveEligible()) {
+ ExprResult Res;
+ if (!TryMoveInitialization(*this, Entity, NRVORes.Candidate, 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();
- 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);
- }
+ QualType QT = NRVORes.Candidate->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, NRVORes.Candidate, FakeValue,
+ false, true, FakeRes);
+ if (!FakeRes.isInvalid()) {
+ bool IsThrow = (Entity.getKind() == InitializedEntity::EK_Exception);
+ SmallString<32> Str;
+ Str += "std::move(";
+ Str += NRVORes.Candidate->getDeclName().getAsString();
+ Str += ")";
+ Diag(Value->getExprLoc(), diag::warn_return_std_move)
+ << Value->getSourceRange() << NRVORes.Candidate->getDeclName()
+ << IsThrow;
+ Diag(Value->getExprLoc(), diag::note_add_std_move)
+ << FixItHint::CreateReplacement(Value->getSourceRange(), Str);
}
}
}
@@ -3292,10 +3301,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 +3315,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<CapturingScopeInfo>(getCurFunction());
@@ -3389,7 +3395,7 @@
if (CurCap->ReturnType.isNull())
CurCap->ReturnType = FnRetType;
}
- assert(!FnRetType.isNull());
+ updNRVOResultWithRetType(NRVORes, FnRetType);
if (auto *CurBlock = dyn_cast<BlockScopeInfo>(CurCap)) {
if (CurBlock->FunctionType->castAs<FunctionType>()->getNoReturnAttr()) {
@@ -3412,7 +3418,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,20 +3445,16 @@
// 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,
- FnRetType, RetValExp);
+ InitializedEntity Entity = InitializedEntity::InitializeResult(
+ ReturnLoc, FnRetType, NRVORes.isCopyElidable());
+ ExprResult Res =
+ PerformMoveOrCopyInitialization(Entity, NRVORes, 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) {
@@ -3463,13 +3464,14 @@
return StmtError();
RetValExp = ER.get();
}
- auto *Result =
- ReturnStmt::Create(Context, ReturnLoc, RetValExp, NRVOCandidate);
+ auto *Result = 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 +3664,10 @@
if (RetValExp && DiagnoseUnexpandedParameterPack(RetValExp))
return StmtError();
+ NRVOResult NRVORes = getNRVOResult(RetValExp);
+
if (isa<CapturingScopeInfo>(getCurFunction()))
- return ActOnCapScopeReturnStmt(ReturnLoc, RetValExp);
+ return ActOnCapScopeReturnStmt(ReturnLoc, RetValExp, NRVORes);
QualType FnRetType;
QualType RelatedRetType;
@@ -3735,6 +3739,7 @@
}
}
}
+ updNRVOResultWithRetType(NRVORes, FnRetType);
bool HasDependentReturnType = FnRetType->isDependentType();
@@ -3841,8 +3846,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 +3854,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, RetValExp);
if (Res.isInvalid()) {
// FIXME: Clean up temporaries here anyway?
return StmtError();
@@ -3892,7 +3892,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
Index: clang/lib/Sema/SemaExprCXX.cpp
===================================================================
--- clang/lib/Sema/SemaExprCXX.cpp
+++ clang/lib/Sema/SemaExprCXX.cpp
@@ -851,6 +851,8 @@
Diag(OpLoc, diag::err_omp_simd_region_cannot_use_stmt) << "throw";
if (Ex && !Ex->isTypeDependent()) {
+ NRVOResult NRVORes = IsThrownVarInScope ? getNRVOResult(Ex) : NRVOResult();
+
QualType ExceptionObjectTy = Context.getExceptionObjectType(Ex->getType());
if (CheckCXXThrowOperand(OpLoc, ExceptionObjectTy, Ex))
return ExprError();
@@ -870,15 +872,11 @@
// 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);
- ExprResult Res = PerformMoveOrCopyInitialization(
- Entity, NRVOVariable, QualType(), Ex, IsThrownVarInScope);
+ /*NRVO=*/NRVORes.isCopyElidable());
+ ExprResult Res = PerformMoveOrCopyInitialization(Entity, NRVORes, Ex);
if (Res.isInvalid())
return ExprError();
Ex = Res.get();
Index: clang/lib/Sema/SemaCoroutine.cpp
===================================================================
--- clang/lib/Sema/SemaCoroutine.cpp
+++ clang/lib/Sema/SemaCoroutine.cpp
@@ -995,17 +995,13 @@
}
// 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();
- }
+ NRVOResult NRVORes = getNRVOResult(E, /*ForceCXX20=*/true);
+ if (NRVORes.isMoveEligible()) {
+ InitializedEntity Entity = InitializedEntity::InitializeResult(
+ Loc, E->getType(), NRVORes.Candidate);
+ ExprResult MoveResult = PerformMoveOrCopyInitialization(Entity, NRVORes, E);
+ if (MoveResult.get())
+ E = MoveResult.get();
}
// FIXME: If the operand is a reference to a variable that's about to go out
@@ -1570,7 +1566,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 +1582,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;
Index: clang/lib/Sema/Sema.cpp
===================================================================
--- clang/lib/Sema/Sema.cpp
+++ 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<Expr>();
Index: clang/include/clang/Sema/Sema.h
===================================================================
--- clang/include/clang/Sema/Sema.h
+++ clang/include/clang/Sema/Sema.h
@@ -3435,12 +3435,6 @@
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);
@@ -4737,28 +4731,28 @@
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;
+
+ enum Status { None, MoveEligible, MoveEligibleAndCopyElidable };
+ Status S;
+
+ bool isMoveEligible() const { return S >= MoveEligible; };
+ bool isCopyElidable() const { return S == MoveEligibleAndCopyElidable; }
};
+ NRVOResult getNRVOResult(const Expr *E, bool ForceCXX20 = false);
+ NRVOResult getNRVOResult(const VarDecl *VD, bool ForceCXX20 = false);
+ void updNRVOResultWithRetType(NRVOResult &Res, QualType ReturnType);
- VarDecl *getCopyElisionCandidate(QualType ReturnType, Expr *E,
- CopyElisionSemanticsKind CESK);
- bool isCopyElisionCandidate(QualType ReturnType, const VarDecl *VD,
- CopyElisionSemanticsKind CESK);
+ ExprResult PerformMoveOrCopyInitialization(const InitializedEntity &Entity,
+ const NRVOResult &NRVORes,
+ Expr *Value);
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,
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits