diff --git a/ets2panda/compiler/lowering/ets/lambdaLowering.cpp b/ets2panda/compiler/lowering/ets/lambdaLowering.cpp index b215a47e80c574e68c599f9d7163f878bb44299e..2da5ecb70cee701333d12fad9e2e5733ad495208 100644 --- a/ets2panda/compiler/lowering/ets/lambdaLowering.cpp +++ b/ets2panda/compiler/lowering/ets/lambdaLowering.cpp @@ -16,6 +16,7 @@ #include "lambdaLowering.h" #include +#include "checker/types/ets/etsTupleType.h" #include "checker/ets/typeRelationContext.h" #include "compiler/lowering/scopesInit/scopesInitPhase.h" #include "compiler/lowering/util.h" @@ -707,10 +708,7 @@ static void CreateInvokeMethodRestParameter(public_lib::Context *ctx, LambdaClas lciInfo->restParameterIdentifier = restIdent->Name(); auto *spread = allocator->New(ir::AstNodeType::REST_ELEMENT, allocator, restIdent); ES2PANDA_ASSERT(spread != nullptr); - auto *restVar = lciInfo->lambdaSignature->RestVar(); - auto *arr = (restVar != nullptr && restVar->TsType()->IsETSTupleType()) - ? restVar->TsType() - : checker->CreateETSArrayType(checker->GlobalETSAnyType()); + auto *arr = checker->CreateETSArrayType(checker->GlobalETSAnyType()); auto *typeAnnotation = allocator->New(arr, allocator); @@ -730,9 +728,28 @@ static ir::Expression *SetRestIdentOfCallArguments(public_lib::Context *ctx, Lam auto *allocator = ctx->allocator; auto restType = lciInfo->lambdaSignature->RestVar()->TsType(); - auto *restIdent = allocator->New( - restType->IsETSTupleType() ? lciInfo->restParameterIdentifier : lciInfo->restArgumentIdentifier, allocator); - if (restType->IsETSArrayType() || restType->IsETSTupleType()) { + if (restType->IsETSTupleType()) { + ArenaVector tupleElements(allocator->Adapter()); + for (std::uint16_t i = 0; i < restType->AsETSTupleType()->GetTupleSize(); ++i) { + auto ident = allocator->New(lciInfo->restParameterIdentifier, allocator); + auto number = allocator->New(lexer::Number(i)); + auto indexed = util::NodeAllocator::ForceSetParent( + allocator, ident, number, ir::MemberExpressionKind::ELEMENT_ACCESS, true, false); + + auto typeNode = + allocator->New(restType->AsETSTupleType()->GetTupleTypesList()[i], allocator); + auto cast = util::NodeAllocator::ForceSetParent(allocator, indexed, typeNode, false); + tupleElements.push_back(cast); + } + auto arrayExpr = + util::NodeAllocator::ForceSetParent(allocator, std::move(tupleElements), allocator); + auto *spread = util::NodeAllocator::ForceSetParent( + allocator, ir::AstNodeType::SPREAD_ELEMENT, allocator, arrayExpr); + return spread; + } + auto *restIdent = + util::NodeAllocator::ForceSetParent(allocator, lciInfo->restArgumentIdentifier, allocator); + if (restType->IsETSArrayType()) { auto *spread = allocator->New(ir::AstNodeType::SPREAD_ELEMENT, allocator, restIdent); restIdent->SetParent(spread); return spread; @@ -1176,14 +1193,6 @@ static ir::ClassDeclaration *CreateLambdaClass(public_lib::Context *ctx, checker lciInfo.arity = signature->ArgCount(); CreateLambdaClassInvokeN(ctx, info, &lciInfo); } - - if (signature->HasRestParameter() && signature->RestVar()->TsType()->IsETSTupleType()) { - auto invokeMethodName = - util::UString {checker->FunctionalInterfaceInvokeName(lciInfo.arity + 1, signature->HasRestParameter()), - ctx->allocator} - .View(); - CreateLambdaClassInvokeMethod(ctx, info, &lciInfo, invokeMethodName, true); - } CreateLambdaClassInvokeMethod(ctx, info, &lciInfo, compiler::Signatures::LAMBDA_OBJECT_INVOKE, false); InitScopesPhaseETS::RunExternalNode(classDeclaration, varBinder); @@ -1463,6 +1472,54 @@ static bool IsTypeErrorCall(ir::CallExpression const *node) return callee->TsType()->IsTypeError(); } +static ir::AstNode *TransformTupleSpread(public_lib::Context *ctx, ir::CallExpression *call) +{ + auto *allocator = ctx->allocator; + auto *checker = ctx->GetChecker()->AsETSChecker(); + ArenaVector newArgs(allocator->Adapter()); + bool modified = false; + + for (auto *arg : call->Arguments()) { + if (!arg->IsSpreadElement() || !arg->TsType()->IsETSTupleType()) { + newArgs.push_back(arg); + continue; + } + modified = true; + + std::stringstream ss; + auto *genSymIdent = Gensym(allocator); + ss << "let @@I1: @@T2 = @@E3;"; + ss << "@@E4 as FixedArray"; + + ArenaVector tupleElements(allocator->Adapter()); + for (std::size_t idx = 0U; idx < arg->TsType()->AsETSTupleType()->GetTupleSize(); ++idx) { + auto *ident = genSymIdent->Clone(allocator, nullptr); + auto *number = allocator->New(lexer::Number(idx)); + auto *indexed = util::NodeAllocator::ForceSetParent( + allocator, ident, number, ir::MemberExpressionKind::ELEMENT_ACCESS, true, false); + tupleElements.push_back(indexed); + } + auto arrayExpr = + util::NodeAllocator::ForceSetParent(allocator, std::move(tupleElements), allocator); + + auto typeNode = util::NodeAllocator::ForceSetParent(allocator, arg->TsType(), allocator); + + auto *blockExpression = ctx->parser->AsETSParser()->CreateFormattedExpression( + ss.str(), genSymIdent, typeNode, arg->AsSpreadElement()->Argument()->Clone(allocator, nullptr), arrayExpr); + + auto *spreadElement = util::NodeAllocator::ForceSetParent( + allocator, ir::AstNodeType::SPREAD_ELEMENT, allocator, blockExpression); + newArgs.push_back(spreadElement); + spreadElement->SetParent(call); + + CheckLoweredNode(checker->VarBinder()->AsETSBinder(), checker, spreadElement); + } + if (modified) { + call->Arguments() = std::move(newArgs); + } + return call; +} + // CC-OFFNXT(G.FUN.01, huge_method, huge_method[C++]) solid logic static ir::AstNode *InsertInvokeCall(public_lib::Context *ctx, ir::CallExpression *call) { @@ -1518,7 +1575,7 @@ static ir::AstNode *InsertInvokeCall(public_lib::Context *ctx, ir::CallExpressio CheckLoweredNode(varBinder, checker, arg); } - return call; + return TransformTupleSpread(ctx, call); } static bool IsRedirectingConstructorCall(ir::CallExpression *expr) diff --git a/ets2panda/test/runtime/ets/lambdaSpreadTupleParam.ets b/ets2panda/test/runtime/ets/lambdaSpreadTupleParam.ets new file mode 100644 index 0000000000000000000000000000000000000000..a312ea3c31f02df113d22eb462e375960c4bd3b2 --- /dev/null +++ b/ets2panda/test/runtime/ets/lambdaSpreadTupleParam.ets @@ -0,0 +1,20 @@ +/* + * 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. + */ + +function test() { + let tuple: [string] = ["str"]; + let f = (...p: [string]) => {} + f(...tuple) +}