Author: Corentin Jabot Date: 2025-06-05T19:30:25+02:00 New Revision: 1be7c6fb4018aac76fc3dbdf997bacc727073f08
URL: https://github.com/llvm/llvm-project/commit/1be7c6fb4018aac76fc3dbdf997bacc727073f08 DIFF: https://github.com/llvm/llvm-project/commit/1be7c6fb4018aac76fc3dbdf997bacc727073f08.diff LOG: [Clang] Fix constant eval of assignment operators with an explicit object parameter (#142964) Fixes #142835 Added: Modified: clang/docs/ReleaseNotes.rst clang/lib/AST/ExprConstant.cpp clang/test/SemaCXX/cxx2b-deducing-this-constexpr.cpp Removed: ################################################################################ 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 diff er 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()); + +} _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits