https://github.com/SuperSodaSea updated https://github.com/llvm/llvm-project/pull/68485
>From 03276260c48d9cafb2a0d80825156e77cdf02eba Mon Sep 17 00:00:00 2001 From: SuperSodaSea <bobby...@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 <bobby...@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 <bobby...@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 <bobby...@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 <bobby...@126.com> Date: Wed, 11 Oct 2023 17:41:02 +0800 Subject: [PATCH 05/10] Apply suggestions from shafik Co-authored-by: Shafik Yaghmour <sha...@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 <bobby...@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 <bobby...@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 <bobby...@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 <bobby...@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 <bobby...@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(); _______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits