From 389ab8d36fff87059378839e4c7db28b308c6c16 Mon Sep 17 00:00:00 2001 From: daizihan Date: Sun, 29 Jun 2025 22:44:57 +0800 Subject: [PATCH] Extend the support for spread expression Reason: Support foo(1,2,...[3,4], 5); Previously, we only support foo(...[3,4]) Description: 1. move convertrestarguments from etscompiler to restargslowering and lambdalowering 2. enable restargslowering to process fixedarray 3. reorder and decoulping some lowering phases in order to support such feature Issue: https://gitee.com/openharmony/arkcompiler_ets_frontend/issues/ICIRSX?from=project-issue Signed-off-by: daizihan --- ets2panda/checker/ets/function.cpp | 10 +- ets2panda/compiler/core/ETSCompiler.cpp | 65 ------------ .../lowering/ets/arrayLiteralLowering.cpp | 2 +- .../compiler/lowering/ets/lambdaLowering.cpp | 23 ++++- .../lowering/ets/restArgsLowering.cpp | 98 ++++++++----------- .../compiler/lowering/ets/spreadLowering.cpp | 59 +++++++---- ets2panda/compiler/lowering/phase.cpp | 6 +- ..._mod.sts => smart_cast_while_test_mod.ets} | 0 .../test/ast/compiler/ets/spread_in_rest.ets | 26 +++++ .../test/ast/compiler/ets/spread_in_rest1.ets | 22 +++++ .../ets/spread_in_rest_fixedArray.ets | 26 +++++ .../ets/FixedArray/trailing_comma_1.ets | 4 +- .../test/ast/parser/ets/trailing_comma_1.ets | 6 +- ets2panda/test/runtime/ets/lambda_spread.ets | 42 ++++++++ .../test/runtime/ets/lambda_spread_fixed.ets | 42 ++++++++ ets2panda/test/runtime/ets/spread_in_rest.ets | 30 ++++++ .../runtime/ets/spread_in_rest_fixedArray.ets | 30 ++++++ ets2panda/util/diagnostic/semantic.yaml | 4 - 18 files changed, 329 insertions(+), 166 deletions(-) rename ets2panda/test/ast/compiler/ets/{smart_cast_while_test_mod.sts => smart_cast_while_test_mod.ets} (100%) create mode 100644 ets2panda/test/ast/compiler/ets/spread_in_rest.ets create mode 100644 ets2panda/test/ast/compiler/ets/spread_in_rest1.ets create mode 100644 ets2panda/test/ast/compiler/ets/spread_in_rest_fixedArray.ets create mode 100644 ets2panda/test/runtime/ets/lambda_spread.ets create mode 100644 ets2panda/test/runtime/ets/lambda_spread_fixed.ets create mode 100644 ets2panda/test/runtime/ets/spread_in_rest.ets create mode 100644 ets2panda/test/runtime/ets/spread_in_rest_fixedArray.ets diff --git a/ets2panda/checker/ets/function.cpp b/ets2panda/checker/ets/function.cpp index c31ff61607..9ff06d80c9 100644 --- a/ets2panda/checker/ets/function.cpp +++ b/ets2panda/checker/ets/function.cpp @@ -571,12 +571,11 @@ bool ETSChecker::SetPreferredTypeForArrayArgument(ir::ArrayExpression *arrayExpr } bool ETSChecker::ValidateSignatureRestParams(Signature *substitutedSig, const ArenaVector &arguments, - TypeRelationFlag flags, bool reportError, + TypeRelationFlag flags, [[maybe_unused]] bool reportError, [[maybe_unused]] const bool unique) { size_t const argumentCount = arguments.size(); auto const commonArity = std::min(substitutedSig->ArgCount(), argumentCount); - auto const restCount = argumentCount - commonArity; if (argumentCount == commonArity && substitutedSig->RestVar()->TsType()->IsETSTupleType()) { return false; @@ -591,13 +590,6 @@ bool ETSChecker::ValidateSignatureRestParams(Signature *substitutedSig, const Ar continue; } - if (restCount > 1U) { - if (reportError) { - LogError(diagnostic::MULTIPLE_SPREADS, {}, argument->Start()); - } - return false; - } - auto *const restArgument = argument->AsSpreadElement()->Argument(); Type *targetType = substitutedSig->RestVar()->TsType(); // backing out of check that results in a signature mismatch would be difficult diff --git a/ets2panda/compiler/core/ETSCompiler.cpp b/ets2panda/compiler/core/ETSCompiler.cpp index 8992e44632..15719bc7db 100644 --- a/ets2panda/compiler/core/ETSCompiler.cpp +++ b/ets2panda/compiler/core/ETSCompiler.cpp @@ -155,35 +155,6 @@ void ETSCompiler::Compile(const ir::ETSNewArrayInstanceExpression *expr) const ES2PANDA_ASSERT(etsg->Checker()->Relation()->IsIdenticalTo(etsg->GetAccumulatorType(), expr->TsType())); } -static void ConvertRestArguments(checker::ETSChecker *const checker, const ir::ETSNewClassInstanceExpression *expr) -{ - if (expr->GetSignature()->RestVar() != nullptr && (expr->GetSignature()->RestVar()->TsType()->IsETSArrayType() || - expr->GetSignature()->RestVar()->TsType()->IsETSTupleType())) { - std::size_t const argumentCount = expr->GetArguments().size(); - std::size_t const parameterCount = expr->GetSignature()->Params().size(); - ES2PANDA_ASSERT(argumentCount >= parameterCount); - - auto &arguments = const_cast &>(expr->GetArguments()); - std::size_t i = parameterCount; - - if (i < argumentCount && expr->GetArguments()[i]->IsSpreadElement()) { - arguments[i] = expr->GetArguments()[i]->AsSpreadElement()->Argument(); - } else if (!expr->GetSignature()->RestVar()->TsType()->IsETSTupleType()) { - ArenaVector elements(checker->Allocator()->Adapter()); - for (; i < argumentCount; ++i) { - elements.emplace_back(expr->GetArguments()[i]); - } - auto *arrayExpression = checker->AllocNode(std::move(elements), checker->Allocator()); - arrayExpression->SetParent(const_cast(expr)); - auto restType = expr->GetSignature()->RestVar()->TsType()->AsETSArrayType(); - arrayExpression->SetTsType(restType); - arrayExpression->SetPreferredType(restType->ElementType()); - arguments.erase(expr->GetArguments().begin() + parameterCount, expr->GetArguments().end()); - arguments.emplace_back(arrayExpression); - } - } -} - static void HandleUnionTypeInForOf(compiler::ETSGen *etsg, checker::Type const *const exprType, const ir::ForOfStatement *st, VReg objReg, VReg *countReg) { @@ -256,7 +227,6 @@ void ETSCompiler::Compile(const ir::ETSNewClassInstanceExpression *expr) const etsg->StoreAccumulator(expr->GetTypeRef(), objReg); etsg->CallAnyNew(expr, expr->GetArguments(), objReg); } else { - ConvertRestArguments(const_cast(etsg->Checker()->AsETSChecker()), expr); etsg->InitObject(expr, expr->signature_, expr->GetArguments()); } etsg->SetAccumulatorType(expr->TsType()); @@ -625,39 +595,6 @@ void ETSCompiler::Compile(const ir::BinaryExpression *expr) const ES2PANDA_ASSERT(etsg->Checker()->Relation()->IsIdenticalTo(etsg->GetAccumulatorType(), expr->TsType())); } -static void ConvertRestArguments(checker::ETSChecker *const checker, const ir::CallExpression *expr, - checker::Signature *signature) -{ - if (signature->RestVar() != nullptr && - (signature->RestVar()->TsType()->IsETSArrayType() || signature->RestVar()->TsType()->IsETSTupleType())) { - std::size_t const argumentCount = expr->Arguments().size(); - std::size_t const parameterCount = signature->Params().size(); - ES2PANDA_ASSERT(argumentCount >= parameterCount); - - auto &arguments = const_cast &>(expr->Arguments()); - std::size_t i = parameterCount; - - if (i < argumentCount && expr->Arguments()[i]->IsSpreadElement()) { - arguments[i] = expr->Arguments()[i]->AsSpreadElement()->Argument(); - } else if (i < argumentCount && expr->Arguments()[i]->IsTSAsExpression() && - expr->Arguments()[i]->AsTSAsExpression()->Expr()->Type() == ir::AstNodeType::SPREAD_ELEMENT) { - arguments[i] = expr->Arguments()[i]->AsTSAsExpression()->Expr()->AsSpreadElement()->Argument(); - } else if (!signature->RestVar()->TsType()->IsETSTupleType()) { - ArenaVector elements(checker->Allocator()->Adapter()); - for (; i < argumentCount; ++i) { - elements.emplace_back(expr->Arguments()[i]); - } - auto *arrayExpression = checker->AllocNode(std::move(elements), checker->Allocator()); - arrayExpression->SetParent(const_cast(expr)); - auto restType = signature->RestVar()->TsType()->AsETSArrayType(); - arrayExpression->SetTsType(restType); - arrayExpression->SetPreferredType(restType->ElementType()); - arguments.erase(expr->Arguments().begin() + parameterCount, expr->Arguments().end()); - arguments.emplace_back(arrayExpression); - } - } -} - void ETSCompiler::Compile(const ir::BlockExpression *expr) const { ETSGen *etsg = GetETSGen(); @@ -760,8 +697,6 @@ void ETSCompiler::Compile(const ir::CallExpression *expr) const bool const isStatic = signature->HasSignatureFlag(checker::SignatureFlags::STATIC); - ConvertRestArguments(const_cast(etsg->Checker()->AsETSChecker()), expr, signature); - if (expr->IsDynamicCall()) { CompileAny(expr, callee, calleeReg); } else if (callee->IsIdentifier()) { diff --git a/ets2panda/compiler/lowering/ets/arrayLiteralLowering.cpp b/ets2panda/compiler/lowering/ets/arrayLiteralLowering.cpp index 42a8564a0f..0178c01f8e 100644 --- a/ets2panda/compiler/lowering/ets/arrayLiteralLowering.cpp +++ b/ets2panda/compiler/lowering/ets/arrayLiteralLowering.cpp @@ -93,7 +93,7 @@ ir::AstNode *ArrayLiteralLowering::TryTransformLiteralArrayToRefArray(ir::ArrayE // ss << "Array.from<@@T4>(@@I5);"; ss << "let @@I1 : FixedArray<@@T2> = @@E3;"; ss << "let @@I4 : Array<@@T5> = new Array<@@T6>(@@I7.length);"; - ss << "for (let i = 0; i < @@I8.length; ++i) { @@I9[i] = @@I10[i]}"; + ss << "for (let i = 0; i < @@I8.length; i = i + 1) { @@I9[i] = @@I10[i]}"; ss << "@@I11;"; newStmts.emplace_back(genSymIdent); newStmts.emplace_back(type); diff --git a/ets2panda/compiler/lowering/ets/lambdaLowering.cpp b/ets2panda/compiler/lowering/ets/lambdaLowering.cpp index 17cfcc6a9a..a255fa784d 100644 --- a/ets2panda/compiler/lowering/ets/lambdaLowering.cpp +++ b/ets2panda/compiler/lowering/ets/lambdaLowering.cpp @@ -639,12 +639,12 @@ static ArenaVector CreateCallArgumentsForLambdaClassInvoke(pub 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()) { auto *spread = allocator->New(ir::AstNodeType::SPREAD_ELEMENT, allocator, restIdent); restIdent->SetParent(spread); callArguments.push_back(spread); } else { - ES2PANDA_ASSERT(restType->IsETSResizableArrayType()); + ES2PANDA_ASSERT(restType->IsETSResizableArrayType() || restType->IsETSArrayType()); restIdent->AddAstNodeFlags(ir::AstNodeFlags::RESIZABLE_REST); callArguments.push_back(restIdent); } @@ -1222,7 +1222,26 @@ static ir::AstNode *InsertInvokeCall(public_lib::Context *ctx, ir::CallExpressio } Recheck(ctx->phaseManager, varBinder, checker, arg); } + if (hasRestParam) { + auto &arguments = call->Arguments(); + std::size_t const argumentCount = arguments.size(); + std::size_t const parameterCount = callSig->Params().size(); + ES2PANDA_ASSERT(argumentCount >= parameterCount); + + std::size_t i = parameterCount; + ArenaVector elements(checker->Allocator()->Adapter()); + for (; i < argumentCount; ++i) { + elements.emplace_back(arguments[i]); + } + auto *arrayExpression = checker->AllocNode(std::move(elements), checker->Allocator()); + arrayExpression->SetParent(call); + // Since the rest param part in invokeRk is Any type, here just force set array to Any type. + arrayExpression->SetTsType(checker->CreateETSArrayType(checker->GlobalETSAnyType())); + arrayExpression->SetPreferredType(checker->GlobalETSAnyType()); + arguments.erase(arguments.begin() + parameterCount, arguments.end()); + arguments.emplace_back(arrayExpression); + } return call; } diff --git a/ets2panda/compiler/lowering/ets/restArgsLowering.cpp b/ets2panda/compiler/lowering/ets/restArgsLowering.cpp index 9fe4183e9b..65f96a1088 100644 --- a/ets2panda/compiler/lowering/ets/restArgsLowering.cpp +++ b/ets2panda/compiler/lowering/ets/restArgsLowering.cpp @@ -16,7 +16,9 @@ #include "restArgsLowering.h" #include "compiler/lowering/util.h" #include "ir/astNode.h" +#include "ir/expression.h" #include "ir/expressions/arrayExpression.h" +#include "util/es2pandaMacros.h" #include #include @@ -25,79 +27,61 @@ namespace ark::es2panda::compiler { using AstNodePtr = ir::AstNode *; -static ir::BlockExpression *CreateRestArgsBlockExpression(public_lib::Context *context, - ir::SpreadElement *spreadElement) +static bool ShouldProcessRestParameters(checker::Signature *signature, const ArenaVector &arguments) { - auto *allocator = context->allocator; - auto *parser = context->parser->AsETSParser(); - auto *checker = context->GetChecker()->AsETSChecker(); - - ArenaVector blockStatements(allocator->Adapter()); - const auto arraySymbol = Gensym(allocator); - const auto argumentSymbol = Gensym(allocator); - const auto iteratorIndex = Gensym(allocator); - const auto iteratorSymbol = Gensym(allocator); - const auto elementType = checker->GetElementTypeOfArray(spreadElement->Argument()->TsType()); - auto *typeNode = allocator->New(elementType, allocator); - blockStatements.push_back( - parser->CreateFormattedStatement("let @@I1 = @@E2;", argumentSymbol, spreadElement->Argument())); - blockStatements.push_back(parser->CreateFormattedStatement("let @@I1 = 0;", iteratorIndex)); - blockStatements.push_back(parser->CreateFormattedStatement("let @@I1 = new Array<@@T2>(@@I3.length);", arraySymbol, - typeNode, argumentSymbol->Clone(allocator, nullptr))); - std::vector args; - std::stringstream ss; - ss << "for (let @@I1 of @@I2){"; - args.emplace_back(iteratorSymbol); - args.emplace_back(argumentSymbol->Clone(allocator, nullptr)); - ss << "@@I3[@@I4] = @@I5;"; - args.emplace_back(arraySymbol->Clone(allocator, nullptr)); - args.emplace_back(iteratorIndex->Clone(allocator, nullptr)); - args.emplace_back(iteratorSymbol->Clone(allocator, nullptr)); - ss << "@@I6 = @@I7 + 1;"; - args.emplace_back(iteratorIndex->Clone(allocator, nullptr)); - args.emplace_back(iteratorIndex->Clone(allocator, nullptr)); - ss << "}"; - ir::Statement *loopStatement = parser->CreateFormattedStatement(ss.str(), args); - - blockStatements.push_back(loopStatement); - blockStatements.push_back(parser->CreateFormattedStatement("@@I1", arraySymbol->Clone(allocator, nullptr))); - auto *blockExpr = util::NodeAllocator::ForceSetParent(allocator, std::move(blockStatements)); - return blockExpr; + return signature != nullptr && signature->HasRestParameter() && arguments.size() >= signature->Params().size() && + !signature->RestVar()->TsType()->IsETSTupleType(); } -static ir::BlockExpression *ConvertSpreadToBlockExpression(public_lib::Context *context, - ir::SpreadElement *spreadElement) +static ir::Expression *CreateRestArgsFixedArray(public_lib::Context *context, ArenaVector &arguments, + checker::Signature *signature, ir::Expression *parent) { - auto *blockExpression = CreateRestArgsBlockExpression(context, spreadElement); - blockExpression->SetParent(spreadElement->Parent()); - blockExpression->SetRange(spreadElement->Range()); + auto *allocator = context->allocator; + auto *parser = context->parser->AsETSParser(); + auto *checker = context->GetChecker()->AsETSChecker(); + std::size_t const argumentCount = arguments.size(); + std::size_t const parameterCount = signature->Params().size(); + const size_t extraArgs = argumentCount - parameterCount; + ES2PANDA_ASSERT(argumentCount >= parameterCount); - for (auto *statement : blockExpression->Statements()) { - SetSourceRangesRecursively(statement, spreadElement->Range()); + auto *arrayIdent = Gensym(allocator); + checker::Type *arrayType = signature->RestVar()->TsType(); + auto *type = checker->AllocNode( + checker->GetApparentType(checker->GetElementTypeOfArray(arrayType)), allocator); + if (extraArgs == 0) { + auto expr = parser->CreateFormattedExpression("let @@I1 : FixedArray<@@T2> = []; @@I3", arrayIdent, type, + arrayIdent->Clone(allocator, nullptr)); + expr->SetParent(parent); + return expr; } - return blockExpression; -} + ArenaVector elements(checker->Allocator()->Adapter()); -static bool ShouldProcessRestParameters(checker::Signature *signature, const ArenaVector &arguments) -{ - return signature != nullptr && signature->HasRestParameter() && !signature->RestVar()->TsType()->IsETSArrayType() && - arguments.size() >= signature->Params().size() && !signature->RestVar()->TsType()->IsETSTupleType(); + for (std::size_t i = parameterCount; i < argumentCount; ++i) { + elements.emplace_back(arguments[i]); + } + auto *arrayExpression = checker->AllocNode(std::move(elements), checker->Allocator()); + auto loweringResult = + parser->CreateFormattedExpression("let @@I1 : FixedArray<@@T2> = @@E3; @@I4", arrayIdent, type, arrayExpression, + arrayIdent->Clone(allocator, nullptr)); + loweringResult->SetParent(parent); + return loweringResult; } static ir::Expression *CreateRestArgsArray(public_lib::Context *context, ArenaVector &arguments, - checker::Signature *signature) + checker::Signature *signature, ir::Expression *parent) { + checker::Type *arrayType = signature->RestVar()->TsType(); + if (arrayType->IsETSArrayType()) { + return CreateRestArgsFixedArray(context, arguments, signature, parent); + } + ES2PANDA_ASSERT(arrayType->IsETSResizableArrayType()); auto *allocator = context->allocator; auto *parser = context->parser->AsETSParser(); auto *checker = context->GetChecker()->AsETSChecker(); // Handle single spread element case const size_t extraArgs = arguments.size() - signature->Params().size(); - if (extraArgs == 1 && arguments.back()->IsSpreadElement()) { - return ConvertSpreadToBlockExpression(context, arguments.back()->AsSpreadElement()); - } // Determine array type - checker::Type *arrayType = signature->RestVar()->TsType(); auto *type = checker->AllocNode(checker->GetElementTypeOfArray(arrayType), allocator); if (extraArgs == 0) { return parser->CreateFormattedExpression("new Array<@@T1>(0)", type); @@ -190,7 +174,7 @@ ir::ETSNewClassInstanceExpression *RestArgsLowering::TransformCallConstructWithR return expr; } - auto *restArgsArray = CreateRestArgsArray(context, expr->GetArguments(), signature); + auto *restArgsArray = CreateRestArgsArray(context, expr->GetArguments(), signature, expr); restArgsArray->AddAstNodeFlags(ir::AstNodeFlags::RESIZABLE_REST); return RebuildNewClassInstanceExpression(context, expr, signature, restArgsArray); @@ -211,7 +195,7 @@ ir::CallExpression *RestArgsLowering::TransformCallExpressionWithRestArgs(ir::Ca return callExpr; } - auto *restArgsArray = CreateRestArgsArray(context, callExpr->Arguments(), signature); + auto *restArgsArray = CreateRestArgsArray(context, callExpr->Arguments(), signature, callExpr); restArgsArray->AddAstNodeFlags(ir::AstNodeFlags::RESIZABLE_REST); return RebuildCallExpression(context, callExpr, signature, restArgsArray); diff --git a/ets2panda/compiler/lowering/ets/spreadLowering.cpp b/ets2panda/compiler/lowering/ets/spreadLowering.cpp index bf6b4e6c2e..11cabd004b 100644 --- a/ets2panda/compiler/lowering/ets/spreadLowering.cpp +++ b/ets2panda/compiler/lowering/ets/spreadLowering.cpp @@ -14,9 +14,13 @@ */ #include "spreadLowering.h" +#include +#include #include "checker/ETSchecker.h" #include "checker/types/ets/etsTupleType.h" #include "compiler/lowering/util.h" +#include "ir/astNode.h" +#include "utils/arena_containers.h" namespace ark::es2panda::compiler { @@ -39,15 +43,33 @@ void CreateSpreadArrayDeclareStatements(public_lib::Context *ctx, ir::ArrayExpre auto *const allocator = ctx->allocator; auto *const parser = ctx->parser->AsETSParser(); for (auto element : array->Elements()) { - if (element->Type() != ir::AstNodeType::SPREAD_ELEMENT) { + if (!element->IsSpreadElement()) { continue; } + std::vector arguments; ir::Identifier *const arrIdent = Gensym(allocator); auto *const spreadArgument = element->AsSpreadElement()->Argument(); auto *const initExpr = spreadArgument->Clone(allocator, nullptr); + auto *const spreadType = spreadArgument->TsType(); SetPossibleTupleType(arrIdent, spreadArgument); - spreadArrayIds.emplace_back(arrIdent); - statements.emplace_back(parser->CreateFormattedStatement("let @@I1 = (@@E2);", arrIdent, initExpr)); + std::stringstream ss; + ss << "let @@I1"; + arguments.emplace_back(arrIdent); + if (spreadType->IsETSArrayType()) { + auto typeNode = allocator->New(spreadType->AsETSArrayType()->ElementType(), allocator); + ss << " : FixedArray<@@T2> = (@@E3)"; + arguments.emplace_back(typeNode); + } else if (spreadType->IsETSResizableArrayType()) { + auto typeNode = + allocator->New(spreadType->AsETSResizableArrayType()->ElementType(), allocator); + ss << " : Array<@@T2> = (@@E3)"; + arguments.emplace_back(typeNode); + } else { + ss << " = (@@E2)"; + } + arguments.emplace_back(initExpr); + spreadArrayIds.emplace_back(arrIdent->Clone(allocator, nullptr)); + statements.emplace_back(parser->CreateFormattedStatement(ss.str(), arguments)); } } @@ -183,24 +205,25 @@ static ir::Identifier *CreateNewTupleDeclareStatement(public_lib::Context *ctx, static ir::Statement *CreateElementsAssignStatementBySpreadArr(public_lib::Context *ctx, ir::Identifier *spId, std::vector &newArrayAndIndex, - ir::Identifier *spreadArrIterator, checker::Type *arrayElementType) { auto *const allocator = ctx->allocator; auto *const parser = ctx->parser->AsETSParser(); auto *const newArrayId = newArrayAndIndex[0]; auto *const newArrayIndexId = newArrayAndIndex[1]; + auto loopIdx = Gensym(allocator); std::stringstream elementsAssignStr; - elementsAssignStr << "for (let @@I1 of @@I2) {"; - elementsAssignStr << "@@I3[@@I4] = @@I5 as @@T6;"; - elementsAssignStr << "@@I7++;"; + elementsAssignStr << "for (let @@I1 = 0; @@I2 < @@I3.length; @@I4 = @@I5 + 1) {"; + elementsAssignStr << "@@I6[@@I7] = @@I8[@@I9] as @@T10;"; + elementsAssignStr << "@@I11 = @@I12 + 1;"; elementsAssignStr << "}"; ir::Statement *elementsAssignStatement = parser->CreateFormattedStatement( - elementsAssignStr.str(), spreadArrIterator->Clone(allocator, nullptr), spId->Clone(allocator, nullptr), - newArrayId->Clone(allocator, nullptr), newArrayIndexId->Clone(allocator, nullptr), - spreadArrIterator->Clone(allocator, nullptr), arrayElementType, newArrayIndexId->Clone(allocator, nullptr)); + elementsAssignStr.str(), loopIdx, loopIdx->Clone(allocator, nullptr), spId->Clone(allocator, nullptr), + loopIdx->Clone(allocator, nullptr), loopIdx->Clone(allocator, nullptr), newArrayId->Clone(allocator, nullptr), + newArrayIndexId->Clone(allocator, nullptr), spId->Clone(allocator, nullptr), loopIdx->Clone(allocator, nullptr), + arrayElementType, newArrayIndexId->Clone(allocator, nullptr), newArrayIndexId->Clone(allocator, nullptr)); return elementsAssignStatement; } @@ -214,11 +237,12 @@ static ir::Statement *CreateElementsAssignStatementBySingle(public_lib::Context auto *const newArrayIndexId = newArrayAndIndex[1]; std::stringstream elementsAssignStr; elementsAssignStr << "@@I1[@@I2] = (@@E3);"; - elementsAssignStr << "@@I4++;"; + elementsAssignStr << "@@I4 = @@I5 + 1;"; ir::Statement *elementsAssignStatement = parser->CreateFormattedStatement( elementsAssignStr.str(), newArrayId->Clone(allocator, nullptr), newArrayIndexId->Clone(allocator, nullptr), - element->Clone(allocator, nullptr), newArrayIndexId->Clone(allocator, nullptr)); + element->Clone(allocator, nullptr), newArrayIndexId->Clone(allocator, nullptr), + newArrayIndexId->Clone(allocator, nullptr)); return elementsAssignStatement; } @@ -239,11 +263,11 @@ static std::vector CreateElementsAssignForTupleElements(public_ std::stringstream tupleAssignmentsStr {}; auto *elementType = spreadType->GetTupleTypesList()[idx]; tupleAssignmentsStr << "@@I1[@@I2] = (@@I3[" << idx << "] as @@T4);"; - tupleAssignmentsStr << "@@I5++;"; + tupleAssignmentsStr << "@@I5 = @@I6 + 1;"; tupleAssignmentStatements.emplace_back(parser->CreateFormattedStatement( tupleAssignmentsStr.str(), newArrayId->Clone(allocator, nullptr), newArrayIndexId->Clone(allocator, nullptr), spId->Clone(allocator, nullptr), elementType, - newArrayIndexId->Clone(allocator, nullptr))); + newArrayIndexId->Clone(allocator, nullptr), newArrayIndexId->Clone(allocator, nullptr))); } return tupleAssignmentStatements; @@ -286,11 +310,10 @@ static void CreateNewArrayElementsAssignStatement(public_lib::Context *ctx, ir:: statements.insert(statements.cend(), newTupleAssignmentStatements.cbegin(), newTupleAssignmentStatements.cend()); } else { - ir::Identifier *spreadArrIterator = Gensym(allocator); checker::Type *arrayElementType = ctx->GetChecker()->AsETSChecker()->GetElementTypeOfArray(array->TsType()); - statements.emplace_back(CreateElementsAssignStatementBySpreadArr( - ctx, spArrIds[spArrIdx++], newArrayAndIndex, spreadArrIterator, arrayElementType)); + statements.emplace_back(CreateElementsAssignStatementBySpreadArr(ctx, spArrIds[spArrIdx++], + newArrayAndIndex, arrayElementType)); } } else { statements.emplace_back(CreateElementsAssignStatementBySingle(ctx, element, newArrayAndIndex)); @@ -365,7 +388,7 @@ bool SpreadConstructionPhase::PerformForModule(public_lib::Context *ctx, parser: [&checker, &varbinder, &ctx](ir::AstNode *const node) -> AstNodePtr { if (node->IsArrayExpression() && std::any_of(node->AsArrayExpression()->Elements().begin(), node->AsArrayExpression()->Elements().end(), - [](const auto *param) { return param->Type() == ir::AstNodeType::SPREAD_ELEMENT; })) { + [](const auto *param) { return param->IsSpreadElement(); })) { auto scopeCtx = varbinder::LexicalScope::Enter(checker->VarBinder(), NearestScope(node)); diff --git a/ets2panda/compiler/lowering/phase.cpp b/ets2panda/compiler/lowering/phase.cpp index 800aef488d..407994a3ea 100644 --- a/ets2panda/compiler/lowering/phase.cpp +++ b/ets2panda/compiler/lowering/phase.cpp @@ -138,18 +138,18 @@ std::vector GetETSPhaseList() new DeclareOverloadLowering, new OverloadMappingLowering, new EnumPostCheckLoweringPhase, - new SpreadConstructionPhase, new RestArgsLowering, - new ArrayLiteralLowering, new BigIntLowering, new OpAssignmentLowering, new LateInitializationConvert, new ExtensionAccessorPhase, new BoxingForLocals, new RecordLowering, - new ObjectIndexLowering, new ObjectIteratorLowering, new LambdaConversionPhase, + new SpreadConstructionPhase, + new ArrayLiteralLowering, + new ObjectIndexLowering, new UnionLowering, new ExpandBracketsPhase, new LocalClassConstructionPhase, diff --git a/ets2panda/test/ast/compiler/ets/smart_cast_while_test_mod.sts b/ets2panda/test/ast/compiler/ets/smart_cast_while_test_mod.ets similarity index 100% rename from ets2panda/test/ast/compiler/ets/smart_cast_while_test_mod.sts rename to ets2panda/test/ast/compiler/ets/smart_cast_while_test_mod.ets diff --git a/ets2panda/test/ast/compiler/ets/spread_in_rest.ets b/ets2panda/test/ast/compiler/ets/spread_in_rest.ets new file mode 100644 index 0000000000..30a59bd337 --- /dev/null +++ b/ets2panda/test/ast/compiler/ets/spread_in_rest.ets @@ -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. + */ + +function foo(a: int, b:number, ...args: Array) { + console.log(args.length); +} + +function main() { + let a : FixedArray = [1,2]; + /* @@ label */foo(3, /* @@ label1 */...[1,2], 4, 5); +} + +/* @@@ label Error TypeError: No matching call signature for foo(Int, Int, Int, Int) */ +/* @@@ label1 Error TypeError: Spread argument cannot be passed for ordinary parameter. */ diff --git a/ets2panda/test/ast/compiler/ets/spread_in_rest1.ets b/ets2panda/test/ast/compiler/ets/spread_in_rest1.ets new file mode 100644 index 0000000000..e338f982db --- /dev/null +++ b/ets2panda/test/ast/compiler/ets/spread_in_rest1.ets @@ -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. + */ + +// original test code from issue + +declare const o1: undefined | ((...args : int[]) => number); +o1?.(); +o1?.(1); +o1?.(...[1,2]); +o1?.(1, ...[2,3], 4); diff --git a/ets2panda/test/ast/compiler/ets/spread_in_rest_fixedArray.ets b/ets2panda/test/ast/compiler/ets/spread_in_rest_fixedArray.ets new file mode 100644 index 0000000000..679ce1caba --- /dev/null +++ b/ets2panda/test/ast/compiler/ets/spread_in_rest_fixedArray.ets @@ -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. + */ + +function foo(a: int, b:number, ...args: FixedArray) { + console.log(args.length); +} + +function main() { + let a : FixedArray = [1,2]; + /* @@ label */foo(3, /* @@ label1 */...[1,2], 4, 5); +} + +/* @@@ label Error TypeError: No matching call signature for foo(Int, Int, Int, Int) */ +/* @@@ label1 Error TypeError: Spread argument cannot be passed for ordinary parameter. */ diff --git a/ets2panda/test/ast/parser/ets/FixedArray/trailing_comma_1.ets b/ets2panda/test/ast/parser/ets/FixedArray/trailing_comma_1.ets index 0a9abed75e..bfffcda620 100644 --- a/ets2panda/test/ast/parser/ets/FixedArray/trailing_comma_1.ets +++ b/ets2panda/test/ast/parser/ets/FixedArray/trailing_comma_1.ets @@ -44,10 +44,8 @@ foo(,) /* @@? 28:15 Error SyntaxError: Unexpected token ','. */ /* @@? 28:16 Error SyntaxError: Unexpected token ']'. */ /* @@? 30:5 Error TypeError: Indexed access is not supported for such expression type. */ -/* @@? 31:1 Error TypeError: No matching call signature for foo(a[0], a[1], ...a) */ /* @@? 31:5 Error TypeError: Indexed access is not supported for such expression type. */ /* @@? 31:11 Error TypeError: Indexed access is not supported for such expression type. */ -/* @@? 31:17 Error TypeError: Spread argument for the rest parameter can be only one. */ /* @@? 32:5 Error TypeError: Indexed access is not supported for such expression type. */ /* @@? 32:10 Error SyntaxError: Unexpected token ','. */ /* @@? 32:11 Error SyntaxError: Unexpected token, expected ',' or ')'. */ @@ -57,8 +55,8 @@ foo(,) /* @@? 33:5 Error TypeError: Indexed access is not supported for such expression type. */ /* @@? 33:10 Error SyntaxError: Unexpected token ','. */ /* @@? 34:5 Error SyntaxError: Unexpected token ','. */ -/* @@? 34:6 Error SyntaxError: Unexpected token 'a'. */ /* @@? 34:6 Error SyntaxError: Unexpected token, expected ',' or ')'. */ +/* @@? 34:6 Error SyntaxError: Unexpected token 'a'. */ /* @@? 34:6 Error TypeError: Indexed access is not supported for such expression type. */ /* @@? 34:10 Error SyntaxError: Unexpected token ')'. */ /* @@? 35:5 Error TypeError: Indexed access is not supported for such expression type. */ diff --git a/ets2panda/test/ast/parser/ets/trailing_comma_1.ets b/ets2panda/test/ast/parser/ets/trailing_comma_1.ets index 3bc32586c9..8c6c0fc45b 100644 --- a/ets2panda/test/ast/parser/ets/trailing_comma_1.ets +++ b/ets2panda/test/ast/parser/ets/trailing_comma_1.ets @@ -44,10 +44,8 @@ foo(,) /* @@? 28:15 Error SyntaxError: Unexpected token ','. */ /* @@? 28:16 Error SyntaxError: Unexpected token ']'. */ /* @@? 30:5 Error TypeError: Indexed access is not supported for such expression type. */ -/* @@? 31:1 Error TypeError: No matching call signature for foo(a[0], a[1], ...a) */ /* @@? 31:5 Error TypeError: Indexed access is not supported for such expression type. */ /* @@? 31:11 Error TypeError: Indexed access is not supported for such expression type. */ -/* @@? 31:17 Error TypeError: Spread argument for the rest parameter can be only one. */ /* @@? 32:5 Error TypeError: Indexed access is not supported for such expression type. */ /* @@? 32:10 Error SyntaxError: Unexpected token ','. */ /* @@? 32:11 Error SyntaxError: Unexpected token, expected ',' or ')'. */ @@ -57,8 +55,8 @@ foo(,) /* @@? 33:5 Error TypeError: Indexed access is not supported for such expression type. */ /* @@? 33:10 Error SyntaxError: Unexpected token ','. */ /* @@? 34:5 Error SyntaxError: Unexpected token ','. */ -/* @@? 34:6 Error SyntaxError: Unexpected token 'a'. */ /* @@? 34:6 Error SyntaxError: Unexpected token, expected ',' or ')'. */ +/* @@? 34:6 Error SyntaxError: Unexpected token 'a'. */ /* @@? 34:6 Error TypeError: Indexed access is not supported for such expression type. */ /* @@? 34:10 Error SyntaxError: Unexpected token ')'. */ /* @@? 35:5 Error TypeError: Indexed access is not supported for such expression type. */ @@ -66,4 +64,4 @@ foo(,) /* @@? 35:10 Error SyntaxError: Unexpected token, expected ',' or ')'. */ /* @@? 35:10 Error TypeError: Indexed access is not supported for such expression type. */ /* @@? 35:14 Error SyntaxError: Unexpected token ')'. */ -/* @@? 36:5 Error SyntaxError: Unexpected token ','. */ +/* @@? 36:5 Error SyntaxError: Unexpected token ','. */ \ No newline at end of file diff --git a/ets2panda/test/runtime/ets/lambda_spread.ets b/ets2panda/test/runtime/ets/lambda_spread.ets new file mode 100644 index 0000000000..897c6894d9 --- /dev/null +++ b/ets2panda/test/runtime/ets/lambda_spread.ets @@ -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. + */ + +function main() { + const o1 = (...args : Array) => { + arktest.assertEQ(args.length, 4); + arktest.assertEQ(args[0], 1); + arktest.assertEQ(args[1], 2); + arktest.assertEQ(args[2], 3); + arktest.assertEQ(args[3], 4); + }; + let a : int[] = [2,3] + o1(1,...a, 4); + o1(1,...[2,3],4); + const o2 = (a: int, b : int, ...args : Array) => { + arktest.assertEQ(args.length, 0); + arktest.assertEQ(a, 1); + arktest.assertEQ(b, 2); + } + o2(1,2); + const o3 = (a: int, ...args : Array) => { + arktest.assertEQ(a, 1); + arktest.assertEQ(args.length, 4); + arktest.assertEQ(args[0], 2); + arktest.assertEQ(args[1], 3); + arktest.assertEQ(args[2], 4); + arktest.assertEQ(args[3], 5); + } + o3(1,2,...[3,4],5); +} \ No newline at end of file diff --git a/ets2panda/test/runtime/ets/lambda_spread_fixed.ets b/ets2panda/test/runtime/ets/lambda_spread_fixed.ets new file mode 100644 index 0000000000..cd7cfaf617 --- /dev/null +++ b/ets2panda/test/runtime/ets/lambda_spread_fixed.ets @@ -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. + */ + +function main() { + const o1 = (...args : FixedArray) => { + arktest.assertEQ(args.length, 4); + arktest.assertEQ(args[0], 1); + arktest.assertEQ(args[1], 2); + arktest.assertEQ(args[2], 3); + arktest.assertEQ(args[3], 4); + }; + let a : FixedArray = [2,3] + o1(1,...a, 4); + o1(1,...[2,3],4); + const o2 = (a: int, b : int, ...args : FixedArray) => { + arktest.assertEQ(args.length, 0); + arktest.assertEQ(a, 1); + arktest.assertEQ(b, 2); + } + o2(1,2); + const o3 = (a: int, ...args : FixedArray) => { + arktest.assertEQ(a, 1); + arktest.assertEQ(args.length, 4); + arktest.assertEQ(args[0], 2); + arktest.assertEQ(args[1], 3); + arktest.assertEQ(args[2], 4); + arktest.assertEQ(args[3], 5); + } + o3(1,2,...[3,4],5); +} \ No newline at end of file diff --git a/ets2panda/test/runtime/ets/spread_in_rest.ets b/ets2panda/test/runtime/ets/spread_in_rest.ets new file mode 100644 index 0000000000..234991c7c0 --- /dev/null +++ b/ets2panda/test/runtime/ets/spread_in_rest.ets @@ -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. + */ + +function foo(a: int, b:number, ...args: Array) { + arktest.assertEQ(a, 1); + arktest.assertEQ(b, 2); + arktest.assertEQ(args.length, 4); + arktest.assertEQ(args[0], 3); + arktest.assertEQ(args[1], 4); + arktest.assertEQ(args[2], 5); + arktest.assertEQ(args[3], 6); +} + +function main() { + foo(1, 2, ...[3,4], 5, 6); + let a : Array = [3,4]; + foo(1, 2, ...a, 5, 6); +} diff --git a/ets2panda/test/runtime/ets/spread_in_rest_fixedArray.ets b/ets2panda/test/runtime/ets/spread_in_rest_fixedArray.ets new file mode 100644 index 0000000000..5fc5a62a3f --- /dev/null +++ b/ets2panda/test/runtime/ets/spread_in_rest_fixedArray.ets @@ -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. + */ + +function foo(a: int, b:number, ...args: FixedArray) { + arktest.assertEQ(a, 1); + arktest.assertEQ(b, 2); + arktest.assertEQ(args.length, 4); + arktest.assertEQ(args[0], 3); + arktest.assertEQ(args[1], 4); + arktest.assertEQ(args[2], 5); + arktest.assertEQ(args[3], 6); +} + +function main() { + foo(1, 2, ...[3,4], 5, 6); + let a: FixedArray = [3,4]; + foo(1, 2, ...a, 5, 6); +} diff --git a/ets2panda/util/diagnostic/semantic.yaml b/ets2panda/util/diagnostic/semantic.yaml index 8de38bb1ef..cb31524032 100644 --- a/ets2panda/util/diagnostic/semantic.yaml +++ b/ets2panda/util/diagnostic/semantic.yaml @@ -492,10 +492,6 @@ semantic: id: 122 message: "Class name can't be the argument of function or method." -- name: MULTIPLE_SPREADS - id: 123 - message: "Spread argument for the rest parameter can be only one." - - name: PARAM_COUNT_MISMATCH id: 124 message: "Expected {} arguments, got {}." -- Gitee