aboutsummaryrefslogtreecommitdiff
path: root/clang/lib
diff options
context:
space:
mode:
authorCorentin Jabot <corentinjabot@gmail.com>2025-06-05 19:30:25 +0200
committerGitHub <noreply@github.com>2025-06-05 19:30:25 +0200
commit1be7c6fb4018aac76fc3dbdf997bacc727073f08 (patch)
tree23dc4fffe610d8ad41b75440da2a97cdab478437 /clang/lib
parent9dc5dac52dfd27fcb6b1ead9dc8c8819cf2e22d6 (diff)
downloadllvm-1be7c6fb4018aac76fc3dbdf997bacc727073f08.zip
llvm-1be7c6fb4018aac76fc3dbdf997bacc727073f08.tar.gz
llvm-1be7c6fb4018aac76fc3dbdf997bacc727073f08.tar.bz2
[Clang] Fix constant eval of assignment operators with an explicit object parameter (#142964)
Fixes #142835
Diffstat (limited to 'clang/lib')
-rw-r--r--clang/lib/AST/ExprConstant.cpp64
1 files changed, 41 insertions, 23 deletions
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index ab964e5..78dd977 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 @@ public:
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 @@ public:
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 @@ public:
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 @@ public:
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 @@ public:
// 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 @@ public:
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))