From 21466175e7207490a05d69ac78b92a2ff21971d5 Mon Sep 17 00:00:00 2001 From: Zelentsov Dmitry Date: Thu, 31 Jul 2025 10:38:38 +0300 Subject: [PATCH] Fix function with receiver processing Issue: https://gitee.com/openharmony/arkcompiler_ets_frontend/issues/ICQ3MS Tests: use Test-U-Runner and CI Signed-off-by: Zelentsov Dmitry --- ets2panda/checker/ETSAnalyzer.cpp | 54 ++++++++++++------- ets2panda/checker/ETSAnalyzerHelpers.cpp | 19 +++---- ets2panda/checker/ETSchecker.h | 5 +- ets2panda/checker/ets/function.cpp | 24 ++++++--- ets2panda/compiler/core/function.cpp | 4 +- .../ast/compiler/ets/interface_literal.ets | 31 +++++++++++ .../ast/compiler/ets/method-assignment_01.ets | 41 ++++++++++++++ ets2panda/util/helpers.cpp | 2 +- ets2panda/util/helpers.h | 2 +- ets2panda/varbinder/varbinder.cpp | 2 +- 10 files changed, 143 insertions(+), 41 deletions(-) create mode 100644 ets2panda/test/ast/compiler/ets/interface_literal.ets create mode 100644 ets2panda/test/ast/compiler/ets/method-assignment_01.ets diff --git a/ets2panda/checker/ETSAnalyzer.cpp b/ets2panda/checker/ETSAnalyzer.cpp index 11bae600fe..3b71d77762 100644 --- a/ets2panda/checker/ETSAnalyzer.cpp +++ b/ets2panda/checker/ETSAnalyzer.cpp @@ -25,10 +25,8 @@ #include "types/signature.h" #include "compiler/lowering/ets/setJumpTarget.h" #include "checker/types/ets/etsAsyncFuncReturnType.h" -#include "types/ts/nullType.h" #include "types/type.h" #include "checker/types/typeError.h" -#include "util/es2pandaMacros.h" #include @@ -1166,30 +1164,50 @@ checker::Type *ETSAnalyzer::GetSmartType(ir::AssignmentExpression *expr, checker return smartType; } -static bool IsMethodDefinition(const ir::Expression *const expression) +static ir::MethodDefinition const *ResolveMethodDefinition(const ir::Expression *const expression, ETSChecker *checker) { - return expression->IsMemberExpression() && expression->AsMemberExpression()->Property() != nullptr && - expression->AsMemberExpression()->Property()->Variable() != nullptr && - expression->AsMemberExpression()->Property()->Variable()->Declaration() != nullptr && - expression->AsMemberExpression()->Property()->Variable()->Declaration()->Node()->IsMethodDefinition(); + if (!expression->IsMemberExpression()) { + return nullptr; + } + + auto const *memberExpression = expression->AsMemberExpression(); + if (memberExpression->Kind() != ir::MemberExpressionKind::PROPERTY_ACCESS || + memberExpression->Property() == nullptr || !memberExpression->Property()->IsIdentifier()) { + return nullptr; + } + + auto const *variable = memberExpression->Property()->Variable(); + if (variable == nullptr) { + if (auto const *objectType = memberExpression->Object()->TsType(); + objectType != nullptr && objectType->IsETSObjectType()) { + // Process possible case of the same name method with receiver defined + auto resolved = checker->ResolveMemberReference(memberExpression, objectType->AsETSObjectType()); + if (resolved.size() == 2U && resolved[1]->Kind() == checker::ResolvedKind::PROPERTY) { + variable = resolved[1U]->Variable()->AsLocalVariable(); + } + } + } + + if (variable != nullptr) { + if (variable->Declaration() != nullptr && variable->Declaration()->Node()->IsMethodDefinition()) { + return variable->Declaration()->Node()->AsMethodDefinition(); + } + } + + return nullptr; } static bool IsInvalidMethodAssignment(const ir::AssignmentExpression *const expr, ETSChecker *checker) { auto left = expr->Left(); - if (IsMethodDefinition(left)) { - { - auto methodDefinition = - left->AsMemberExpression()->Property()->Variable()->Declaration()->Node()->AsMethodDefinition(); - if (!methodDefinition->IsSetter() && - std::none_of(methodDefinition->Overloads().cbegin(), methodDefinition->Overloads().cend(), - [](const auto *overload) { return overload->IsSetter(); })) { - checker->LogError(diagnostic::METHOD_ASSIGNMENT, expr->Left()->Start()); - return true; - } + if (auto const *methodDefinition = ResolveMethodDefinition(left, checker); methodDefinition != nullptr) { + if (!methodDefinition->IsSetter() && + std::none_of(methodDefinition->Overloads().cbegin(), methodDefinition->Overloads().cend(), + [](const auto *overload) { return overload->IsSetter(); })) { + checker->LogError(diagnostic::METHOD_ASSIGNMENT, left->Start()); + return true; } } - return false; } diff --git a/ets2panda/checker/ETSAnalyzerHelpers.cpp b/ets2panda/checker/ETSAnalyzerHelpers.cpp index 2075563dcd..d3127e33aa 100644 --- a/ets2panda/checker/ETSAnalyzerHelpers.cpp +++ b/ets2panda/checker/ETSAnalyzerHelpers.cpp @@ -404,18 +404,19 @@ checker::Signature *GetMostSpecificSigFromExtensionFuncAndClassMethod(checker::E // For allCallSignatures in ClassMethodType, temporarily insert the dummyReceiver into their signatureInfo, // otherwise we can't get the most suitable classMethod signature if all the extensionFunction signature mismatched. ArenaVector signatures(checker->ProgramAllocator()->Adapter()); - signatures.insert(signatures.end(), type->ClassMethodType()->CallSignatures().begin(), - type->ClassMethodType()->CallSignatures().end()); - signatures.insert(signatures.end(), type->ExtensionMethodType()->CallSignatures().begin(), - type->ExtensionMethodType()->CallSignatures().end()); + auto const &classMethodSignatures = type->ClassMethodType()->CallSignatures(); + auto const &extensionMethodSignatures = type->ExtensionMethodType()->CallSignaturesOfMethodOrArrow(); + + signatures.insert(signatures.end(), classMethodSignatures.cbegin(), classMethodSignatures.cend()); + signatures.insert(signatures.end(), extensionMethodSignatures.cbegin(), extensionMethodSignatures.cend()); auto *memberExpr = expr->Callee()->AsMemberExpression(); auto *dummyReceiver = memberExpr->Object(); - auto *dummyReceiverVar = type->ExtensionMethodType()->CallSignatures()[0]->Params()[0]; + auto *dummyReceiverVar = extensionMethodSignatures[0]->Params()[0]; expr->Arguments().insert(expr->Arguments().begin(), dummyReceiver); const bool typeParamsNeeded = dummyReceiverVar->TsType()->IsETSObjectType(); - for (auto *methodCallSig : type->ClassMethodType()->CallSignatures()) { + for (auto *methodCallSig : classMethodSignatures) { methodCallSig->GetSignatureInfo()->minArgCount++; auto ¶msVar = methodCallSig->Params(); paramsVar.insert(paramsVar.begin(), dummyReceiverVar); @@ -431,7 +432,7 @@ checker::Signature *GetMostSpecificSigFromExtensionFuncAndClassMethod(checker::E auto *signature = checker->ResolveCallExpressionAndTrailingLambda(signatures, expr, expr->Start(), checker::TypeRelationFlag::NO_THROW); - for (auto *methodCallSig : type->ClassMethodType()->CallSignatures()) { + for (auto *methodCallSig : classMethodSignatures) { methodCallSig->GetSignatureInfo()->minArgCount--; auto ¶msVar = methodCallSig->Params(); paramsVar.erase(paramsVar.begin()); @@ -479,8 +480,8 @@ checker::Signature *ResolveCallForETSExtensionFuncHelperType(checker::ETSExtensi signature = GetMostSpecificSigFromExtensionFuncAndClassMethod(type, checker, expr); if (signature == nullptr) { - checker->ThrowSignatureMismatch(type->ExtensionMethodType()->CallSignatures(), expr->Arguments(), expr->Start(), - "call"); + checker->ThrowSignatureMismatch(type->ExtensionMethodType()->CallSignaturesOfMethodOrArrow(), expr->Arguments(), + expr->Start(), "call"); } return signature; diff --git a/ets2panda/checker/ETSchecker.h b/ets2panda/checker/ETSchecker.h index 076eba0be4..cb4d3756e4 100644 --- a/ets2panda/checker/ETSchecker.h +++ b/ets2panda/checker/ETSchecker.h @@ -482,8 +482,9 @@ public: bool SetPreferredTypeForArrayArgument(ir::ArrayExpression *arrayExpr, Signature *substitutedSig); bool ValidateSignatureRestParams(Signature *substitutedSig, const ArenaVector &arguments, TypeRelationFlag flags, bool reportError, bool unique); - void ThrowSignatureMismatch(ArenaVector &signatures, const ArenaVector &arguments, - const lexer::SourcePosition &pos, std::string_view signatureKind); + void ThrowSignatureMismatch(ArenaVector const &signatures, + const ArenaVector &arguments, const lexer::SourcePosition &pos, + std::string_view signatureKind); Signature *FirstMatchSignatures(ir::CallExpression *expr, checker::Type *calleeType); Signature *MatchOrderSignatures(ArenaVector &signatures, const ir::TSTypeParameterInstantiation *typeArguments, diff --git a/ets2panda/checker/ets/function.cpp b/ets2panda/checker/ets/function.cpp index ff31328770..ec58d9f58d 100644 --- a/ets2panda/checker/ets/function.cpp +++ b/ets2panda/checker/ets/function.cpp @@ -873,7 +873,7 @@ Signature *ETSChecker::GetMostSpecificSignature(ArenaVector &compat return mostSpecificSignature; } -void ETSChecker::ThrowSignatureMismatch(ArenaVector &signatures, +void ETSChecker::ThrowSignatureMismatch(ArenaVector const &signatures, const ArenaVector &arguments, const lexer::SourcePosition &pos, std::string_view signatureKind) { @@ -883,7 +883,7 @@ void ETSChecker::ThrowSignatureMismatch(ArenaVector &signatures, if (someSignature->HasFunction()) { if (someSignature->Function()->IsConstructor()) { - msg.append(util::Helpers::GetClassDefiniton(someSignature->Function())->InternalName().Mutf8()); + msg.append(util::Helpers::GetClassDefinition(someSignature->Function())->InternalName().Mutf8()); } else { msg.append(someSignature->Function()->Id()->Name().Mutf8()); } @@ -945,6 +945,16 @@ Signature *ETSChecker::ValidateSignatures(ArenaVector &signatures, return nullptr; } +// Excluded from 'FindMostSpecificSignature' to reduce its size due to code-style check +static std::size_t GetParameterNumber(Signature const *const sig) +{ + if (sig->HasFunction()) { + return sig->Function()->Params().size(); + } + auto num = sig->Params().size(); + return !sig->HasRestParameter() ? num : ++num; +} + Signature *ETSChecker::FindMostSpecificSignature(const ArenaVector &signatures, const ArenaMultiMap &bestSignaturesForParameter, size_t paramCount) @@ -969,25 +979,25 @@ Signature *ETSChecker::FindMostSpecificSignature(const ArenaVector continue; } - const auto candidateLength = candidate->Function()->Params().size(); + const auto candidateLength = GetParameterNumber(candidate); if (candidateLength > currentMinLength && !candidate->HasRestParameter()) { continue; } if (result == nullptr) { result = candidate; // First valid candidate - currentMinLength = result->Function()->Params().size(); + currentMinLength = GetParameterNumber(result); continue; } - const auto currentLength = result->Function()->Params().size(); + const auto currentLength = GetParameterNumber(result); if (candidate->HasRestParameter() && result->HasRestParameter()) { if (result->Owner() == candidate->Owner()) { result = nullptr; } } else if (candidateLength < currentLength) { result = candidate; // Shorter parameter count wins - currentMinLength = result->Function()->Params().size(); + currentMinLength = GetParameterNumber(result); } else if (candidateLength >= currentLength) { continue; // NOTE (smartin): all other cases below are unreachable code @@ -1078,7 +1088,7 @@ void ETSChecker::SearchAmongMostSpecificTypes(Type *&mostSpecificType, Signature { auto [pos, idx, sig] = info; Type *sigType = GetParameterTypeOrRestAtIdx(this, sig, idx); - if (prevSig->Function()->Params()[idx]->IsETSParameterExpression()) { + if (prevSig->HasFunction() && prevSig->Function()->Params()[idx]->IsETSParameterExpression()) { Relation()->SetNode(prevSig->Function()->Params()[idx]->AsETSParameterExpression()); } const bool isClassType = diff --git a/ets2panda/compiler/core/function.cpp b/ets2panda/compiler/core/function.cpp index e5c05ca78b..e548223ac8 100644 --- a/ets2panda/compiler/core/function.cpp +++ b/ets2panda/compiler/core/function.cpp @@ -197,7 +197,7 @@ void Function::IterateOverElements(const ArenaVector &elements, P void Function::CompileInstanceFields(PandaGen *pg, const ir::ScriptFunction *decl) { - const auto klass = util::Helpers::GetClassDefiniton(decl); + const auto klass = util::Helpers::GetClassDefinition(decl); const auto &elements = klass->Body(); RegScope rs(pg); @@ -227,7 +227,7 @@ static void CompileFunction(PandaGen *pg) { const auto *decl = pg->RootNode()->AsScriptFunction(); - if (decl->IsConstructor() && (util::Helpers::GetClassDefiniton(decl)->Super() == nullptr)) { + if (decl->IsConstructor() && (util::Helpers::GetClassDefinition(decl)->Super() == nullptr)) { Function::CompileInstanceFields(pg, decl); } diff --git a/ets2panda/test/ast/compiler/ets/interface_literal.ets b/ets2panda/test/ast/compiler/ets/interface_literal.ets new file mode 100644 index 0000000000..2790c2406a --- /dev/null +++ b/ets2panda/test/ast/compiler/ets/interface_literal.ets @@ -0,0 +1,31 @@ +/* + * 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. + */ + +interface I { + f(x: int): int; +} + +let f = (this: I, x: int): int => x; + +function main() { + let obj: I = { + /* @@ label */f: f + } + + let r: int = obj.f(5); + arktest.assertEQ(r, 5); +} + +/* @@@ label Error TypeError: Method 'f' cannot be used as a key of object literal. */ diff --git a/ets2panda/test/ast/compiler/ets/method-assignment_01.ets b/ets2panda/test/ast/compiler/ets/method-assignment_01.ets new file mode 100644 index 0000000000..e572ac6981 --- /dev/null +++ b/ets2panda/test/ast/compiler/ets/method-assignment_01.ets @@ -0,0 +1,41 @@ +/* + * 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 { + f(): void { + console.println("OK"); + } +} + +let f = (this: A): void => { + throw new Error("Boom"); +} + +function main() { + let a: A = new A(); + + /* @@ label */a.f = f; + + try { + a.f(); + f(a); + } catch (e) { + if (e instanceof Error) { + arktest.assertEQ(e.message, "Boom"); + } + } +} + +/* @@@ label Error TypeError: Class methods cannot be overwritten. */ diff --git a/ets2panda/util/helpers.cpp b/ets2panda/util/helpers.cpp index 37b43e97a8..83885d4b0f 100644 --- a/ets2panda/util/helpers.cpp +++ b/ets2panda/util/helpers.cpp @@ -325,7 +325,7 @@ const ir::ScriptFunction *Helpers::GetContainingFunction(const ir::AstNode *node return nullptr; } -const ir::ClassDefinition *Helpers::GetClassDefiniton(const ir::ScriptFunction *node) +const ir::ClassDefinition *Helpers::GetClassDefinition(const ir::ScriptFunction *node) { ES2PANDA_ASSERT(node->IsConstructor()); ES2PANDA_ASSERT(node->Parent()->IsFunctionExpression()); diff --git a/ets2panda/util/helpers.h b/ets2panda/util/helpers.h index a1a7ed6cd7..396ff2c401 100644 --- a/ets2panda/util/helpers.h +++ b/ets2panda/util/helpers.h @@ -145,7 +145,7 @@ public: static const ir::MethodDefinition *GetContainingClassMethodDefinition(const ir::AstNode *node); static const ir::ClassStaticBlock *GetContainingClassStaticBlock(const ir::AstNode *node); static const ir::ScriptFunction *GetContainingFunction(const ir::AstNode *node); - static const ir::ClassDefinition *GetClassDefiniton(const ir::ScriptFunction *node); + static const ir::ClassDefinition *GetClassDefinition(const ir::ScriptFunction *node); static bool IsSpecialPropertyKey(const ir::Expression *expr); static bool IsConstantPropertyKey(const ir::Expression *expr, bool isComputed); static compiler::Literal ToConstantLiteral(const ir::Expression *expr); diff --git a/ets2panda/varbinder/varbinder.cpp b/ets2panda/varbinder/varbinder.cpp index 0c8e39ff4e..0235e6cf16 100644 --- a/ets2panda/varbinder/varbinder.cpp +++ b/ets2panda/varbinder/varbinder.cpp @@ -659,7 +659,7 @@ void VarBinder::AddMandatoryParams() const ir::ScriptFunction *ctor = util::Helpers::GetContainingConstructor(scriptFunc); bool lexicalFunctionObject {}; - if (ctor != nullptr && util::Helpers::GetClassDefiniton(ctor)->Super() != nullptr && + if (ctor != nullptr && util::Helpers::GetClassDefinition(ctor)->Super() != nullptr && funcScope->HasFlag(ScopeFlags::USE_SUPER)) { ES2PANDA_ASSERT(ctor->Scope()->HasFlag(ScopeFlags::INNER_ARROW)); ctor->Scope()->AddFlag(ScopeFlags::SET_LEXICAL_FUNCTION); -- Gitee