@@ -3083,12 +3083,20 @@ bool Sema::isCopyElisionCandidate(QualType ReturnType, const VarDecl *VD,
3083
3083
// Return false if VD is a __block variable. We don't want to implicitly move
3084
3084
// out of a __block variable during a return because we cannot assume the
3085
3085
// variable will no longer be used.
3086
- if (VD->hasAttr <BlocksAttr>()) return false ;
3086
+ if (VD->hasAttr <BlocksAttr>())
3087
+ return false ;
3087
3088
3088
3089
// ...non-volatile...
3089
3090
if (VD->getType ().isVolatileQualified ())
3090
3091
return false ;
3091
3092
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
+
3092
3100
if (CESK & CES_AllowDifferentTypes)
3093
3101
return true ;
3094
3102
@@ -3104,13 +3112,13 @@ bool Sema::isCopyElisionCandidate(QualType ReturnType, const VarDecl *VD,
3104
3112
// / Try to perform the initialization of a potentially-movable value,
3105
3113
// / which is the operand to a return or throw statement.
3106
3114
// /
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.
3110
3118
// /
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
3114
3122
// / conversion sequences.
3115
3123
// /
3116
3124
// / \param Res We will fill this in if move-initialization was possible.
@@ -3151,9 +3159,10 @@ static bool TryMoveInitialization(Sema &S, const InitializedEntity &Entity,
3151
3159
FunctionDecl *FD = Step.Function .Function ;
3152
3160
if (ConvertingConstructorsOnly) {
3153
3161
if (isa<CXXConstructorDecl>(FD)) {
3162
+ // C++11 [class.copy]p32:
3154
3163
// 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
3157
3166
// is not an rvalue reference to the object's type (possibly
3158
3167
// cv-qualified), overload resolution is performed again, considering
3159
3168
// the object as an lvalue.
@@ -3172,7 +3181,8 @@ static bool TryMoveInitialization(Sema &S, const InitializedEntity &Entity,
3172
3181
// Check that overload resolution selected a constructor taking an
3173
3182
// rvalue reference. If it selected an lvalue reference, then we
3174
3183
// 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 ()))
3176
3186
break ;
3177
3187
} else if (isa<CXXMethodDecl>(FD)) {
3178
3188
// Check that overload resolution selected a conversion operator
@@ -3202,73 +3212,38 @@ static bool TryMoveInitialization(Sema &S, const InitializedEntity &Entity,
3202
3212
// / Perform the initialization of a potentially-movable value, which
3203
3213
// / is the result of return value.
3204
3214
// /
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) {
3223
3221
ExprResult Res = ExprError ();
3224
3222
bool NeedSecondOverloadResolution = true ;
3225
3223
3226
3224
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
+ }
3228
3231
3229
3232
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);
3238
3234
}
3239
3235
3240
3236
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);
3243
3240
}
3244
3241
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);
3272
3247
if (FakeNRVOCandidate) {
3273
3248
QualType QT = FakeNRVOCandidate->getType ();
3274
3249
if (QT->isLValueReferenceType ()) {
0 commit comments