[PATCH] D43898: Preliminary refactoring in service of -Wreturn-std-move. NFC.
This revision was not accepted when it landed; it landed in state "Needs Review". This revision was automatically updated to reflect the committed changes. Closed by commit rL327598: Refactoring code around move/copy initialization. NFC. (authored by rtrieu, committed by ). Herald added a subscriber: llvm-commits. Changed prior to commit: https://reviews.llvm.org/D43898?vs=137920=138488#toc Repository: rL LLVM https://reviews.llvm.org/D43898 Files: cfe/trunk/include/clang/Sema/Sema.h cfe/trunk/lib/Sema/SemaExprCXX.cpp cfe/trunk/lib/Sema/SemaStmt.cpp cfe/trunk/lib/Sema/SemaTemplateInstantiateDecl.cpp Index: cfe/trunk/include/clang/Sema/Sema.h === --- cfe/trunk/include/clang/Sema/Sema.h +++ cfe/trunk/include/clang/Sema/Sema.h @@ -3795,10 +3795,18 @@ RecordDecl *CreateCapturedStmtRecordDecl(CapturedDecl *, SourceLocation Loc, unsigned NumParams); + + enum CopyElisionSemanticsKind { +CES_Strict = 0, +CES_AllowParameters = 1, +CES_AllowDifferentTypes = 2, +CES_Default = (CES_AllowParameters | CES_AllowDifferentTypes), + }; + VarDecl *getCopyElisionCandidate(QualType ReturnType, Expr *E, - bool AllowParamOrMoveConstructible); + CopyElisionSemanticsKind CESK); bool isCopyElisionCandidate(QualType ReturnType, const VarDecl *VD, - bool AllowParamOrMoveConstructible); + CopyElisionSemanticsKind CESK); StmtResult ActOnReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp, Scope *CurScope); Index: cfe/trunk/lib/Sema/SemaStmt.cpp === --- cfe/trunk/lib/Sema/SemaStmt.cpp +++ cfe/trunk/lib/Sema/SemaStmt.cpp @@ -2862,16 +2862,16 @@ /// \param E The expression being returned from the function or block, or /// being thrown. /// -/// \param AllowParamOrMoveConstructible Whether we allow function parameters or +/// \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, - bool AllowParamOrMoveConstructible) { + CopyElisionSemanticsKind CESK) { if (!getLangOpts().CPlusPlus) return nullptr; @@ -2884,29 +2884,29 @@ if (!VD) return nullptr; - if (isCopyElisionCandidate(ReturnType, VD, AllowParamOrMoveConstructible)) + if (isCopyElisionCandidate(ReturnType, VD, CESK)) return VD; return nullptr; } bool Sema::isCopyElisionCandidate(QualType ReturnType, const VarDecl *VD, - bool AllowParamOrMoveConstructible) { + 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 (!AllowParamOrMoveConstructible && !VDType->isDependentType() && +if (!(CESK & CES_AllowDifferentTypes) && !VDType->isDependentType() && !Context.hasSameUnqualifiedType(ReturnType, VDType)) return false; } // ...object (other than a function or catch-clause parameter)... if (VD->getKind() != Decl::Var && - !(AllowParamOrMoveConstructible && VD->getKind() == Decl::ParmVar)) + !((CESK & CES_AllowParameters) && VD->getKind() == Decl::ParmVar)) return false; if (VD->isExceptionVariable()) return false; @@ -2918,7 +2918,7 @@ // variable will no longer be used. if (VD->hasAttr()) return false; - if (AllowParamOrMoveConstructible) + if (CESK & CES_AllowDifferentTypes) return true; // ...non-volatile... @@ -2933,6 +2933,71 @@ return true; } +/// \brief Try to perform the initialization of a potentially-movable value, +/// which is the operand to a return or throw statement. +/// +/// This routine implements C++14 [class.copy]p32, which attempts to treat +/// returned lvalues as rvalues in certain cases (to prefer move construction), +/// then falls back to treating them as lvalues if that failed. +/// +/// \param Res We will fill this in if move-initialization was
[PATCH] D43898: Preliminary refactoring in service of -Wreturn-std-move. NFC.
Quuxplusone added a comment. @rtrieu thanks! I don't have commit privileges; could I ask you to commit this on my behalf? (Or if not, please say no, and I'll know to go looking for someone else to ask.) Once/if https://reviews.llvm.org/D43898 hits master, I'll rebase https://reviews.llvm.org/D43322 and await a LGTM there, at which point I'll again ask for help committing it. :) Repository: rC Clang https://reviews.llvm.org/D43898 ___ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[PATCH] D43898: Preliminary refactoring in service of -Wreturn-std-move. NFC.
rtrieu added a comment. Everything looks good and ready for commit. Repository: rC Clang https://reviews.llvm.org/D43898 ___ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[PATCH] D43898: Preliminary refactoring in service of -Wreturn-std-move. NFC.
Quuxplusone updated this revision to Diff 137920. Quuxplusone marked an inline comment as done. Quuxplusone added a comment. Addressed @rtrieu's comments. @rtrieu, please take another look at this and https://reviews.llvm.org/D43322? Thanks! Repository: rC Clang https://reviews.llvm.org/D43898 Files: include/clang/Sema/Sema.h lib/Sema/SemaExprCXX.cpp lib/Sema/SemaStmt.cpp lib/Sema/SemaTemplateInstantiateDecl.cpp Index: lib/Sema/SemaTemplateInstantiateDecl.cpp === --- lib/Sema/SemaTemplateInstantiateDecl.cpp +++ lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -741,7 +741,7 @@ if (D->isNRVOVariable()) { QualType ReturnType = cast(DC)->getReturnType(); -if (SemaRef.isCopyElisionCandidate(ReturnType, Var, false)) +if (SemaRef.isCopyElisionCandidate(ReturnType, Var, Sema::CES_Strict)) Var->setNRVOVariable(true); } Index: lib/Sema/SemaStmt.cpp === --- lib/Sema/SemaStmt.cpp +++ lib/Sema/SemaStmt.cpp @@ -2862,16 +2862,16 @@ /// \param E The expression being returned from the function or block, or /// being thrown. /// -/// \param AllowParamOrMoveConstructible Whether we allow function parameters or +/// \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, - bool AllowParamOrMoveConstructible) { + CopyElisionSemanticsKind CESK) { if (!getLangOpts().CPlusPlus) return nullptr; @@ -2884,29 +2884,29 @@ if (!VD) return nullptr; - if (isCopyElisionCandidate(ReturnType, VD, AllowParamOrMoveConstructible)) + if (isCopyElisionCandidate(ReturnType, VD, CESK)) return VD; return nullptr; } bool Sema::isCopyElisionCandidate(QualType ReturnType, const VarDecl *VD, - bool AllowParamOrMoveConstructible) { + 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 (!AllowParamOrMoveConstructible && !VDType->isDependentType() && +if (!(CESK & CES_AllowDifferentTypes) && !VDType->isDependentType() && !Context.hasSameUnqualifiedType(ReturnType, VDType)) return false; } // ...object (other than a function or catch-clause parameter)... if (VD->getKind() != Decl::Var && - !(AllowParamOrMoveConstructible && VD->getKind() == Decl::ParmVar)) + !((CESK & CES_AllowParameters) && VD->getKind() == Decl::ParmVar)) return false; if (VD->isExceptionVariable()) return false; @@ -2918,7 +2918,7 @@ // variable will no longer be used. if (VD->hasAttr()) return false; - if (AllowParamOrMoveConstructible) + if (CESK & CES_AllowDifferentTypes) return true; // ...non-volatile... @@ -2933,6 +2933,71 @@ return true; } +/// \brief Try to perform the initialization of a potentially-movable value, +/// which is the operand to a return or throw statement. +/// +/// This routine implements C++14 [class.copy]p32, which attempts to treat +/// returned lvalues as rvalues in certain cases (to prefer move construction), +/// then falls back to treating them as lvalues if that failed. +/// +/// \param Res We will fill this in if move-initialization was possible. +/// If move-initialization is not possible, such that we must fall back to +/// treating the operand as an lvalue, we will leave Res in its original +/// invalid state. +static void TryMoveInitialization(Sema& S, + const InitializedEntity , + const VarDecl *NRVOCandidate, + QualType ResultType, + Expr *, + ExprResult ) { + ImplicitCastExpr AsRvalue(ImplicitCastExpr::OnStack, Value->getType(), +CK_NoOp, Value, VK_XValue); + + Expr *InitExpr = + + InitializationKind Kind = InitializationKind::CreateCopy( + Value->getLocStart(), Value->getLocStart()); + + InitializationSequence Seq(S, Entity, Kind, InitExpr); + + if (!Seq) +return; + +
[PATCH] D43898: Preliminary refactoring in service of -Wreturn-std-move. NFC.
Quuxplusone marked 4 inline comments as done. Quuxplusone added inline comments. Comment at: lib/Sema/SemaStmt.cpp:2970 + FunctionDecl *FD = Step.Function.Function; + if (isa(FD)) { +// C++14 [class.copy]p32: rtrieu wrote: > Use early exit here: > > > ``` > if (!isa(FD) > continue; > > // old if-block code > ``` I'd prefer not to do this, since D43322 is going to change this code into "if isa-red-fish... else if isa-blue-fish...". Therefore I think it makes sense to keep this intermediate stage as "if isa-red-fish...", rather than changing it into "if not-isa-red-fish continue... otherwise..." If you really want this, I can change it; but it's just going to change back in D43322, and the goal of this patch was to make D43322 smaller. Comment at: lib/Sema/SemaStmt.cpp:2999-3000 -// expression node to persist. -Value = ImplicitCastExpr::Create(Context, Value->getType(), CK_NoOp, - Value, nullptr, VK_XValue); rtrieu wrote: > At this point, the variable Value is updated. Value is scoped to this > function, and used again after this code. In your change, there is a new > Value variable in the static function. Only that variable is updated and not > this one, making this a change in behavior. Good catch! I've addressed this now by making the parameter `Expr *`; but I'd be open to better approaches. Particularly because I still don't know what to do about the "unnecessary promoting `Value` to the heap" that will happen in D43322. Repository: rC Clang https://reviews.llvm.org/D43898 ___ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[PATCH] D43898: Preliminary refactoring in service of -Wreturn-std-move. NFC.
rtrieu added inline comments. Comment at: lib/Sema/SemaStmt.cpp:2953 + ExprResult ) +{ + ImplicitCastExpr AsRvalue(ImplicitCastExpr::OnStack, Value->getType(), Opening brace should follow the closing paren on previous line. Comment at: lib/Sema/SemaStmt.cpp:2963 + InitializationSequence Seq(S, Entity, Kind, InitExpr); + if (Seq) { +for (const InitializationSequence::Step : Seq.steps()) { Use early exit here: ``` if (!Seq) return; // rest of function ``` Comment at: lib/Sema/SemaStmt.cpp:2965-2966 +for (const InitializationSequence::Step : Seq.steps()) { + if (!(Step.Kind == InitializationSequence::SK_ConstructorInitialization || +Step.Kind == InitializationSequence::SK_UserConversion)) +continue; Since you're simplifying the condition here, bring the not operator inside the parentheses. ``` if (Step.Kind != ... && Step.Kind != ...) ``` Comment at: lib/Sema/SemaStmt.cpp:2970 + FunctionDecl *FD = Step.Function.Function; + if (isa(FD)) { +// C++14 [class.copy]p32: Use early exit here: ``` if (!isa(FD) continue; // old if-block code ``` Comment at: lib/Sema/SemaStmt.cpp:2999-3000 -// expression node to persist. -Value = ImplicitCastExpr::Create(Context, Value->getType(), CK_NoOp, - Value, nullptr, VK_XValue); At this point, the variable Value is updated. Value is scoped to this function, and used again after this code. In your change, there is a new Value variable in the static function. Only that variable is updated and not this one, making this a change in behavior. Repository: rC Clang https://reviews.llvm.org/D43898 ___ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[PATCH] D43898: Preliminary refactoring in service of -Wreturn-std-move. NFC.
Quuxplusone added a comment. @rtrieu ping? Repository: rC Clang https://reviews.llvm.org/D43898 ___ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[PATCH] D43898: Preliminary refactoring in service of -Wreturn-std-move. NFC.
Quuxplusone added a comment. @rtrieu please take a look? Also, I would need someone to commit this for me, as I don't have commit privs. Repository: rC Clang https://reviews.llvm.org/D43898 ___ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[PATCH] D43898: Preliminary refactoring in service of -Wreturn-std-move. NFC.
Quuxplusone updated this revision to Diff 136380. Quuxplusone added a comment. Add a block comment for function `TryMoveInitialization`. Repository: rC Clang https://reviews.llvm.org/D43898 Files: include/clang/Sema/Sema.h lib/Sema/SemaExprCXX.cpp lib/Sema/SemaStmt.cpp lib/Sema/SemaTemplateInstantiateDecl.cpp Index: lib/Sema/SemaTemplateInstantiateDecl.cpp === --- lib/Sema/SemaTemplateInstantiateDecl.cpp +++ lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -741,7 +741,7 @@ if (D->isNRVOVariable()) { QualType ReturnType = cast(DC)->getReturnType(); -if (SemaRef.isCopyElisionCandidate(ReturnType, Var, false)) +if (SemaRef.isCopyElisionCandidate(ReturnType, Var, Sema::CES_Strict)) Var->setNRVOVariable(true); } Index: lib/Sema/SemaStmt.cpp === --- lib/Sema/SemaStmt.cpp +++ lib/Sema/SemaStmt.cpp @@ -2862,16 +2862,16 @@ /// \param E The expression being returned from the function or block, or /// being thrown. /// -/// \param AllowParamOrMoveConstructible Whether we allow function parameters or +/// \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, - bool AllowParamOrMoveConstructible) { + CopyElisionSemanticsKind CESK) { if (!getLangOpts().CPlusPlus) return nullptr; @@ -2884,29 +2884,29 @@ if (!VD) return nullptr; - if (isCopyElisionCandidate(ReturnType, VD, AllowParamOrMoveConstructible)) + if (isCopyElisionCandidate(ReturnType, VD, CESK)) return VD; return nullptr; } bool Sema::isCopyElisionCandidate(QualType ReturnType, const VarDecl *VD, - bool AllowParamOrMoveConstructible) { + 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 (!AllowParamOrMoveConstructible && !VDType->isDependentType() && +if (!(CESK & CES_AllowDifferentTypes) && !VDType->isDependentType() && !Context.hasSameUnqualifiedType(ReturnType, VDType)) return false; } // ...object (other than a function or catch-clause parameter)... if (VD->getKind() != Decl::Var && - !(AllowParamOrMoveConstructible && VD->getKind() == Decl::ParmVar)) + !((CESK & CES_AllowParameters) && VD->getKind() == Decl::ParmVar)) return false; if (VD->isExceptionVariable()) return false; @@ -2918,7 +2918,7 @@ // variable will no longer be used. if (VD->hasAttr()) return false; - if (AllowParamOrMoveConstructible) + if (CESK & CES_AllowDifferentTypes) return true; // ...non-volatile... @@ -2933,6 +2933,70 @@ return true; } +/// \brief Try to perform the initialization of a potentially-movable value, +/// which is the operand to a return or throw statement. +/// +/// This routine implements C++14 [class.copy]p32, which attempts to treat +/// returned lvalues as rvalues in certain cases (to prefer move construction), +/// then falls back to treating them as lvalues if that failed. +/// +/// \param Res We will fill this in if move-initialization was possible. +/// If move-initialization is not possible, such that we must fall back to +/// treating the operand as an lvalue, we will leave Res in its original +/// invalid state. +static void TryMoveInitialization(Sema& S, + const InitializedEntity , + const VarDecl *NRVOCandidate, + QualType ResultType, + Expr *Value, + ExprResult ) +{ + ImplicitCastExpr AsRvalue(ImplicitCastExpr::OnStack, Value->getType(), +CK_NoOp, Value, VK_XValue); + + Expr *InitExpr = + + InitializationKind Kind = InitializationKind::CreateCopy( + Value->getLocStart(), Value->getLocStart()); + + InitializationSequence Seq(S, Entity, Kind, InitExpr); + if (Seq) { +for (const InitializationSequence::Step : Seq.steps()) { + if (!(Step.Kind ==
[PATCH] D43898: Preliminary refactoring in service of -Wreturn-std-move. NFC.
Quuxplusone created this revision. Quuxplusone added a reviewer: rtrieu. Herald added a subscriber: cfe-commits. This patch is extracted from https://reviews.llvm.org/D43322, which adds a new diagnostic `-Wreturn-std-move`. This patch here is just the non-functional parts of that patch. It pulls `TryMoveInitialization` out into a separate function so that we can (in the next patch) try it twice — once with current C++ rules, and once with the rules as they would be if `return x` considered rvalue-ref-qualified conversion operators. This patch here does *not* add those new rules; it merely rearranges the existing code to make the next patch less bulky. Repository: rC Clang https://reviews.llvm.org/D43898 Files: include/clang/Sema/Sema.h lib/Sema/SemaExprCXX.cpp lib/Sema/SemaStmt.cpp lib/Sema/SemaTemplateInstantiateDecl.cpp Index: lib/Sema/SemaTemplateInstantiateDecl.cpp === --- lib/Sema/SemaTemplateInstantiateDecl.cpp +++ lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -741,7 +741,7 @@ if (D->isNRVOVariable()) { QualType ReturnType = cast(DC)->getReturnType(); -if (SemaRef.isCopyElisionCandidate(ReturnType, Var, false)) +if (SemaRef.isCopyElisionCandidate(ReturnType, Var, Sema::CES_Strict)) Var->setNRVOVariable(true); } Index: lib/Sema/SemaStmt.cpp === --- lib/Sema/SemaStmt.cpp +++ lib/Sema/SemaStmt.cpp @@ -2862,16 +2862,16 @@ /// \param E The expression being returned from the function or block, or /// being thrown. /// -/// \param AllowParamOrMoveConstructible Whether we allow function parameters or +/// \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, - bool AllowParamOrMoveConstructible) { + CopyElisionSemanticsKind CESK) { if (!getLangOpts().CPlusPlus) return nullptr; @@ -2884,29 +2884,29 @@ if (!VD) return nullptr; - if (isCopyElisionCandidate(ReturnType, VD, AllowParamOrMoveConstructible)) + if (isCopyElisionCandidate(ReturnType, VD, CESK)) return VD; return nullptr; } bool Sema::isCopyElisionCandidate(QualType ReturnType, const VarDecl *VD, - bool AllowParamOrMoveConstructible) { + 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 (!AllowParamOrMoveConstructible && !VDType->isDependentType() && +if (!(CESK & CES_AllowDifferentTypes) && !VDType->isDependentType() && !Context.hasSameUnqualifiedType(ReturnType, VDType)) return false; } // ...object (other than a function or catch-clause parameter)... if (VD->getKind() != Decl::Var && - !(AllowParamOrMoveConstructible && VD->getKind() == Decl::ParmVar)) + !((CESK & CES_AllowParameters) && VD->getKind() == Decl::ParmVar)) return false; if (VD->isExceptionVariable()) return false; @@ -2918,7 +2918,7 @@ // variable will no longer be used. if (VD->hasAttr()) return false; - if (AllowParamOrMoveConstructible) + if (CESK & CES_AllowDifferentTypes) return true; // ...non-volatile... @@ -2933,6 +2933,58 @@ return true; } +static void TryMoveInitialization(Sema& S, + const InitializedEntity , + const VarDecl *NRVOCandidate, + QualType ResultType, + Expr *Value, + ExprResult ) +{ + ImplicitCastExpr AsRvalue(ImplicitCastExpr::OnStack, Value->getType(), +CK_NoOp, Value, VK_XValue); + + Expr *InitExpr = + + InitializationKind Kind = InitializationKind::CreateCopy( + Value->getLocStart(), Value->getLocStart()); + + InitializationSequence Seq(S, Entity, Kind, InitExpr); + if (Seq) { +for (const InitializationSequence::Step : Seq.steps()) { + if (!(Step.Kind == InitializationSequence::SK_ConstructorInitialization || +Step.Kind ==