-
Notifications
You must be signed in to change notification settings - Fork 15.4k
[Clang] Fix constant eval of assignment operators with an explicit object parameter #142964
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
af369cd to
6d98a33
Compare
|
@llvm/pr-subscribers-clang Author: Corentin Jabot (cor3ntin) ChangesFixes #142835 Full diff: https://github.com/llvm/llvm-project/pull/142964.diff 3 Files Affected:
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 512071427b65c..747388fb985aa 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -830,6 +830,7 @@ Bug Fixes to C++ Support
- Clang modules now allow a module and its user to differ on TrivialAutoVarInit*
- Fixed an access checking bug when initializing non-aggregates in default arguments (#GH62444), (#GH83608)
- Fixed a pack substitution bug in deducing class template partial specializations. (#GH53609)
+- Fixed a crash when constant evaluating some explicit object member assignment operators. (#GH142835)
Bug Fixes to AST Handling
^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -1049,7 +1050,7 @@ Sanitizers
----------
- ``-fsanitize=vptr`` is no longer a part of ``-fsanitize=undefined``.
-- Sanitizer ignorelists now support the syntax ``src:*=sanitize``,
+- Sanitizer ignorelists now support the syntax ``src:*=sanitize``,
``type:*=sanitize``, ``fun:*=sanitize``, ``global:*=sanitize``,
and ``mainfile:*=sanitize``.
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index ab964e592de80..78dd9770f65f6 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -6549,8 +6549,8 @@ static bool MaybeHandleUnionActiveMemberChange(EvalInfo &Info,
}
static bool EvaluateCallArg(const ParmVarDecl *PVD, const Expr *Arg,
- CallRef Call, EvalInfo &Info,
- bool NonNull = false) {
+ CallRef Call, EvalInfo &Info, bool NonNull = false,
+ APValue **EvaluatedArg = nullptr) {
LValue LV;
// Create the parameter slot and register its destruction. For a vararg
// argument, create a temporary.
@@ -6570,13 +6570,17 @@ static bool EvaluateCallArg(const ParmVarDecl *PVD, const Expr *Arg,
return false;
}
+ if (EvaluatedArg)
+ *EvaluatedArg = &V;
+
return true;
}
/// Evaluate the arguments to a function call.
static bool EvaluateArgs(ArrayRef<const Expr *> Args, CallRef Call,
EvalInfo &Info, const FunctionDecl *Callee,
- bool RightToLeft = false) {
+ bool RightToLeft = false,
+ LValue *ObjectArg = nullptr) {
bool Success = true;
llvm::SmallBitVector ForbiddenNullArgs;
if (Callee->hasAttr<NonNullAttr>()) {
@@ -6599,13 +6603,16 @@ static bool EvaluateArgs(ArrayRef<const Expr *> Args, CallRef Call,
const ParmVarDecl *PVD =
Idx < Callee->getNumParams() ? Callee->getParamDecl(Idx) : nullptr;
bool NonNull = !ForbiddenNullArgs.empty() && ForbiddenNullArgs[Idx];
- if (!EvaluateCallArg(PVD, Args[Idx], Call, Info, NonNull)) {
+ APValue *That = nullptr;
+ if (!EvaluateCallArg(PVD, Args[Idx], Call, Info, NonNull, &That)) {
// If we're checking for a potential constant expression, evaluate all
// initializers even if some of them fail.
if (!Info.noteFailure())
return false;
Success = false;
}
+ if (PVD && PVD->isExplicitObjectParameter() && That && That->isLValue())
+ ObjectArg->setFrom(Info.Ctx, *That);
}
return Success;
}
@@ -6633,14 +6640,15 @@ static bool handleTrivialCopy(EvalInfo &Info, const ParmVarDecl *Param,
/// Evaluate a function call.
static bool HandleFunctionCall(SourceLocation CallLoc,
- const FunctionDecl *Callee, const LValue *This,
- const Expr *E, ArrayRef<const Expr *> Args,
- CallRef Call, const Stmt *Body, EvalInfo &Info,
+ const FunctionDecl *Callee,
+ const LValue *ObjectArg, const Expr *E,
+ ArrayRef<const Expr *> Args, CallRef Call,
+ const Stmt *Body, EvalInfo &Info,
APValue &Result, const LValue *ResultSlot) {
if (!Info.CheckCallLimit(CallLoc))
return false;
- CallStackFrame Frame(Info, E->getSourceRange(), Callee, This, E, Call);
+ CallStackFrame Frame(Info, E->getSourceRange(), Callee, ObjectArg, E, Call);
// For a trivial copy or move assignment, perform an APValue copy. This is
// essential for unions, where the operations performed by the assignment
@@ -6653,16 +6661,20 @@ static bool HandleFunctionCall(SourceLocation CallLoc,
(MD->getParent()->isUnion() ||
(MD->isTrivial() &&
isReadByLvalueToRvalueConversion(MD->getParent())))) {
- assert(This &&
+ unsigned ExplicitOffset = MD->isExplicitObjectMemberFunction() ? 1 : 0;
+ assert(ObjectArg &&
(MD->isCopyAssignmentOperator() || MD->isMoveAssignmentOperator()));
APValue RHSValue;
if (!handleTrivialCopy(Info, MD->getParamDecl(0), Args[0], RHSValue,
MD->getParent()->isUnion()))
return false;
- if (!handleAssignment(Info, Args[0], *This, MD->getThisType(),
+
+ LValue Obj;
+ if (!handleAssignment(Info, Args[ExplicitOffset], *ObjectArg,
+ MD->getFunctionObjectParameterReferenceType(),
RHSValue))
return false;
- This->moveInto(Result);
+ ObjectArg->moveInto(Result);
return true;
} else if (MD && isLambdaCallOperator(MD)) {
// We're in a lambda; determine the lambda capture field maps unless we're
@@ -8289,7 +8301,7 @@ class ExprEvaluatorBase
QualType CalleeType = Callee->getType();
const FunctionDecl *FD = nullptr;
- LValue *This = nullptr, ThisVal;
+ LValue *This = nullptr, ObjectArg;
auto Args = llvm::ArrayRef(E->getArgs(), E->getNumArgs());
bool HasQualifier = false;
@@ -8300,28 +8312,28 @@ class ExprEvaluatorBase
const CXXMethodDecl *Member = nullptr;
if (const MemberExpr *ME = dyn_cast<MemberExpr>(Callee)) {
// Explicit bound member calls, such as x.f() or p->g();
- if (!EvaluateObjectArgument(Info, ME->getBase(), ThisVal))
+ if (!EvaluateObjectArgument(Info, ME->getBase(), ObjectArg))
return false;
Member = dyn_cast<CXXMethodDecl>(ME->getMemberDecl());
if (!Member)
return Error(Callee);
- This = &ThisVal;
+ This = &ObjectArg;
HasQualifier = ME->hasQualifier();
} else if (const BinaryOperator *BE = dyn_cast<BinaryOperator>(Callee)) {
// Indirect bound member calls ('.*' or '->*').
const ValueDecl *D =
- HandleMemberPointerAccess(Info, BE, ThisVal, false);
+ HandleMemberPointerAccess(Info, BE, ObjectArg, false);
if (!D)
return false;
Member = dyn_cast<CXXMethodDecl>(D);
if (!Member)
return Error(Callee);
- This = &ThisVal;
+ This = &ObjectArg;
} else if (const auto *PDE = dyn_cast<CXXPseudoDestructorExpr>(Callee)) {
if (!Info.getLangOpts().CPlusPlus20)
Info.CCEDiag(PDE, diag::note_constexpr_pseudo_destructor);
- return EvaluateObjectArgument(Info, PDE->getBase(), ThisVal) &&
- HandleDestruction(Info, PDE, ThisVal, PDE->getDestroyedType());
+ return EvaluateObjectArgument(Info, PDE->getBase(), ObjectArg) &&
+ HandleDestruction(Info, PDE, ObjectArg, PDE->getDestroyedType());
} else
return Error(Callee);
FD = Member;
@@ -8358,7 +8370,7 @@ class ExprEvaluatorBase
if (const auto *MD = dyn_cast<CXXMethodDecl>(FD))
HasThis = MD->isImplicitObjectMemberFunction();
if (!EvaluateArgs(HasThis ? Args.slice(1) : Args, Call, Info, FD,
- /*RightToLeft=*/true))
+ /*RightToLeft=*/true, &ObjectArg))
return false;
}
@@ -8373,20 +8385,20 @@ class ExprEvaluatorBase
if (Args.empty())
return Error(E);
- if (!EvaluateObjectArgument(Info, Args[0], ThisVal))
+ if (!EvaluateObjectArgument(Info, Args[0], ObjectArg))
return false;
// If we are calling a static operator, the 'this' argument needs to be
// ignored after being evaluated.
if (MD->isInstance())
- This = &ThisVal;
+ This = &ObjectArg;
// If this is syntactically a simple assignment using a trivial
// assignment operator, start the lifetimes of union members as needed,
// per C++20 [class.union]5.
if (Info.getLangOpts().CPlusPlus20 && OCE &&
OCE->getOperator() == OO_Equal && MD->isTrivial() &&
- !MaybeHandleUnionActiveMemberChange(Info, Args[0], ThisVal))
+ !MaybeHandleUnionActiveMemberChange(Info, Args[0], ObjectArg))
return false;
Args = Args.slice(1);
@@ -8441,7 +8453,8 @@ class ExprEvaluatorBase
// Evaluate the arguments now if we've not already done so.
if (!Call) {
Call = Info.CurrentCall->createCall(FD);
- if (!EvaluateArgs(Args, Call, Info, FD))
+ if (!EvaluateArgs(Args, Call, Info, FD, /*RightToLeft*/ false,
+ &ObjectArg))
return false;
}
@@ -8475,6 +8488,11 @@ class ExprEvaluatorBase
Stmt *Body = FD->getBody(Definition);
SourceLocation Loc = E->getExprLoc();
+ // Treat the object argument as `this` when evaluating defaulted
+ // special menmber functions
+ if (FD->hasCXXExplicitFunctionObjectParameter())
+ This = &ObjectArg;
+
if (!CheckConstexprFunction(Info, Loc, FD, Definition, Body) ||
!HandleFunctionCall(Loc, Definition, This, E, Args, Call, Body, Info,
Result, ResultSlot))
diff --git a/clang/test/SemaCXX/cxx2b-deducing-this-constexpr.cpp b/clang/test/SemaCXX/cxx2b-deducing-this-constexpr.cpp
index 191fb013e0316..5b06c7d1827a7 100644
--- a/clang/test/SemaCXX/cxx2b-deducing-this-constexpr.cpp
+++ b/clang/test/SemaCXX/cxx2b-deducing-this-constexpr.cpp
@@ -73,3 +73,32 @@ int test() {
}
}
+
+namespace GH142835 {
+struct MoveMe {
+ MoveMe& operator=(this MoveMe&, const MoveMe&) = default;
+ constexpr MoveMe& operator=(this MoveMe& self, MoveMe&& other) {
+ self.value = other.value;
+ other.value = 0;
+ return self;
+ }
+ int value = 4242;
+};
+
+struct S {
+ constexpr S& operator=(this S&, const S&) = default;
+ S& operator=(this S&, S&&) = default;
+
+ MoveMe move_me;
+};
+
+constexpr bool f() {
+ S s1{};
+ S s2{};
+ s2 = s1;
+ return true;
+}
+
+static_assert(f());
+
+}
|
erichkeane
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not particularly comfortable in ExprConstant, so perhaps we should wait for a smarter reviewer, but I didn't see anything that looked wrong to me.
AaronBallman
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM!
Fixes #142835