Skip to content

Commit fbee4a0

Browse files
nullptr-cppArthur O'Dwyer
authored andcommitted
[C++20] [P1825] More implicit moves
Implement all of P1825R0: - implicitly movable entity can be an rvalue reference to non-volatile automatic object. - operand of throw-expression can be a function or catch-clause parameter (support for function parameter has already been implemented). - in the first overload resolution, the selected function no need to be a constructor. - in the first overload resolution, the first parameter of the selected function no need to be an rvalue reference to the object's type. This patch also removes the diagnostic `-Wreturn-std-move-in-c++11`. Differential Revision: https://reviews.llvm.org/D88220
1 parent 16af973 commit fbee4a0

File tree

9 files changed

+390
-105
lines changed

9 files changed

+390
-105
lines changed

clang/include/clang/Basic/DiagnosticGroups.td

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -499,7 +499,6 @@ def Packed : DiagGroup<"packed">;
499499
def Padded : DiagGroup<"padded">;
500500

501501
def PessimizingMove : DiagGroup<"pessimizing-move">;
502-
def ReturnStdMoveInCXX11 : DiagGroup<"return-std-move-in-c++11">;
503502
def ReturnStdMove : DiagGroup<"return-std-move">;
504503

505504
def PointerArith : DiagGroup<"pointer-arith">;

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6370,13 +6370,6 @@ def warn_return_std_move : Warning<
63706370
InGroup<ReturnStdMove>, DefaultIgnore;
63716371
def note_add_std_move : Note<
63726372
"call 'std::move' explicitly to avoid copying">;
6373-
def warn_return_std_move_in_cxx11 : Warning<
6374-
"prior to the resolution of a defect report against ISO C++11, "
6375-
"local variable %0 would have been copied despite being returned by name, "
6376-
"due to its not matching the function return type%diff{ ($ vs $)|}1,2">,
6377-
InGroup<ReturnStdMoveInCXX11>, DefaultIgnore;
6378-
def note_add_std_move_in_cxx11 : Note<
6379-
"call 'std::move' explicitly to avoid copying on older compilers">;
63806373

63816374
def warn_string_plus_int : Warning<
63826375
"adding %0 to a string does not append to the string">,

clang/include/clang/Sema/Sema.h

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4724,10 +4724,12 @@ class Sema final {
47244724
CES_AllowParameters = 1,
47254725
CES_AllowDifferentTypes = 2,
47264726
CES_AllowExceptionVariables = 4,
4727-
CES_FormerDefault = (CES_AllowParameters),
4728-
CES_Default = (CES_AllowParameters | CES_AllowDifferentTypes),
4729-
CES_AsIfByStdMove = (CES_AllowParameters | CES_AllowDifferentTypes |
4730-
CES_AllowExceptionVariables),
4727+
CES_AllowRValueReferenceType = 8,
4728+
CES_ImplicitlyMovableCXX11CXX14CXX17 =
4729+
(CES_AllowParameters | CES_AllowDifferentTypes),
4730+
CES_ImplicitlyMovableCXX20 =
4731+
(CES_AllowParameters | CES_AllowDifferentTypes |
4732+
CES_AllowExceptionVariables | CES_AllowRValueReferenceType),
47314733
};
47324734

47334735
VarDecl *getCopyElisionCandidate(QualType ReturnType, Expr *E,

clang/lib/Sema/SemaCoroutine.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -996,7 +996,8 @@ StmtResult Sema::BuildCoreturnStmt(SourceLocation Loc, Expr *E,
996996

997997
// Move the return value if we can
998998
if (E) {
999-
auto NRVOCandidate = this->getCopyElisionCandidate(E->getType(), E, CES_AsIfByStdMove);
999+
const VarDecl *NRVOCandidate = this->getCopyElisionCandidate(
1000+
E->getType(), E, CES_ImplicitlyMovableCXX20);
10001001
if (NRVOCandidate) {
10011002
InitializedEntity Entity =
10021003
InitializedEntity::InitializeResult(Loc, E->getType(), NRVOCandidate);

clang/lib/Sema/SemaStmt.cpp

Lines changed: 41 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -3083,12 +3083,20 @@ bool Sema::isCopyElisionCandidate(QualType ReturnType, const VarDecl *VD,
30833083
// Return false if VD is a __block variable. We don't want to implicitly move
30843084
// out of a __block variable during a return because we cannot assume the
30853085
// variable will no longer be used.
3086-
if (VD->hasAttr<BlocksAttr>()) return false;
3086+
if (VD->hasAttr<BlocksAttr>())
3087+
return false;
30873088

30883089
// ...non-volatile...
30893090
if (VD->getType().isVolatileQualified())
30903091
return false;
30913092

3093+
// C++20 [class.copy.elision]p3:
3094+
// ...rvalue reference to a non-volatile...
3095+
if (VD->getType()->isRValueReferenceType() &&
3096+
(!(CESK & CES_AllowRValueReferenceType) ||
3097+
VD->getType().getNonReferenceType().isVolatileQualified()))
3098+
return false;
3099+
30923100
if (CESK & CES_AllowDifferentTypes)
30933101
return true;
30943102

@@ -3104,13 +3112,13 @@ bool Sema::isCopyElisionCandidate(QualType ReturnType, const VarDecl *VD,
31043112
/// Try to perform the initialization of a potentially-movable value,
31053113
/// which is the operand to a return or throw statement.
31063114
///
3107-
/// This routine implements C++14 [class.copy]p32, which attempts to treat
3108-
/// returned lvalues as rvalues in certain cases (to prefer move construction),
3109-
/// then falls back to treating them as lvalues if that failed.
3115+
/// This routine implements C++20 [class.copy.elision]p3, which attempts to
3116+
/// treat returned lvalues as rvalues in certain cases (to prefer move
3117+
/// construction), then falls back to treating them as lvalues if that failed.
31103118
///
3111-
/// \param ConvertingConstructorsOnly If true, follow [class.copy]p32 and reject
3112-
/// resolutions that find non-constructors, such as derived-to-base conversions
3113-
/// or `operator T()&&` member functions. If false, do consider such
3119+
/// \param ConvertingConstructorsOnly If true, follow [class.copy.elision]p3 and
3120+
/// reject resolutions that find non-constructors, such as derived-to-base
3121+
/// conversions or `operator T()&&` member functions. If false, do consider such
31143122
/// conversion sequences.
31153123
///
31163124
/// \param Res We will fill this in if move-initialization was possible.
@@ -3151,9 +3159,10 @@ static bool TryMoveInitialization(Sema &S, const InitializedEntity &Entity,
31513159
FunctionDecl *FD = Step.Function.Function;
31523160
if (ConvertingConstructorsOnly) {
31533161
if (isa<CXXConstructorDecl>(FD)) {
3162+
// C++11 [class.copy]p32:
31543163
// C++14 [class.copy]p32:
3155-
// [...] If the first overload resolution fails or was not performed,
3156-
// or if the type of the first parameter of the selected constructor
3164+
// C++17 [class.copy.elision]p3:
3165+
// [...] if the type of the first parameter of the selected constructor
31573166
// is not an rvalue reference to the object's type (possibly
31583167
// cv-qualified), overload resolution is performed again, considering
31593168
// the object as an lvalue.
@@ -3172,7 +3181,8 @@ static bool TryMoveInitialization(Sema &S, const InitializedEntity &Entity,
31723181
// Check that overload resolution selected a constructor taking an
31733182
// rvalue reference. If it selected an lvalue reference, then we
31743183
// didn't need to cast this thing to an rvalue in the first place.
3175-
if (!isa<RValueReferenceType>(FD->getParamDecl(0)->getType()))
3184+
if (IsDiagnosticsCheck &&
3185+
!isa<RValueReferenceType>(FD->getParamDecl(0)->getType()))
31763186
break;
31773187
} else if (isa<CXXMethodDecl>(FD)) {
31783188
// Check that overload resolution selected a conversion operator
@@ -3202,73 +3212,38 @@ static bool TryMoveInitialization(Sema &S, const InitializedEntity &Entity,
32023212
/// Perform the initialization of a potentially-movable value, which
32033213
/// is the result of return value.
32043214
///
3205-
/// This routine implements C++14 [class.copy]p32, which attempts to treat
3206-
/// returned lvalues as rvalues in certain cases (to prefer move construction),
3207-
/// then falls back to treating them as lvalues if that failed.
3208-
ExprResult
3209-
Sema::PerformMoveOrCopyInitialization(const InitializedEntity &Entity,
3210-
const VarDecl *NRVOCandidate,
3211-
QualType ResultType,
3212-
Expr *Value,
3213-
bool AllowNRVO) {
3214-
// C++14 [class.copy]p32:
3215-
// When the criteria for elision of a copy/move operation are met, but not for
3216-
// an exception-declaration, and the object to be copied is designated by an
3217-
// lvalue, or when the expression in a return statement is a (possibly
3218-
// parenthesized) id-expression that names an object with automatic storage
3219-
// duration declared in the body or parameter-declaration-clause of the
3220-
// innermost enclosing function or lambda-expression, overload resolution to
3221-
// select the constructor for the copy is first performed as if the object
3222-
// were designated by an rvalue.
3215+
/// This routine implements C++20 [class.copy.elision]p3, which attempts to
3216+
/// treat returned lvalues as rvalues in certain cases (to prefer move
3217+
/// construction), then falls back to treating them as lvalues if that failed.
3218+
ExprResult Sema::PerformMoveOrCopyInitialization(
3219+
const InitializedEntity &Entity, const VarDecl *NRVOCandidate,
3220+
QualType ResultType, Expr *Value, bool AllowNRVO) {
32233221
ExprResult Res = ExprError();
32243222
bool NeedSecondOverloadResolution = true;
32253223

32263224
if (AllowNRVO) {
3227-
bool AffectedByCWG1579 = false;
3225+
CopyElisionSemanticsKind CESK = CES_Strict;
3226+
if (getLangOpts().CPlusPlus20) {
3227+
CESK = CES_ImplicitlyMovableCXX20;
3228+
} else if (getLangOpts().CPlusPlus11) {
3229+
CESK = CES_ImplicitlyMovableCXX11CXX14CXX17;
3230+
}
32283231

32293232
if (!NRVOCandidate) {
3230-
NRVOCandidate = getCopyElisionCandidate(ResultType, Value, CES_Default);
3231-
if (NRVOCandidate &&
3232-
!getDiagnostics().isIgnored(diag::warn_return_std_move_in_cxx11,
3233-
Value->getExprLoc())) {
3234-
const VarDecl *NRVOCandidateInCXX11 =
3235-
getCopyElisionCandidate(ResultType, Value, CES_FormerDefault);
3236-
AffectedByCWG1579 = (!NRVOCandidateInCXX11);
3237-
}
3233+
NRVOCandidate = getCopyElisionCandidate(ResultType, Value, CESK);
32383234
}
32393235

32403236
if (NRVOCandidate) {
3241-
NeedSecondOverloadResolution = TryMoveInitialization(
3242-
*this, Entity, NRVOCandidate, ResultType, Value, true, false, Res);
3237+
NeedSecondOverloadResolution =
3238+
TryMoveInitialization(*this, Entity, NRVOCandidate, ResultType, Value,
3239+
!getLangOpts().CPlusPlus20, false, Res);
32433240
}
32443241

3245-
if (!NeedSecondOverloadResolution && AffectedByCWG1579) {
3246-
QualType QT = NRVOCandidate->getType();
3247-
if (QT.getNonReferenceType().getUnqualifiedType().isTriviallyCopyableType(
3248-
Context)) {
3249-
// Adding 'std::move' around a trivially copyable variable is probably
3250-
// pointless. Don't suggest it.
3251-
} else {
3252-
// Common cases for this are returning unique_ptr<Derived> from a
3253-
// function of return type unique_ptr<Base>, or returning T from a
3254-
// function of return type Expected<T>. This is totally fine in a
3255-
// post-CWG1579 world, but was not fine before.
3256-
assert(!ResultType.isNull());
3257-
SmallString<32> Str;
3258-
Str += "std::move(";
3259-
Str += NRVOCandidate->getDeclName().getAsString();
3260-
Str += ")";
3261-
Diag(Value->getExprLoc(), diag::warn_return_std_move_in_cxx11)
3262-
<< Value->getSourceRange() << NRVOCandidate->getDeclName()
3263-
<< ResultType << QT;
3264-
Diag(Value->getExprLoc(), diag::note_add_std_move_in_cxx11)
3265-
<< FixItHint::CreateReplacement(Value->getSourceRange(), Str);
3266-
}
3267-
} else if (NeedSecondOverloadResolution &&
3268-
!getDiagnostics().isIgnored(diag::warn_return_std_move,
3269-
Value->getExprLoc())) {
3270-
const VarDecl *FakeNRVOCandidate =
3271-
getCopyElisionCandidate(QualType(), Value, CES_AsIfByStdMove);
3242+
if (!getLangOpts().CPlusPlus20 && NeedSecondOverloadResolution &&
3243+
!getDiagnostics().isIgnored(diag::warn_return_std_move,
3244+
Value->getExprLoc())) {
3245+
const VarDecl *FakeNRVOCandidate = getCopyElisionCandidate(
3246+
QualType(), Value, CES_ImplicitlyMovableCXX20);
32723247
if (FakeNRVOCandidate) {
32733248
QualType QT = FakeNRVOCandidate->getType();
32743249
if (QT->isLValueReferenceType()) {

0 commit comments

Comments
 (0)