From 84343786b06cbc8066d4d0e78267ea7004c47c19 Mon Sep 17 00:00:00 2001 From: hasansemihkahveci Date: Mon, 16 Jun 2025 15:18:52 +0300 Subject: [PATCH] Fix arrow type in record Issue : https://gitee.com/openharmony/arkcompiler_ets_frontend/issues/ICB5SX Signed-off-by: hasansemihkahveci --- ets2panda/ir/expressions/memberExpression.cpp | 128 ++++++++++++++++-- .../test/runtime/ets/recordwithArrowType.ets | 25 ++++ 2 files changed, 143 insertions(+), 10 deletions(-) create mode 100755 ets2panda/test/runtime/ets/recordwithArrowType.ets diff --git a/ets2panda/ir/expressions/memberExpression.cpp b/ets2panda/ir/expressions/memberExpression.cpp index 2ea5a0d847..8dfa5ea89d 100644 --- a/ets2panda/ir/expressions/memberExpression.cpp +++ b/ets2panda/ir/expressions/memberExpression.cpp @@ -247,12 +247,108 @@ checker::Type *MemberExpression::CheckUnionMember(checker::ETSChecker *checker, return commonPropType; } -static checker::Type *AdjustRecordReturnType(checker::Type *type, checker::Type *objType) +util::StringView FindTargetVariableName(ir::BlockExpression *block) +{ + for (auto *stmt : block->Statements()) { + if (!stmt->IsVariableDeclaration()) { + continue; + } + for (auto *decl : stmt->AsVariableDeclaration()->Declarators()) { + if (auto *id = decl->AsVariableDeclarator()->Id()) { + return id->AsIdentifier()->Name(); + } + } + } + return ""; +} + +bool BlockContainsRecordKey(ir::Identifier *ident, const util::StringView &targetKey) +{ + if (ident == nullptr || ident->Variable() == nullptr || ident->Variable()->Declaration() == nullptr) { + return false; + } + + auto *declNode = ident->Variable()->Declaration()->Node()->Parent(); + if (declNode == nullptr || !declNode->IsVariableDeclarator()) { + return false; + } + + auto *init = declNode->AsVariableDeclarator()->Init(); + if (!init->IsBlockExpression()) { + return false; + } + + auto targetName = FindTargetVariableName(init->AsBlockExpression()); + if (targetName.Empty()) { + return false; + } + + for (auto *stmt : init->AsBlockExpression()->Statements()) { + if (!stmt->IsExpressionStatement() || !stmt->AsExpressionStatement()->GetExpression()->IsCallExpression() || + !stmt->AsExpressionStatement()->GetExpression()->AsCallExpression()->Callee()->IsMemberExpression()) { + continue; + } + + auto *memberExpr = + stmt->AsExpressionStatement()->GetExpression()->AsCallExpression()->Callee()->AsMemberExpression(); + if (!memberExpr->Object()->IsIdentifier() || memberExpr->Object()->AsIdentifier()->Name() != targetName || + !memberExpr->Property()->IsIdentifier()) { + continue; + } + + if (!stmt->AsExpressionStatement()->GetExpression()->AsCallExpression()->Arguments().empty()) { + auto *arg = stmt->AsExpressionStatement()->GetExpression()->AsCallExpression()->Arguments()[0]; + if (arg->IsStringLiteral() && arg->AsStringLiteral()->Str() == targetKey) { + return true; + } + } + } + return false; +} + +bool AdjustReturnTypeToIncludeUndefined(ir::Expression *property) +{ + if (!property->Parent()->Parent()->IsCallExpression()) { + return false; + } + + auto parentCall = property->Parent()->Parent()->AsCallExpression(); + if (parentCall->Arguments().empty() || !parentCall->Callee()->IsMemberExpression()) { + return false; + } + + auto *memberExpr = parentCall->Callee()->AsMemberExpression(); + if (!parentCall->Arguments()[0]->IsStringLiteral()) { + return false; + } + + auto memObj = memberExpr->Object(); + while (memObj != nullptr) { + if (memObj->IsIdentifier()) { + break; + } + if (memObj->IsMemberExpression()) { + memObj = memObj->AsMemberExpression()->Object(); + } else { + memObj = nullptr; + } + } + + if (memObj == nullptr) { + return false; + } + + return memObj->IsIdentifier() && + !BlockContainsRecordKey(memObj->AsIdentifier(), parentCall->Arguments()[0]->AsStringLiteral()->Str()); +} + +static checker::Type *AdjustRecordReturnType(checker::Type *type, checker::Type *objType, ir::Expression *property, + checker::ETSChecker *checker) { auto *recordKeyType = objType->AsETSObjectType()->TypeArguments()[0]; auto *recordValueType = objType->AsETSObjectType()->TypeArguments()[1]; - auto const isStringLiteralOrConstantUnion = [](checker::Type *recordKey) { + auto const isNarrowKey = [](checker::Type *recordKey) { if (recordKey->IsETSStringType() && recordKey->IsConstantType()) { return true; } @@ -263,14 +359,25 @@ static checker::Type *AdjustRecordReturnType(checker::Type *type, checker::Type return std::all_of(constituentTypes.begin(), constituentTypes.end(), [](auto *it) { return it->IsETSStringType() && it->IsConstantType(); }); }; - if (isStringLiteralOrConstantUnion(recordKeyType)) { - if (type->IsETSUnionType()) { - return recordValueType; - } + auto const isStringOrNumberKey = [](checker::Type *recordKey) { + return recordKey->ToAssemblerName().str() == compiler::Signatures::BUILTIN_DOUBLE || + recordKey->ToAssemblerName().str() == compiler::Signatures::BUILTIN_STRING; + }; + if (!(isNarrowKey(recordKeyType) || (isStringOrNumberKey(recordKeyType) && recordValueType->IsETSArrowType()))) { + return type; + } + + if (type->IsETSUnionType() || type->IsETSArrowType()) { + return recordValueType; + } - if (type->IsETSFunctionType() && type->AsETSFunctionType()->Name().Is(compiler::Signatures::GET_INDEX_METHOD)) { - type->AsETSFunctionType()->CallSignatures()[0]->SetReturnType(recordValueType); + if (type->IsETSFunctionType() && type->AsETSFunctionType()->Name().Is(compiler::Signatures::GET_INDEX_METHOD)) { + if (AdjustReturnTypeToIncludeUndefined(property) && !recordKeyType->IsETSUnionType()) { + type->AsETSFunctionType()->CallSignatures()[0]->SetReturnType(checker->CreateETSUnionType( + {type->AsETSFunctionType()->CallSignatures()[0]->ReturnType(), checker->GlobalETSUndefinedType()})); + return type; } + type->AsETSFunctionType()->CallSignatures()[0]->SetReturnType(recordValueType); } return type; @@ -281,7 +388,7 @@ checker::Type *MemberExpression::AdjustType(checker::ETSChecker *checker, checke auto *const objType = checker->GetApparentType(Object()->TsType()); if (type != nullptr && objType->IsETSObjectType() && objType->ToAssemblerName().str() == compiler::Signatures::BUILTIN_RECORD) { - type = AdjustRecordReturnType(type, objType); + type = AdjustRecordReturnType(type, objType, property_, checker); } if (PropVar() != nullptr) { // access erased property type @@ -397,7 +504,8 @@ checker::Type *MemberExpression::CheckIndexAccessMethod(checker::ETSChecker *che { checker::PropertySearchFlags searchFlag = checker::PropertySearchFlags::SEARCH_METHOD; searchFlag |= checker::PropertySearchFlags::SEARCH_IN_BASE | checker::PropertySearchFlags::SEARCH_IN_INTERFACES; - // NOTE(DZ) maybe we need to exclude static methods: search_flag &= ~(checker::PropertySearchFlags::SEARCH_STATIC); + // NOTE(DZ) maybe we need to exclude static methods: search_flag &= + // ~(checker::PropertySearchFlags::SEARCH_STATIC); if (objType_->HasTypeFlag(checker::TypeFlag::GENERIC)) { searchFlag |= checker::PropertySearchFlags::SEARCH_ALL; diff --git a/ets2panda/test/runtime/ets/recordwithArrowType.ets b/ets2panda/test/runtime/ets/recordwithArrowType.ets new file mode 100755 index 0000000000..e1f8cd9c8f --- /dev/null +++ b/ets2panda/test/runtime/ets/recordwithArrowType.ets @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +function main() { + let r: Record number> = { + 1: (p: number): number => p + p, + 2: (p: number): number => p * p + } + assertEQ(r[1](42), 84) + assertEQ(r[2](12), 144) + r[1] = (p: number): number => p + p + p + assertEQ(r[1](3), 9) +} -- Gitee