From 80a4ada13d17036ed49262f5eca97c5582455070 Mon Sep 17 00:00:00 2001 From: xingshunxiang Date: Wed, 8 Jan 2025 11:50:59 +0800 Subject: [PATCH] Support Lambda with Receiver Issue:https://gitee.com/openharmony/arkcompiler_ets_frontend/issues/IBGFYO?from=project-issue Description: 1.support function type with receiver. 2.support lambda expression with receiver. Signed-off-by: xingshunxiang --- ets2panda/checker/ETSAnalyzer.cpp | 24 ++++--- ets2panda/checker/ETSAnalyzerHelpers.cpp | 43 +++++++---- ets2panda/checker/ETSAnalyzerHelpers.h | 6 +- ets2panda/checker/ETSchecker.h | 6 +- ets2panda/checker/checkerContext.h | 2 +- ets2panda/checker/ets/function.cpp | 3 +- ets2panda/checker/ets/helpers.cpp | 18 ++++- ets2panda/checker/ets/object.cpp | 72 +++++++++++++++---- ets2panda/checker/ets/typeCheckingHelpers.cpp | 9 ++- ets2panda/checker/ets/typeCreation.cpp | 23 +++++- ets2panda/checker/resolveResult.h | 4 +- ets2panda/checker/types/ets/etsObjectType.cpp | 13 +++- ets2panda/checker/types/ets/etsObjectType.h | 3 +- .../types/ets/etsObjectTypeConstants.h | 3 +- ets2panda/checker/types/signature.h | 2 + .../compiler/lowering/ets/lambdaLowering.cpp | 12 +++- ets2panda/ir/ets/etsFunctionType.h | 5 ++ ets2panda/ir/expressions/memberExpression.cpp | 2 +- ets2panda/parser/ETSparserExpressions.cpp | 15 +++- ets2panda/parser/ETSparserTypes.cpp | 39 +++++++--- ets2panda/parser/context/parserContext.h | 1 + ets2panda/parser/parserImpl.cpp | 2 +- ...ionUsage_duplicate_on_extension_lambda.sts | 23 ++++++ .../ExtensionFunctypeUncompatible.sts | 39 ++++++++++ .../MakeNormalFunctypeAsMethodCall.sts | 33 +++++++++ .../optional_chaining_invalid_property.sts | 1 + .../AnnotationForLambdaExpression.sts | 12 +++- .../extensionFunctionType.sts | 30 ++++++++ .../extensionFunctionType2.sts | 36 ++++++++++ ...xtensionFunctionTypeAssignedNormalFunc.sts | 28 ++++++++ .../extensionFunctionTypeCompatible.sts | 36 ++++++++++ .../extensionFunctionTypeCompatible2.sts | 32 +++++++++ .../extensionFunctionTypeGeneric.sts | 51 +++++++++++++ .../normalFuncTypeAssignedExtensionFunc.sts | 27 +++++++ .../validReturnThisOfExtensionFunction.sts | 22 ++++++ .../lambda_with_receiver1.sts | 25 +++++++ .../lambda_with_receiver2.sts | 29 ++++++++ .../lambda_with_receiver_generics1.sts | 40 +++++++++++ .../lambda_with_receiver_generics2.sts | 34 +++++++++ ...bda_with_receiver_generics_return_this.sts | 36 ++++++++++ ...h_receiver_generics_return_this_rotate.sts | 48 +++++++++++++ .../lambda_with_receiver_in_class_body1.sts | 40 +++++++++++ .../lambda_with_receiver_in_class_body2.sts | 42 +++++++++++ .../lambda_with_receiver_return_this1.sts | 35 +++++++++ .../lambda_with_receiver_return_this2.sts | 48 +++++++++++++ .../lambda_with_receiver_return_this3.sts | 31 ++++++++ ets2panda/varbinder/ETSBinder.cpp | 2 +- 47 files changed, 1012 insertions(+), 75 deletions(-) create mode 100644 ets2panda/test/ast/compiler/ets/annotation_tests/annotationUsage_duplicate_on_extension_lambda.sts create mode 100644 ets2panda/test/ast/compiler/ets/lambda_with_receiver_tests/ExtensionFunctypeUncompatible.sts create mode 100644 ets2panda/test/ast/compiler/ets/lambda_with_receiver_tests/MakeNormalFunctypeAsMethodCall.sts create mode 100644 ets2panda/test/runtime/ets/function_type_with_receiver/extensionFunctionType.sts create mode 100644 ets2panda/test/runtime/ets/function_type_with_receiver/extensionFunctionType2.sts create mode 100644 ets2panda/test/runtime/ets/function_type_with_receiver/extensionFunctionTypeAssignedNormalFunc.sts create mode 100644 ets2panda/test/runtime/ets/function_type_with_receiver/extensionFunctionTypeCompatible.sts create mode 100644 ets2panda/test/runtime/ets/function_type_with_receiver/extensionFunctionTypeCompatible2.sts create mode 100644 ets2panda/test/runtime/ets/function_type_with_receiver/extensionFunctionTypeGeneric.sts create mode 100644 ets2panda/test/runtime/ets/function_type_with_receiver/normalFuncTypeAssignedExtensionFunc.sts create mode 100644 ets2panda/test/runtime/ets/function_type_with_receiver/validReturnThisOfExtensionFunction.sts create mode 100644 ets2panda/test/runtime/ets/lambda_with_receiver/lambda_with_receiver1.sts create mode 100644 ets2panda/test/runtime/ets/lambda_with_receiver/lambda_with_receiver2.sts create mode 100644 ets2panda/test/runtime/ets/lambda_with_receiver/lambda_with_receiver_generics1.sts create mode 100644 ets2panda/test/runtime/ets/lambda_with_receiver/lambda_with_receiver_generics2.sts create mode 100644 ets2panda/test/runtime/ets/lambda_with_receiver/lambda_with_receiver_generics_return_this.sts create mode 100644 ets2panda/test/runtime/ets/lambda_with_receiver/lambda_with_receiver_generics_return_this_rotate.sts create mode 100644 ets2panda/test/runtime/ets/lambda_with_receiver/lambda_with_receiver_in_class_body1.sts create mode 100644 ets2panda/test/runtime/ets/lambda_with_receiver/lambda_with_receiver_in_class_body2.sts create mode 100644 ets2panda/test/runtime/ets/lambda_with_receiver/lambda_with_receiver_return_this1.sts create mode 100644 ets2panda/test/runtime/ets/lambda_with_receiver/lambda_with_receiver_return_this2.sts create mode 100644 ets2panda/test/runtime/ets/lambda_with_receiver/lambda_with_receiver_return_this3.sts diff --git a/ets2panda/checker/ETSAnalyzer.cpp b/ets2panda/checker/ETSAnalyzer.cpp index 70c2cfe62d..31c675b57a 100644 --- a/ets2panda/checker/ETSAnalyzer.cpp +++ b/ets2panda/checker/ETSAnalyzer.cpp @@ -870,16 +870,16 @@ checker::Type *ETSAnalyzer::Check(ir::ArrowFunctionExpression *expr) const } checker::ScopeContext scopeCtx(checker, expr->Function()->Scope()); - if (checker->HasStatus(checker::CheckerStatus::IN_INSTANCE_EXTENSION_METHOD)) { + if (checker->HasStatus(checker::CheckerStatus::IN_EXTENSION_METHOD) && !expr->Function()->IsExtensionMethod()) { /* example code: ``` class A { prop:number } - function A.method() { + function method(this: A) { let a = () => { - console.println(this.prop) + console.log(this.prop) } } ``` @@ -899,6 +899,11 @@ checker::Type *ETSAnalyzer::Check(ir::ArrowFunctionExpression *expr) const if (expr->Function()->Signature() == nullptr) { return checker->InvalidateType(expr); } + + if (expr->Function()->IsExtensionMethod()) { + checker->AddStatus(checker::CheckerStatus::IN_EXTENSION_METHOD); + CheckExtensionMethod(checker, expr->Function(), expr); + } auto *signature = expr->Function()->Signature(); checker->Context().SetContainingSignature(signature); @@ -1179,7 +1184,7 @@ checker::Signature *ETSAnalyzer::ResolveSignature(ETSChecker *checker, ir::CallE } if (checker->IsExtensionETSFunctionType(calleeType)) { - auto *signature = ResolveCallExtensionFunction(calleeType->AsETSFunctionType(), checker, expr); + auto *signature = ResolveCallExtensionFunction(calleeType, checker, expr, isFunctionalInterface); bool isReturnTypeLambda = (signature != nullptr) && signature->ReturnType()->IsETSObjectType() && signature->ReturnType()->AsETSObjectType()->HasObjectFlag(ETSObjectFlags::FUNCTIONAL); bool isSignatureExtensionAccessor = (signature != nullptr) && (signature->Function()->IsExtensionAccessor()); @@ -1260,10 +1265,11 @@ checker::Type *ETSAnalyzer::GetReturnType(ir::CallExpression *expr, checker::Typ } auto *returnType = signature->ReturnType(); - if (signature->HasSignatureFlag(SignatureFlags::THIS_RETURN_TYPE)) { - returnType = signature->Function()->IsExtensionMethod() - ? expr->Arguments()[0]->TsType() - : ChooseCalleeObj(checker, expr, calleeType, isConstructorCall); + + if (signature->HasSignatureFlag(SignatureFlags::EXTENSION_FUNCTION_RETURN_THIS)) { + returnType = expr->Arguments()[0]->TsType(); + } else if (signature->HasSignatureFlag(SignatureFlags::THIS_RETURN_TYPE)) { + returnType = ChooseCalleeObj(checker, expr, calleeType, isConstructorCall); } return returnType; @@ -1863,7 +1869,7 @@ checker::Type *ETSAnalyzer::Check(ir::ThisExpression *expr) const parameter(MANDATORY_PARAM_THIS), and capture the parameter's variable other than containing class's variable */ auto *variable = checker->AsETSChecker()->Scope()->Find(varbinder::VarBinder::MANDATORY_PARAM_THIS).variable; - if (checker->HasStatus(checker::CheckerStatus::IN_INSTANCE_EXTENSION_METHOD)) { + if (checker->HasStatus(checker::CheckerStatus::IN_EXTENSION_METHOD)) { ASSERT(variable != nullptr); expr->SetTsType(variable->TsType()); } else { diff --git a/ets2panda/checker/ETSAnalyzerHelpers.cpp b/ets2panda/checker/ETSAnalyzerHelpers.cpp index 660dbff301..57cf48c9c7 100644 --- a/ets2panda/checker/ETSAnalyzerHelpers.cpp +++ b/ets2panda/checker/ETSAnalyzerHelpers.cpp @@ -86,7 +86,7 @@ static void ReplaceThisInExtensionMethod(checker::ETSChecker *checker, ir::Scrip if (extensionFunc->ReturnTypeAnnotation() != nullptr && extensionFunc->ReturnTypeAnnotation()->IsTSThisType()) { // when return `this` in extensionFunction, the type of `this` actually should be type of the receiver, - // so some substitution should be done + // so some substitution should be done, temporary solution(xingshunxiang). auto *const thisType = extensionFunc->Signature()->Params()[0]->TsType(); auto *const thisTypeAnnotation = extensionFunc->Params()[0]->AsETSParameterExpression()->Ident()->TypeAnnotation(); @@ -109,7 +109,7 @@ static void ReplaceThisInExtensionMethod(checker::ETSChecker *checker, ir::Scrip "replace-this-in-extension-method"); } -void CheckExtensionMethod(checker::ETSChecker *checker, ir::ScriptFunction *extensionFunc, ir::MethodDefinition *node) +void CheckExtensionMethod(checker::ETSChecker *checker, ir::ScriptFunction *extensionFunc, ir::AstNode *node) { auto *const thisType = extensionFunc->Signature()->Params()[0]->TsType(); @@ -126,6 +126,9 @@ void CheckExtensionMethod(checker::ETSChecker *checker, ir::ScriptFunction *exte // NOTE(gogabr): should be done in a lowering ReplaceThisInExtensionMethod(checker, extensionFunc); + if (extensionFunc->IsArrow()) { + return; + } checker::SignatureInfo *originalExtensionSigInfo = checker->Allocator()->New( extensionFunc->Signature()->GetSignatureInfo(), checker->Allocator()); @@ -371,25 +374,26 @@ static void SwitchMethodCallToFunctionCall(checker::ETSChecker *checker, ir::Cal expr->Callee()->Check(checker); } -checker::Signature *ResolveCallExtensionFunction(checker::ETSFunctionType *functionType, checker::ETSChecker *checker, - ir::CallExpression *expr, const TypeRelationFlag reportFlag) +checker::Signature *ResolveCallExtensionFunction(checker::Type *functionType, checker::ETSChecker *checker, + ir::CallExpression *expr, bool isFunctionalInterface, + const TypeRelationFlag reportFlag) { // We have to ways to call ExtensionFunction `function foo(this: A, ...)`: // 1. Make ExtensionFunction as FunctionCall: `foo(a,...);` // 2. Make ExtensionFunction as MethodCall: `a.foo(...)`, here `a` is the receiver; + auto &signatures = ChooseSignatures(checker, functionType, false, isFunctionalInterface, false); if (expr->Callee()->IsMemberExpression()) { // when handle extension function as MethodCall, we temporarily transfer the expr node from `a.foo(...)` to - // `a.foo(a, ...)` and resolve the call. If we find the suitable signature, then switch the expr node to + // `foo(a, ...)` and resolve the call. If we find the suitable signature, then switch the expr node to // function call. auto *memberExpr = expr->Callee()->AsMemberExpression(); expr->Arguments().insert(expr->Arguments().begin(), memberExpr->Object()); - auto *signature = checker->ResolveCallExpressionAndTrailingLambda(functionType->CallSignatures(), expr, - expr->Start(), reportFlag); + auto *signature = checker->ResolveCallExpressionAndTrailingLambda(signatures, expr, expr->Start(), reportFlag); if (signature == nullptr) { expr->Arguments().erase(expr->Arguments().begin()); return nullptr; } - if (!signature->Function()->IsExtensionMethod()) { + if (!signature->Function()->IsExtensionMethod() && !isFunctionalInterface) { checker->LogTypeError({"Property '", memberExpr->Property()->AsIdentifier()->Name(), "' does not exist on type '", memberExpr->ObjType()->Name(), "'"}, memberExpr->Property()->Start()); @@ -400,7 +404,7 @@ checker::Signature *ResolveCallExtensionFunction(checker::ETSFunctionType *funct return signature; } - return checker->ResolveCallExpressionAndTrailingLambda(functionType->CallSignatures(), expr, expr->Start()); + return checker->ResolveCallExpressionAndTrailingLambda(signatures, expr, expr->Start()); } checker::Signature *ResolveCallForClassMethod(checker::ETSExtensionFuncHelperType *type, checker::ETSChecker *checker, @@ -477,13 +481,15 @@ checker::Signature *ResolveCallForETSExtensionFuncHelperType(checker::ETSExtensi checker::ETSChecker *checker, ir::CallExpression *expr) { ASSERT(expr->Callee()->IsMemberExpression()); + auto *calleeObj = expr->Callee()->AsMemberExpression()->Object(); + bool isCalleeObjETSGlobal = + calleeObj->IsIdentifier() && calleeObj->AsIdentifier()->Name() == compiler::Signatures::ETS_GLOBAL; // for callExpr `a.foo`, there are 3 situations: // 1.`a.foo` is private method call of class A; // 2.`a.foo` is extension function of `A`(function with receiver `A`) // 3.`a.foo` is public method call of class A; Signature *signature = nullptr; - if (checker->IsTypeIdenticalTo(checker->Context().ContainingClass(), - expr->Callee()->AsMemberExpression()->Object()->TsType())) { + if (checker->IsTypeIdenticalTo(checker->Context().ContainingClass(), calleeObj->TsType()) || isCalleeObjETSGlobal) { // When called `a.foo` in `a.anotherFunc`, we should find signature through private or protected method firstly. signature = ResolveCallForClassMethod(type, checker, expr, checker::TypeRelationFlag::NO_THROW); if (signature != nullptr) { @@ -880,12 +886,23 @@ ETSObjectType *CreateInterfaceTypeForETSFunctionType(ETSChecker *checker, ir::ET } } - auto *returnType = node->ReturnType()->GetType(checker); + Type *returnType = nullptr; + if (node->ReturnType()->IsTSThisType() && node->IsExtensionFunction()) { + returnType = node->Params()[0]->AsETSParameterExpression()->TypeAnnotation()->TsType(); + } else { + returnType = node->ReturnType()->GetType(checker); + } + checker::ETSChecker::EmplaceSubstituted( substitution, genericInterfaceType->TypeArguments()[paramsNum]->AsETSTypeParameter()->GetOriginal(), InstantiateBoxedPrimitiveType(checker, node->ReturnType(), returnType)); - return genericInterfaceType->Substitute(checker->Relation(), substitution)->AsETSObjectType(); + auto *interfaceType = + genericInterfaceType->Substitute(checker->Relation(), substitution, true, node->IsExtensionFunction()); + if (node->IsExtensionFunction() && node->ReturnType()->IsTSThisType()) { + checker->AddThisReturnTypeFlagForInterfaceInvoke(interfaceType->AsETSObjectType()); + } + return interfaceType->AsETSObjectType(); } Type *CreateParamTypeWithDefaultParam(ETSChecker *checker, ir::Expression *param, Type *paramType) diff --git a/ets2panda/checker/ETSAnalyzerHelpers.h b/ets2panda/checker/ETSAnalyzerHelpers.h index e4b7b76f8e..70540c6304 100644 --- a/ets2panda/checker/ETSAnalyzerHelpers.h +++ b/ets2panda/checker/ETSAnalyzerHelpers.h @@ -31,7 +31,7 @@ void CheckExtensionIsShadowedInCurrentClassOrInterface(checker::ETSChecker *chec checker::Signature *signature); void CheckExtensionIsShadowedByMethod(checker::ETSChecker *checker, checker::ETSObjectType *objType, ir::ScriptFunction *extensionFunc, checker::Signature *signature); -void CheckExtensionMethod(checker::ETSChecker *checker, ir::ScriptFunction *extensionFunc, ir::MethodDefinition *node); +void CheckExtensionMethod(checker::ETSChecker *checker, ir::ScriptFunction *extensionFunc, ir::AstNode *node); void DoBodyTypeChecking(ETSChecker *checker, ir::MethodDefinition *node, ir::ScriptFunction *scriptFunc); void ComposeAsyncImplFuncReturnType(ETSChecker *checker, ir::ScriptFunction *scriptFunc); void ComposeAsyncImplMethod(ETSChecker *checker, ir::MethodDefinition *node); @@ -40,8 +40,8 @@ void CheckIteratorMethodReturnType(ETSChecker *checker, ir::ScriptFunction *scri const lexer::SourcePosition &position, const std::string &methodName); checker::Type *InitAnonymousLambdaCallee(checker::ETSChecker *checker, ir::Expression *callee, checker::Type *calleeType); -checker::Signature *ResolveCallExtensionFunction(checker::ETSFunctionType *functionType, checker::ETSChecker *checker, - ir::CallExpression *expr, +checker::Signature *ResolveCallExtensionFunction(checker::Type *functionType, checker::ETSChecker *checker, + ir::CallExpression *expr, bool isFunctionalInterface, TypeRelationFlag reportFlag = TypeRelationFlag::NONE); checker::Signature *ResolveCallForClassMethod(checker::ETSFunctionType *functionType, checker::ETSChecker *checker, ir::CallExpression *expr, diff --git a/ets2panda/checker/ETSchecker.h b/ets2panda/checker/ETSchecker.h index 5bd098bf6e..5ef6caea26 100644 --- a/ets2panda/checker/ETSchecker.h +++ b/ets2panda/checker/ETSchecker.h @@ -232,6 +232,9 @@ public: std::vector ResolveMemberReference(const ir::MemberExpression *memberExpr, const ETSObjectType *target); void WarnForEndlessLoopInGetterSetter(const ir::MemberExpression *const memberExpr); + varbinder::Variable *GetExtensionFuncVarInGlobalFunction(const ir::MemberExpression *const memberExpr); + varbinder::Variable *GetExtensionFuncVarInGlobalField(const ir::MemberExpression *const memberExpr); + varbinder::Variable *GetExtensionFuncVarInFunctionScope(const ir::MemberExpression *const memberExpr); varbinder::Variable *ResolveInstanceExtension(const ir::MemberExpression *memberExpr); void CheckImplicitSuper(ETSObjectType *classType, Signature *ctorSig); void CheckThisOrSuperCallInConstructor(ETSObjectType *classType, Signature *ctorSig); @@ -288,6 +291,7 @@ public: ETSExtensionFuncHelperType *CreateETSExtensionFuncHelperType(ETSFunctionType *classMethodType, ETSFunctionType *extensionFunctionType); + void AddThisReturnTypeFlagForInterfaceInvoke(ETSObjectType *interface); ETSObjectType *FunctionTypeToFunctionalInterfaceType(Signature *signature); ETSTypeParameter *CreateTypeParameter(); ETSObjectType *CreateETSObjectType(ir::AstNode *declNode, ETSObjectFlags flags); @@ -637,7 +641,7 @@ public: void CheckRethrowingFunction(ir::ScriptFunction *func); ETSObjectType *GetRelevantArgumentedTypeFromChild(ETSObjectType *child, ETSObjectType *target); util::StringView GetHashFromTypeArguments(const ArenaVector &typeArgTypes); - util::StringView GetHashFromSubstitution(const Substitution *substitution); + util::StringView GetHashFromSubstitution(const Substitution *substitution, const bool isExtensionFuncFlag); util::StringView GetHashFromFunctionType(ir::ETSFunctionType *type); static ETSObjectType *GetOriginalBaseType(Type *object); void SetArrayPreferredTypeForNestedMemberExpressions(ir::MemberExpression *expr, Type *annotationType); diff --git a/ets2panda/checker/checkerContext.h b/ets2panda/checker/checkerContext.h index dbbdb00a0c..70fc1c95df 100644 --- a/ets2panda/checker/checkerContext.h +++ b/ets2panda/checker/checkerContext.h @@ -44,7 +44,7 @@ enum class CheckerStatus : uint32_t { BUILTINS_INITIALIZED = 1U << 12U, IN_LAMBDA = 1U << 13U, IGNORE_VISIBILITY = 1U << 14U, - IN_INSTANCE_EXTENSION_METHOD = 1U << 15U, + IN_EXTENSION_METHOD = 1U << 15U, IN_LOCAL_CLASS = 1U << 16U, IN_INSTANCEOF_CONTEXT = 1U << 17U, IN_TEST_EXPRESSION = 1U << 18U, diff --git a/ets2panda/checker/ets/function.cpp b/ets2panda/checker/ets/function.cpp index bb0aeb8e49..7d9d114935 100644 --- a/ets2panda/checker/ets/function.cpp +++ b/ets2panda/checker/ets/function.cpp @@ -1025,7 +1025,8 @@ Signature *ETSChecker::ComposeSignature(ir::ScriptFunction *func, SignatureInfo } if (returnTypeAnnotation != nullptr && returnTypeAnnotation->IsTSThisType()) { - signature->AddSignatureFlag(SignatureFlags::THIS_RETURN_TYPE); + func->IsExtensionMethod() ? signature->AddSignatureFlag(SignatureFlags::EXTENSION_FUNCTION_RETURN_THIS) + : signature->AddSignatureFlag(SignatureFlags::THIS_RETURN_TYPE); } if (func->IsAbstract()) { diff --git a/ets2panda/checker/ets/helpers.cpp b/ets2panda/checker/ets/helpers.cpp index 3fb085ce57..984d5de64a 100644 --- a/ets2panda/checker/ets/helpers.cpp +++ b/ets2panda/checker/ets/helpers.cpp @@ -2101,7 +2101,7 @@ util::StringView ETSChecker::GetHashFromTypeArguments(const ArenaVector return util::UString(ss.str(), Allocator()).View(); } -util::StringView ETSChecker::GetHashFromSubstitution(const Substitution *substitution) +util::StringView ETSChecker::GetHashFromSubstitution(const Substitution *substitution, const bool extensionFuncFlag) { std::vector fields; for (auto [k, v] : *substitution) { @@ -2120,6 +2120,10 @@ util::StringView ETSChecker::GetHashFromSubstitution(const Substitution *substit ss << fstr; ss << ";"; } + + if (extensionFuncFlag) { + ss << "extensionFunctionType;"; + } return util::UString(ss.str(), Allocator()).View(); } @@ -2132,7 +2136,17 @@ util::StringView ETSChecker::GetHashFromFunctionType(ir::ETSFunctionType *type) ss << ";"; } - type->ReturnType()->GetType(this)->ToString(ss, true); + if (type->IsExtensionFunction()) { + if (type->ReturnType()->IsTSThisType()) { + type->Params()[0]->AsETSParameterExpression()->TypeAnnotation()->TsType()->ToString(ss, true); + } else { + type->ReturnType()->GetType(this)->ToString(ss, true); + } + ss << "extensionFunction;"; + } else { + type->ReturnType()->GetType(this)->ToString(ss, true); + } + ss << ";"; if (type->IsThrowing()) { diff --git a/ets2panda/checker/ets/object.cpp b/ets2panda/checker/ets/object.cpp index 40a2b36121..bdddfe8378 100644 --- a/ets2panda/checker/ets/object.cpp +++ b/ets2panda/checker/ets/object.cpp @@ -466,7 +466,6 @@ static void ResolveDeclaredMethodsOfObject(ETSChecker *checker, const ETSObjectT (void)_; auto *method = it->Declaration()->Node()->AsMethodDefinition(); auto *function = method->Function(); - if (function->IsProxy()) { continue; } @@ -1662,16 +1661,11 @@ void ETSChecker::ValidateResolvedProperty(varbinder::LocalVariable **property, c LogTypeError({"'", ident->Name(), "' is an instance property of '", target->Name(), "'"}, ident->Start()); } -varbinder::Variable *ETSChecker::ResolveInstanceExtension(const ir::MemberExpression *const memberExpr) +using VO = varbinder::ResolveBindingOptions; +varbinder::Variable *ETSChecker::GetExtensionFuncVarInGlobalFunction(const ir::MemberExpression *const memberExpr) { - // clang-format off - auto *globalFunctionVar = Scope() - ->FindInGlobal(memberExpr->Property()->AsIdentifier()->Name(), - // CC-OFFNXT(G.FMT.06-CPP) project code style - varbinder::ResolveBindingOptions::STATIC_METHODS) - .variable; - // clang-format on - + auto propertyName = memberExpr->Property()->AsIdentifier()->Name(); + auto *globalFunctionVar = Scope()->FindInGlobal(propertyName, VO::STATIC_METHODS).variable; if (globalFunctionVar == nullptr || !IsExtensionETSFunctionType(this->GetTypeOfVariable(globalFunctionVar))) { return nullptr; } @@ -1679,6 +1673,43 @@ varbinder::Variable *ETSChecker::ResolveInstanceExtension(const ir::MemberExpres return globalFunctionVar; } +varbinder::Variable *ETSChecker::GetExtensionFuncVarInGlobalField(const ir::MemberExpression *const memberExpr) +{ + auto propertyName = memberExpr->Property()->AsIdentifier()->Name(); + auto *globalFieldVar = Scope()->FindInGlobal(propertyName, VO::STATIC_VARIABLES).variable; + if (globalFieldVar == nullptr || !IsExtensionETSFunctionType(this->GetTypeOfVariable(globalFieldVar))) { + return nullptr; + } + + return globalFieldVar; +} + +varbinder::Variable *ETSChecker::GetExtensionFuncVarInFunctionScope(const ir::MemberExpression *const memberExpr) +{ + auto propertyName = memberExpr->Property()->AsIdentifier()->Name(); + auto *funcScopeVar = Scope()->FindInFunctionScope(propertyName, VO::ALL_NON_TYPE).variable; + if (funcScopeVar == nullptr || !IsExtensionETSFunctionType(funcScopeVar->TsType())) { + return nullptr; + } + return funcScopeVar; +} + +varbinder::Variable *ETSChecker::ResolveInstanceExtension(const ir::MemberExpression *const memberExpr) +{ + auto *globalFunctionVar = GetExtensionFuncVarInGlobalFunction(memberExpr); + if (globalFunctionVar != nullptr) { + return globalFunctionVar; + } + + auto *globalFieldVar = GetExtensionFuncVarInGlobalField(memberExpr); + if (globalFieldVar != nullptr) { + return globalFieldVar; + } + + // extension function maybe a parameter, or some lambda function defined in function. + return GetExtensionFuncVarInFunctionScope(memberExpr); +} + PropertySearchFlags ETSChecker::GetInitialSearchFlags(const ir::MemberExpression *const memberExpr) { constexpr auto FUNCTIONAL_FLAGS = @@ -1899,6 +1930,21 @@ void ETSChecker::ValidateVarDeclaratorOrClassProperty(const ir::MemberExpression } } +static ResolvedKind DecideResolvedKind(Type *typeOfGlobalFunctionVar) +{ + ASSERT(typeOfGlobalFunctionVar->IsETSObjectType() || typeOfGlobalFunctionVar->IsETSFunctionType()); + if (typeOfGlobalFunctionVar->IsETSObjectType()) { + return ResolvedKind::EXTENSION_FUNCTION; + } + + auto const *callSig = typeOfGlobalFunctionVar->AsETSFunctionType()->CallSignatures()[0]; + if (callSig->Function()->IsGetter() || callSig->Function()->IsSetter()) { + return ResolvedKind::EXTENSION_ACCESSOR; + } + + return ResolvedKind::EXTENSION_FUNCTION; +} + // NOLINTNEXTLINE(readability-function-size) std::vector ETSChecker::ResolveMemberReference(const ir::MemberExpression *const memberExpr, const ETSObjectType *const target) @@ -1932,7 +1978,6 @@ std::vector ETSChecker::ResolveMemberReference(const ir::Member prop == nullptr) { globalFunctionVar = ResolveInstanceExtension(memberExpr); } - if (globalFunctionVar == nullptr || (targetRef != nullptr && targetRef->HasFlag(varbinder::VariableFlags::CLASS_OR_INTERFACE))) { /* @@ -1953,10 +1998,7 @@ std::vector ETSChecker::ResolveMemberReference(const ir::Member return resolveRes; } } else { - auto *const callSig = globalFunctionVar->TsType()->AsETSFunctionType()->CallSignatures()[0]; - ResolvedKind resolvedKind = (callSig->Function()->IsGetter() || callSig->Function()->IsSetter()) - ? ResolvedKind::INSTANCE_EXTENSION_ACCESSOR - : ResolvedKind::INSTANCE_EXTENSION_FUNCTION; + ResolvedKind resolvedKind = DecideResolvedKind(globalFunctionVar->TsType()); resolveRes.emplace_back(Allocator()->New(globalFunctionVar, resolvedKind)); if (prop == nullptr) { diff --git a/ets2panda/checker/ets/typeCheckingHelpers.cpp b/ets2panda/checker/ets/typeCheckingHelpers.cpp index 31ff303853..7cd274ddb4 100644 --- a/ets2panda/checker/ets/typeCheckingHelpers.cpp +++ b/ets2panda/checker/ets/typeCheckingHelpers.cpp @@ -576,7 +576,8 @@ Type *ETSChecker::GuaranteedTypeForUncheckedPropertyAccess(varbinder::Variable * // Determine if substituted method cast requires cast from erased type Type *ETSChecker::GuaranteedTypeForUncheckedCallReturn(Signature *sig) { - if (sig->HasSignatureFlag(checker::SignatureFlags::THIS_RETURN_TYPE)) { + if (sig->HasSignatureFlag(checker::SignatureFlags::THIS_RETURN_TYPE) || + sig->HasSignatureFlag(checker::SignatureFlags::EXTENSION_FUNCTION_RETURN_THIS)) { return sig->ReturnType(); } auto *const baseSig = sig->Function() != nullptr ? sig->Function()->Signature() : nullptr; @@ -1353,10 +1354,14 @@ bool ETSChecker::TypeInference(Signature *signature, const ArenaVectorIsETSFunctionType()) { + if (type == nullptr || (!type->IsETSFunctionType() && !type->IsETSObjectType())) { return false; } + if (type->IsETSObjectType()) { + return type->AsETSObjectType()->HasObjectFlag(checker::ETSObjectFlags::EXTENSION_FUNCTION); + } + for (auto *signature : type->AsETSFunctionType()->CallSignatures()) { if (signature->Function() != nullptr && signature->Function()->IsExtensionMethod()) { return true; diff --git a/ets2panda/checker/ets/typeCreation.cpp b/ets2panda/checker/ets/typeCreation.cpp index 26b327239f..b75abc7fb3 100644 --- a/ets2panda/checker/ets/typeCreation.cpp +++ b/ets2panda/checker/ets/typeCreation.cpp @@ -541,6 +541,19 @@ Signature *ETSChecker::CreateBuiltinArraySignature(ETSArrayType *arrayType, size return signature; } +void ETSChecker::AddThisReturnTypeFlagForInterfaceInvoke(ETSObjectType *interface) +{ + auto &callSigsOfInvoke0 = + interface->AsETSObjectType() + ->GetOwnProperty(FUNCTIONAL_INTERFACE_INVOKE_METHOD_NAME) + ->TsType() + ->AsETSFunctionType() + ->CallSignatures(); + for (auto sig : callSigsOfInvoke0) { + sig->AddSignatureFlag(SignatureFlags::EXTENSION_FUNCTION_RETURN_THIS); + } +} + ETSObjectType *ETSChecker::FunctionTypeToFunctionalInterfaceType(Signature *signature) { ir::ScriptFunctionFlags flags = ir::ScriptFunctionFlags::NONE; @@ -560,7 +573,7 @@ ETSObjectType *ETSChecker::FunctionTypeToFunctionalInterfaceType(Signature *sign GlobalBuiltinFunctionType(GlobalBuiltinFunctionTypeVariadicThreshold(), flags)->AsETSObjectType(); auto *substitution = NewSubstitution(); substitution->emplace(functionN->TypeArguments()[0]->AsETSTypeParameter(), MaybeBoxType(retType)); - return functionN->Substitute(Relation(), substitution); + return functionN->Substitute(Relation(), substitution, signature->Function()->IsExtensionMethod()); } // Note: FunctionN is not supported yet @@ -577,7 +590,13 @@ ETSObjectType *ETSChecker::FunctionTypeToFunctionalInterfaceType(Signature *sign } substitution->emplace(funcIface->TypeArguments()[signature->Params().size()]->AsETSTypeParameter(), MaybeBoxType(signature->ReturnType())); - return funcIface->Substitute(Relation(), substitution); + + auto *interFaceType = + funcIface->Substitute(Relation(), substitution, true, signature->Function()->IsExtensionMethod()); + if (signature->HasSignatureFlag(SignatureFlags::EXTENSION_FUNCTION_RETURN_THIS)) { + AddThisReturnTypeFlagForInterfaceInvoke(interFaceType); + } + return interFaceType; } ETSObjectType *ETSChecker::CreatePromiseOf(Type *type) diff --git a/ets2panda/checker/resolveResult.h b/ets2panda/checker/resolveResult.h index a65356f598..8a3c451bb0 100644 --- a/ets2panda/checker/resolveResult.h +++ b/ets2panda/checker/resolveResult.h @@ -26,8 +26,8 @@ enum class OverrideErrorCode { enum class ResolvedKind { PROPERTY, - INSTANCE_EXTENSION_FUNCTION, - INSTANCE_EXTENSION_ACCESSOR, + EXTENSION_FUNCTION, + EXTENSION_ACCESSOR, }; class ResolveResult { diff --git a/ets2panda/checker/types/ets/etsObjectType.cpp b/ets2panda/checker/types/ets/etsObjectType.cpp index b1141e0917..da4417e84a 100644 --- a/ets2panda/checker/types/ets/etsObjectType.cpp +++ b/ets2panda/checker/types/ets/etsObjectType.cpp @@ -449,7 +449,7 @@ bool ETSObjectType::CheckIdenticalFlags(ETSObjectType *other) const { constexpr auto FLAGS_TO_REMOVE = ETSObjectFlags::INCOMPLETE_INSTANTIATION | ETSObjectFlags::CHECKED_COMPATIBLE_ABSTRACTS | - ETSObjectFlags::CHECKED_INVOKE_LEGITIMACY; + ETSObjectFlags::CHECKED_INVOKE_LEGITIMACY | ETSObjectFlags::EXTENSION_FUNCTION; auto cleanedTargetFlags = other->ObjectFlags(); cleanedTargetFlags &= ~FLAGS_TO_REMOVE; @@ -992,6 +992,9 @@ void ETSObjectType::SetCopiedTypeProperties(TypeRelation *const relation, ETSObj copySignatures[i]->Function()->Params()[j]->AsETSParameterExpression()->SetInitializer( thisSignatures[i]->Function()->Params()[j]->AsETSParameterExpression()->Initializer()); } + if (thisSignatures[i]->HasSignatureFlag(SignatureFlags::EXTENSION_FUNCTION_RETURN_THIS)) { + copySignatures[i]->AddSignatureFlag(SignatureFlags::EXTENSION_FUNCTION_RETURN_THIS); + } } } } @@ -1029,7 +1032,8 @@ void ETSObjectType::UpdateTypeProperties(checker::ETSChecker *checker, PropertyP } } -ETSObjectType *ETSObjectType::Substitute(TypeRelation *relation, const Substitution *substitution, bool cache) +ETSObjectType *ETSObjectType::Substitute(TypeRelation *relation, const Substitution *substitution, bool cache, + bool isExtensionFunctionType) { if (substitution == nullptr || substitution->empty()) { return this; @@ -1047,7 +1051,7 @@ ETSObjectType *ETSObjectType::Substitute(TypeRelation *relation, const Substitut return this; } - const util::StringView hash = checker->GetHashFromSubstitution(substitution); + const util::StringView hash = checker->GetHashFromSubstitution(substitution, isExtensionFunctionType); if (cache) { if (auto *inst = GetInstantiatedType(hash); inst != nullptr) { return inst; @@ -1061,6 +1065,9 @@ ETSObjectType *ETSObjectType::Substitute(TypeRelation *relation, const Substitut auto *const copiedType = checker->CreateETSObjectType(declNode_, flags_); SetCopiedTypeProperties(relation, copiedType, std::move(newTypeArgs), base); + if (isExtensionFunctionType) { + copiedType->AddObjectFlag(checker::ETSObjectFlags::EXTENSION_FUNCTION); + } if (cache) { GetInstantiationMap().try_emplace(hash, copiedType); diff --git a/ets2panda/checker/types/ets/etsObjectType.h b/ets2panda/checker/types/ets/etsObjectType.h index e509e8db7d..06a53dbbde 100644 --- a/ets2panda/checker/types/ets/etsObjectType.h +++ b/ets2panda/checker/types/ets/etsObjectType.h @@ -383,7 +383,8 @@ public: Type *Instantiate(ArenaAllocator *allocator, TypeRelation *relation, GlobalTypesHolder *globalTypes) override; void UpdateTypeProperties(checker::ETSChecker *checker, PropertyProcesser const &func); ETSObjectType *Substitute(TypeRelation *relation, const Substitution *substitution) override; - ETSObjectType *Substitute(TypeRelation *relation, const Substitution *substitution, bool cache); + ETSObjectType *Substitute(TypeRelation *relation, const Substitution *substitution, bool cache, + bool isExtensionFunctionType = false); ETSObjectType *SubstituteArguments(TypeRelation *relation, ArenaVector const &arguments); void Cast(TypeRelation *relation, Type *target) override; bool CastNumericObject(TypeRelation *relation, Type *target); diff --git a/ets2panda/checker/types/ets/etsObjectTypeConstants.h b/ets2panda/checker/types/ets/etsObjectTypeConstants.h index 22db9cb246..d0ecfef854 100644 --- a/ets2panda/checker/types/ets/etsObjectTypeConstants.h +++ b/ets2panda/checker/types/ets/etsObjectTypeConstants.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 Huawei Device Co., Ltd. + * Copyright (c) 2025 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at @@ -56,6 +56,7 @@ enum class ETSObjectFlags : std::uint64_t { BUILTIN_DOUBLE = 1U << 31U, BOXED_ENUM = 1ULL << 32U, + EXTENSION_FUNCTION = 1ULL << 33U, BUILTIN_NUMERIC = BUILTIN_BYTE | BUILTIN_SHORT | BUILTIN_INT | BUILTIN_LONG | BUILTIN_FLOAT | BUILTIN_DOUBLE, // Complete set includes null|undefined|Object diff --git a/ets2panda/checker/types/signature.h b/ets2panda/checker/types/signature.h index 056138208e..eb9a4fd5bc 100644 --- a/ets2panda/checker/types/signature.h +++ b/ets2panda/checker/types/signature.h @@ -17,6 +17,7 @@ #define ES2PANDA_COMPILER_CHECKER_TYPES_SIGNATURE_H #include "type.h" +#include "ets/etsObjectType.h" #include "varbinder/variable.h" @@ -88,6 +89,7 @@ enum class SignatureFlags : uint32_t { SETTER = 1U << 17U, THROWS = 1U << 18U, RETHROWS = 1U << 19U, + EXTENSION_FUNCTION_RETURN_THIS = 1U << 20U, INTERNAL_PROTECTED = INTERNAL | PROTECTED, GETTER_OR_SETTER = GETTER | SETTER, diff --git a/ets2panda/compiler/lowering/ets/lambdaLowering.cpp b/ets2panda/compiler/lowering/ets/lambdaLowering.cpp index 4978dbeee8..b84fbd277e 100644 --- a/ets2panda/compiler/lowering/ets/lambdaLowering.cpp +++ b/ets2panda/compiler/lowering/ets/lambdaLowering.cpp @@ -672,10 +672,13 @@ static void CreateLambdaClassInvoke(public_lib::Context *ctx, LambdaInfo const * auto *returnType2 = allocator->New( wrapToObject ? anyType : lciInfo->lambdaSignature->ReturnType()->Substitute(checker->Relation(), lciInfo->substitution)); + ir::ScriptFunctionFlags functionFlag = function->IsExtensionMethod() + ? ir::ScriptFunctionFlags::INSTANCE_EXTENSION_METHOD + : ir::ScriptFunctionFlags::METHOD; auto *func = util::NodeAllocator::ForceSetParent( allocator, allocator, ir::ScriptFunction::ScriptFunctionData {body, ir::FunctionSignature(nullptr, std::move(params), returnType2), - ir::ScriptFunctionFlags::METHOD}); + functionFlag}); PostprocessLambdaClassInvoke(ctx, lciInfo, methodName, func); } @@ -733,7 +736,6 @@ static ir::ClassDeclaration *CreateLambdaClass(public_lib::Context *ctx, ArenaVe lambdaClassName.Append(info->calleeClass->Definition()->Ident()->Name()).Append("$").Append(info->name); ArenaVector funcInterfaces(allocator->Adapter()); - auto *classDeclaration = parser ->CreateFormattedTopLevelStatement(BuildLambdaClass(ctx, lambdaSigs, substitution, funcInterfaces), @@ -1163,7 +1165,11 @@ static ir::AstNode *InsertInvokeCall(public_lib::Context *ctx, ir::CallExpressio call->SetCallee(newCallee); call->SetSignature(propSignature); ASSERT(propSignature->ReturnType() != nullptr); - call->SetTsType(propSignature->ReturnType()); + if (propSignature->HasSignatureFlag(checker::SignatureFlags::EXTENSION_FUNCTION_RETURN_THIS)) { + call->SetTsType(call->Arguments()[0]->TsType()); + } else { + call->SetTsType(propSignature->ReturnType()); + } return InsertInvokeArguments(ctx, call); } diff --git a/ets2panda/ir/ets/etsFunctionType.h b/ets2panda/ir/ets/etsFunctionType.h index 486077543d..f395c8df64 100644 --- a/ets2panda/ir/ets/etsFunctionType.h +++ b/ets2panda/ir/ets/etsFunctionType.h @@ -113,6 +113,11 @@ public: return (funcFlags_ & ir::ScriptFunctionFlags::RETHROWS) != 0; } + bool IsExtensionFunction() const + { + return (funcFlags_ & ir::ScriptFunctionFlags::INSTANCE_EXTENSION_METHOD) != 0; + } + void TransformChildren(const NodeTransformer &cb, std::string_view transformationName) override; void Iterate(const NodeTraverser &cb) const override; void Dump(ir::AstDumper *dumper) const override; diff --git a/ets2panda/ir/expressions/memberExpression.cpp b/ets2panda/ir/expressions/memberExpression.cpp index 3880f8d0ec..1a8aadf17a 100644 --- a/ets2panda/ir/expressions/memberExpression.cpp +++ b/ets2panda/ir/expressions/memberExpression.cpp @@ -204,7 +204,7 @@ std::pair MemberExpression::Resolve return {checker->GetTypeOfVariable(var), var}; } - if (resolveRes[0]->Kind() == checker::ResolvedKind::INSTANCE_EXTENSION_ACCESSOR) { + if (resolveRes[0]->Kind() == checker::ResolvedKind::EXTENSION_ACCESSOR) { auto *callee = const_cast(this->AsExpression()); callee->AsMemberExpression()->AddMemberKind(ir::MemberExpressionKind::EXTENSION_ACCESSOR); } diff --git a/ets2panda/parser/ETSparserExpressions.cpp b/ets2panda/parser/ETSparserExpressions.cpp index e38de8a7ec..d22d8b6582 100644 --- a/ets2panda/parser/ETSparserExpressions.cpp +++ b/ets2panda/parser/ETSparserExpressions.cpp @@ -372,6 +372,13 @@ bool IsPunctuartorSpecialCharacter(lexer::TokenType tokenType) } } +// This function was created to reduce the size of `ETSParser::IsArrowFunctionExpressionStart`. +static bool IsValidTokenTypeOfArrowFunctionStart(lexer::TokenType tokenType) +{ + return (tokenType == lexer::TokenType::LITERAL_IDENT || + tokenType == lexer::TokenType::PUNCTUATOR_PERIOD_PERIOD_PERIOD || tokenType == lexer::TokenType::KEYW_THIS); +} + bool ETSParser::IsArrowFunctionExpressionStart() { const auto savedPos = Lexer()->Save(); @@ -405,8 +412,7 @@ bool ETSParser::IsArrowFunctionExpressionStart() if (!expectIdentifier) { break; } - if (tokenType != lexer::TokenType::LITERAL_IDENT && - tokenType != lexer::TokenType::PUNCTUATOR_PERIOD_PERIOD_PERIOD) { + if (!IsValidTokenTypeOfArrowFunctionStart(tokenType)) { Lexer()->Rewind(savedPos); return false; } @@ -429,8 +435,11 @@ bool ETSParser::IsArrowFunctionExpressionStart() ir::ArrowFunctionExpression *ETSParser::ParseArrowFunctionExpression() { - auto newStatus = ParserStatus::ARROW_FUNCTION; + auto newStatus = ParserStatus::ARROW_FUNCTION | ParserStatus::ALLOW_RECEIVER; auto *func = ParseFunction(newStatus); + if (func->HasReceiver()) { + func->AddFlag(ir::ScriptFunctionFlags::INSTANCE_EXTENSION_METHOD); + } auto *arrowFuncNode = AllocNode(func, Allocator()); arrowFuncNode->SetRange(func->Range()); return arrowFuncNode; diff --git a/ets2panda/parser/ETSparserTypes.cpp b/ets2panda/parser/ETSparserTypes.cpp index 17728a4be4..6f6b29d886 100644 --- a/ets2panda/parser/ETSparserTypes.cpp +++ b/ets2panda/parser/ETSparserTypes.cpp @@ -191,18 +191,28 @@ ir::TypeNode *ETSParser::ParseFunctionType() auto startLoc = Lexer()->GetToken().Start(); auto params = ParseFunctionParams(); + bool hasReceiver = !params.empty() && params[0]->AsETSParameterExpression()->Ident()->IsReceiver(); ExpectToken(lexer::TokenType::PUNCTUATOR_ARROW); TypeAnnotationParsingOptions options = TypeAnnotationParsingOptions::REPORT_ERROR; - auto *const returnTypeAnnotation = ParseTypeAnnotation(&options); + ir::TypeNode *returnTypeAnnotation = nullptr; + if (hasReceiver) { + SavedParserContext contextAfterParseParams(this, GetContext().Status() | ParserStatus::HAS_RECEIVER); + returnTypeAnnotation = ParseTypeAnnotation(&options); + } else { + returnTypeAnnotation = ParseTypeAnnotation(&options); + } + if (returnTypeAnnotation == nullptr) { return nullptr; } ir::ScriptFunctionFlags throwMarker = ParseFunctionThrowMarker(false); - + if (hasReceiver) { + throwMarker |= ir::ScriptFunctionFlags::INSTANCE_EXTENSION_METHOD; + } auto *funcType = AllocNode( - ir::FunctionSignature(nullptr, std::move(params), returnTypeAnnotation), throwMarker); + ir::FunctionSignature(nullptr, std::move(params), returnTypeAnnotation, hasReceiver), throwMarker); funcType->SetRange({startLoc, returnTypeAnnotation->End()}); return funcType; @@ -289,11 +299,11 @@ ir::TypeNode *ETSParser::ParsePotentialFunctionalType(TypeAnnotationParsingOptio if (((*options) & TypeAnnotationParsingOptions::IGNORE_FUNCTION_TYPE) == 0 && (Lexer()->GetToken().Type() == lexer::TokenType::PUNCTUATOR_RIGHT_PARENTHESIS || Lexer()->Lookahead() == lexer::LEX_CHAR_COLON || Lexer()->Lookahead() == lexer::LEX_CHAR_QUESTION)) { - GetContext().Status() |= ParserStatus::ALLOW_DEFAULT_VALUE; + GetContext().Status() |= (ParserStatus::ALLOW_DEFAULT_VALUE | ParserStatus::ALLOW_RECEIVER); // '(' is consumed in `ParseFunctionType` Lexer()->Rewind(savePos); auto typeAnnotation = ParseFunctionType(); - GetContext().Status() ^= ParserStatus::ALLOW_DEFAULT_VALUE; + GetContext().Status() ^= (ParserStatus::ALLOW_DEFAULT_VALUE | ParserStatus::ALLOW_RECEIVER); return typeAnnotation; } Lexer()->Rewind(savePos); @@ -385,13 +395,24 @@ ir::TypeNode *ETSParser::ParseThisType(TypeAnnotationParsingOptions *options) // - the usage of 'this' as a type is not allowed in the current context, or // - 'this' is not used as a return type, or // - the current context is an arrow function (might be inside a method of a class where 'this' is allowed). + // - Specially, Function with Receiver, Lambda with Receiver and Function type with Receiver can return 'this'. bool reportErr = (*options & TypeAnnotationParsingOptions::REPORT_ERROR) != 0; - bool allowThisType = (GetContext().Status() & ParserStatus::ALLOW_THIS_TYPE) != 0; - bool parseReturnType = (*options & TypeAnnotationParsingOptions::RETURN_TYPE) != 0; + bool allowThisType = ((GetContext().Status() & ParserStatus::ALLOW_THIS_TYPE) != 0); bool isArrowFunc = (GetContext().Status() & ParserStatus::ARROW_FUNCTION) != 0; + bool parseReturnType = (*options & TypeAnnotationParsingOptions::RETURN_TYPE) != 0; + bool isExtensionFunction = (GetContext().Status() & ParserStatus::HAS_RECEIVER) != 0; + bool isInUnion = (*options & TypeAnnotationParsingOptions::DISALLOW_UNION) != 0; bool notSimpleReturnThisType = - (allowThisType && parseReturnType && (Lexer()->Lookahead() != lexer::LEX_CHAR_LEFT_BRACE)); - if (reportErr && (!allowThisType || !parseReturnType || isArrowFunc || notSimpleReturnThisType)) { + (Lexer()->Lookahead() != lexer::LEX_CHAR_LEFT_BRACE) && (Lexer()->Lookahead() != lexer::LEX_CHAR_EQUALS) && + (Lexer()->Lookahead() != lexer::LEX_CHAR_COMMA) && (Lexer()->Lookahead() != lexer::LEX_CHAR_RIGHT_PAREN) && + (Lexer()->Lookahead() != lexer::LEX_CHAR_SEMICOLON) && (Lexer()->Lookahead() != lexer::UNICODE_INVALID_CP); + if (isExtensionFunction) { + if (reportErr && (notSimpleReturnThisType || isInUnion)) { + LogSyntaxError( + "A 'this' type is available only as return type in a non-static method of a class or struct and " + "extension functions."); + } + } else if (reportErr && (!allowThisType || !parseReturnType || isArrowFunc)) { LogSyntaxError( "A 'this' type is available only as return type in a non-static method of a class or struct and extension " "functions."); diff --git a/ets2panda/parser/context/parserContext.h b/ets2panda/parser/context/parserContext.h index cdad002788..8206f7d503 100644 --- a/ets2panda/parser/context/parserContext.h +++ b/ets2panda/parser/context/parserContext.h @@ -68,6 +68,7 @@ enum class ParserStatus : uint64_t { FUNCTION_HAS_THROW_STATEMENT = 1ULL << 34ULL, ALLOW_RECEIVER = 1ULL << 35ULL, EXTENSION_ACCESSOR = 1ULL << 36ULL, + HAS_RECEIVER = 1ULL << 37ULL, }; } // namespace ark::es2panda::parser diff --git a/ets2panda/parser/parserImpl.cpp b/ets2panda/parser/parserImpl.cpp index c2477036ff..85856a6118 100644 --- a/ets2panda/parser/parserImpl.cpp +++ b/ets2panda/parser/parserImpl.cpp @@ -935,7 +935,7 @@ FunctionSignature ParserImpl::ParseFunctionSignature(ParserStatus status) bool hasReceiver = !params.empty() && params[0]->IsETSParameterExpression() && params[0]->AsETSParameterExpression()->Ident()->IsReceiver(); if (hasReceiver) { - SavedParserContext contextAfterParseParams(this, GetContext().Status() | ParserStatus::ALLOW_THIS_TYPE); + SavedParserContext contextAfterParseParams(this, GetContext().Status() | ParserStatus::HAS_RECEIVER); returnTypeAnnotation = ParseFunctionReturnType(status); } else { returnTypeAnnotation = ParseFunctionReturnType(status); diff --git a/ets2panda/test/ast/compiler/ets/annotation_tests/annotationUsage_duplicate_on_extension_lambda.sts b/ets2panda/test/ast/compiler/ets/annotation_tests/annotationUsage_duplicate_on_extension_lambda.sts new file mode 100644 index 0000000000..d5db39f657 --- /dev/null +++ b/ets2panda/test/ast/compiler/ets/annotation_tests/annotationUsage_duplicate_on_extension_lambda.sts @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +@interface Anno {} +class A { name = "Bob" } +let a = new A(); + +let show = @Anno()@/* @@ label */Anno(this: A): string => { + return "Hello," + this.name; +} +/* @@@ label Error TypeError: Duplicate annotations are not allowed. The annotation 'Anno' has already been applied to this element. */ +/* @@@ label Error TypeError: Duplicate annotations are not allowed. The annotation 'Anno' has already been applied to this element. */ diff --git a/ets2panda/test/ast/compiler/ets/lambda_with_receiver_tests/ExtensionFunctypeUncompatible.sts b/ets2panda/test/ast/compiler/ets/lambda_with_receiver_tests/ExtensionFunctypeUncompatible.sts new file mode 100644 index 0000000000..d7c2f535d4 --- /dev/null +++ b/ets2panda/test/ast/compiler/ets/lambda_with_receiver_tests/ExtensionFunctypeUncompatible.sts @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +class Base {} +class Derived extends Base {} +type FuncTypeBaseBase = (this: Base) => Base +type FuncTypeBaseDerived = (this: Base) => Derived +type FuncTypeDerivedBase = (this: Derived) => Base +type FuncTypeDerivedDerived = (this: Derived) => Derived + +function BB(this: Base) : Base { return new Base() } +function BD(this: Base) : Derived { return new Derived() } +function DB(this: Derived) : Base { return new Base() } +function DD(this: Derived) : Derived { return new Derived() } + +let bb:FuncTypeBaseBase = BB; +let bd:FuncTypeBaseDerived = BD; +let db:FuncTypeDerivedBase = DB; +let dd:FuncTypeDerivedDerived = DD; + +bb = /* @@ label1 */db; +bb = /* @@ label2 */dd; +bd = /* @@ label3 */db; + +/* @@@ label1 Error TypeError: Type '(p1: Derived) => Base' cannot be assigned to type '(p1: Base) => Base' */ +/* @@@ label2 Error TypeError: Type '(p1: Derived) => Derived' cannot be assigned to type '(p1: Base) => Base' */ +/* @@@ label3 Error TypeError: Type '(p1: Derived) => Base' cannot be assigned to type '(p1: Base) => Derived' */ diff --git a/ets2panda/test/ast/compiler/ets/lambda_with_receiver_tests/MakeNormalFunctypeAsMethodCall.sts b/ets2panda/test/ast/compiler/ets/lambda_with_receiver_tests/MakeNormalFunctypeAsMethodCall.sts new file mode 100644 index 0000000000..d656816390 --- /dev/null +++ b/ets2panda/test/ast/compiler/ets/lambda_with_receiver_tests/MakeNormalFunctypeAsMethodCall.sts @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +class A { + value: boolean = true; +} +type F1 = (this: A) => boolean; +type F2 = (a: A) => boolean; + +function foo(this: A): boolean { + return this.value; +} + +let a = new A(); +let f1: F2 = foo; +let f2: F2 = (this: A): boolean => { return this.value } +a./* @@ label1 */f1(); +a./* @@ label2 */f2(); + +/* @@@ label1 Error TypeError: Property 'f1' does not exist on type 'A' */ +/* @@@ label2 Error TypeError: Property 'f2' does not exist on type 'A' */ diff --git a/ets2panda/test/ast/parser/ets/optional_chaining_invalid_property.sts b/ets2panda/test/ast/parser/ets/optional_chaining_invalid_property.sts index 04d31b501a..df2deb842f 100644 --- a/ets2panda/test/ast/parser/ets/optional_chaining_invalid_property.sts +++ b/ets2panda/test/ast/parser/ets/optional_chaining_invalid_property.sts @@ -25,5 +25,6 @@ let dog: Dog = { let hasSting = dog?.hasSting; +/* @@? 26:5 Error TypeError: Circular dependency detected for identifier: hasSting */ /* @@? 26:21 Error TypeError: Property 'hasSting' does not exist on type 'Dog' */ /* @@? 26:21 Error TypeError: Property 'hasSting' does not exist on type 'Dog' */ diff --git a/ets2panda/test/runtime/ets/annotation_tests/AnnotationForLambdaExpression.sts b/ets2panda/test/runtime/ets/annotation_tests/AnnotationForLambdaExpression.sts index 34afea210c..ae820664e2 100644 --- a/ets2panda/test/runtime/ets/annotation_tests/AnnotationForLambdaExpression.sts +++ b/ets2panda/test/runtime/ets/annotation_tests/AnnotationForLambdaExpression.sts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 Huawei Device Co., Ltd. + * Copyright (c) 2025 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at @@ -65,4 +65,14 @@ function foo(a:()=>number){} function main(){ foo(@Log ()=>1) +} + +class A { name = "Bob" } +let a = new A(); +let show = @Anno(this: A): string => { + return "Hi," + this.name; +} + +let show2 = @Anno()(this: A): string => { + return "Hello," + this.name; } \ No newline at end of file diff --git a/ets2panda/test/runtime/ets/function_type_with_receiver/extensionFunctionType.sts b/ets2panda/test/runtime/ets/function_type_with_receiver/extensionFunctionType.sts new file mode 100644 index 0000000000..5709f9bf4a --- /dev/null +++ b/ets2panda/test/runtime/ets/function_type_with_receiver/extensionFunctionType.sts @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +class A { + value: boolean = true; +} + +type F1 = (this: A) => boolean; + +function foo(this: A): boolean { + return this.value; +} + +let f1: F1 = foo; + +let a = new A(); +assert a.f1() +assert f1(a) diff --git a/ets2panda/test/runtime/ets/function_type_with_receiver/extensionFunctionType2.sts b/ets2panda/test/runtime/ets/function_type_with_receiver/extensionFunctionType2.sts new file mode 100644 index 0000000000..91ba7c7dcc --- /dev/null +++ b/ets2panda/test/runtime/ets/function_type_with_receiver/extensionFunctionType2.sts @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +class A { name = "Bob" } +let a = new A(); +let show = (this: A): string => { + return "Hi," + this.name; +} + +function foo(this: A): string { + return "Hello," + this.name; +} + +let show2: (this: A) => string; +show2 = show; +assert show2(a) == "Hi,Bob"; +assert a.show2() == "Hi,Bob"; +assert show2(new A()) == "Hi,Bob"; +assert new A().show2() == "Hi,Bob"; +show2 = foo; +assert show2(a) == "Hello,Bob"; +assert a.show2() == "Hello,Bob"; +assert show2(new A()) == "Hello,Bob"; +assert new A().show2() == "Hello,Bob"; diff --git a/ets2panda/test/runtime/ets/function_type_with_receiver/extensionFunctionTypeAssignedNormalFunc.sts b/ets2panda/test/runtime/ets/function_type_with_receiver/extensionFunctionTypeAssignedNormalFunc.sts new file mode 100644 index 0000000000..feac00da16 --- /dev/null +++ b/ets2panda/test/runtime/ets/function_type_with_receiver/extensionFunctionTypeAssignedNormalFunc.sts @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +class A { + value: boolean = true; +} +type F1 = (this: A) => boolean; + +function goo(a: A): boolean { + return a.value; +} + +let a = new A(); +let f1: F1 = goo; +a.f1(); +f1(a); diff --git a/ets2panda/test/runtime/ets/function_type_with_receiver/extensionFunctionTypeCompatible.sts b/ets2panda/test/runtime/ets/function_type_with_receiver/extensionFunctionTypeCompatible.sts new file mode 100644 index 0000000000..2220ef8d5e --- /dev/null +++ b/ets2panda/test/runtime/ets/function_type_with_receiver/extensionFunctionTypeCompatible.sts @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +class A { + value: boolean = true; +} + +type F1 = (this: A) => boolean; +type F2 = (a: A) => boolean; + +function foo(this: A): boolean { + return this.value; +} + +function goo(a: A): boolean { + return a.value; +} + +let f1: F1 = foo; +f1 = goo; + +let f2: F2 = goo; +f2 = foo; +f1 = f2; diff --git a/ets2panda/test/runtime/ets/function_type_with_receiver/extensionFunctionTypeCompatible2.sts b/ets2panda/test/runtime/ets/function_type_with_receiver/extensionFunctionTypeCompatible2.sts new file mode 100644 index 0000000000..c4d79fac3e --- /dev/null +++ b/ets2panda/test/runtime/ets/function_type_with_receiver/extensionFunctionTypeCompatible2.sts @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +class Base {} +class Derived extends Base {} +type FuncTypeBaseBase = (this: Base) => Base +type FuncTypeBaseDerived = (this: Base) => Derived +type FuncTypeDerivedBase = (this: Derived) => Base +type FuncTypeDerivedDerived = (this: Derived) => Derived + +function BB(this: Base) : Base { return new Base() } +function BD(this: Base) : Derived { return new Derived() } +function DB(this: Derived) : Base { return new Base() } + +let bb:FuncTypeBaseBase = BB; +let bd:FuncTypeBaseDerived = BD; +let db:FuncTypeDerivedBase = DB; + +bb = bd; +db = bd diff --git a/ets2panda/test/runtime/ets/function_type_with_receiver/extensionFunctionTypeGeneric.sts b/ets2panda/test/runtime/ets/function_type_with_receiver/extensionFunctionTypeGeneric.sts new file mode 100644 index 0000000000..aa765f22f6 --- /dev/null +++ b/ets2panda/test/runtime/ets/function_type_with_receiver/extensionFunctionTypeGeneric.sts @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +class B { + value: T; + constructor(value: T) { + this.value = value; + } +} + +type FB = (this: B, x: T) => T; +type FBS = (this: B, x: string) => string; + +function bar(this: B, x: string): string { + return this.value + x; +} + +let f1: FB = bar; +let f2: FBS = bar; +let b = new B("Hello!"); +assert b.f1("world") == "Hello!world"; +assert f1(b, "world") == "Hello!world"; +assert b.f2("world") == "Hello!world"; +assert f2(b, "world") == "Hello!world"; + +f1 = bar; +f2 = bar; +assert b.f1("world") == "Hello!world"; +assert f1(b, "world") == "Hello!world"; +assert b.f2("world") == "Hello!world"; +assert f2(b, "world") == "Hello!world"; + +f1 = f2; +assert b.f1("world") == "Hello!world"; +assert f1(b, "world") == "Hello!world"; + +f2 = f1; +assert b.f2("world") == "Hello!world"; +assert f2(b, "world") == "Hello!world"; diff --git a/ets2panda/test/runtime/ets/function_type_with_receiver/normalFuncTypeAssignedExtensionFunc.sts b/ets2panda/test/runtime/ets/function_type_with_receiver/normalFuncTypeAssignedExtensionFunc.sts new file mode 100644 index 0000000000..b6c65999d6 --- /dev/null +++ b/ets2panda/test/runtime/ets/function_type_with_receiver/normalFuncTypeAssignedExtensionFunc.sts @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +class A { + value: boolean = true; +} +type F1 = (a: A) => boolean; + +function goo(this: A): boolean { + return this.value; +} + +let a = new A(); +let f1: F1 = goo; +f1(a); diff --git a/ets2panda/test/runtime/ets/function_type_with_receiver/validReturnThisOfExtensionFunction.sts b/ets2panda/test/runtime/ets/function_type_with_receiver/validReturnThisOfExtensionFunction.sts new file mode 100644 index 0000000000..4a624f3037 --- /dev/null +++ b/ets2panda/test/runtime/ets/function_type_with_receiver/validReturnThisOfExtensionFunction.sts @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +class A {}; + +let y: (this: A, n: number) => this; +(this: A, n: number):this => {}; +function foo(a: A, ff:(this: A, n: number) => this) {}; +function foo2(ff:(this: A, n: number) => this, a:A) {}; +function foo3(this: A, n: number):this {return this}; diff --git a/ets2panda/test/runtime/ets/lambda_with_receiver/lambda_with_receiver1.sts b/ets2panda/test/runtime/ets/lambda_with_receiver/lambda_with_receiver1.sts new file mode 100644 index 0000000000..acfbbdf6b0 --- /dev/null +++ b/ets2panda/test/runtime/ets/lambda_with_receiver/lambda_with_receiver1.sts @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +class A { name = "Bob" } +let a = new A(); +let show = (this: A): string => { + return "Hi," + this.name; +} + +assert show(a) == "Hi,Bob"; +assert a.show() == "Hi,Bob"; +assert new A().show() == "Hi,Bob"; +assert show(new A()) == "Hi,Bob"; diff --git a/ets2panda/test/runtime/ets/lambda_with_receiver/lambda_with_receiver2.sts b/ets2panda/test/runtime/ets/lambda_with_receiver/lambda_with_receiver2.sts new file mode 100644 index 0000000000..d831aaf86c --- /dev/null +++ b/ets2panda/test/runtime/ets/lambda_with_receiver/lambda_with_receiver2.sts @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +class A { + name: string + constructor (n:string) { + this.name = n; + } +} +function apply(aa: A[], ff: (this: A) => string) { + for (let a of aa) { + assert a.ff() == a.name; + assert ff(a) == a.name; + } +} +let aa: A[] = [new A("aa"), new A("bb")] +apply(aa, (this: A): string => { return this.name } ) diff --git a/ets2panda/test/runtime/ets/lambda_with_receiver/lambda_with_receiver_generics1.sts b/ets2panda/test/runtime/ets/lambda_with_receiver/lambda_with_receiver_generics1.sts new file mode 100644 index 0000000000..1d8af877fd --- /dev/null +++ b/ets2panda/test/runtime/ets/lambda_with_receiver/lambda_with_receiver_generics1.sts @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +class B { + value: T; + constructor(value: T) { + this.value = value; + } +} + +type FB = (this: B, x: T) => T; +type FBS = (this: B, x: string) => string; + +let f1: FB = (this: B, x: string): string => {return this.value + x;}; +let f2: FBS = (this: B, x: string): string => {return this.value + x;}; +let b = new B("Hello!"); +assert b.f1("world") == "Hello!world"; +assert f1(b, "world") == "Hello!world"; +assert b.f2("world") == "Hello!world"; +assert f2(b, "world") == "Hello!world"; + +f1 = f2; +assert b.f1("world") == "Hello!world"; +assert f1(b, "world") == "Hello!world"; + +f2 = f1; +assert b.f2("world") == "Hello!world"; +assert f2(b, "world") == "Hello!world"; diff --git a/ets2panda/test/runtime/ets/lambda_with_receiver/lambda_with_receiver_generics2.sts b/ets2panda/test/runtime/ets/lambda_with_receiver/lambda_with_receiver_generics2.sts new file mode 100644 index 0000000000..e0b1f77b34 --- /dev/null +++ b/ets2panda/test/runtime/ets/lambda_with_receiver/lambda_with_receiver_generics2.sts @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +class B { + value: T; + x:T; + constructor(value: T) { + this.value = value; + } +} + +type FB = (this: B, x: T) => T; +type FBSI = (this: B, x: string|Int) => string|Int; + +function apply(bb: B[], ff: FBSI) { + for (let b of bb) { + assert b.ff("hello") == b.value; + assert ff(b, "hello") == b.value; + } +} +let bb: B[] = [new B("aa"), new B(1)] +apply(bb, (this: B, x: string|Int): string|Int => {this.x = x; return this.value;} ) diff --git a/ets2panda/test/runtime/ets/lambda_with_receiver/lambda_with_receiver_generics_return_this.sts b/ets2panda/test/runtime/ets/lambda_with_receiver/lambda_with_receiver_generics_return_this.sts new file mode 100644 index 0000000000..f958df15ef --- /dev/null +++ b/ets2panda/test/runtime/ets/lambda_with_receiver/lambda_with_receiver_generics_return_this.sts @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +class B { + value: T; + x:T; + constructor(value: T) { + this.value = value; + } +} + +type FB = (this: B, x: T) => this; +type FBSI = (this: B, x: string|Int) => this; +function apply(bb: B[], ff: FBSI) { + for (let b of bb) { + assert b.ff("hello").value == b.value; + assert b.ff("hello").x == "hello"; + assert ff(b, "hello").value == b.value; + assert ff(b, "hello").x == "hello"; + } +} +let bb: B[] = [new B("aa"), new B(1)] +apply(bb, (this: B, x: string|Int): this => {this.x = x; return this;} ) + diff --git a/ets2panda/test/runtime/ets/lambda_with_receiver/lambda_with_receiver_generics_return_this_rotate.sts b/ets2panda/test/runtime/ets/lambda_with_receiver/lambda_with_receiver_generics_return_this_rotate.sts new file mode 100644 index 0000000000..77dd412ba7 --- /dev/null +++ b/ets2panda/test/runtime/ets/lambda_with_receiver/lambda_with_receiver_generics_return_this_rotate.sts @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +class B { + x: T; + constructor(value: T) { + this.x = value; + } + setX(x: T): this { this.x = x; return this } +} + +class D extends B { + y: T; + constructor(value: T) { + super(value); + this.y = value; + } + setY(y: T): this { this.y = y; return this } +} + +type F = (this: B) => this; +let rotate:F = (this: B): this => { return this } +let rotate2 = (this: B): this => { return this } +function main() { + let d = new D(1).setX(3).rotate().setY(33); + assert d.x == 3; + assert d.y == 33; + let dd = new D(1).setX(6).rotate2().setY(66); + assert dd.x == 6; + assert dd.y == 66; + + let b = new B(1).setX(6).rotate().setX(66); + assert b.x == 66; + let bb = new B(1).setX(6).rotate2().setX(66); + assert bb.x == 66; +} diff --git a/ets2panda/test/runtime/ets/lambda_with_receiver/lambda_with_receiver_in_class_body1.sts b/ets2panda/test/runtime/ets/lambda_with_receiver/lambda_with_receiver_in_class_body1.sts new file mode 100644 index 0000000000..d7d0e998fd --- /dev/null +++ b/ets2panda/test/runtime/ets/lambda_with_receiver/lambda_with_receiver_in_class_body1.sts @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +class A { + X: number + Y: number + constructor(x:number, y:number) { + this.X = x; + this.Y = y; + } +} + +class B { + a: A; + constructor(x:number, y:number) { + this.a = new A(1, 2); + } + + foo() { + let getSum = (this: A): number => { return this.X + this.Y } + assert this.a.getSum() == 3; + assert getSum(this.a) == 3; + } +} + +function main(){ + new B(1, 2).foo(); +} diff --git a/ets2panda/test/runtime/ets/lambda_with_receiver/lambda_with_receiver_in_class_body2.sts b/ets2panda/test/runtime/ets/lambda_with_receiver/lambda_with_receiver_in_class_body2.sts new file mode 100644 index 0000000000..8c265931dc --- /dev/null +++ b/ets2panda/test/runtime/ets/lambda_with_receiver/lambda_with_receiver_in_class_body2.sts @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +class A { + X: number; + Y: number; + sum: number = 0; + constructor(x:number, y:number) { + this.X = x; + this.Y = y; + } +} + +class B { + aa: A[]; + constructor() { + this.aa = [new A(1, 2), new A(3, 4), new A(5, 6)]; + } + + apply(ff: (this: A) => number) { + for (let a of this.aa) { + assert a.ff() == a.sum; + assert ff(a) == a.sum; + } + } +} + +function main(){ + new B().apply((this: A): number => { this.sum = this.X + this.Y; return this.sum; }); +} \ No newline at end of file diff --git a/ets2panda/test/runtime/ets/lambda_with_receiver/lambda_with_receiver_return_this1.sts b/ets2panda/test/runtime/ets/lambda_with_receiver/lambda_with_receiver_return_this1.sts new file mode 100644 index 0000000000..bf712a05e3 --- /dev/null +++ b/ets2panda/test/runtime/ets/lambda_with_receiver/lambda_with_receiver_return_this1.sts @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +class A { + data_:number = 1; +} + +let foo = (this: A): this => { + this.data_ += 1; + return this; +} + +function main() { + assert (new A().foo().data_ == 2); + assert (foo(new A()).data_ == 2); + assert (new A().foo().foo().data_ == 3); + assert (foo(new A()).foo().data_ == 3); + let a = new A(); + assert(a.foo().data_ == 2); + assert(foo(a).data_ == 3); + assert(a.foo().foo().data_ == 5); + assert(foo(a.foo()).foo().data_ == 8); +} diff --git a/ets2panda/test/runtime/ets/lambda_with_receiver/lambda_with_receiver_return_this2.sts b/ets2panda/test/runtime/ets/lambda_with_receiver/lambda_with_receiver_return_this2.sts new file mode 100644 index 0000000000..0e380419ae --- /dev/null +++ b/ets2panda/test/runtime/ets/lambda_with_receiver/lambda_with_receiver_return_this2.sts @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +class A { + data_:number = 1; + x: number; + setX(x: number): this { this.x = x; return this } +} + +class B extends A { + y: number; + setY(y: number): this { this.y = y; return this } +} + +let foo = (this: A): this => { + this.data_ += 1; + return this; +} + +let rotate = (this: A): this => { return this } + +function main() { + assert (new B().foo().data_ == 2); + assert (foo(new B()).data_ == 2); + assert (new B().foo().foo().data_ == 3); + assert (foo(new B()).foo().data_ == 3); + let b = new B(); + assert(b.foo().data_ == 2); + assert(foo(b).data_ == 3); + assert(b.foo().foo().data_ == 5); + assert(foo(b.foo()).foo().data_ == 8); + + b = new B().setX(3).rotate().setY(33); + assert b.x == 3; + assert b.y == 33; +} diff --git a/ets2panda/test/runtime/ets/lambda_with_receiver/lambda_with_receiver_return_this3.sts b/ets2panda/test/runtime/ets/lambda_with_receiver/lambda_with_receiver_return_this3.sts new file mode 100644 index 0000000000..7d20048183 --- /dev/null +++ b/ets2panda/test/runtime/ets/lambda_with_receiver/lambda_with_receiver_return_this3.sts @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +class A { + name: string + constructor (n:string) { + this.name = n; + } +} +function apply(aa: A[], ff: (this: A) => this) { + for (let a of aa) { + assert a.ff().name == a.name; + assert a.ff().ff().name == a.name; + assert ff(a).name == a.name; + assert ff(a).ff().name == a.name; + } +} +let aa: A[] = [new A("aa"), new A("bb")] +apply(aa, (this: A): this => { return this } ) diff --git a/ets2panda/varbinder/ETSBinder.cpp b/ets2panda/varbinder/ETSBinder.cpp index ccc7b50458..86de167e36 100644 --- a/ets2panda/varbinder/ETSBinder.cpp +++ b/ets2panda/varbinder/ETSBinder.cpp @@ -416,7 +416,7 @@ void ETSBinder::ResolveMethodDefinition(ir::MethodDefinition *methodDef) auto paramScopeCtx = LexicalScope::Enter(this, func->Scope()->ParamScope()); auto params = func->Scope()->ParamScope()->Params(); - if (!params.empty() && params.front()->Name() == MANDATORY_PARAM_THIS) { + if (!params.empty() && params.front()->Name() == MANDATORY_PARAM_THIS && !func->IsExtensionMethod()) { return; // Implicit this parameter is already inserted by ResolveReferences(), don't insert it twice. } -- Gitee