diff --git a/ets2panda/checker/ETSchecker.h b/ets2panda/checker/ETSchecker.h index 81fed860b6cb35c565669e0d6cccd15630130206..686d7f4163f6d10ec395416d2b86438a6a76e169 100644 --- a/ets2panda/checker/ETSchecker.h +++ b/ets2panda/checker/ETSchecker.h @@ -297,6 +297,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 2d39801012ab2d928b6e171f2a63d7393f3dfe36..a652c1fcb51d50f173f260e85b492c6e9534e9b7 100644 --- a/ets2panda/checker/ets/assignAnalyzer.cpp +++ b/ets2panda/checker/ets/assignAnalyzer.cpp @@ -1294,26 +1294,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()) { @@ -1345,7 +1325,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 a9e62345acc15565b5e1a0a22c66b43345befd32..3ac89f21a3af92b543b4f6dd5b515c69f61633c5 100644 --- a/ets2panda/checker/ets/object.cpp +++ b/ets2panda/checker/ets/object.cpp @@ -82,6 +82,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()); @@ -622,6 +631,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 b1730286944f0d3e5dd487a9b694e1fa03d7206c..57fbcfe897cadb21060fd14c22629cdaa6feaeae 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 { @@ -560,6 +561,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) @@ -589,8 +597,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); } @@ -627,15 +636,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)); @@ -801,7 +809,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 0716446a5f8dc9c1865954d58d4ab2c4eca59946..bda1083c5486217cdefa2eeae44b240f376a412c 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 0000000000000000000000000000000000000000..15cdbe65cf32e5d6f173b9b13467ae3d7692241c --- /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 */