[libcxx-commits] [clang-tools-extra] [libc] [mlir] [compiler-rt] [llvm] [clang] [lld] [libcxxabi] [flang] [libcxx] [lldb] [clang] static operators should evaluate object argument (PR #68485)

Tianlan Zhou via libcxx-commits libcxx-commits at lists.llvm.org
Thu Jan 4 21:22:36 PST 2024


https://github.com/SuperSodaSea updated https://github.com/llvm/llvm-project/pull/68485

>From 03276260c48d9cafb2a0d80825156e77cdf02eba Mon Sep 17 00:00:00 2001
From: SuperSodaSea <bobby825 at 126.com>
Date: Sat, 7 Oct 2023 21:05:17 +0800
Subject: [PATCH 01/10] [clang] static operators should evaluate object
 argument

---
 clang/lib/AST/ExprConstant.cpp                |  3 +-
 clang/lib/CodeGen/CGExpr.cpp                  |  2 +-
 clang/lib/CodeGen/CGExprCXX.cpp               | 41 ++++++++++++--
 clang/lib/CodeGen/CodeGenFunction.h           |  3 +
 clang/lib/Sema/SemaChecking.cpp               |  5 +-
 clang/lib/Sema/SemaOverload.cpp               | 33 ++++-------
 clang/test/AST/ast-dump-static-operators.cpp  | 55 +++++++++++++++++++
 .../CodeGenCXX/cxx2b-static-call-operator.cpp | 26 ++++++---
 .../cxx2b-static-subscript-operator.cpp       | 11 +++-
 9 files changed, 137 insertions(+), 42 deletions(-)
 create mode 100644 clang/test/AST/ast-dump-static-operators.cpp

diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 5a33e918db8e8c..a6c81f467fbe01 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -7806,7 +7806,8 @@ class ExprEvaluatorBase
       // Overloaded operator calls to member functions are represented as normal
       // calls with '*this' as the first argument.
       const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(FD);
-      if (MD && MD->isImplicitObjectMemberFunction()) {
+      if (MD &&
+          (MD->isImplicitObjectMemberFunction() || (OCE && MD->isStatic()))) {
         // FIXME: When selecting an implicit conversion for an overloaded
         // operator delete, we sometimes try to evaluate calls to conversion
         // operators without a 'this' parameter!
diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp
index 54a1d300a9ac73..19406ff174dea1 100644
--- a/clang/lib/CodeGen/CGExpr.cpp
+++ b/clang/lib/CodeGen/CGExpr.cpp
@@ -5070,7 +5070,7 @@ RValue CodeGenFunction::EmitCallExpr(const CallExpr *E,
   if (const auto *CE = dyn_cast<CXXOperatorCallExpr>(E))
     if (const auto *MD =
             dyn_cast_if_present<CXXMethodDecl>(CE->getCalleeDecl());
-        MD && MD->isImplicitObjectMemberFunction())
+        MD && !MD->isExplicitObjectMemberFunction())
       return EmitCXXOperatorMemberCallExpr(CE, MD, ReturnValue);
 
   CGCallee callee = EmitCallee(E->getCallee());
diff --git a/clang/lib/CodeGen/CGExprCXX.cpp b/clang/lib/CodeGen/CGExprCXX.cpp
index 2e7059cc8f5b63..a580c635998510 100644
--- a/clang/lib/CodeGen/CGExprCXX.cpp
+++ b/clang/lib/CodeGen/CGExprCXX.cpp
@@ -489,11 +489,42 @@ RValue
 CodeGenFunction::EmitCXXOperatorMemberCallExpr(const CXXOperatorCallExpr *E,
                                                const CXXMethodDecl *MD,
                                                ReturnValueSlot ReturnValue) {
-  assert(MD->isImplicitObjectMemberFunction() &&
-         "Trying to emit a member call expr on a static method!");
-  return EmitCXXMemberOrOperatorMemberCallExpr(
-      E, MD, ReturnValue, /*HasQualifier=*/false, /*Qualifier=*/nullptr,
-      /*IsArrow=*/false, E->getArg(0));
+  assert(!MD->isExplicitObjectMemberFunction() &&
+         "Trying to emit a member call expr on an explicit object member "
+         "function!");
+
+  if (MD->isStatic())
+    return EmitCXXStaticOperatorMemberCallExpr(E, MD, ReturnValue);
+  else
+    return EmitCXXMemberOrOperatorMemberCallExpr(
+        E, MD, ReturnValue, /*HasQualifier=*/false, /*Qualifier=*/nullptr,
+        /*IsArrow=*/false, E->getArg(0));
+}
+
+RValue CodeGenFunction::EmitCXXStaticOperatorMemberCallExpr(
+    const CXXOperatorCallExpr *E, const CXXMethodDecl *MD,
+    ReturnValueSlot ReturnValue) {
+  assert(MD->isStatic());
+
+  CGCallee Callee = EmitCallee(E->getCallee());
+
+  // Emit and ignore `this` pointer.
+  EmitIgnoredExpr(E->getArg(0));
+
+  auto ProtoType = MD->getFunctionType()->castAs<FunctionProtoType>();
+
+  // Emit the rest of the call args.
+  CallArgList Args;
+  EmitCallArgs(Args, ProtoType, drop_begin(E->arguments(), 1),
+               E->getDirectCallee());
+
+  bool Chain = E == MustTailCall;
+  const CGFunctionInfo &FnInfo =
+      CGM.getTypes().arrangeFreeFunctionCall(Args, ProtoType, Chain);
+  llvm::CallBase *CallOrInvoke = nullptr;
+
+  return EmitCall(FnInfo, Callee, ReturnValue, Args, &CallOrInvoke, Chain,
+                  E->getExprLoc());
 }
 
 RValue CodeGenFunction::EmitCUDAKernelCallExpr(const CUDAKernelCallExpr *E,
diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h
index d5336382a2b9c9..42de125e748991 100644
--- a/clang/lib/CodeGen/CodeGenFunction.h
+++ b/clang/lib/CodeGen/CodeGenFunction.h
@@ -4163,6 +4163,9 @@ class CodeGenFunction : public CodeGenTypeCache {
   RValue EmitCXXOperatorMemberCallExpr(const CXXOperatorCallExpr *E,
                                        const CXXMethodDecl *MD,
                                        ReturnValueSlot ReturnValue);
+  RValue EmitCXXStaticOperatorMemberCallExpr(const CXXOperatorCallExpr *CE,
+                                             const CXXMethodDecl *MD,
+                                             ReturnValueSlot ReturnValue);
   RValue EmitCXXPseudoDestructorExpr(const CXXPseudoDestructorExpr *E);
 
   RValue EmitCUDAKernelCallExpr(const CUDAKernelCallExpr *E,
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index 446e35218bff0a..a536f5f72811ec 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -6942,9 +6942,8 @@ bool Sema::CheckFunctionCall(FunctionDecl *FDecl, CallExpr *TheCall,
   unsigned NumArgs = TheCall->getNumArgs();
 
   Expr *ImplicitThis = nullptr;
-  if (IsMemberOperatorCall && !FDecl->isStatic() &&
-      !FDecl->hasCXXExplicitFunctionObjectParameter()) {
-    // If this is a call to a non-static member operator, hide the first
+  if (IsMemberOperatorCall && !FDecl->hasCXXExplicitFunctionObjectParameter()) {
+    // If this is a call to a member operator, hide the first
     // argument from checkCall.
     // FIXME: Our choice of AST representation here is less than ideal.
     ImplicitThis = Args[0];
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index ce78994e655381..82b4bfe980b796 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -14935,7 +14935,7 @@ ExprResult Sema::CreateOverloadedArraySubscriptExpr(SourceLocation LLoc,
         CXXMethodDecl *Method = cast<CXXMethodDecl>(FnDecl);
         SmallVector<Expr *, 2> MethodArgs;
 
-        // Handle 'this' parameter if the selected function is not static.
+        // Initialize the explicit / implicit object parameter.
         if (Method->isExplicitObjectMemberFunction()) {
           ExprResult Res =
               InitializeExplicitObjectArgument(*this, Args[0], Method);
@@ -14943,7 +14943,7 @@ ExprResult Sema::CreateOverloadedArraySubscriptExpr(SourceLocation LLoc,
             return ExprError();
           Args[0] = Res.get();
           ArgExpr = Args;
-        } else if (Method->isInstance()) {
+        } else {
           ExprResult Arg0 = PerformImplicitObjectArgumentInitialization(
               Args[0], /*Qualifier=*/nullptr, Best->FoundDecl, Method);
           if (Arg0.isInvalid())
@@ -14971,15 +14971,9 @@ ExprResult Sema::CreateOverloadedArraySubscriptExpr(SourceLocation LLoc,
         ExprValueKind VK = Expr::getValueKindForType(ResultTy);
         ResultTy = ResultTy.getNonLValueExprType(Context);
 
-        CallExpr *TheCall;
-        if (Method->isInstance())
-          TheCall = CXXOperatorCallExpr::Create(
-              Context, OO_Subscript, FnExpr.get(), MethodArgs, ResultTy, VK,
-              RLoc, CurFPFeatureOverrides());
-        else
-          TheCall =
-              CallExpr::Create(Context, FnExpr.get(), MethodArgs, ResultTy, VK,
-                               RLoc, CurFPFeatureOverrides());
+        CallExpr *TheCall = CXXOperatorCallExpr::Create(
+            Context, OO_Subscript, FnExpr.get(), MethodArgs, ResultTy, VK, RLoc,
+            CurFPFeatureOverrides());
 
         if (CheckCallReturnType(FnDecl->getReturnType(), LLoc, TheCall, FnDecl))
           return ExprError();
@@ -15607,15 +15601,13 @@ Sema::BuildCallToObjectOfClassType(Scope *S, Expr *Obj,
 
   bool IsError = false;
 
-  // Initialize the implicit object parameter if needed.
-  // Since C++23, this could also be a call to a static call operator
-  // which we emit as a regular CallExpr.
+  // Initialize the explicit / implicit object parameter.
   llvm::SmallVector<Expr *, 8> NewArgs;
   if (Method->isExplicitObjectMemberFunction()) {
     // FIXME: we should do that during the definition of the lambda when we can.
     DiagnoseInvalidExplicitObjectParameterInLambda(Method);
     PrepareExplicitObjectArgument(*this, Method, Obj, Args, NewArgs);
-  } else if (Method->isInstance()) {
+  } else {
     ExprResult ObjRes = PerformImplicitObjectArgumentInitialization(
         Object.get(), /*Qualifier=*/nullptr, Best->FoundDecl, Method);
     if (ObjRes.isInvalid())
@@ -15649,14 +15641,9 @@ Sema::BuildCallToObjectOfClassType(Scope *S, Expr *Obj,
   ExprValueKind VK = Expr::getValueKindForType(ResultTy);
   ResultTy = ResultTy.getNonLValueExprType(Context);
 
-  CallExpr *TheCall;
-  if (Method->isInstance())
-    TheCall = CXXOperatorCallExpr::Create(Context, OO_Call, NewFn.get(),
-                                          MethodArgs, ResultTy, VK, RParenLoc,
-                                          CurFPFeatureOverrides());
-  else
-    TheCall = CallExpr::Create(Context, NewFn.get(), MethodArgs, ResultTy, VK,
-                               RParenLoc, CurFPFeatureOverrides());
+  CallExpr *TheCall = CXXOperatorCallExpr::Create(
+      Context, OO_Call, NewFn.get(), MethodArgs, ResultTy, VK, RParenLoc,
+      CurFPFeatureOverrides());
 
   if (CheckCallReturnType(Method->getReturnType(), LParenLoc, TheCall, Method))
     return true;
diff --git a/clang/test/AST/ast-dump-static-operators.cpp b/clang/test/AST/ast-dump-static-operators.cpp
new file mode 100644
index 00000000000000..87a15403e6f8b2
--- /dev/null
+++ b/clang/test/AST/ast-dump-static-operators.cpp
@@ -0,0 +1,55 @@
+// RUN: %clang_cc1 -std=c++23 %s -ast-dump -triple x86_64-unknown-unknown -o - | FileCheck -strict-whitespace %s
+
+struct Functor {
+  static int operator()(int x, int y) {
+    return x + y;
+  }
+  static int operator[](int x, int y) {
+    return x + y;
+  }
+};
+
+Functor& get_functor() {
+  static Functor functor;
+  return functor;
+}
+
+void call_static_operators() {
+  Functor functor;
+  
+  int z1 = functor(1, 2);
+  // CHECK:      CXXOperatorCallExpr {{.*}} 'int' '()'
+  // CHECK-NEXT: |-ImplicitCastExpr {{.*}} <col:19, col:24> 'int (*)(int, int)' <FunctionToPointerDecay>
+  // CHECK-NEXT: | `-DeclRefExpr {{.*}} <col:19, col:24> 'int (int, int)' lvalue CXXMethod {{.*}} 'operator()' 'int (int, int)'
+  // CHECK-NEXT: |-DeclRefExpr {{.*}} <col:12> 'Functor':'Functor' lvalue Var {{.*}} 'functor' 'Functor':'Functor'
+  // CHECK-NEXT: |-IntegerLiteral {{.*}} <col:20> 'int' 1
+  // CHECK-NEXT: `-IntegerLiteral {{.*}} <col:23> 'int' 2
+  
+  int z2 = functor[1, 2];
+  // CHECK:      CXXOperatorCallExpr {{.*}} 'int' '[]'
+  // CHECK-NEXT: |-ImplicitCastExpr {{.*}} <col:19, col:24> 'int (*)(int, int)' <FunctionToPointerDecay>
+  // CHECK-NEXT: | `-DeclRefExpr {{.*}} <col:19, col:24> 'int (int, int)' lvalue CXXMethod {{.*}} 'operator[]' 'int (int, int)'
+  // CHECK-NEXT: |-DeclRefExpr {{.*}} <col:12> 'Functor':'Functor' lvalue Var {{.*}} 'functor' 'Functor':'Functor'
+  // CHECK-NEXT: |-IntegerLiteral {{.*}} <col:20> 'int' 1
+  // CHECK-NEXT: `-IntegerLiteral {{.*}} <col:23> 'int' 2
+  
+  int z3 = get_functor()(1, 2);
+  // CHECK:      CXXOperatorCallExpr {{.*}} 'int' '()'
+  // CHECK-NEXT: |-ImplicitCastExpr {{.*}} <col:25, col:30> 'int (*)(int, int)' <FunctionToPointerDecay>
+  // CHECK-NEXT: | `-DeclRefExpr {{.*}} <col:25, col:30> 'int (int, int)' lvalue CXXMethod {{.*}} 'operator()' 'int (int, int)'
+  // CHECK-NEXT: |-CallExpr {{.*}} <col:12, col:24> 'Functor':'Functor' lvalue
+  // CHECK-NEXT: | `-ImplicitCastExpr {{.*}} <col:12> 'Functor &(*)()' <FunctionToPointerDecay>
+  // CHECK-NEXT: |   `-DeclRefExpr {{.*}} <col:12> 'Functor &()' lvalue Function {{.*}} 'get_functor' 'Functor &()'
+  // CHECK-NEXT: |-IntegerLiteral {{.*}} <col:26> 'int' 1
+  // CHECK-NEXT: `-IntegerLiteral {{.*}} <col:29> 'int' 2
+  
+  int z4 = get_functor()[1, 2];
+  // CHECK:      CXXOperatorCallExpr {{.*}} 'int' '[]'
+  // CHECK-NEXT: |-ImplicitCastExpr {{.*}} <col:25, col:30> 'int (*)(int, int)' <FunctionToPointerDecay>
+  // CHECK-NEXT: | `-DeclRefExpr {{.*}} <col:25, col:30> 'int (int, int)' lvalue CXXMethod {{.*}} 'operator[]' 'int (int, int)'
+  // CHECK-NEXT: |-CallExpr {{.*}} <col:12, col:24> 'Functor':'Functor' lvalue
+  // CHECK-NEXT: | `-ImplicitCastExpr {{.*}} <col:12> 'Functor &(*)()' <FunctionToPointerDecay>
+  // CHECK-NEXT: |   `-DeclRefExpr {{.*}} <col:12> 'Functor &()' lvalue Function {{.*}} 'get_functor' 'Functor &()'
+  // CHECK-NEXT: |-IntegerLiteral {{.*}} <col:26> 'int' 1
+  // CHECK-NEXT: `-IntegerLiteral {{.*}} <col:29> 'int' 2
+}
diff --git a/clang/test/CodeGenCXX/cxx2b-static-call-operator.cpp b/clang/test/CodeGenCXX/cxx2b-static-call-operator.cpp
index fd53649c9b0618..9cf5a7e00e7b4e 100644
--- a/clang/test/CodeGenCXX/cxx2b-static-call-operator.cpp
+++ b/clang/test/CodeGenCXX/cxx2b-static-call-operator.cpp
@@ -19,16 +19,22 @@ void CallsTheLambda() {
 
 // CHECK:      define {{.*}}CallsTheLambda{{.*}}
 // CHECK-NEXT: entry:
-// CHECK-NEXT:   %call = call noundef i32 {{.*}}(i32 noundef 1, i32 noundef 2)
+// CHECK:        {{.*}}call {{.*}}GetALambda{{.*}}()
+// CHECK-NEXT:   {{.*}} = call noundef i32 {{.*}}(i32 noundef 1, i32 noundef 2)
 // CHECK-NEXT:   ret void
 // CHECK-NEXT: }
 
+Functor GetAFunctor() {
+  return {};
+}
+
 void call_static_call_operator() {
   Functor f;
   f(101, 102);
   f.operator()(201, 202);
   Functor{}(301, 302);
   Functor::operator()(401, 402);
+  GetAFunctor()(501, 502);
 }
 
 // CHECK:      define {{.*}}call_static_call_operator{{.*}}
@@ -37,6 +43,8 @@ void call_static_call_operator() {
 // CHECK-NEXT:   {{.*}} = call noundef i32 {{.*}}Functor{{.*}}(i32 noundef 201, i32 noundef 202)
 // CHECK-NEXT:   {{.*}} = call noundef i32 {{.*}}Functor{{.*}}(i32 noundef 301, i32 noundef 302)
 // CHECK-NEXT:   {{.*}} = call noundef i32 {{.*}}Functor{{.*}}(i32 noundef 401, i32 noundef 402)
+// CHECK:        {{.*}}call {{.*}}GetAFunctor{{.*}}()
+// CHECK-NEXT:   {{.*}} = call noundef i32 {{.*}}Functor{{.*}}(i32 noundef 501, i32 noundef 502)
 // CHECK-NEXT:   ret void
 // CHECK-NEXT: }
 
@@ -106,12 +114,16 @@ void test_dep_functors() {
 
 // CHECK:      define {{.*}}test_dep_functors{{.*}}
 // CHECK-NEXT: entry:
-// CHECK:        %call = call noundef i32 {{.*}}DepFunctor{{.*}}(float noundef 1.000000e+00)
-// CHECK:        %call1 = call noundef i32 {{.*}}DepFunctor{{.*}}(i1 noundef zeroext true)
-// CHECK:        %call2 = call noundef i32 {{.*}}dep_lambda1{{.*}}(float noundef 1.000000e+00)
-// CHECK:        %call3 = call noundef i32 {{.*}}dep_lambda1{{.*}}(i1 noundef zeroext true)
-// CHECK:        %call4 = call noundef i32 {{.*}}dep_lambda2{{.*}}(float noundef 1.000000e+00)
-// CHECK:        %call5 = call noundef i32 {{.*}}dep_lambda2{{.*}}(i1 noundef zeroext true)
+// CHECK:        {{.*}} = call noundef i32 {{.*}}DepFunctor{{.*}}(float noundef 1.000000e+00)
+// CHECK:        {{.*}} = call noundef i32 {{.*}}DepFunctor{{.*}}(i1 noundef zeroext true)
+// CHECK:        {{.*}}call {{.*}}dep_lambda1{{.*}}()
+// CHECK:        {{.*}} = call noundef i32 {{.*}}dep_lambda1{{.*}}(float noundef 1.000000e+00)
+// CHECK:        {{.*}}call {{.*}}dep_lambda1{{.*}}()
+// CHECK:        {{.*}} = call noundef i32 {{.*}}dep_lambda1{{.*}}(i1 noundef zeroext true)
+// CHECK:        {{.*}}call {{.*}}dep_lambda2{{.*}}()
+// CHECK:        {{.*}} = call noundef i32 {{.*}}dep_lambda2{{.*}}(float noundef 1.000000e+00)
+// CHECK:        {{.*}}call {{.*}}dep_lambda2{{.*}}()
+// CHECK:        {{.*}} = call noundef i32 {{.*}}dep_lambda2{{.*}}(i1 noundef zeroext true)
 // CHECK:        ret void
 // CHECK-NEXT: }
 
diff --git a/clang/test/CodeGenCXX/cxx2b-static-subscript-operator.cpp b/clang/test/CodeGenCXX/cxx2b-static-subscript-operator.cpp
index 5dbd2c50cc56bd..5d8258978c50d5 100644
--- a/clang/test/CodeGenCXX/cxx2b-static-subscript-operator.cpp
+++ b/clang/test/CodeGenCXX/cxx2b-static-subscript-operator.cpp
@@ -7,12 +7,17 @@ struct Functor {
   }
 };
 
+Functor GetAFunctor() {
+  return {};
+}
+
 void call_static_subscript_operator() {
   Functor f;
   f[101, 102];
   f.operator[](201, 202);
   Functor{}[301, 302];
   Functor::operator[](401, 402);
+  GetAFunctor()[501, 502];
 }
 
 // CHECK:      define {{.*}}call_static_subscript_operator{{.*}}
@@ -21,6 +26,8 @@ void call_static_subscript_operator() {
 // CHECK-NEXT:   {{.*}} = call noundef i32 {{.*}}Functor{{.*}}(i32 noundef 201, i32 noundef 202)
 // CHECK-NEXT:   {{.*}} = call noundef i32 {{.*}}Functor{{.*}}(i32 noundef 301, i32 noundef 302)
 // CHECK-NEXT:   {{.*}} = call noundef i32 {{.*}}Functor{{.*}}(i32 noundef 401, i32 noundef 402)
+// CHECK:        {{.*}}call {{.*}}GetAFunctor{{.*}}()
+// CHECK-NEXT:   {{.*}} = call noundef i32 {{.*}}Functor{{.*}}(i32 noundef 501, i32 noundef 502)
 // CHECK-NEXT:   ret void
 // CHECK-NEXT: }
 
@@ -60,7 +67,7 @@ void test_dep_functors() {
 
 // CHECK:      define {{.*}}test_dep_functors{{.*}}
 // CHECK-NEXT: entry:
-// CHECK:        %call = call noundef i32 {{.*}}DepFunctor{{.*}}(float noundef 1.000000e+00)
-// CHECK:        %call1 = call noundef i32 {{.*}}DepFunctor{{.*}}(i1 noundef zeroext true)
+// CHECK:        {{.*}} = call noundef i32 {{.*}}DepFunctor{{.*}}(float noundef 1.000000e+00)
+// CHECK:        {{.*}} = call noundef i32 {{.*}}DepFunctor{{.*}}(i1 noundef zeroext true)
 // CHECK:        ret void
 // CHECK-NEXT: }

>From 63a3627d733477d5aa5c7add80bdece3d14eba94 Mon Sep 17 00:00:00 2001
From: SuperSodaSea <bobby825 at 126.com>
Date: Sun, 8 Oct 2023 21:25:44 +0800
Subject: [PATCH 02/10] Deal with static operator in EmitCall

---
 clang/lib/CodeGen/CGExpr.cpp        | 24 ++++++++++++++---
 clang/lib/CodeGen/CGExprCXX.cpp     | 41 ++++-------------------------
 clang/lib/CodeGen/CodeGenFunction.h |  3 ---
 3 files changed, 25 insertions(+), 43 deletions(-)

diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp
index 19406ff174dea1..427ad0f01a59ba 100644
--- a/clang/lib/CodeGen/CGExpr.cpp
+++ b/clang/lib/CodeGen/CGExpr.cpp
@@ -5070,7 +5070,7 @@ RValue CodeGenFunction::EmitCallExpr(const CallExpr *E,
   if (const auto *CE = dyn_cast<CXXOperatorCallExpr>(E))
     if (const auto *MD =
             dyn_cast_if_present<CXXMethodDecl>(CE->getCalleeDecl());
-        MD && !MD->isExplicitObjectMemberFunction())
+        MD && MD->isImplicitObjectMemberFunction())
       return EmitCXXOperatorMemberCallExpr(CE, MD, ReturnValue);
 
   CGCallee callee = EmitCallee(E->getCallee());
@@ -5519,7 +5519,9 @@ RValue CodeGenFunction::EmitCall(QualType CalleeType, const CGCallee &OrigCallee
   // destruction order is not necessarily reverse construction order.
   // FIXME: Revisit this based on C++ committee response to unimplementability.
   EvaluationOrder Order = EvaluationOrder::Default;
-  if (auto *OCE = dyn_cast<CXXOperatorCallExpr>(E)) {
+  auto *OCE = dyn_cast<CXXOperatorCallExpr>(E);
+  bool StaticOperator = false;
+  if (OCE) {
     if (OCE->isAssignmentOp())
       Order = EvaluationOrder::ForceRightToLeft;
     else {
@@ -5536,10 +5538,24 @@ RValue CodeGenFunction::EmitCall(QualType CalleeType, const CGCallee &OrigCallee
         break;
       }
     }
+
+    if (const auto *MD =
+            dyn_cast_if_present<CXXMethodDecl>(OCE->getCalleeDecl());
+        MD && MD->isStatic())
+      StaticOperator = true;
   }
 
-  EmitCallArgs(Args, dyn_cast<FunctionProtoType>(FnType), E->arguments(),
-               E->getDirectCallee(), /*ParamsToSkip*/ 0, Order);
+  if (StaticOperator) {
+    // If we're calling a static operator, we need to emit the object argument
+    // and ignore it.
+    EmitIgnoredExpr(OCE->getArg(0));
+
+    EmitCallArgs(Args, dyn_cast<FunctionProtoType>(FnType),
+                 drop_begin(E->arguments(), 1), E->getDirectCallee(),
+                 /*ParamsToSkip*/ 0, Order);
+  } else
+    EmitCallArgs(Args, dyn_cast<FunctionProtoType>(FnType), E->arguments(),
+                 E->getDirectCallee(), /*ParamsToSkip*/ 0, Order);
 
   const CGFunctionInfo &FnInfo = CGM.getTypes().arrangeFreeFunctionCall(
       Args, FnType, /*ChainCall=*/Chain);
diff --git a/clang/lib/CodeGen/CGExprCXX.cpp b/clang/lib/CodeGen/CGExprCXX.cpp
index a580c635998510..2e7059cc8f5b63 100644
--- a/clang/lib/CodeGen/CGExprCXX.cpp
+++ b/clang/lib/CodeGen/CGExprCXX.cpp
@@ -489,42 +489,11 @@ RValue
 CodeGenFunction::EmitCXXOperatorMemberCallExpr(const CXXOperatorCallExpr *E,
                                                const CXXMethodDecl *MD,
                                                ReturnValueSlot ReturnValue) {
-  assert(!MD->isExplicitObjectMemberFunction() &&
-         "Trying to emit a member call expr on an explicit object member "
-         "function!");
-
-  if (MD->isStatic())
-    return EmitCXXStaticOperatorMemberCallExpr(E, MD, ReturnValue);
-  else
-    return EmitCXXMemberOrOperatorMemberCallExpr(
-        E, MD, ReturnValue, /*HasQualifier=*/false, /*Qualifier=*/nullptr,
-        /*IsArrow=*/false, E->getArg(0));
-}
-
-RValue CodeGenFunction::EmitCXXStaticOperatorMemberCallExpr(
-    const CXXOperatorCallExpr *E, const CXXMethodDecl *MD,
-    ReturnValueSlot ReturnValue) {
-  assert(MD->isStatic());
-
-  CGCallee Callee = EmitCallee(E->getCallee());
-
-  // Emit and ignore `this` pointer.
-  EmitIgnoredExpr(E->getArg(0));
-
-  auto ProtoType = MD->getFunctionType()->castAs<FunctionProtoType>();
-
-  // Emit the rest of the call args.
-  CallArgList Args;
-  EmitCallArgs(Args, ProtoType, drop_begin(E->arguments(), 1),
-               E->getDirectCallee());
-
-  bool Chain = E == MustTailCall;
-  const CGFunctionInfo &FnInfo =
-      CGM.getTypes().arrangeFreeFunctionCall(Args, ProtoType, Chain);
-  llvm::CallBase *CallOrInvoke = nullptr;
-
-  return EmitCall(FnInfo, Callee, ReturnValue, Args, &CallOrInvoke, Chain,
-                  E->getExprLoc());
+  assert(MD->isImplicitObjectMemberFunction() &&
+         "Trying to emit a member call expr on a static method!");
+  return EmitCXXMemberOrOperatorMemberCallExpr(
+      E, MD, ReturnValue, /*HasQualifier=*/false, /*Qualifier=*/nullptr,
+      /*IsArrow=*/false, E->getArg(0));
 }
 
 RValue CodeGenFunction::EmitCUDAKernelCallExpr(const CUDAKernelCallExpr *E,
diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h
index 42de125e748991..d5336382a2b9c9 100644
--- a/clang/lib/CodeGen/CodeGenFunction.h
+++ b/clang/lib/CodeGen/CodeGenFunction.h
@@ -4163,9 +4163,6 @@ class CodeGenFunction : public CodeGenTypeCache {
   RValue EmitCXXOperatorMemberCallExpr(const CXXOperatorCallExpr *E,
                                        const CXXMethodDecl *MD,
                                        ReturnValueSlot ReturnValue);
-  RValue EmitCXXStaticOperatorMemberCallExpr(const CXXOperatorCallExpr *CE,
-                                             const CXXMethodDecl *MD,
-                                             ReturnValueSlot ReturnValue);
   RValue EmitCXXPseudoDestructorExpr(const CXXPseudoDestructorExpr *E);
 
   RValue EmitCUDAKernelCallExpr(const CUDAKernelCallExpr *E,

>From 1269bc3dd7bfaa269a96586c263f922094aa8c8c Mon Sep 17 00:00:00 2001
From: Tianlan Zhou <bobby825 at 126.com>
Date: Tue, 10 Oct 2023 15:37:36 +0800
Subject: [PATCH 03/10] Apply suggestions from cor3ntin

---
 clang/lib/Sema/SemaOverload.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index 82b4bfe980b796..c36e1b1da409b2 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -14935,7 +14935,7 @@ ExprResult Sema::CreateOverloadedArraySubscriptExpr(SourceLocation LLoc,
         CXXMethodDecl *Method = cast<CXXMethodDecl>(FnDecl);
         SmallVector<Expr *, 2> MethodArgs;
 
-        // Initialize the explicit / implicit object parameter.
+        // Initialize the object parameter.
         if (Method->isExplicitObjectMemberFunction()) {
           ExprResult Res =
               InitializeExplicitObjectArgument(*this, Args[0], Method);
@@ -15601,7 +15601,7 @@ Sema::BuildCallToObjectOfClassType(Scope *S, Expr *Obj,
 
   bool IsError = false;
 
-  // Initialize the explicit / implicit object parameter.
+  // Initialize the object parameter.
   llvm::SmallVector<Expr *, 8> NewArgs;
   if (Method->isExplicitObjectMemberFunction()) {
     // FIXME: we should do that during the definition of the lambda when we can.

>From 755bcaaba13471204d5383dae99aaa65a305855a Mon Sep 17 00:00:00 2001
From: Tianlan Zhou <bobby825 at 126.com>
Date: Tue, 10 Oct 2023 17:17:40 +0800
Subject: [PATCH 04/10] Minor changes

---
 clang/lib/CodeGen/CGExpr.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp
index 427ad0f01a59ba..cb9d26a3f2487f 100644
--- a/clang/lib/CodeGen/CGExpr.cpp
+++ b/clang/lib/CodeGen/CGExpr.cpp
@@ -5519,8 +5519,8 @@ RValue CodeGenFunction::EmitCall(QualType CalleeType, const CGCallee &OrigCallee
   // destruction order is not necessarily reverse construction order.
   // FIXME: Revisit this based on C++ committee response to unimplementability.
   EvaluationOrder Order = EvaluationOrder::Default;
-  auto *OCE = dyn_cast<CXXOperatorCallExpr>(E);
   bool StaticOperator = false;
+  auto *OCE = dyn_cast<CXXOperatorCallExpr>(E);
   if (OCE) {
     if (OCE->isAssignmentOp())
       Order = EvaluationOrder::ForceRightToLeft;
@@ -5548,7 +5548,7 @@ RValue CodeGenFunction::EmitCall(QualType CalleeType, const CGCallee &OrigCallee
   if (StaticOperator) {
     // If we're calling a static operator, we need to emit the object argument
     // and ignore it.
-    EmitIgnoredExpr(OCE->getArg(0));
+    EmitIgnoredExpr(E->getArg(0));
 
     EmitCallArgs(Args, dyn_cast<FunctionProtoType>(FnType),
                  drop_begin(E->arguments(), 1), E->getDirectCallee(),

>From 12d3ea25a0afe29bfa4f91f7f3a9b1f01026dadb Mon Sep 17 00:00:00 2001
From: Tianlan Zhou <bobby825 at 126.com>
Date: Wed, 11 Oct 2023 17:41:02 +0800
Subject: [PATCH 05/10] Apply suggestions from shafik

Co-authored-by: Shafik Yaghmour <shafik at users.noreply.github.com>
---
 clang/lib/CodeGen/CGExpr.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp
index cb9d26a3f2487f..53af5dea8d9e0f 100644
--- a/clang/lib/CodeGen/CGExpr.cpp
+++ b/clang/lib/CodeGen/CGExpr.cpp
@@ -5552,10 +5552,10 @@ RValue CodeGenFunction::EmitCall(QualType CalleeType, const CGCallee &OrigCallee
 
     EmitCallArgs(Args, dyn_cast<FunctionProtoType>(FnType),
                  drop_begin(E->arguments(), 1), E->getDirectCallee(),
-                 /*ParamsToSkip*/ 0, Order);
+                 /*ParamsToSkip=*/0, Order);
   } else
     EmitCallArgs(Args, dyn_cast<FunctionProtoType>(FnType), E->arguments(),
-                 E->getDirectCallee(), /*ParamsToSkip*/ 0, Order);
+                 E->getDirectCallee(), /*ParamsToSkip=*/0, Order);
 
   const CGFunctionInfo &FnInfo = CGM.getTypes().arrangeFreeFunctionCall(
       Args, FnType, /*ChainCall=*/Chain);

>From e725a8f2000690986872ad05e5bde582967f81de Mon Sep 17 00:00:00 2001
From: SuperSodaSea <bobby825 at 126.com>
Date: Thu, 12 Oct 2023 01:46:31 +0800
Subject: [PATCH 06/10] Ignore `this` in constexpr evaluation

---
 clang/lib/AST/ExprConstant.cpp | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index a6c81f467fbe01..31cadf5aac5010 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -7816,7 +7816,11 @@ class ExprEvaluatorBase
 
         if (!EvaluateObjectArgument(Info, Args[0], ThisVal))
           return false;
-        This = &ThisVal;
+
+        // If we are calling a static operator, the 'this' argument needs to be
+        // ignored after being evaluated.
+        if (MD->isInstance())
+          This = &ThisVal;
 
         // If this is syntactically a simple assignment using a trivial
         // assignment operator, start the lifetimes of union members as needed,

>From 5accc5d469d3d388eeaf2cce3b3ecb29bf05edc3 Mon Sep 17 00:00:00 2001
From: SuperSodaSea <bobby825 at 126.com>
Date: Thu, 4 Jan 2024 23:16:55 +0800
Subject: [PATCH 07/10] Update clang/docs/ReleaseNotes.rst

---
 clang/docs/ReleaseNotes.rst | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 30cfe66703f5a9..6101046595c533 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -685,9 +685,11 @@ Bug Fixes in This Version
   (`#65568 <https://github.com/llvm/llvm-project/issues/65568>`_)
 - Fix an issue where clang doesn't respect detault template arguments that
   are added in a later redeclaration for CTAD.
-  Fixes (#69987 <https://github.com/llvm/llvm-project/issues/69987>`_)
+  Fixes (`#69987 <https://github.com/llvm/llvm-project/issues/69987>`_)
 - Fix an issue where CTAD fails for explicit type conversion.
-  Fixes (#64347 <https://github.com/llvm/llvm-project/issues/64347>`_)
+  Fixes (`#64347 <https://github.com/llvm/llvm-project/issues/64347>`_)
+- Fix an issue that the object argument of ``static operator()`` and ``static operator[]`` is not evaluate.
+  Fixes (`#67976 <https://github.com/llvm/llvm-project/issues/67976>`_)
 
 
 Bug Fixes to Compiler Builtins

>From 6bcc590f206629f58360dc9d01bd88eeb5968ffa Mon Sep 17 00:00:00 2001
From: SuperSodaSea <bobby825 at 126.com>
Date: Fri, 5 Jan 2024 00:10:16 +0800
Subject: [PATCH 08/10] Update ast-dump-static-operators.cpp

---
 clang/test/AST/ast-dump-static-operators.cpp | 16 ++++++++--------
 1 file changed, 8 insertions(+), 8 deletions(-)

diff --git a/clang/test/AST/ast-dump-static-operators.cpp b/clang/test/AST/ast-dump-static-operators.cpp
index 87a15403e6f8b2..e8454bdac02f7b 100644
--- a/clang/test/AST/ast-dump-static-operators.cpp
+++ b/clang/test/AST/ast-dump-static-operators.cpp
@@ -16,38 +16,38 @@ Functor& get_functor() {
 
 void call_static_operators() {
   Functor functor;
-  
+
   int z1 = functor(1, 2);
   // CHECK:      CXXOperatorCallExpr {{.*}} 'int' '()'
   // CHECK-NEXT: |-ImplicitCastExpr {{.*}} <col:19, col:24> 'int (*)(int, int)' <FunctionToPointerDecay>
   // CHECK-NEXT: | `-DeclRefExpr {{.*}} <col:19, col:24> 'int (int, int)' lvalue CXXMethod {{.*}} 'operator()' 'int (int, int)'
-  // CHECK-NEXT: |-DeclRefExpr {{.*}} <col:12> 'Functor':'Functor' lvalue Var {{.*}} 'functor' 'Functor':'Functor'
+  // CHECK-NEXT: |-DeclRefExpr {{.*}} <col:12> 'Functor' lvalue Var {{.*}} 'functor' 'Functor'
   // CHECK-NEXT: |-IntegerLiteral {{.*}} <col:20> 'int' 1
   // CHECK-NEXT: `-IntegerLiteral {{.*}} <col:23> 'int' 2
-  
+
   int z2 = functor[1, 2];
   // CHECK:      CXXOperatorCallExpr {{.*}} 'int' '[]'
   // CHECK-NEXT: |-ImplicitCastExpr {{.*}} <col:19, col:24> 'int (*)(int, int)' <FunctionToPointerDecay>
   // CHECK-NEXT: | `-DeclRefExpr {{.*}} <col:19, col:24> 'int (int, int)' lvalue CXXMethod {{.*}} 'operator[]' 'int (int, int)'
-  // CHECK-NEXT: |-DeclRefExpr {{.*}} <col:12> 'Functor':'Functor' lvalue Var {{.*}} 'functor' 'Functor':'Functor'
+  // CHECK-NEXT: |-DeclRefExpr {{.*}} <col:12> 'Functor' lvalue Var {{.*}} 'functor' 'Functor'
   // CHECK-NEXT: |-IntegerLiteral {{.*}} <col:20> 'int' 1
   // CHECK-NEXT: `-IntegerLiteral {{.*}} <col:23> 'int' 2
-  
+
   int z3 = get_functor()(1, 2);
   // CHECK:      CXXOperatorCallExpr {{.*}} 'int' '()'
   // CHECK-NEXT: |-ImplicitCastExpr {{.*}} <col:25, col:30> 'int (*)(int, int)' <FunctionToPointerDecay>
   // CHECK-NEXT: | `-DeclRefExpr {{.*}} <col:25, col:30> 'int (int, int)' lvalue CXXMethod {{.*}} 'operator()' 'int (int, int)'
-  // CHECK-NEXT: |-CallExpr {{.*}} <col:12, col:24> 'Functor':'Functor' lvalue
+  // CHECK-NEXT: |-CallExpr {{.*}} <col:12, col:24> 'Functor' lvalue
   // CHECK-NEXT: | `-ImplicitCastExpr {{.*}} <col:12> 'Functor &(*)()' <FunctionToPointerDecay>
   // CHECK-NEXT: |   `-DeclRefExpr {{.*}} <col:12> 'Functor &()' lvalue Function {{.*}} 'get_functor' 'Functor &()'
   // CHECK-NEXT: |-IntegerLiteral {{.*}} <col:26> 'int' 1
   // CHECK-NEXT: `-IntegerLiteral {{.*}} <col:29> 'int' 2
-  
+
   int z4 = get_functor()[1, 2];
   // CHECK:      CXXOperatorCallExpr {{.*}} 'int' '[]'
   // CHECK-NEXT: |-ImplicitCastExpr {{.*}} <col:25, col:30> 'int (*)(int, int)' <FunctionToPointerDecay>
   // CHECK-NEXT: | `-DeclRefExpr {{.*}} <col:25, col:30> 'int (int, int)' lvalue CXXMethod {{.*}} 'operator[]' 'int (int, int)'
-  // CHECK-NEXT: |-CallExpr {{.*}} <col:12, col:24> 'Functor':'Functor' lvalue
+  // CHECK-NEXT: |-CallExpr {{.*}} <col:12, col:24> 'Functor' lvalue
   // CHECK-NEXT: | `-ImplicitCastExpr {{.*}} <col:12> 'Functor &(*)()' <FunctionToPointerDecay>
   // CHECK-NEXT: |   `-DeclRefExpr {{.*}} <col:12> 'Functor &()' lvalue Function {{.*}} 'get_functor' 'Functor &()'
   // CHECK-NEXT: |-IntegerLiteral {{.*}} <col:26> 'int' 1

>From eb42407a523f9a79afca4fbf221b344330888cc6 Mon Sep 17 00:00:00 2001
From: SuperSodaSea <bobby825 at 126.com>
Date: Fri, 5 Jan 2024 13:17:59 +0800
Subject: [PATCH 09/10] Should work with const / volatile

---
 clang/lib/Sema/SemaOverload.cpp              |  5 +++-
 clang/test/SemaCXX/cxx2b-static-operator.cpp | 31 ++++++++++++++++++++
 2 files changed, 35 insertions(+), 1 deletion(-)
 create mode 100644 clang/test/SemaCXX/cxx2b-static-operator.cpp

diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index 673f8313c2517b..1810fdd12bd444 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -5680,8 +5680,11 @@ static ImplicitConversionSequence TryObjectArgumentInitialization(
   QualType ClassType = S.Context.getTypeDeclType(ActingContext);
   // [class.dtor]p2: A destructor can be invoked for a const, volatile or
   //                 const volatile object.
+  // Also, a static operator can be invoked for a const, volatile or const
+  // volatile object, apparently.
+  bool IsStaticOperator = Method->getDeclName().getCXXOverloadedOperator() != OO_None && Method->isStatic();
   Qualifiers Quals = Method->getMethodQualifiers();
-  if (isa<CXXDestructorDecl>(Method)) {
+  if (isa<CXXDestructorDecl>(Method) || IsStaticOperator) {
     Quals.addConst();
     Quals.addVolatile();
   }
diff --git a/clang/test/SemaCXX/cxx2b-static-operator.cpp b/clang/test/SemaCXX/cxx2b-static-operator.cpp
new file mode 100644
index 00000000000000..4d6f1f76d13157
--- /dev/null
+++ b/clang/test/SemaCXX/cxx2b-static-operator.cpp
@@ -0,0 +1,31 @@
+// RUN: %clang_cc1 -fsyntax-only -verify -std=c++23 %s
+
+// expected-no-diagnostics
+
+namespace A {
+
+struct Foo {
+  static int operator()(int a, int b) { return a + b; }
+  static int operator[](int a, int b) { return a + b; }
+};
+
+void ok() {
+  // Should pass regardless of const / volatile
+  Foo foo;
+  foo(1, 2);
+  foo[1, 2];
+
+  const Foo fooC;
+  fooC(1, 2);
+  fooC[1, 2];
+
+  const Foo fooV;
+  fooV(1, 2);
+  fooV[1, 2];
+
+  const volatile Foo fooCV;
+  fooCV(1, 2);
+  fooCV[1, 2];
+}
+
+}

>From fa85d874fcf416e3eb5269fec7c6ce2f544e2b97 Mon Sep 17 00:00:00 2001
From: SuperSodaSea <bobby825 at 126.com>
Date: Fri, 5 Jan 2024 13:22:10 +0800
Subject: [PATCH 10/10] Format code

---
 clang/lib/Sema/SemaOverload.cpp | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index 1810fdd12bd444..f9c7cf9b65b35c 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -5682,7 +5682,9 @@ static ImplicitConversionSequence TryObjectArgumentInitialization(
   //                 const volatile object.
   // Also, a static operator can be invoked for a const, volatile or const
   // volatile object, apparently.
-  bool IsStaticOperator = Method->getDeclName().getCXXOverloadedOperator() != OO_None && Method->isStatic();
+  bool IsStaticOperator =
+      Method->getDeclName().getCXXOverloadedOperator() != OO_None &&
+      Method->isStatic();
   Qualifiers Quals = Method->getMethodQualifiers();
   if (isa<CXXDestructorDecl>(Method) || IsStaticOperator) {
     Quals.addConst();



More information about the libcxx-commits mailing list