From 2c04af6063713f22dd367b952971de4cc441b9c3 Mon Sep 17 00:00:00 2001 From: bunyaminakcay Date: Wed, 13 Aug 2025 16:38:24 +0300 Subject: [PATCH] Title: Overriding class prop Issue: ICSY5A Testing: Build Signed-off-by: bunyaminakcay --- ets2panda/checker/ETSchecker.h | 2 ++ ets2panda/checker/ets/assignAnalyzer.cpp | 22 +------------- ets2panda/checker/ets/object.cpp | 29 +++++++++++++++++++ .../compiler/lowering/ets/lambdaLowering.cpp | 25 +++++++++++----- .../compiler}/ets/multisource_inheritance.ets | 2 ++ .../ets/overriding_fields_prohibited.ets | 27 +++++++++++++++++ 6 files changed, 78 insertions(+), 29 deletions(-) rename ets2panda/test/{runtime => ast/compiler}/ets/multisource_inheritance.ets (90%) create mode 100644 ets2panda/test/ast/compiler/ets/overriding_fields_prohibited.ets diff --git a/ets2panda/checker/ETSchecker.h b/ets2panda/checker/ETSchecker.h index 5aaf218056..cfd30e25b7 100644 --- a/ets2panda/checker/ETSchecker.h +++ b/ets2panda/checker/ETSchecker.h @@ -298,6 +298,8 @@ public: [[nodiscard]] Type const *GetApparentType(Type const *type) const; ETSObjectType *GetClosestCommonAncestor(ETSObjectType *source, ETSObjectType *target); bool HasETSFunctionType(ir::TypeNode *typeAnnotation); + static const ir::AstNode *CheckInterfaceProp(const ark::es2panda::ir::AstNode *const node, + const ir::ClassDefinition *classDef); void VariableTypeFromInitializer(varbinder::Variable *variable, Type *annotationType, Type *initType); diff --git a/ets2panda/checker/ets/assignAnalyzer.cpp b/ets2panda/checker/ets/assignAnalyzer.cpp index a411f9d429..62e262e3e8 100644 --- a/ets2panda/checker/ets/assignAnalyzer.cpp +++ b/ets2panda/checker/ets/assignAnalyzer.cpp @@ -1295,26 +1295,6 @@ varbinder::Variable *AssignAnalyzer::GetBoundVariable(const ir::AstNode *node) return ret; } -static const ir::AstNode *CheckInterfaceProp(const ark::es2panda::ir::AstNode *const node, - const ir::ClassDefinition *classDef) -{ - util::StringView methodName = node->AsMethodDefinition()->Key()->AsIdentifier()->Name(); - // the property from interface should start with to distinguish from its getter/setter. - std::string interfaceProp = std::string("") + std::string(methodName.Utf8()); - for (const auto it : classDef->Body()) { - // Check if there is corresponding class property in the same class. - if (it->IsClassProperty() && !it->IsStatic()) { - const auto *prop = it->AsClassProperty(); - auto *propIdentifier = prop->Key()->AsIdentifier(); - if (propIdentifier->Name().Is(interfaceProp)) { - // Use property node as declNode to ensure obtaining NodeId and add it to inits. - return prop; - } - } - } - return nullptr; -} - const ir::AstNode *AssignAnalyzer::GetDeclaringNode(const ir::AstNode *node) { if (node->IsClassProperty() || node->IsVariableDeclarator()) { @@ -1346,7 +1326,7 @@ const ir::AstNode *AssignAnalyzer::GetDeclaringNode(const ir::AstNode *node) if (ret != nullptr) { // if declNode is a getter/setter method, actual node initialized should be a class proterty node. if ((ret->Modifiers() & ir::ModifierFlags::GETTER_SETTER) != 0U) { - if (const auto *interfaceProp = CheckInterfaceProp(ret, classDef_); interfaceProp != nullptr) { + if (const auto *interfaceProp = ETSChecker::CheckInterfaceProp(ret, classDef_); interfaceProp != nullptr) { ret = interfaceProp; } } diff --git a/ets2panda/checker/ets/object.cpp b/ets2panda/checker/ets/object.cpp index 21a7d82d7f..0d31d8e033 100644 --- a/ets2panda/checker/ets/object.cpp +++ b/ets2panda/checker/ets/object.cpp @@ -83,6 +83,15 @@ static bool CheckGetterSetterDecl(varbinder::LocalVariable const *child, varbind return true; }; + if (parent->TsType()->IsETSMethodType() && child->Declaration()->Node()->IsClassProperty()) { + const ir::ClassDefinition *classDefOfParent = + util::Helpers::GetContainingClassDefinition(parent->Declaration()->Node()); + if (classDefOfParent != nullptr && + ETSChecker::CheckInterfaceProp(parent->Declaration()->Node(), classDefOfParent) == nullptr) { + return false; + } + } + bool checkChild = readonlyCheck(child, false, parent->Declaration()->Type() == varbinder::DeclType::READONLY); bool checkParent = readonlyCheck(parent, true, child->Declaration()->Type() == varbinder::DeclType::READONLY); return checkChild && checkParent && (child->TsType()->IsETSFunctionType() || parent->TsType()->IsETSFunctionType()); @@ -635,6 +644,26 @@ ETSObjectType *ETSChecker::BuildAnonymousClassProperties(ir::ClassDefinition *cl return classType; } +const ir::AstNode *ETSChecker::CheckInterfaceProp(const ark::es2panda::ir::AstNode *const node, + const ir::ClassDefinition *classDef) +{ + util::StringView methodName = node->AsMethodDefinition()->Key()->AsIdentifier()->Name(); + // the property from interface should start with to distinguish from its getter/setter. + std::string interfaceProp = std::string("") + std::string(methodName.Utf8()); + for (const auto it : classDef->Body()) { + // Check if there is corresponding class property in the same class. + if (it->IsClassProperty() && !it->IsStatic()) { + const auto *prop = it->AsClassProperty(); + auto *propIdentifier = prop->Key()->AsIdentifier(); + if (propIdentifier->Name().Is(interfaceProp)) { + // Use property node as declNode to ensure obtaining NodeId and add it to inits. + return prop; + } + } + } + return nullptr; +} + static void ResolveDeclaredFieldsOfObject(ETSChecker *checker, const ETSObjectType *type, varbinder::ClassScope *scope) { for (auto &[_, it] : scope->InstanceFieldScope()->Bindings()) { diff --git a/ets2panda/compiler/lowering/ets/lambdaLowering.cpp b/ets2panda/compiler/lowering/ets/lambdaLowering.cpp index 1d45f1c371..462269a1ee 100644 --- a/ets2panda/compiler/lowering/ets/lambdaLowering.cpp +++ b/ets2panda/compiler/lowering/ets/lambdaLowering.cpp @@ -24,6 +24,7 @@ #include "ir/astNode.h" #include "util/options.h" #include "util/nameMangler.h" +#include "util/ustring.h" namespace ark::es2panda::compiler { @@ -538,6 +539,13 @@ static ir::MethodDefinition *CreateCallee(public_lib::Context *ctx, ir::ArrowFun return method; } +static util::StringView GetPrivateCapturedName(checker::ETSChecker *checker, const util::StringView &name) +{ + util::UString privateCapturedName(checker->Allocator()); + privateCapturedName.Append(name.Utf8()).Append(u8"_"); + return privateCapturedName.View(); +} + // The name "=t" used in extension methods has special meaning for the code generator; // avoid it as parameter and field name in our generated code. static util::StringView AvoidMandatoryThis(util::StringView name) @@ -567,8 +575,9 @@ static void CreateLambdaClassFields(public_lib::Context *ctx, ir::ClassDefinitio for (auto *captured : *info->capturedVars) { auto *varDeclaration = parser->CreateFormattedClassFieldDefinition( - "@@I1: @@T2", AvoidMandatoryThis(captured->Name()), + "private @@I1: @@T2", AvoidMandatoryThis(GetPrivateCapturedName(checker, captured->Name())), captured->TsType()->Substitute(checker->Relation(), substitution)); + varDeclaration->SetRange(captured->Declaration()->Node()->Range()); props.push_back(varDeclaration); } @@ -605,15 +614,14 @@ static void CreateLambdaClassConstructor(public_lib::Context *ctx, ir::ClassDefi } auto bodyStmts = ArenaVector(allocator->Adapter()); - auto makeStatement = [&parser, &bodyStmts](util::StringView name) { - auto adjustedName = AvoidMandatoryThis(name); - bodyStmts.push_back(parser->CreateFormattedStatement("this.@@I1 = @@I2", adjustedName, adjustedName)); - }; + if (info->callReceiver != nullptr) { - makeStatement("$this"); + bodyStmts.push_back(parser->CreateFormattedStatement("this.@@I1 = @@I2", "$this", "$this")); } for (auto *var : *info->capturedVars) { - makeStatement(var->Name()); + bodyStmts.push_back(parser->CreateFormattedStatement( + "this.@@I1 = @@I2", AvoidMandatoryThis(GetPrivateCapturedName(checker, var->Name())), + AvoidMandatoryThis(var->Name()))); } auto *body = util::NodeAllocator::ForceSetParent(allocator, allocator, std::move(bodyStmts)); @@ -779,7 +787,8 @@ static ArenaVector CreateCallArgumentsForLambdaClassInvoke(pub auto callArguments = ArenaVector(allocator->Adapter()); for (auto *captured : *info->capturedVars) { - auto *arg = parser->CreateFormattedExpression("this.@@I1", AvoidMandatoryThis(captured->Name())); + auto *arg = parser->CreateFormattedExpression( + "this.@@I1", AvoidMandatoryThis(GetPrivateCapturedName(checker, captured->Name()))); callArguments.push_back(arg); } for (size_t idx = 0; idx < lciInfo->lambdaSignature->ArgCount(); ++idx) { diff --git a/ets2panda/test/runtime/ets/multisource_inheritance.ets b/ets2panda/test/ast/compiler/ets/multisource_inheritance.ets similarity index 90% rename from ets2panda/test/runtime/ets/multisource_inheritance.ets rename to ets2panda/test/ast/compiler/ets/multisource_inheritance.ets index 0716446a5f..bda1083c54 100644 --- a/ets2panda/test/runtime/ets/multisource_inheritance.ets +++ b/ets2panda/test/ast/compiler/ets/multisource_inheritance.ets @@ -47,3 +47,5 @@ function main() { arktest.assertEQ(d.method(), 0 ) arktest.assertEQ(d.instance_field, -2) } + +/* @@? 35:23 Error TypeError: Cannot inherit from class Base, because field instance_field is inherited with a different declaration type */ \ No newline at end of file diff --git a/ets2panda/test/ast/compiler/ets/overriding_fields_prohibited.ets b/ets2panda/test/ast/compiler/ets/overriding_fields_prohibited.ets new file mode 100644 index 0000000000..15cdbe65cf --- /dev/null +++ b/ets2panda/test/ast/compiler/ets/overriding_fields_prohibited.ets @@ -0,0 +1,27 @@ +/** + * 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 C{ + private _code: number = 1 + get code(): number{ return this._code} + set code(v: number) {this._code = v} +} + +class D extends C{ + code: number = 1 +} + +/* @@? 23:17 Error TypeError: Cannot inherit from class C, because field code is inherited with a different declaration type */ -- Gitee