diff --git a/ets2panda/checker/ETSAnalyzerHelpers.cpp b/ets2panda/checker/ETSAnalyzerHelpers.cpp index 40646014ca61efb7f6276c917be53b8021160cb4..d354200ec27f8554a4a85bf6392576fec3f35a90 100644 --- a/ets2panda/checker/ETSAnalyzerHelpers.cpp +++ b/ets2panda/checker/ETSAnalyzerHelpers.cpp @@ -439,18 +439,21 @@ checker::Signature *GetMostSpecificSigFromExtensionFuncAndClassMethod(checker::E type->ExtensionMethodType()->CallSignatures().end()); auto *memberExpr = expr->Callee()->AsMemberExpression(); - expr->Arguments().insert(expr->Arguments().begin(), memberExpr->Object()); - auto *dummyReceiver = type->ExtensionMethodType()->CallSignatures()[0]->Params()[0]; - const bool typeParamsNeeded = dummyReceiver->TsType()->IsETSObjectType(); + auto *dummyReceiver = memberExpr->Object(); + auto *dummyReceiverVar = type->ExtensionMethodType()->CallSignatures()[0]->Params()[0]; + expr->Arguments().insert(expr->Arguments().begin(), dummyReceiver); + const bool typeParamsNeeded = dummyReceiverVar->TsType()->IsETSObjectType(); for (auto *methodCallSig : type->ClassMethodType()->CallSignatures()) { methodCallSig->GetSignatureInfo()->minArgCount++; - auto ¶ms = methodCallSig->Params(); + auto ¶msVar = methodCallSig->Params(); + paramsVar.insert(paramsVar.begin(), dummyReceiverVar); + auto ¶ms = methodCallSig->Function()->Params(); params.insert(params.begin(), dummyReceiver); if (typeParamsNeeded) { auto &typeParams = methodCallSig->TypeParams(); - typeParams.insert(typeParams.end(), dummyReceiver->TsType()->AsETSObjectType()->TypeArguments().begin(), - dummyReceiver->TsType()->AsETSObjectType()->TypeArguments().end()); + typeParams.insert(typeParams.end(), dummyReceiverVar->TsType()->AsETSObjectType()->TypeArguments().begin(), + dummyReceiverVar->TsType()->AsETSObjectType()->TypeArguments().end()); } } @@ -459,11 +462,14 @@ checker::Signature *GetMostSpecificSigFromExtensionFuncAndClassMethod(checker::E for (auto *methodCallSig : type->ClassMethodType()->CallSignatures()) { methodCallSig->GetSignatureInfo()->minArgCount--; - auto ¶ms = methodCallSig->Params(); + auto ¶msVar = methodCallSig->Params(); + paramsVar.erase(paramsVar.begin()); + auto ¶ms = methodCallSig->Function()->Params(); params.erase(params.begin()); if (typeParamsNeeded) { auto &typeParams = methodCallSig->TypeParams(); - typeParams.resize(typeParams.size() - dummyReceiver->TsType()->AsETSObjectType()->TypeArguments().size()); + typeParams.resize(typeParams.size() - + dummyReceiverVar->TsType()->AsETSObjectType()->TypeArguments().size()); } } expr->Arguments().erase(expr->Arguments().begin()); diff --git a/ets2panda/checker/ETSchecker.h b/ets2panda/checker/ETSchecker.h index e688f95868c4cb57940fef678ea5bf3b623867a6..c6538288e7c2f55e7b2ca5784e6a71bd3e234b57 100644 --- a/ets2panda/checker/ETSchecker.h +++ b/ets2panda/checker/ETSchecker.h @@ -446,6 +446,9 @@ public: Signature *ChooseMostSpecificSignature(ArenaVector &signatures, const std::vector &argTypeInferenceRequired, const lexer::SourcePosition &pos, size_t argumentsSize = ULONG_MAX); + Signature *ResolvePotentialTrailingLambdaWithReceiver(ir::CallExpression *callExpr, + ArenaVector const &signatures, + ArenaVector &arguments); Signature *ResolveCallExpressionAndTrailingLambda(ArenaVector &signatures, ir::CallExpression *callExpr, const lexer::SourcePosition &pos, TypeRelationFlag reportFlag = TypeRelationFlag::NONE); @@ -916,7 +919,7 @@ private: const lexer::SourcePosition &pos, TypeRelationFlag resolveFlags); // Trailing lambda void MoveTrailingBlockToEnclosingBlockStatement(ir::CallExpression *callExpr); - void TransformTraillingLambda(ir::CallExpression *callExpr); + void TransformTraillingLambda(ir::CallExpression *callExpr, Signature *sig); ArenaVector ExtendArgumentsWithFakeLamda(ir::CallExpression *callExpr); // Static invoke diff --git a/ets2panda/checker/ets/function.cpp b/ets2panda/checker/ets/function.cpp index e941e2083c4a3cecc07861dd4d7d3fed07edb3eb..e2286e5950ac04309e1ad230586f9f07595e32c2 100644 --- a/ets2panda/checker/ets/function.cpp +++ b/ets2panda/checker/ets/function.cpp @@ -899,6 +899,47 @@ Signature *ETSChecker::ChooseMostSpecificSignature(ArenaVector &sig return mostSpecificSignature; } +static bool IsLastParameterLambdaWithReceiver(Signature *sig) +{ + auto const ¶ms = sig->Function()->Params(); + + return !params.empty() && (params.back()->AsETSParameterExpression()->TypeAnnotation() != nullptr) && + params.back()->AsETSParameterExpression()->TypeAnnotation()->IsETSFunctionType() && + params.back()->AsETSParameterExpression()->TypeAnnotation()->AsETSFunctionType()->IsExtensionFunction(); +} + +Signature *ETSChecker::ResolvePotentialTrailingLambdaWithReceiver(ir::CallExpression *callExpr, + ArenaVector const &signatures, + ArenaVector &arguments) +{ + auto *trailingLambda = arguments.back()->AsArrowFunctionExpression(); + ArenaVector normalSig(Allocator()->Adapter()); + ArenaVector sigContainLambdaWithReceiverAsParam(Allocator()->Adapter()); + Signature *signature = nullptr; + for (auto sig : signatures) { + if (!IsLastParameterLambdaWithReceiver(sig)) { + normalSig.emplace_back(sig); + continue; + } + + auto *candidateFunctionType = + sig->Function()->Params().back()->AsETSParameterExpression()->TypeAnnotation()->AsETSFunctionType(); + auto *currentReceiver = candidateFunctionType->Params()[0]; + trailingLambda->Function()->Params().emplace_back(currentReceiver); + sigContainLambdaWithReceiverAsParam.emplace_back(sig); + signature = ValidateSignatures(sigContainLambdaWithReceiverAsParam, callExpr->TypeParams(), arguments, + callExpr->Start(), "call", + TypeRelationFlag::NO_THROW | TypeRelationFlag::NO_CHECK_TRAILING_LAMBDA); + if (signature != nullptr) { + return signature; + } + sigContainLambdaWithReceiverAsParam.clear(); + trailingLambda->Function()->Params().clear(); + } + return ValidateSignatures(normalSig, callExpr->TypeParams(), arguments, callExpr->Start(), "call", + TypeRelationFlag::NO_THROW | TypeRelationFlag::NO_CHECK_TRAILING_LAMBDA); +} + Signature *ETSChecker::ResolveCallExpressionAndTrailingLambda(ArenaVector &signatures, ir::CallExpression *callExpr, const lexer::SourcePosition &pos, @@ -912,11 +953,10 @@ Signature *ETSChecker::ResolveCallExpressionAndTrailingLambda(ArenaVectorTypeParams(), arguments, pos, "call", - TypeRelationFlag::NO_THROW | TypeRelationFlag::NO_CHECK_TRAILING_LAMBDA); + sig = ResolvePotentialTrailingLambdaWithReceiver(callExpr, signatures, arguments); if (sig != nullptr) { // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) - TransformTraillingLambda(callExpr); + TransformTraillingLambda(callExpr, sig); TypeInference(sig, callExpr->Arguments()); return sig; } @@ -1970,7 +2010,8 @@ void ETSChecker::MoveTrailingBlockToEnclosingBlockStatement(ir::CallExpression * } } -void ETSChecker::TransformTraillingLambda(ir::CallExpression *callExpr) +using SFunctionData = ir::ScriptFunction::ScriptFunctionData; +void ETSChecker::TransformTraillingLambda(ir::CallExpression *callExpr, Signature *sig) { auto *trailingBlock = callExpr->TrailingBlock(); ASSERT(trailingBlock != nullptr); @@ -1993,10 +2034,23 @@ void ETSChecker::TransformTraillingLambda(ir::CallExpression *callExpr) } ArenaVector params(Allocator()->Adapter()); + ir::ScriptFunctionFlags flags = ir::ScriptFunctionFlags::ARROW; + if (IsLastParameterLambdaWithReceiver(sig)) { + auto *actualLambdaType = + sig->Function()->Params().back()->AsETSParameterExpression()->TypeAnnotation()->AsETSFunctionType(); + auto *receiverOfTrailingBlock = actualLambdaType->Params()[0]->Clone(Allocator(), nullptr)->AsExpression(); + auto *receiverVar = receiverOfTrailingBlock->AsETSParameterExpression()->Ident()->Variable(); + auto *receiverVarClone = + Allocator()->New(receiverVar->Declaration(), receiverVar->Flags()); + receiverVarClone->SetTsType(receiverVar->TsType()); + receiverVarClone->SetScope(funcParamScope); + funcScope->InsertBinding(receiverVarClone->Name(), receiverVarClone); + receiverOfTrailingBlock->AsETSParameterExpression()->Ident()->SetVariable(receiverVarClone); + params.emplace_back(receiverOfTrailingBlock); + flags |= ir::ScriptFunctionFlags::INSTANCE_EXTENSION_METHOD; + } auto *funcNode = AllocNode( - Allocator(), ir::ScriptFunction::ScriptFunctionData {trailingBlock, - ir::FunctionSignature(nullptr, std::move(params), nullptr), - ir::ScriptFunctionFlags::ARROW}); + Allocator(), SFunctionData {trailingBlock, ir::FunctionSignature(nullptr, std::move(params), nullptr), flags}); funcNode->SetScope(funcScope); funcScope->BindNode(funcNode); funcParamScope->BindNode(funcNode); diff --git a/ets2panda/compiler/lowering/ets/lambdaLowering.cpp b/ets2panda/compiler/lowering/ets/lambdaLowering.cpp index f803792737fa1e391fc2a07296d4b436c9f2303c..437b84c4c3ea5ad6fa94d7f6ffb0f7ac8f1707cb 100644 --- a/ets2panda/compiler/lowering/ets/lambdaLowering.cpp +++ b/ets2panda/compiler/lowering/ets/lambdaLowering.cpp @@ -720,6 +720,41 @@ static std::string BuildLambdaClass(public_lib::Context *ctx, ArenaVectorchecker->AsETSChecker(); + auto *classScope = lambdaClass->Definition()->Scope(); + ArenaVector invokeFuncsOfLambda(checker->Allocator()->Adapter()); + invokeFuncsOfLambda.emplace_back( + classScope->FindLocal(compiler::Signatures::LAMBDA_OBJECT_INVOKE, varbinder::ResolveBindingOptions::METHODS)); + invokeFuncsOfLambda.emplace_back(classScope->FindLocal(checker::FUNCTIONAL_INTERFACE_INVOKE_METHOD_NAME, + varbinder::ResolveBindingOptions::METHODS)); + for (auto *invokeFuncOfLambda : invokeFuncsOfLambda) { + if (invokeFuncOfLambda == nullptr) { + continue; + } + auto *scriptFunc = invokeFuncOfLambda->Declaration() + ->AsFunctionDecl() + ->Node() + ->AsMethodDefinition() + ->Value() + ->AsFunctionExpression() + ->Function(); + if (!scriptFunc->IsExtensionMethod()) { + continue; + } + auto *functionScope = scriptFunc->Scope(); + auto *functionParamScope = scriptFunc->Scope()->ParamScope(); + auto *theTrueThisVar = functionParamScope->Params()[0]; + auto &bindings = const_cast(functionScope->Bindings()); + bindings.erase(varbinder::ETSBinder::MANDATORY_PARAM_THIS); + bindings.insert({varbinder::ETSBinder::MANDATORY_PARAM_THIS, theTrueThisVar}); + } +} + static ir::ClassDeclaration *CreateLambdaClass(public_lib::Context *ctx, ArenaVector &lambdaSigs, ir::MethodDefinition *callee, LambdaInfo const *info) { @@ -775,7 +810,7 @@ static ir::ClassDeclaration *CreateLambdaClass(public_lib::Context *ctx, ArenaVe InitScopesPhaseETS::RunExternalNode(classDeclaration, varBinder); varBinder->ResolveReferencesForScopeWithContext(classDeclaration, varBinder->TopScope()); classDeclaration->Check(checker); - + CorrectTheTrueThisForExtensionLambda(ctx, classDeclaration); return classDeclaration; } diff --git a/ets2panda/parser/ETSparser.cpp b/ets2panda/parser/ETSparser.cpp index 1dabbf471136cee7e72e64f73b15bc8ffc1177d1..a9ed12a970c7f3eae4b4dd0f5898ad5e6d7c651f 100644 --- a/ets2panda/parser/ETSparser.cpp +++ b/ets2panda/parser/ETSparser.cpp @@ -1830,6 +1830,7 @@ bool ETSParser::IsStructKeyword() const void ETSParser::ParseTrailingBlock(ir::CallExpression *callExpr) { if (Lexer()->GetToken().Type() == lexer::TokenType::PUNCTUATOR_LEFT_BRACE) { + SavedParserContext svCtx(this, ParserStatus::PARSE_TRAILING_BLOCK); callExpr->SetIsTrailingBlockInNewLine(Lexer()->GetToken().NewLine()); callExpr->SetTrailingBlock(ParseBlockStatement()); } diff --git a/ets2panda/parser/ETSparserTypes.cpp b/ets2panda/parser/ETSparserTypes.cpp index 7d99291faca23ea398193e326868b614c787ad7b..4a833f16b3525dad8c34d0bffcad7a3d1eefa0f8 100644 --- a/ets2panda/parser/ETSparserTypes.cpp +++ b/ets2panda/parser/ETSparserTypes.cpp @@ -387,10 +387,24 @@ std::pair ETSParser::GetTypeAnnotationFromParentheses(Type return std::make_pair(typeAnnotation, true); } +static bool IsSimpleReturnThis(lexer::Token const &tokenAfterThis) +{ + return (tokenAfterThis.Type() == lexer::TokenType::PUNCTUATOR_ARROW) || + (tokenAfterThis.Type() == lexer::TokenType::PUNCTUATOR_COMMA) || + (tokenAfterThis.Type() == lexer::TokenType::PUNCTUATOR_RIGHT_PARENTHESIS) || + (tokenAfterThis.Type() == lexer::TokenType::PUNCTUATOR_LEFT_BRACE) || + (tokenAfterThis.Type() == lexer::TokenType::PUNCTUATOR_SEMI_COLON) || + ((tokenAfterThis.Flags() & lexer::TokenFlags::NEW_LINE) != 0); +} + ir::TypeNode *ETSParser::ParseThisType(TypeAnnotationParsingOptions *options) { ASSERT(Lexer()->GetToken().Type() == lexer::TokenType::KEYW_THIS); + auto startPos = Lexer()->GetToken().Start(); + auto const tokenLoc = Lexer()->GetToken().Loc(); + Lexer()->NextToken(); // eat 'this' + // A syntax error should be thrown if // - the usage of 'this' as a type is not allowed in the current context, or // - 'this' is not used as a return type, or @@ -402,26 +416,22 @@ ir::TypeNode *ETSParser::ParseThisType(TypeAnnotationParsingOptions *options) bool parseReturnType = (*options & TypeAnnotationParsingOptions::RETURN_TYPE) != 0; bool isExtensionFunction = (GetContext().Status() & ParserStatus::HAS_RECEIVER) != 0; bool isInUnion = (*options & TypeAnnotationParsingOptions::DISALLOW_UNION) != 0; - bool 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)) { + if (reportErr && (!IsSimpleReturnThis(Lexer()->GetToken()) || isInUnion)) { LogSyntaxError( "A 'this' type is available only as return type in a non-static method of a class or struct and " - "extension functions."); + "extension functions.", + startPos); } } 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."); + "functions.", + startPos); } auto *const thisType = AllocNode(Allocator()); - thisType->SetRange(Lexer()->GetToken().Loc()); - - Lexer()->NextToken(); // eat 'this' + thisType->SetRange(tokenLoc); return thisType; } diff --git a/ets2panda/parser/context/parserContext.h b/ets2panda/parser/context/parserContext.h index 8206f7d503d669aaff1c927dbf70daf244d2ab65..702415110a63c9c5dee5a383f11015fb077e68c9 100644 --- a/ets2panda/parser/context/parserContext.h +++ b/ets2panda/parser/context/parserContext.h @@ -69,6 +69,7 @@ enum class ParserStatus : uint64_t { ALLOW_RECEIVER = 1ULL << 35ULL, EXTENSION_ACCESSOR = 1ULL << 36ULL, HAS_RECEIVER = 1ULL << 37ULL, + PARSE_TRAILING_BLOCK = 1ULL << 38ULL, }; } // namespace ark::es2panda::parser diff --git a/ets2panda/parser/statementParser.cpp b/ets2panda/parser/statementParser.cpp index f05ee7afa2ed7856e31b13fe59580f9e6fd43f25..933aff9c7c1af426af7585ec5abcc4957db5f0bf 100644 --- a/ets2panda/parser/statementParser.cpp +++ b/ets2panda/parser/statementParser.cpp @@ -1100,7 +1100,8 @@ ir::LabelledStatement *ParserImpl::ParseLabelledStatement(const lexer::LexerPosi ir::Statement *ParserImpl::ParseReturnStatement() { - if ((context_.Status() & ParserStatus::FUNCTION) == 0) { + if ((context_.Status() & ParserStatus::FUNCTION) == 0 && + (context_.Status() & ParserStatus::PARSE_TRAILING_BLOCK) == 0) { LogSyntaxError("return keyword should be used in function body"); } diff --git a/ets2panda/test/ast/compiler/ets/lambda_with_receiver_tests/extensionFuncTypeAsParamsNameDuplicated.sts b/ets2panda/test/ast/compiler/ets/lambda_with_receiver_tests/extensionFuncTypeAsParamsNameDuplicated.sts new file mode 100644 index 0000000000000000000000000000000000000000..eab917e75ae900905e634fadc4445338f2092206 --- /dev/null +++ b/ets2panda/test/ast/compiler/ets/lambda_with_receiver_tests/extensionFuncTypeAsParamsNameDuplicated.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. + */ + +class B {} + +class A { + foo(n: number, f: (this: A) => void) {} + /* @@ label1 */foo(n: number, g: (this: B) => void) {} +} + +/* @@@ label1 Error TypeError: Function foo is already declared. */ diff --git a/ets2panda/test/ast/compiler/ets/lambda_with_receiver_tests/extensionFuncTypeAsParamsNameDuplicated2.sts b/ets2panda/test/ast/compiler/ets/lambda_with_receiver_tests/extensionFuncTypeAsParamsNameDuplicated2.sts new file mode 100644 index 0000000000000000000000000000000000000000..6ca8fa0fc255b63585d46e5e7313e65656e9df13 --- /dev/null +++ b/ets2panda/test/ast/compiler/ets/lambda_with_receiver_tests/extensionFuncTypeAsParamsNameDuplicated2.sts @@ -0,0 +1,21 @@ +/* + * 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 {} +class A {} +function foo(this: A, f: (this: A) => void) {} +/* @@ label1 */function foo(this: A, f: (this: B) => void) {} + +/* @@@ label1 Error TypeError: Function foo is already declared. */ diff --git a/ets2panda/test/ast/compiler/ets/lambda_with_receiver_tests/extensionFuncTypeAsParamsNameDuplicated3.sts b/ets2panda/test/ast/compiler/ets/lambda_with_receiver_tests/extensionFuncTypeAsParamsNameDuplicated3.sts new file mode 100644 index 0000000000000000000000000000000000000000..e97bafda8fd1af93cf73acba84c06084eb10a021 --- /dev/null +++ b/ets2panda/test/ast/compiler/ets/lambda_with_receiver_tests/extensionFuncTypeAsParamsNameDuplicated3.sts @@ -0,0 +1,24 @@ +/* + * 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 {} + +class A { + foo(g: (this: B) => void) {} +} + +function foo(this: A, f: (this: A) => void) /* @@ label1 */{} + +/* @@@ label1 Error TypeError: The extension function 'foo' has the same name with public method in class A */ diff --git a/ets2panda/test/runtime/ets/extension_function_with_generic/extension_function_with_generic3.sts b/ets2panda/test/runtime/ets/extension_function_with_generic/extension_function_with_generic3.sts new file mode 100644 index 0000000000000000000000000000000000000000..065f6698f741272f645f0b9296cf829244377eb2 --- /dev/null +++ b/ets2panda/test/runtime/ets/extension_function_with_generic/extension_function_with_generic3.sts @@ -0,0 +1,26 @@ +/* + * 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 {} + +class B {} + +function foo(this:B>):void {} + +new B>().foo(); +foo(new B>()); +let bas = new B>(); +bas.foo(); +foo(bas); \ No newline at end of file diff --git a/ets2panda/test/runtime/ets/extension_function_with_generic/extension_function_with_generic4.sts b/ets2panda/test/runtime/ets/extension_function_with_generic/extension_function_with_generic4.sts new file mode 100644 index 0000000000000000000000000000000000000000..1a0b2e427cf727cadb27907d9ea4657a6802e3ec --- /dev/null +++ b/ets2panda/test/runtime/ets/extension_function_with_generic/extension_function_with_generic4.sts @@ -0,0 +1,26 @@ +/* + * 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 {} + +class B {} + +function foo(this:B>):void {} + +new B>().foo(); +foo(new B>()); +let basn = new B>(); +basn.foo(); +foo(basn); \ No newline at end of file diff --git a/ets2panda/test/runtime/ets/function_type_with_receiver/validReturnThisOfExtensionFunction.sts b/ets2panda/test/runtime/ets/function_type_with_receiver/validReturnThisOfExtensionFunction.sts index 4a624f303745e7c8d90fb9b29823db3ecbd5c1b5..f506d8ea788dde2dbe1eafbde6500cd6622df6f0 100644 --- a/ets2panda/test/runtime/ets/function_type_with_receiver/validReturnThisOfExtensionFunction.sts +++ b/ets2panda/test/runtime/ets/function_type_with_receiver/validReturnThisOfExtensionFunction.sts @@ -15,6 +15,7 @@ class A {}; +let x: (this: A, n: number) => this let y: (this: A, n: number) => this; (this: A, n: number):this => {}; function foo(a: A, ff:(this: A, n: number) => this) {}; diff --git a/ets2panda/test/runtime/ets/lambda_with_receiver/lambda_with_receiver_catch_outside_field.sts b/ets2panda/test/runtime/ets/lambda_with_receiver/lambda_with_receiver_catch_outside_field.sts new file mode 100644 index 0000000000000000000000000000000000000000..04d923604dd0228625e684170a0d69218ddf4ac2 --- /dev/null +++ b/ets2panda/test/runtime/ets/lambda_with_receiver/lambda_with_receiver_catch_outside_field.sts @@ -0,0 +1,54 @@ +/* + * 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 C{ + out: number = 0; + in: number = 0; + data: number = 0; +} + +function process1(con: (this:C)=>void){ + let c = new C(); + c.con(); + assertTrue(c.out == 1); + assertTrue(c.in == 2); + assertTrue(c.data == 3); +} + +function process2(con: (this:C)=>void){ + let c = new C(); + c.con(); + assertTrue(c.out == 1); + assertTrue(c.in == 2); + assertTrue(c.data == 3); +} + +let field_out:number = 1; +function main(){ + let field_in:number = 2; + // passed lambda as parameter. + process1((this:C):void => { + this.out = field_out; + this.in = field_in; + this.data = 3; + }) + + // passed lambda as trailing lambda. + process2() { + this.out = field_out; + this.in = field_in; + this.data = 3; + } +} \ No newline at end of file diff --git a/ets2panda/test/runtime/ets/lambda_with_receiver/lambda_with_receiver_trailing_in_class_method.sts b/ets2panda/test/runtime/ets/lambda_with_receiver/lambda_with_receiver_trailing_in_class_method.sts new file mode 100644 index 0000000000000000000000000000000000000000..92c800bab0804fd58171cdf32d255ade99e18826 --- /dev/null +++ b/ets2panda/test/runtime/ets/lambda_with_receiver/lambda_with_receiver_trailing_in_class_method.sts @@ -0,0 +1,44 @@ +/* + * 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 { + data: number = 0; +} + +class A { + b:B; + data:number = 0; + constructor() { + this.b = new B(); + } + + foo(f: (this:B) => void) { + this.b.f(); + } + + foo2(f: (this:B) => void) { + f(this.b); + } + +} + +let a = new A(); +a.foo() { this.data = 666 } +assertTrue(a.b.data == 666); +assertTrue(a.data == 0); + +a.foo2() { this.data = 66 } +assertTrue(a.b.data == 66); +assertTrue(a.data == 0); diff --git a/ets2panda/test/runtime/ets/lambda_with_receiver/lambda_with_receiver_trailing_in_class_method2.sts b/ets2panda/test/runtime/ets/lambda_with_receiver/lambda_with_receiver_trailing_in_class_method2.sts new file mode 100644 index 0000000000000000000000000000000000000000..4ab091251edf22e6bdfbf8c4bd9c96dc20971963 --- /dev/null +++ b/ets2panda/test/runtime/ets/lambda_with_receiver/lambda_with_receiver_trailing_in_class_method2.sts @@ -0,0 +1,46 @@ +/* + * 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 { + data: number = 0; +} + +class D extends B {} + +class A { + d:D; + data:number = 0; + constructor() { + this.d = new D(); + } + + foo(f: (this:B) => void) { + this.d.f(); + } + + foo2(f: (this:B) => void) { + f(this.d); + } + +} + +let a = new A(); +a.foo() { this.data = 666 } +assertTrue(a.d.data == 666); +assertTrue(a.data == 0); + +a.foo2() { this.data = 66 } +assertTrue(a.d.data == 66); +assertTrue(a.data == 0); diff --git a/ets2panda/test/runtime/ets/lambda_with_receiver/lambda_with_receiver_trailing_in_class_method_return_this_rotate.sts b/ets2panda/test/runtime/ets/lambda_with_receiver/lambda_with_receiver_trailing_in_class_method_return_this_rotate.sts new file mode 100644 index 0000000000000000000000000000000000000000..6c5bf27f823534c3afeac5105f79a9d4f37f71ce --- /dev/null +++ b/ets2panda/test/runtime/ets/lambda_with_receiver/lambda_with_receiver_trailing_in_class_method_return_this_rotate.sts @@ -0,0 +1,62 @@ +/* + * 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: number = 0; + data: number = 0; + setX(n: number): this { + this.X = n; + return this; + } +} + +class D extends B { + Y: number = 0; + setY(n: number): this { + this.Y = n; + return this; + } +} + +class A { + d:D; + data:number = 0; + constructor() { + this.d = new D(); + } + + foo(f: (this:B) => this) { + this.d.setX(1).f().setY(2); + assertTrue(this.d.X == 1); + assertTrue(this.d.Y == 2); + } + + foo2(f: (this:B) => this) { + f(this.d.setX(1)).setY(2); + assertTrue(this.d.X == 1); + assertTrue(this.d.Y == 2); + } + +} + +let a = new A(); +a.foo() { this.data = 666; return this; } +assertTrue(a.d.data == 666); +assertTrue(a.data == 0); + +a.foo2() { this.data = 66; return this; } +assertTrue(a.d.data == 66); +assertTrue(a.data == 0); + diff --git a/ets2panda/test/runtime/ets/lambda_with_receiver/lambda_with_receiver_trailing_in_function.sts b/ets2panda/test/runtime/ets/lambda_with_receiver/lambda_with_receiver_trailing_in_function.sts new file mode 100644 index 0000000000000000000000000000000000000000..15221dd4577d7a9acdde34dc5788280b566d9feb --- /dev/null +++ b/ets2panda/test/runtime/ets/lambda_with_receiver/lambda_with_receiver_trailing_in_function.sts @@ -0,0 +1,67 @@ +/* + * 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 { + data: number = 0; +} + +class A { + b:B; + data: number = 0; + constructor() { + this.b = new B(); + } +} + +function foo(this: A, f: (this:B) => void) { + this.b.f(); +} + +function foo2(this: A, f: (this:B) => void) { + f(this.b); +} + +function goo(n:number, f: (this:B) => void) { + let b = new B(); + b.f(); + assertTrue(b.data == n); +} + +function goo2(n:number, f: (this:B) => void) { + let b = new B(); + f(b); + assertTrue(b.data == n); +} + +let a = new A(); +// function with receiver test; +a.foo() { this.data = 666; } +assertTrue(a.b.data == 666); +assertTrue(a.data == 0); +foo(a) { this.data = 66; } +assertTrue(a.b.data == 66); +assertTrue(a.data == 0); + +a.foo2() { this.data = 66; } +assertTrue(a.b.data == 66); +assertTrue(a.data == 0); +foo2(a) { this.data = 666; } +assertTrue(a.b.data == 666); +assertTrue(a.data == 0); + +// normal function test; +let n: number = 66; +goo(n) { this.data = n; } +goo2(n) { this.data = n; } diff --git a/ets2panda/test/runtime/ets/lambda_with_receiver/lambda_with_receiver_trailing_in_function_return_this_rotate.sts b/ets2panda/test/runtime/ets/lambda_with_receiver/lambda_with_receiver_trailing_in_function_return_this_rotate.sts new file mode 100644 index 0000000000000000000000000000000000000000..a4cdd6743b7992f46373db4d3b14c5e7253306f6 --- /dev/null +++ b/ets2panda/test/runtime/ets/lambda_with_receiver/lambda_with_receiver_trailing_in_function_return_this_rotate.sts @@ -0,0 +1,88 @@ +/* + * 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: number = 0; + data: number = 0; + setX(n: number): this { + this.X = n; + return this; + } +} + +class D extends B { + Y: number = 0; + setY(n: number): this { + this.Y = n; + return this; + } +} + +class A { + d:D; + data: number = 0; + constructor() { + this.d = new D(); + } +} + +function foo(this: A, f: (this:B) => this) { + this.d.setX(1).f().setY(2); + assertTrue(this.d.X == 1); + assertTrue(this.d.Y == 2); +} + +function foo2(this: A, f: (this:B) => this) { + f(this.d.setX(1)).setY(2); + assertTrue(this.d.X == 1); + assertTrue(this.d.Y == 2); +} + +function goo(n:number, f: (this:B) => this) { + let d = new D(); + d.setX(1).f().setY(2); + assertTrue(d.data == n); + assertTrue(d.X == 1); + assertTrue(d.Y == 2); +} + +function goo2(n:number, f: (this:B) => this) { + let d = new D(); + f(d.setX(1)).setY(2); + assertTrue(d.data == n); + assertTrue(d.X == 1); + assertTrue(d.Y == 2); +} + +let a = new A(); +// function with receiver test; +a.foo() { this.data = 666; return this; } +assertTrue(a.d.data == 666); +assertTrue(a.data == 0); +foo(a) { this.data = 66; return this; } +assertTrue(a.d.data == 66); +assertTrue(a.data == 0); + +a.foo2() { this.data = 66; return this; } +assertTrue(a.d.data == 66); +assertTrue(a.data == 0); +foo2(a) { this.data = 666; return this; } +assertTrue(a.d.data == 666); +assertTrue(a.data == 0); + +// normal function test; +let n: number = 66; +goo(n) { this.data = n; return this; } +goo2(n) { this.data = n; return this; } diff --git a/ets2panda/test/runtime/ets/lambda_with_receiver/lambda_with_receiver_trailing_in_function_with_receiver.sts b/ets2panda/test/runtime/ets/lambda_with_receiver/lambda_with_receiver_trailing_in_function_with_receiver.sts new file mode 100644 index 0000000000000000000000000000000000000000..ffc0ebe06e444fbfbad8c3d735a2bdc12a62ca89 --- /dev/null +++ b/ets2panda/test/runtime/ets/lambda_with_receiver/lambda_with_receiver_trailing_in_function_with_receiver.sts @@ -0,0 +1,53 @@ +/* + * 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 = 0; +} + +class B { + a: A; + data: number = 0; + constructor(){ + this.a = new A(); + } +} + +function foo(this: A, f: (this: A) => void) { + this.f(); +} + +function goo(this: B, f: (this: A) => void) { + this.a.f(); + assertTrue(this.a.data == 555); + + this.a.foo(){this.data = 55;} + assertTrue(this.a.data == 55); + + f(this.a); + assertTrue(this.a.data == 555); + + foo(this.a){this.data = 55;} + assertTrue(this.a.data == 55); +} + +let a = new A(); +a.foo(){this.data = 666} +assertTrue(a.data == 666); +foo(a){this.data = 66} +assertTrue(a.data == 66); + +let b = new B(); +b.goo(){this.data = 555;} diff --git a/ets2panda/test/runtime/ets/lambda_with_receiver/lambda_with_receiver_trailing_name_duplicated.sts b/ets2panda/test/runtime/ets/lambda_with_receiver/lambda_with_receiver_trailing_name_duplicated.sts new file mode 100644 index 0000000000000000000000000000000000000000..c13dc79cb066fb2c7eca8f0123ff399136a03e51 --- /dev/null +++ b/ets2panda/test/runtime/ets/lambda_with_receiver/lambda_with_receiver_trailing_name_duplicated.sts @@ -0,0 +1,81 @@ +/* + * 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 = 0; + foo(n: number, f: (this: A) => void) { + this.f(); + } + + foo(n: number, f: () => void) { + f(); + } +} + +function foo(this: A, f: (this: A) => void) { + this.f(); +} + +function foo(this: A, n: number, n2: number, f: () => void) { + f(); +} + +function foo(this: A, f: (this: A) => void, n: number) { + this.f(); +} + +function foo(a: A, n: number, f: (this: A) => void) { + a.f(); +} + +function foo(a: A, n: number, f: () => void) { + f(); +} + +let a = new A(); +// function with receiver append trailing lambda with receiver. +a.foo(){ this.data = 1; } +assertTrue(a.data == 1); +foo(a) {this.data = 2; } +assertTrue(a.data == 2); + +// function with receiver append trailing lambda. +a.foo(1, 2){ a.data = -1; } +assertTrue(a.data == -1); +foo(a, 1, 2) {a.data = -2; } +assertTrue(a.data == -2); + +// function with receiver contain param as lambda with receiver. +let n: number = 3; +a.foo((this: A):void =>{ this.data = n}, n); +assertTrue(a.data == 3); +foo(a, (this: A):void =>{ this.data = n + 1}, n); +assertTrue(a.data == 4); + +// normal function append trailing lambda with receiver. +foo(a, n + 2){ this.data = n + 2 }; +assertTrue(a.data == 5); + +// method append trailing lambda with receiver. +a.foo(n + 3){ this.data = n + 3 }; +assertTrue(a.data == 6); + +// method append normal trailing lambda. +a.foo(n + 4){a.data = n + 4} +assertTrue(a.data == 7); + +// normal function append normal trailing lambda. +foo(a, n + 5){a.data = n + 5} +assertTrue(a.data == 8);