diff options
author | Corentin Jabot <corentinjabot@gmail.com> | 2025-06-05 19:30:25 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2025-06-05 19:30:25 +0200 |
commit | 1be7c6fb4018aac76fc3dbdf997bacc727073f08 (patch) | |
tree | 23dc4fffe610d8ad41b75440da2a97cdab478437 /clang/lib | |
parent | 9dc5dac52dfd27fcb6b1ead9dc8c8819cf2e22d6 (diff) | |
download | llvm-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.cpp | 64 |
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)) |