Skip to content

Commit 1be7c6f

Browse files
authored
[Clang] Fix constant eval of assignment operators with an explicit object parameter (#142964)
Fixes #142835
1 parent 9dc5dac commit 1be7c6f

File tree

3 files changed

+72
-24
lines changed

3 files changed

+72
-24
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -830,6 +830,7 @@ Bug Fixes to C++ Support
830830
- Clang modules now allow a module and its user to differ on TrivialAutoVarInit*
831831
- Fixed an access checking bug when initializing non-aggregates in default arguments (#GH62444), (#GH83608)
832832
- Fixed a pack substitution bug in deducing class template partial specializations. (#GH53609)
833+
- Fixed a crash when constant evaluating some explicit object member assignment operators. (#GH142835)
833834

834835
Bug Fixes to AST Handling
835836
^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -1049,7 +1050,7 @@ Sanitizers
10491050
----------
10501051

10511052
- ``-fsanitize=vptr`` is no longer a part of ``-fsanitize=undefined``.
1052-
- Sanitizer ignorelists now support the syntax ``src:*=sanitize``,
1053+
- Sanitizer ignorelists now support the syntax ``src:*=sanitize``,
10531054
``type:*=sanitize``, ``fun:*=sanitize``, ``global:*=sanitize``,
10541055
and ``mainfile:*=sanitize``.
10551056

clang/lib/AST/ExprConstant.cpp

Lines changed: 41 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -6549,8 +6549,8 @@ static bool MaybeHandleUnionActiveMemberChange(EvalInfo &Info,
65496549
}
65506550

65516551
static bool EvaluateCallArg(const ParmVarDecl *PVD, const Expr *Arg,
6552-
CallRef Call, EvalInfo &Info,
6553-
bool NonNull = false) {
6552+
CallRef Call, EvalInfo &Info, bool NonNull = false,
6553+
APValue **EvaluatedArg = nullptr) {
65546554
LValue LV;
65556555
// Create the parameter slot and register its destruction. For a vararg
65566556
// argument, create a temporary.
@@ -6570,13 +6570,17 @@ static bool EvaluateCallArg(const ParmVarDecl *PVD, const Expr *Arg,
65706570
return false;
65716571
}
65726572

6573+
if (EvaluatedArg)
6574+
*EvaluatedArg = &V;
6575+
65736576
return true;
65746577
}
65756578

65766579
/// Evaluate the arguments to a function call.
65776580
static bool EvaluateArgs(ArrayRef<const Expr *> Args, CallRef Call,
65786581
EvalInfo &Info, const FunctionDecl *Callee,
6579-
bool RightToLeft = false) {
6582+
bool RightToLeft = false,
6583+
LValue *ObjectArg = nullptr) {
65806584
bool Success = true;
65816585
llvm::SmallBitVector ForbiddenNullArgs;
65826586
if (Callee->hasAttr<NonNullAttr>()) {
@@ -6599,13 +6603,16 @@ static bool EvaluateArgs(ArrayRef<const Expr *> Args, CallRef Call,
65996603
const ParmVarDecl *PVD =
66006604
Idx < Callee->getNumParams() ? Callee->getParamDecl(Idx) : nullptr;
66016605
bool NonNull = !ForbiddenNullArgs.empty() && ForbiddenNullArgs[Idx];
6602-
if (!EvaluateCallArg(PVD, Args[Idx], Call, Info, NonNull)) {
6606+
APValue *That = nullptr;
6607+
if (!EvaluateCallArg(PVD, Args[Idx], Call, Info, NonNull, &That)) {
66036608
// If we're checking for a potential constant expression, evaluate all
66046609
// initializers even if some of them fail.
66056610
if (!Info.noteFailure())
66066611
return false;
66076612
Success = false;
66086613
}
6614+
if (PVD && PVD->isExplicitObjectParameter() && That && That->isLValue())
6615+
ObjectArg->setFrom(Info.Ctx, *That);
66096616
}
66106617
return Success;
66116618
}
@@ -6633,14 +6640,15 @@ static bool handleTrivialCopy(EvalInfo &Info, const ParmVarDecl *Param,
66336640

66346641
/// Evaluate a function call.
66356642
static bool HandleFunctionCall(SourceLocation CallLoc,
6636-
const FunctionDecl *Callee, const LValue *This,
6637-
const Expr *E, ArrayRef<const Expr *> Args,
6638-
CallRef Call, const Stmt *Body, EvalInfo &Info,
6643+
const FunctionDecl *Callee,
6644+
const LValue *ObjectArg, const Expr *E,
6645+
ArrayRef<const Expr *> Args, CallRef Call,
6646+
const Stmt *Body, EvalInfo &Info,
66396647
APValue &Result, const LValue *ResultSlot) {
66406648
if (!Info.CheckCallLimit(CallLoc))
66416649
return false;
66426650

6643-
CallStackFrame Frame(Info, E->getSourceRange(), Callee, This, E, Call);
6651+
CallStackFrame Frame(Info, E->getSourceRange(), Callee, ObjectArg, E, Call);
66446652

66456653
// For a trivial copy or move assignment, perform an APValue copy. This is
66466654
// essential for unions, where the operations performed by the assignment
@@ -6653,16 +6661,20 @@ static bool HandleFunctionCall(SourceLocation CallLoc,
66536661
(MD->getParent()->isUnion() ||
66546662
(MD->isTrivial() &&
66556663
isReadByLvalueToRvalueConversion(MD->getParent())))) {
6656-
assert(This &&
6664+
unsigned ExplicitOffset = MD->isExplicitObjectMemberFunction() ? 1 : 0;
6665+
assert(ObjectArg &&
66576666
(MD->isCopyAssignmentOperator() || MD->isMoveAssignmentOperator()));
66586667
APValue RHSValue;
66596668
if (!handleTrivialCopy(Info, MD->getParamDecl(0), Args[0], RHSValue,
66606669
MD->getParent()->isUnion()))
66616670
return false;
6662-
if (!handleAssignment(Info, Args[0], *This, MD->getThisType(),
6671+
6672+
LValue Obj;
6673+
if (!handleAssignment(Info, Args[ExplicitOffset], *ObjectArg,
6674+
MD->getFunctionObjectParameterReferenceType(),
66636675
RHSValue))
66646676
return false;
6665-
This->moveInto(Result);
6677+
ObjectArg->moveInto(Result);
66666678
return true;
66676679
} else if (MD && isLambdaCallOperator(MD)) {
66686680
// We're in a lambda; determine the lambda capture field maps unless we're
@@ -8289,7 +8301,7 @@ class ExprEvaluatorBase
82898301
QualType CalleeType = Callee->getType();
82908302

82918303
const FunctionDecl *FD = nullptr;
8292-
LValue *This = nullptr, ThisVal;
8304+
LValue *This = nullptr, ObjectArg;
82938305
auto Args = llvm::ArrayRef(E->getArgs(), E->getNumArgs());
82948306
bool HasQualifier = false;
82958307

@@ -8300,28 +8312,28 @@ class ExprEvaluatorBase
83008312
const CXXMethodDecl *Member = nullptr;
83018313
if (const MemberExpr *ME = dyn_cast<MemberExpr>(Callee)) {
83028314
// Explicit bound member calls, such as x.f() or p->g();
8303-
if (!EvaluateObjectArgument(Info, ME->getBase(), ThisVal))
8315+
if (!EvaluateObjectArgument(Info, ME->getBase(), ObjectArg))
83048316
return false;
83058317
Member = dyn_cast<CXXMethodDecl>(ME->getMemberDecl());
83068318
if (!Member)
83078319
return Error(Callee);
8308-
This = &ThisVal;
8320+
This = &ObjectArg;
83098321
HasQualifier = ME->hasQualifier();
83108322
} else if (const BinaryOperator *BE = dyn_cast<BinaryOperator>(Callee)) {
83118323
// Indirect bound member calls ('.*' or '->*').
83128324
const ValueDecl *D =
8313-
HandleMemberPointerAccess(Info, BE, ThisVal, false);
8325+
HandleMemberPointerAccess(Info, BE, ObjectArg, false);
83148326
if (!D)
83158327
return false;
83168328
Member = dyn_cast<CXXMethodDecl>(D);
83178329
if (!Member)
83188330
return Error(Callee);
8319-
This = &ThisVal;
8331+
This = &ObjectArg;
83208332
} else if (const auto *PDE = dyn_cast<CXXPseudoDestructorExpr>(Callee)) {
83218333
if (!Info.getLangOpts().CPlusPlus20)
83228334
Info.CCEDiag(PDE, diag::note_constexpr_pseudo_destructor);
8323-
return EvaluateObjectArgument(Info, PDE->getBase(), ThisVal) &&
8324-
HandleDestruction(Info, PDE, ThisVal, PDE->getDestroyedType());
8335+
return EvaluateObjectArgument(Info, PDE->getBase(), ObjectArg) &&
8336+
HandleDestruction(Info, PDE, ObjectArg, PDE->getDestroyedType());
83258337
} else
83268338
return Error(Callee);
83278339
FD = Member;
@@ -8358,7 +8370,7 @@ class ExprEvaluatorBase
83588370
if (const auto *MD = dyn_cast<CXXMethodDecl>(FD))
83598371
HasThis = MD->isImplicitObjectMemberFunction();
83608372
if (!EvaluateArgs(HasThis ? Args.slice(1) : Args, Call, Info, FD,
8361-
/*RightToLeft=*/true))
8373+
/*RightToLeft=*/true, &ObjectArg))
83628374
return false;
83638375
}
83648376

@@ -8373,20 +8385,20 @@ class ExprEvaluatorBase
83738385
if (Args.empty())
83748386
return Error(E);
83758387

8376-
if (!EvaluateObjectArgument(Info, Args[0], ThisVal))
8388+
if (!EvaluateObjectArgument(Info, Args[0], ObjectArg))
83778389
return false;
83788390

83798391
// If we are calling a static operator, the 'this' argument needs to be
83808392
// ignored after being evaluated.
83818393
if (MD->isInstance())
8382-
This = &ThisVal;
8394+
This = &ObjectArg;
83838395

83848396
// If this is syntactically a simple assignment using a trivial
83858397
// assignment operator, start the lifetimes of union members as needed,
83868398
// per C++20 [class.union]5.
83878399
if (Info.getLangOpts().CPlusPlus20 && OCE &&
83888400
OCE->getOperator() == OO_Equal && MD->isTrivial() &&
8389-
!MaybeHandleUnionActiveMemberChange(Info, Args[0], ThisVal))
8401+
!MaybeHandleUnionActiveMemberChange(Info, Args[0], ObjectArg))
83908402
return false;
83918403

83928404
Args = Args.slice(1);
@@ -8441,7 +8453,8 @@ class ExprEvaluatorBase
84418453
// Evaluate the arguments now if we've not already done so.
84428454
if (!Call) {
84438455
Call = Info.CurrentCall->createCall(FD);
8444-
if (!EvaluateArgs(Args, Call, Info, FD))
8456+
if (!EvaluateArgs(Args, Call, Info, FD, /*RightToLeft*/ false,
8457+
&ObjectArg))
84458458
return false;
84468459
}
84478460

@@ -8475,6 +8488,11 @@ class ExprEvaluatorBase
84758488
Stmt *Body = FD->getBody(Definition);
84768489
SourceLocation Loc = E->getExprLoc();
84778490

8491+
// Treat the object argument as `this` when evaluating defaulted
8492+
// special menmber functions
8493+
if (FD->hasCXXExplicitFunctionObjectParameter())
8494+
This = &ObjectArg;
8495+
84788496
if (!CheckConstexprFunction(Info, Loc, FD, Definition, Body) ||
84798497
!HandleFunctionCall(Loc, Definition, This, E, Args, Call, Body, Info,
84808498
Result, ResultSlot))

clang/test/SemaCXX/cxx2b-deducing-this-constexpr.cpp

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,3 +73,32 @@ int test() {
7373
}
7474

7575
}
76+
77+
namespace GH142835 {
78+
struct MoveMe {
79+
MoveMe& operator=(this MoveMe&, const MoveMe&) = default;
80+
constexpr MoveMe& operator=(this MoveMe& self, MoveMe&& other) {
81+
self.value = other.value;
82+
other.value = 0;
83+
return self;
84+
}
85+
int value = 4242;
86+
};
87+
88+
struct S {
89+
constexpr S& operator=(this S&, const S&) = default;
90+
S& operator=(this S&, S&&) = default;
91+
92+
MoveMe move_me;
93+
};
94+
95+
constexpr bool f() {
96+
S s1{};
97+
S s2{};
98+
s2 = s1;
99+
return true;
100+
}
101+
102+
static_assert(f());
103+
104+
}

0 commit comments

Comments
 (0)