Skip to content

Commit 7d2d5a3

Browse files
committed
[clang] Apply P1825 as Defect Report from C++11 up to C++20.
This extends the effects of [[ http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1825r0.html | P1825 ]] to all C++ standards from C++11 up to C++20. According to Motion 23 from Cologne 2019, P1825R0 was accepted as a Defect Report, so we retroactively apply this all the way back to C++11. Note that we also remove implicit moves from C++98 as an extension altogether, since the expanded first overload resolution from P1825 can cause some meaning changes in C++98. For example it can change which copy constructor is picked when both const and non-const ones are available. This also rips out warn_return_std_move since there are no cases where it would be worthwhile to suggest it. This also fixes a bug with bailing into the second overload resolution when encountering a non-rvref qualified conversion operator. This was unnoticed until now, so two new test cases cover these. Signed-off-by: Matheus Izvekov <mizvekov@gmail.com> Reviewed By: rsmith Differential Revision: https://reviews.llvm.org/D104500
1 parent cd8f979 commit 7d2d5a3

File tree

8 files changed

+194
-641
lines changed

8 files changed

+194
-641
lines changed

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6485,12 +6485,6 @@ def warn_pessimizing_move_on_initialization : Warning<
64856485
InGroup<PessimizingMove>, DefaultIgnore;
64866486
def note_remove_move : Note<"remove std::move call here">;
64876487

6488-
def warn_return_std_move : Warning<
6489-
"local variable %0 will be copied despite being %select{returned|thrown}1 by name">,
6490-
InGroup<ReturnStdMove>, DefaultIgnore;
6491-
def note_add_std_move : Note<
6492-
"call 'std::move' explicitly to avoid copying">;
6493-
64946488
def warn_string_plus_int : Warning<
64956489
"adding %0 to a string does not append to the string">,
64966490
InGroup<StringPlusInt>;

clang/include/clang/Sema/Sema.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4782,8 +4782,7 @@ class Sema final {
47824782
bool isCopyElidable() const { return S == MoveEligibleAndCopyElidable; }
47834783
};
47844784
NamedReturnInfo getNamedReturnInfo(Expr *&E, bool ForceCXX2b = false);
4785-
NamedReturnInfo getNamedReturnInfo(const VarDecl *VD,
4786-
bool ForceCXX20 = false);
4785+
NamedReturnInfo getNamedReturnInfo(const VarDecl *VD);
47874786
const VarDecl *getCopyElisionCandidate(NamedReturnInfo &Info,
47884787
QualType ReturnType);
47894788

clang/lib/Sema/SemaStmt.cpp

Lines changed: 25 additions & 158 deletions
Original file line numberDiff line numberDiff line change
@@ -3332,7 +3332,7 @@ Sema::NamedReturnInfo Sema::getNamedReturnInfo(Expr *&E, bool ForceCXX2b) {
33323332
const auto *VD = dyn_cast<VarDecl>(DR->getDecl());
33333333
if (!VD)
33343334
return NamedReturnInfo();
3335-
NamedReturnInfo Res = getNamedReturnInfo(VD, /*ForceCXX20=*/ForceCXX2b);
3335+
NamedReturnInfo Res = getNamedReturnInfo(VD);
33363336
if (Res.Candidate && !E->isXValue() &&
33373337
(ForceCXX2b || getLangOpts().CPlusPlus2b)) {
33383338
E = ImplicitCastExpr::Create(Context, VD->getType().getNonReferenceType(),
@@ -3342,46 +3342,28 @@ Sema::NamedReturnInfo Sema::getNamedReturnInfo(Expr *&E, bool ForceCXX2b) {
33423342
return Res;
33433343
}
33443344

3345-
/// Updates the status in the given NamedReturnInfo object to disallow
3346-
/// copy elision, and optionally also implicit move.
3347-
///
3348-
/// \param Info The NamedReturnInfo object to update.
3349-
///
3350-
/// \param CanMove If true, disallow only copy elision.
3351-
/// If false, also disallow implcit move.
3352-
static void disallowNRVO(Sema::NamedReturnInfo &Info, bool CanMove) {
3353-
Info.S = std::min(Info.S, CanMove ? Sema::NamedReturnInfo::MoveEligible
3354-
: Sema::NamedReturnInfo::None);
3355-
}
3356-
33573345
/// Determine whether the given NRVO candidate variable is move-eligible or
33583346
/// copy-elidable, without considering function return type.
33593347
///
33603348
/// \param VD The NRVO candidate variable.
33613349
///
3362-
/// \param ForceCXX20 Overrides detection of current language mode
3363-
/// and uses the rules for C++20.
3364-
///
33653350
/// \returns An aggregate which contains the Candidate and isMoveEligible
33663351
/// and isCopyElidable methods. If Candidate is non-null, it means
33673352
/// isMoveEligible() would be true under the most permissive language standard.
3368-
Sema::NamedReturnInfo Sema::getNamedReturnInfo(const VarDecl *VD,
3369-
bool ForceCXX20) {
3370-
bool hasCXX11 = getLangOpts().CPlusPlus11 || ForceCXX20;
3371-
bool hasCXX20 = getLangOpts().CPlusPlus20 || ForceCXX20;
3353+
Sema::NamedReturnInfo Sema::getNamedReturnInfo(const VarDecl *VD) {
33723354
NamedReturnInfo Info{VD, NamedReturnInfo::MoveEligibleAndCopyElidable};
33733355

33743356
// C++20 [class.copy.elision]p3:
33753357
// - in a return statement in a function with ...
33763358
// (other than a function ... parameter)
33773359
if (VD->getKind() == Decl::ParmVar)
3378-
disallowNRVO(Info, hasCXX11);
3360+
Info.S = NamedReturnInfo::MoveEligible;
33793361
else if (VD->getKind() != Decl::Var)
33803362
return NamedReturnInfo();
33813363

33823364
// (other than ... a catch-clause parameter)
33833365
if (VD->isExceptionVariable())
3384-
disallowNRVO(Info, hasCXX20);
3366+
Info.S = NamedReturnInfo::MoveEligible;
33853367

33863368
// ...automatic...
33873369
if (!VD->hasLocalStorage())
@@ -3406,7 +3388,7 @@ Sema::NamedReturnInfo Sema::getNamedReturnInfo(const VarDecl *VD,
34063388
if (VDReferencedType.isVolatileQualified() ||
34073389
!VDReferencedType->isObjectType())
34083390
return NamedReturnInfo();
3409-
disallowNRVO(Info, hasCXX20);
3391+
Info.S = NamedReturnInfo::MoveEligible;
34103392
} else {
34113393
return NamedReturnInfo();
34123394
}
@@ -3415,7 +3397,7 @@ Sema::NamedReturnInfo Sema::getNamedReturnInfo(const VarDecl *VD,
34153397
// alignment cannot use NRVO.
34163398
if (!VDType->isDependentType() && VD->hasAttr<AlignedAttr>() &&
34173399
Context.getDeclAlign(VD) > Context.getTypeAlignInChars(VDType))
3418-
disallowNRVO(Info, hasCXX11);
3400+
Info.S = NamedReturnInfo::MoveEligible;
34193401

34203402
return Info;
34213403
}
@@ -3459,110 +3441,11 @@ const VarDecl *Sema::getCopyElisionCandidate(NamedReturnInfo &Info,
34593441
// When considering moving this expression out, allow dissimilar types.
34603442
if (!VDType->isDependentType() &&
34613443
!Context.hasSameUnqualifiedType(ReturnType, VDType))
3462-
disallowNRVO(Info, getLangOpts().CPlusPlus11);
3444+
Info.S = NamedReturnInfo::MoveEligible;
34633445
}
34643446
return Info.isCopyElidable() ? Info.Candidate : nullptr;
34653447
}
34663448

3467-
/// Try to perform the initialization of a potentially-movable value,
3468-
/// which is the operand to a return or throw statement.
3469-
///
3470-
/// This routine implements C++20 [class.copy.elision]p3, which attempts to
3471-
/// treat returned lvalues as rvalues in certain cases (to prefer move
3472-
/// construction), then falls back to treating them as lvalues if that failed.
3473-
///
3474-
/// \param ConvertingConstructorsOnly If true, follow [class.copy.elision]p3 and
3475-
/// reject resolutions that find non-constructors, such as derived-to-base
3476-
/// conversions or `operator T()&&` member functions. If false, do consider such
3477-
/// conversion sequences.
3478-
///
3479-
/// \param Res We will fill this in if move-initialization was possible.
3480-
/// If move-initialization is not possible, such that we must fall back to
3481-
/// treating the operand as an lvalue, we will leave Res in its original
3482-
/// invalid state.
3483-
///
3484-
/// \returns Whether we need to do the second overload resolution. If the first
3485-
/// overload resolution fails, or if the first overload resolution succeeds but
3486-
/// the selected constructor/operator doesn't match the additional criteria, we
3487-
/// need to do the second overload resolution.
3488-
static bool TryMoveInitialization(Sema &S, const InitializedEntity &Entity,
3489-
const VarDecl *NRVOCandidate, Expr *&Value,
3490-
bool ConvertingConstructorsOnly,
3491-
bool IsDiagnosticsCheck, ExprResult &Res) {
3492-
ImplicitCastExpr AsRvalue(ImplicitCastExpr::OnStack, Value->getType(),
3493-
CK_NoOp, Value, VK_XValue, FPOptionsOverride());
3494-
3495-
Expr *InitExpr = &AsRvalue;
3496-
3497-
InitializationKind Kind = InitializationKind::CreateCopy(
3498-
Value->getBeginLoc(), Value->getBeginLoc());
3499-
3500-
InitializationSequence Seq(S, Entity, Kind, InitExpr);
3501-
3502-
bool NeedSecondOverloadResolution = true;
3503-
if (!Seq &&
3504-
(IsDiagnosticsCheck || Seq.getFailedOverloadResult() != OR_Deleted)) {
3505-
return NeedSecondOverloadResolution;
3506-
}
3507-
3508-
for (const InitializationSequence::Step &Step : Seq.steps()) {
3509-
if (Step.Kind != InitializationSequence::SK_ConstructorInitialization &&
3510-
Step.Kind != InitializationSequence::SK_UserConversion)
3511-
continue;
3512-
3513-
FunctionDecl *FD = Step.Function.Function;
3514-
if (ConvertingConstructorsOnly) {
3515-
if (isa<CXXConstructorDecl>(FD)) {
3516-
// C++11 [class.copy]p32:
3517-
// C++14 [class.copy]p32:
3518-
// C++17 [class.copy.elision]p3:
3519-
// [...] if the type of the first parameter of the selected constructor
3520-
// is not an rvalue reference to the object's type (possibly
3521-
// cv-qualified), overload resolution is performed again, considering
3522-
// the object as an lvalue.
3523-
const RValueReferenceType *RRefType =
3524-
FD->getParamDecl(0)->getType()->getAs<RValueReferenceType>();
3525-
if (!RRefType)
3526-
break;
3527-
if (!S.Context.hasSameUnqualifiedType(RRefType->getPointeeType(),
3528-
NRVOCandidate->getType()))
3529-
break;
3530-
} else {
3531-
continue;
3532-
}
3533-
} else {
3534-
if (isa<CXXConstructorDecl>(FD)) {
3535-
// Check that overload resolution selected a constructor taking an
3536-
// rvalue reference. If it selected an lvalue reference, then we
3537-
// didn't need to cast this thing to an rvalue in the first place.
3538-
if (IsDiagnosticsCheck &&
3539-
!isa<RValueReferenceType>(FD->getParamDecl(0)->getType()))
3540-
break;
3541-
} else if (isa<CXXMethodDecl>(FD)) {
3542-
// Check that overload resolution selected a conversion operator
3543-
// taking an rvalue reference.
3544-
if (cast<CXXMethodDecl>(FD)->getRefQualifier() != RQ_RValue)
3545-
break;
3546-
} else {
3547-
continue;
3548-
}
3549-
}
3550-
3551-
NeedSecondOverloadResolution = false;
3552-
// Promote "AsRvalue" to the heap, since we now need this
3553-
// expression node to persist.
3554-
Value =
3555-
ImplicitCastExpr::Create(S.Context, Value->getType(), CK_NoOp, Value,
3556-
nullptr, VK_XValue, FPOptionsOverride());
3557-
3558-
// Complete type-checking the initialization of the return type
3559-
// using the constructor we found.
3560-
Res = Seq.Perform(S, Entity, Kind, Value);
3561-
}
3562-
3563-
return NeedSecondOverloadResolution;
3564-
}
3565-
35663449
/// Perform the initialization of a potentially-movable value, which
35673450
/// is the result of return value.
35683451
///
@@ -3573,42 +3456,26 @@ ExprResult
35733456
Sema::PerformMoveOrCopyInitialization(const InitializedEntity &Entity,
35743457
const NamedReturnInfo &NRInfo,
35753458
Expr *Value) {
3576-
3577-
if (NRInfo.Candidate && !getLangOpts().CPlusPlus2b) {
3578-
if (NRInfo.isMoveEligible()) {
3579-
ExprResult Res;
3580-
if (!TryMoveInitialization(*this, Entity, NRInfo.Candidate, Value,
3581-
!getLangOpts().CPlusPlus20, false, Res))
3582-
return Res;
3583-
}
3584-
if (!getDiagnostics().isIgnored(diag::warn_return_std_move,
3585-
Value->getExprLoc())) {
3586-
QualType QT = NRInfo.Candidate->getType();
3587-
if (QT.getNonReferenceType().getUnqualifiedType().isTriviallyCopyableType(
3588-
Context)) {
3589-
// Adding 'std::move' around a trivially copyable variable is probably
3590-
// pointless. Don't suggest it.
3591-
} else {
3592-
ExprResult FakeRes = ExprError();
3593-
Expr *FakeValue = Value;
3594-
TryMoveInitialization(*this, Entity, NRInfo.Candidate, FakeValue, false,
3595-
true, FakeRes);
3596-
if (!FakeRes.isInvalid()) {
3597-
bool IsThrow = (Entity.getKind() == InitializedEntity::EK_Exception);
3598-
SmallString<32> Str;
3599-
Str += "std::move(";
3600-
Str += NRInfo.Candidate->getDeclName().getAsString();
3601-
Str += ")";
3602-
Diag(Value->getExprLoc(), diag::warn_return_std_move)
3603-
<< Value->getSourceRange() << NRInfo.Candidate->getDeclName()
3604-
<< IsThrow;
3605-
Diag(Value->getExprLoc(), diag::note_add_std_move)
3606-
<< FixItHint::CreateReplacement(Value->getSourceRange(), Str);
3607-
}
3608-
}
3459+
if (getLangOpts().CPlusPlus11 && !getLangOpts().CPlusPlus2b &&
3460+
NRInfo.isMoveEligible()) {
3461+
ImplicitCastExpr AsRvalue(ImplicitCastExpr::OnStack, Value->getType(),
3462+
CK_NoOp, Value, VK_XValue, FPOptionsOverride());
3463+
Expr *InitExpr = &AsRvalue;
3464+
auto Kind = InitializationKind::CreateCopy(Value->getBeginLoc(),
3465+
Value->getBeginLoc());
3466+
InitializationSequence Seq(*this, Entity, Kind, InitExpr);
3467+
auto Res = Seq.getFailedOverloadResult();
3468+
if (Res == OR_Success || Res == OR_Deleted) {
3469+
// Promote "AsRvalue" to the heap, since we now need this
3470+
// expression node to persist.
3471+
Value =
3472+
ImplicitCastExpr::Create(Context, Value->getType(), CK_NoOp, Value,
3473+
nullptr, VK_XValue, FPOptionsOverride());
3474+
// Complete type-checking the initialization of the return type
3475+
// using the constructor we found.
3476+
return Seq.Perform(*this, Entity, Kind, Value);
36093477
}
36103478
}
3611-
36123479
// Either we didn't meet the criteria for treating an lvalue as an rvalue,
36133480
// above, or overload resolution failed. Either way, we need to try
36143481
// (again) now with the return value expression as written.

0 commit comments

Comments
 (0)