From aa779a468ac9eee4d0df2c18b26585ab7a2b6b39 Mon Sep 17 00:00:00 2001 From: Torok Gergo Date: Tue, 2 Sep 2025 08:58:12 +0200 Subject: [PATCH] Allow arrays to be spread into FixedArray Reason: Spreading any type of array into any type of rest parameter by design should be allowed. Description: Allowing the signature matching in checker Extending lambda lowering with required functionalities Issue: https://gitee.com/openharmony/arkcompiler_ets_frontend/issues/ICVTF6 Signed-off-by: Torok Gergo Original author: Laszlo Lango --- ets2panda/checker/ets/function.cpp | 28 +++++++----- ets2panda/compiler/core/ETSCompiler.cpp | 5 +++ .../lowering/ets/restArgsLowering.cpp | 43 ++++++++++--------- .../test/ast/compiler/ets/overload_crash.ets | 4 -- .../test/runtime/ets/fixedArrayRestParam.ets | 23 ++++++++++ 5 files changed, 68 insertions(+), 35 deletions(-) create mode 100644 ets2panda/test/runtime/ets/fixedArrayRestParam.ets diff --git a/ets2panda/checker/ets/function.cpp b/ets2panda/checker/ets/function.cpp index f4e415d09f..3aa88f34e8 100644 --- a/ets2panda/checker/ets/function.cpp +++ b/ets2panda/checker/ets/function.cpp @@ -748,18 +748,26 @@ bool ETSChecker::ValidateSignatureRestParams(Signature *substitutedSig, const Ar restArgument->SetPreferredType(targetType); argument->Check(this); auto const argumentType = restArgument->TsType(); + if (Relation()->IsAssignableTo(argumentType, targetType)) { + continue; + } - auto const invocationCtx = checker::InvocationContext( - Relation(), restArgument, argumentType, substitutedSig->RestVar()->TsType(), argument->Start(), - {{diagnostic::REST_PARAM_INCOMPAT_AT, {argumentType, substitutedSig->RestVar()->TsType(), index + 1}}}, - flags); - if (!invocationCtx.IsInvocable()) { - if (restArgument->IsArrayExpression()) { - ModifyPreferredType(restArgument->AsArrayExpression(), nullptr); - argument->SetTsType(nullptr); - } - return false; + if ((argumentType->IsETSArrayType() || argumentType->IsETSResizableArrayType()) && + !targetType->IsETSTupleType() && + Relation()->IsAssignableTo(GetElementTypeOfArray(argumentType), GetElementTypeOfArray(targetType))) { + continue; } + + if ((flags & TypeRelationFlag::NO_THROW) == 0) { + Relation()->RaiseError(diagnostic::REST_PARAM_INCOMPAT_AT, + {argumentType, substitutedSig->RestVar()->TsType(), index + 1}, argument->Start()); + } + + if (restArgument->IsArrayExpression()) { + ModifyPreferredType(restArgument->AsArrayExpression(), nullptr); + argument->SetTsType(nullptr); + } + return false; } return true; diff --git a/ets2panda/compiler/core/ETSCompiler.cpp b/ets2panda/compiler/core/ETSCompiler.cpp index 758d0da822..bb0d111559 100644 --- a/ets2panda/compiler/core/ETSCompiler.cpp +++ b/ets2panda/compiler/core/ETSCompiler.cpp @@ -648,10 +648,15 @@ static void ConvertRestArguments(checker::ETSChecker *const checker, const ir::C 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()) { + if (i == argumentCount - 1 && expr->Arguments()[i]->TsType()->IsETSArrayType()) { + return; + } + ArenaVector elements(checker->Allocator()->Adapter()); for (; i < argumentCount; ++i) { elements.emplace_back(expr->Arguments()[i]); } + // NOTE (torokg): Node allocation in this segment should not be present, should be moved to lowering auto *arrayExpression = checker->AllocNode(std::move(elements), checker->Allocator()); ES2PANDA_ASSERT(arrayExpression != nullptr); arrayExpression->SetParent(const_cast(expr)); diff --git a/ets2panda/compiler/lowering/ets/restArgsLowering.cpp b/ets2panda/compiler/lowering/ets/restArgsLowering.cpp index 6872d027f0..32bc1dc264 100644 --- a/ets2panda/compiler/lowering/ets/restArgsLowering.cpp +++ b/ets2panda/compiler/lowering/ets/restArgsLowering.cpp @@ -28,7 +28,7 @@ namespace ark::es2panda::compiler { using AstNodePtr = ir::AstNode *; static ir::BlockExpression *CreateRestArgsBlockExpression(public_lib::Context *context, - ir::SpreadElement *spreadElement) + ir::SpreadElement *spreadElement, bool const toFixedArray) { auto *allocator = context->allocator; auto *parser = context->parser->AsETSParser(); @@ -39,29 +39,26 @@ static ir::BlockExpression *CreateRestArgsBlockExpression(public_lib::Context *c ES2PANDA_ASSERT(arraySymbol != nullptr); const auto argumentSymbol = Gensym(allocator); ES2PANDA_ASSERT(argumentSymbol != nullptr); - const auto iteratorIndex = Gensym(allocator); - ES2PANDA_ASSERT(iteratorIndex != nullptr); - const auto iteratorSymbol = Gensym(allocator); - ES2PANDA_ASSERT(iteratorSymbol != nullptr); 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))); + if (toFixedArray) { + blockStatements.push_back(parser->CreateFormattedStatement( + "let @@I1 = new @@T2[@@I3.length] as FixedArray<@@T4>;", arraySymbol, typeNode, + argumentSymbol->Clone(allocator, nullptr), typeNode->Clone(allocator, nullptr))); + } else { + 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); + ss << "for (let i = 0; i < @@I1.length; ++i){"; args.emplace_back(argumentSymbol->Clone(allocator, nullptr)); - ss << "@@I3[@@I4] = @@I5;"; + ss << "@@I2[i] = @@I3[i];"; 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)); + args.emplace_back(argumentSymbol->Clone(allocator, nullptr)); ss << "}"; ir::Statement *loopStatement = parser->CreateFormattedStatement(ss.str(), args); @@ -71,10 +68,10 @@ static ir::BlockExpression *CreateRestArgsBlockExpression(public_lib::Context *c return blockExpr; } -static ir::BlockExpression *ConvertSpreadToBlockExpression(public_lib::Context *context, - ir::SpreadElement *spreadElement) +static ir::Expression *ConvertSpreadToBlockExpression(public_lib::Context *context, ir::SpreadElement *spreadElement, + bool const toFixedArray) { - auto *blockExpression = CreateRestArgsBlockExpression(context, spreadElement); + auto *blockExpression = CreateRestArgsBlockExpression(context, spreadElement, toFixedArray); ES2PANDA_ASSERT(blockExpression != nullptr); blockExpression->SetParent(spreadElement->Parent()); blockExpression->SetRange(spreadElement->Range()); @@ -145,7 +142,10 @@ bool ShouldSkipParamCheck(checker::Signature *signature, const ArenaVector &arguments) { - return signature != nullptr && signature->HasRestParameter() && !signature->RestVar()->TsType()->IsETSArrayType() && + return signature != nullptr && signature->HasRestParameter() && + (!signature->RestVar()->TsType()->IsETSArrayType() || + (!arguments.empty() && arguments.back()->IsSpreadElement() && + arguments.back()->AsSpreadElement()->Argument()->TsType()->IsETSResizableArrayType())) && (arguments.size() >= signature->Params().size() || ShouldSkipParamCheck(signature, arguments)) && !signature->RestVar()->TsType()->IsETSTupleType() && !signature->Function()->IsDynamic(); } @@ -161,7 +161,8 @@ static ir::Expression *CreateRestArgsArray(public_lib::Context *context, ArenaVe const int diffArgs = arguments.size() - signature->Params().size(); const size_t extraArgs = diffArgs < 0 ? 0 : diffArgs; if (extraArgs == 1 && arguments.back()->IsSpreadElement()) { - return ConvertSpreadToBlockExpression(context, arguments.back()->AsSpreadElement()); + return ConvertSpreadToBlockExpression(context, arguments.back()->AsSpreadElement(), + signature->RestVar()->TsType()->IsETSArrayType()); } // Determine array type checker::Type *arrayType = signature->RestVar()->TsType(); diff --git a/ets2panda/test/ast/compiler/ets/overload_crash.ets b/ets2panda/test/ast/compiler/ets/overload_crash.ets index 7350e84edd..2e59b37d50 100644 --- a/ets2panda/test/ast/compiler/ets/overload_crash.ets +++ b/ets2panda/test/ast/compiler/ets/overload_crash.ets @@ -18,9 +18,5 @@ function path(obj: Object, ...keys: (string | number)[]); function path(obj: Object, ...keys: (string | number)[]) { } -function f1(thing: string) { - path(thing, ...['a']); -} - /* @@? 16:10 Error TypeError: Only abstract or native methods can't have body. */ /* @@? 18:1 Error TypeError: Function path with this assembly signature already declared. */ diff --git a/ets2panda/test/runtime/ets/fixedArrayRestParam.ets b/ets2panda/test/runtime/ets/fixedArrayRestParam.ets new file mode 100644 index 0000000000..d8d75c9586 --- /dev/null +++ b/ets2panda/test/runtime/ets/fixedArrayRestParam.ets @@ -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. + */ + +function foo(...items: FixedArray) {} + +function main(){ + let a : Array = [new A(), new A()]; + foo(...a); +} + +class A {} -- Gitee