https://github.com/cor3ntin created 
https://github.com/llvm/llvm-project/pull/142964

Fixes #142835

>From af369cdf235c0a334498da428e05140a5eee0b30 Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinja...@gmail.com>
Date: Thu, 5 Jun 2025 15:35:22 +0200
Subject: [PATCH] [Clang] Fix constant eval of assignent operators with an
 explicit object param

Fixes #142835
---
 clang/docs/ReleaseNotes.rst                   |  1 +
 clang/lib/AST/ExprConstant.cpp                | 58 ++++++++++++-------
 .../SemaCXX/cxx2b-deducing-this-constexpr.cpp | 29 ++++++++++
 3 files changed, 68 insertions(+), 20 deletions(-)

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 512071427b65c..d416827407a46 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
 ^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index ab964e592de80..1e10e126534bf 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -6550,7 +6550,8 @@ static bool MaybeHandleUnionActiveMemberChange(EvalInfo 
&Info,
 
 static bool EvaluateCallArg(const ParmVarDecl *PVD, const Expr *Arg,
                             CallRef Call, EvalInfo &Info,
-                            bool NonNull = false) {
+                            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 +6571,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 +6604,17 @@ 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 +6642,14 @@ static bool handleTrivialCopy(EvalInfo &Info, const 
ParmVarDecl *Param,
 
 /// Evaluate a function call.
 static bool HandleFunctionCall(SourceLocation CallLoc,
-                               const FunctionDecl *Callee, const LValue *This,
+                               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 +6662,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 +8302,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 +8313,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 +8371,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 +8386,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 +8454,7 @@ 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

Reply via email to