[flang-commits] [clang] [compiler-rt] [flang] [libc] [libunwind] [llvm] [lld] [libcxxabi] [mlir] [libcxx] [lldb] [clang-tools-extra] [clang] static operators should evaluate object argument (PR #68485)
Tianlan Zhou via flang-commits
flang-commits at lists.llvm.org
Sat Jan 6 09:48:12 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 flang-commits
mailing list