From 34f8c9dd39d251d36fb2df8292bdc59c5c193f3b Mon Sep 17 00:00:00 2001 From: "584648456@qq.com" Date: Thu, 22 May 2025 09:16:41 +0800 Subject: [PATCH] Title:remove CTOR_CLASS_NOT_FIRST Description(optional):support write any statement (not only this) before `super` Issue:https://gitee.com/openharmony/arkcompiler_ets_frontend/issues/ICA9HY Signed-off-by: semon <584648456@qq.com> --- ets2panda/checker/ETSAnalyzer.cpp | 63 +++++++++++++- ets2panda/checker/checkerContext.h | 1 + ets2panda/checker/ets/object.cpp | 55 +++--------- ets2panda/compiler/scripts/signatures.yaml | 4 + .../ast/parser/ets/InvalidExpressions1.ets | 2 +- ets2panda/test/ast/parser/ets/super_call1.ets | 28 +++++++ ets2panda/test/ast/parser/ets/super_call2.ets | 27 ++++++ ets2panda/test/ast/parser/ets/super_call3.ets | 28 +++++++ ets2panda/test/ast/parser/ets/super_call4.ets | 34 ++++++++ ets2panda/test/runtime/ets/superCall2.ets | 83 +++++++++++++++++++ ets2panda/test/runtime/ets/superCall3.ets | 36 ++++++++ ets2panda/util/diagnostic/semantic.yaml | 16 +++- 12 files changed, 326 insertions(+), 51 deletions(-) create mode 100644 ets2panda/test/ast/parser/ets/super_call1.ets create mode 100644 ets2panda/test/ast/parser/ets/super_call2.ets create mode 100644 ets2panda/test/ast/parser/ets/super_call3.ets create mode 100644 ets2panda/test/ast/parser/ets/super_call4.ets create mode 100644 ets2panda/test/runtime/ets/superCall2.ets create mode 100644 ets2panda/test/runtime/ets/superCall3.ets diff --git a/ets2panda/checker/ETSAnalyzer.cpp b/ets2panda/checker/ETSAnalyzer.cpp index 0f45d11123..6f89927720 100644 --- a/ets2panda/checker/ETSAnalyzer.cpp +++ b/ets2panda/checker/ETSAnalyzer.cpp @@ -2378,7 +2378,8 @@ checker::Type *ETSAnalyzer::Check(ir::SuperExpression *expr) const return expr->TsType(); } - expr->SetTsType(checker->CheckThisOrSuperAccess(expr, checker->Context().ContainingClass()->SuperType(), "super")); + expr->SetTsType(checker->CheckThisOrSuperAccess(expr, checker->Context().ContainingClass()->SuperType(), + compiler::Signatures::SUPER)); return expr->TsType(); } @@ -2440,9 +2441,18 @@ checker::Type *ETSAnalyzer::Check(ir::ThisExpression *expr) const ES2PANDA_ASSERT(variable != nullptr); expr->SetTsType(variable->TsType()); } else { - expr->SetTsType(checker->CheckThisOrSuperAccess(expr, checker->Context().ContainingClass(), "this")); + expr->SetTsType( + checker->CheckThisOrSuperAccess(expr, checker->Context().ContainingClass(), compiler::Signatures::THIS)); } + if (expr->Parent()->IsMemberExpression()) { + auto sign = checker->Context().ContainingSignature(); + if (sign != nullptr && sign->Function()->IsConstructor() && !sign->Function()->IsNative() && + !(sign->Function()->IsImplicitSuperCallNeeded() || + checker->HasStatus(CheckerStatus::CONSTRUCTOR_RECORDED))) { + checker->LogError(diagnostic::SUPER_BEFORE_THIS, {}, expr->Start()); + } + } return expr->TsType(); } @@ -2740,11 +2750,60 @@ checker::Type *ETSAnalyzer::Check([[maybe_unused]] ir::AssertStatement *st) cons ES2PANDA_UNREACHABLE(); } +static void AddImplicitSuperCallNeededFlag(ETSChecker *checker, ir::BlockStatement *st) +{ + if (st->Parent() == nullptr || !st->Parent()->IsScriptFunction()) { + return; + } + auto func = st->Parent()->AsScriptFunction(); + if (func->IsNative() || !func->IsConstructor() || func->Body() == nullptr) { + return; + } + auto *variable = checker->Scope()->Find(varbinder::VarBinder::MANDATORY_PARAM_THIS).variable; + if (variable == nullptr) { + return; + } + auto classType = variable->TsType()->AsETSObjectType(); + if (classType->SuperType() == nullptr || classType == checker->GlobalETSObjectType()) { + return; + } + auto decl = classType->GetDeclNode(); + if (decl->IsDeclare() || (decl->IsClassDefinition() && decl->AsClassDefinition()->IsGlobal())) { + return; + } + + auto &stmts = func->Body()->AsBlockStatement()->Statements(); + const auto thisCall = std::find_if(stmts.begin(), stmts.end(), [](const ir::Statement *stmt) { + return stmt->IsExpressionStatement() && stmt->AsExpressionStatement()->GetExpression()->IsCallExpression() && + stmt->AsExpressionStatement()->GetExpression()->AsCallExpression()->Callee()->IsThisExpression(); + }); + // There is an alternate constructor invocation, no need for super constructor invocation + if (thisCall != stmts.end()) { + return; + } + const auto superExpr = std::find_if(stmts.begin(), stmts.end(), [](const ir::Statement *stmt) { + return stmt->IsExpressionStatement() && stmt->AsExpressionStatement()->GetExpression()->IsCallExpression() && + stmt->AsExpressionStatement()->GetExpression()->AsCallExpression()->Callee()->IsSuperExpression(); + }); + // There is no super expression + if (superExpr == stmts.end()) { + const auto superTypeCtorSigs = classType->SuperType()->ConstructSignatures(); + const auto superTypeCtorSig = std::find_if(superTypeCtorSigs.begin(), superTypeCtorSigs.end(), + [](const Signature *sig) { return sig->MinArgCount() == 0; }); + // Super type has no parameterless ctor + if (superTypeCtorSig == superTypeCtorSigs.end()) { + checker->LogError(diagnostic::CTOR_MISSING_SUPER_CALL, {}, func->Start()); + } + func->AddFlag(ir::ScriptFunctionFlags::IMPLICIT_SUPER_CALL_NEEDED); + } +} + checker::Type *ETSAnalyzer::Check(ir::BlockStatement *st) const { ETSChecker *checker = GetETSChecker(); checker::ScopeContext scopeCtx(checker, st->Scope()); + AddImplicitSuperCallNeededFlag(checker, st); // Iterator type checking of statements is modified to index type, to allow modifying the statement list during // checking without invalidating the iterator //---- Don't modify this to iterator, as it may break things during checking diff --git a/ets2panda/checker/checkerContext.h b/ets2panda/checker/checkerContext.h index aa5e6b26fc..fab993f856 100644 --- a/ets2panda/checker/checkerContext.h +++ b/ets2panda/checker/checkerContext.h @@ -77,6 +77,7 @@ enum class CheckerStatus : uint32_t { IN_SETTER = 1U << 27U, IN_EXTENSION_ACCESSOR_CHECK = 1U << 28U, IN_TYPE_INFER = 1U << 29U, + CONSTRUCTOR_RECORDED = 1U << 30U, }; } // namespace ark::es2panda::checker diff --git a/ets2panda/checker/ets/object.cpp b/ets2panda/checker/ets/object.cpp index c5724a9d7e..f3160b5b51 100644 --- a/ets2panda/checker/ets/object.cpp +++ b/ets2panda/checker/ets/object.cpp @@ -1286,50 +1286,11 @@ void ETSChecker::CheckConstructors(ir::ClassDefinition *classDef, ETSObjectType continue; } CheckCyclicConstructorCall(it); - CheckImplicitSuper(classType, it); CheckThisOrSuperCallInConstructor(classType, it); } } } -void ETSChecker::CheckImplicitSuper(ETSObjectType *classType, Signature *ctorSig) -{ - if (classType == GlobalETSObjectType()) { - return; - } - - if (ctorSig->Function()->IsNative() && ctorSig->Function()->IsConstructor()) { - return; - } - - auto &stmts = ctorSig->Function()->Body()->AsBlockStatement()->Statements(); - const auto thisCall = std::find_if(stmts.begin(), stmts.end(), [](const ir::Statement *stmt) { - return stmt->IsExpressionStatement() && stmt->AsExpressionStatement()->GetExpression()->IsCallExpression() && - stmt->AsExpressionStatement()->GetExpression()->AsCallExpression()->Callee()->IsThisExpression(); - }); - // There is an alternate constructor invocation, no need for super constructor invocation - if (thisCall != stmts.end()) { - return; - } - - const auto superExpr = std::find_if(stmts.begin(), stmts.end(), [](const ir::Statement *stmt) { - return stmt->IsExpressionStatement() && stmt->AsExpressionStatement()->GetExpression()->IsCallExpression() && - stmt->AsExpressionStatement()->GetExpression()->AsCallExpression()->Callee()->IsSuperExpression(); - }); - // There is no super expression - if (superExpr == stmts.end()) { - const auto superTypeCtorSigs = classType->SuperType()->ConstructSignatures(); - const auto superTypeCtorSig = std::find_if(superTypeCtorSigs.begin(), superTypeCtorSigs.end(), - [](const Signature *sig) { return sig->MinArgCount() == 0; }); - // Super type has no parameterless ctor - if (superTypeCtorSig == superTypeCtorSigs.end()) { - LogError(diagnostic::CTOR_MISSING_SUPER_CALL, {}, ctorSig->Function()->Start()); - } - - ctorSig->Function()->AddFlag(ir::ScriptFunctionFlags::IMPLICIT_SUPER_CALL_NEEDED); - } -} - void ETSChecker::CheckThisOrSuperCallInConstructor(ETSObjectType *classType, Signature *ctorSig) { if (classType == GlobalETSObjectType()) { @@ -1697,8 +1658,8 @@ ETSObjectType *ETSChecker::CheckThisOrSuperAccess(ir::Expression *node, ETSObjec if (node->Parent()->IsCallExpression() && (node->Parent()->AsCallExpression()->Callee() == node) && !node->Parent()->HasAstNodeFlags(ir::AstNodeFlags::RESIZABLE_REST)) { + // like 'static constructor(){}' ,there is already an error:'INVALID_DECORATOR_CONSTRUCTOR' ,so return directly. if (Context().ContainingSignature() == nullptr) { - LogError(diagnostic::CTOR_CLASS_NOT_FIRST, {msg}, node->Start()); return classType; } @@ -1706,13 +1667,19 @@ ETSObjectType *ETSChecker::CheckThisOrSuperAccess(ir::Expression *node, ETSObjec ES2PANDA_ASSERT(sig->Function()->Body() && sig->Function()->Body()->IsBlockStatement()); if (!sig->HasSignatureFlag(checker::SignatureFlags::CONSTRUCT)) { - LogError(diagnostic::CTOR_CLASS_NOT_FIRST, {msg}, node->Start()); + LogError(diagnostic::SUPER_MUST_BE_IN_CONSTRUCTOR, {msg}, node->Start()); return classType; } - if (sig->Function()->Body()->AsBlockStatement()->Statements().front() != node->Parent()->Parent()) { - LogError(diagnostic::CTOR_CLASS_NOT_FIRST, {msg}, node->Start()); - return classType; + auto statements = sig->Function()->Body()->AsBlockStatement()->Statements(); + if (statements.end() != std::find_if(statements.begin(), statements.end(), [node](ir::Statement *st) { + return st->IsExpressionStatement() && st->AsExpressionStatement()->GetExpression() == node->Parent(); + })) { + if (HasStatus(CheckerStatus::CONSTRUCTOR_RECORDED)) { + LogError(diagnostic::SUPER_ONLY_ONCE, {}, node->Start()); + return classType; + } + AddStatus(CheckerStatus::CONSTRUCTOR_RECORDED); } } diff --git a/ets2panda/compiler/scripts/signatures.yaml b/ets2panda/compiler/scripts/signatures.yaml index 496aa8e961..53574daea9 100644 --- a/ets2panda/compiler/scripts/signatures.yaml +++ b/ets2panda/compiler/scripts/signatures.yaml @@ -218,6 +218,10 @@ defines: - name: 'InterfaceObjectLiteral' ref: INTERFACE_OBJ_LITERAL comment: ETS annotation type + - name: 'super' + ref: SUPER + - name: 'this' + ref: THIS packages: - name: 'std.core' diff --git a/ets2panda/test/ast/parser/ets/InvalidExpressions1.ets b/ets2panda/test/ast/parser/ets/InvalidExpressions1.ets index 0509c0f38c..588ddfa9fc 100644 --- a/ets2panda/test/ast/parser/ets/InvalidExpressions1.ets +++ b/ets2panda/test/ast/parser/ets/InvalidExpressions1.ets @@ -69,7 +69,7 @@ let a = [1, 2, 3); /* @@? 30:13 Error SyntaxError: Import declarations can only be used on the top level and before any other declaration, top level statement or directive. */ /* @@? 30:13 Error SyntaxError: Invalid Type. */ /* @@? 32:10 Error TypeError: Variable 'f' has already been declared. */ -/* @@? 33:5 Error TypeError: Call to 'super' must be first statement in constructor */ +/* @@? 33:5 Error TypeError: 'super()' calls are not permitted outside constructors. */ /* @@? 33:5 Error TypeError: Expected 0 arguments, got 1. */ /* @@? 33:5 Error TypeError: No matching call signature for std.core.Object(int) */ /* @@? 33:10 Error SyntaxError: Unexpected super keyword. */ diff --git a/ets2panda/test/ast/parser/ets/super_call1.ets b/ets2panda/test/ast/parser/ets/super_call1.ets new file mode 100644 index 0000000000..c006104d3e --- /dev/null +++ b/ets2panda/test/ast/parser/ets/super_call1.ets @@ -0,0 +1,28 @@ +/* + * 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 { + constructor() { + } + } + +class B extends A { + foo() { + /* @@ label */super/* @@ label1 */(); + } +} + +/* @@@ label Error TypeError: 'super()' calls are not permitted outside constructors. */ +/* @@@ label1 Error SyntaxError: Unexpected super keyword. */ diff --git a/ets2panda/test/ast/parser/ets/super_call2.ets b/ets2panda/test/ast/parser/ets/super_call2.ets new file mode 100644 index 0000000000..d031d06c9f --- /dev/null +++ b/ets2panda/test/ast/parser/ets/super_call2.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 A { + constructor() {} + } + +class B extends A { + constructor() { + super(); + /* @@ label */super(); + } +} + +/* @@@ label Error TypeError: Super constructor may only be called once. */ diff --git a/ets2panda/test/ast/parser/ets/super_call3.ets b/ets2panda/test/ast/parser/ets/super_call3.ets new file mode 100644 index 0000000000..8ebdb0e76f --- /dev/null +++ b/ets2panda/test/ast/parser/ets/super_call3.ets @@ -0,0 +1,28 @@ +/* + * 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 { + constructor() {} + } + +class B extends A { + x:number = 0; + constructor() { + /* @@ label */this.x = 6; + super(); + } +} + +/* @@@ label Error TypeError: 'super()' must be called before accessing 'this' in the constructor of a derived class. */ diff --git a/ets2panda/test/ast/parser/ets/super_call4.ets b/ets2panda/test/ast/parser/ets/super_call4.ets new file mode 100644 index 0000000000..6351c3e7eb --- /dev/null +++ b/ets2panda/test/ast/parser/ets/super_call4.ets @@ -0,0 +1,34 @@ +/* + * 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{ + a:number; + constructor(n:number){ + this.a = n; + console.log('A') + } +} +class B extends A { + x: number = 0; + constructor() { + if(true){ + /* @@ label */this.a = 6; + } + super(8) + this.x = 6; + } +} + +/* @@@ label Error TypeError: 'super()' must be called before accessing 'this' in the constructor of a derived class. */ diff --git a/ets2panda/test/runtime/ets/superCall2.ets b/ets2panda/test/runtime/ets/superCall2.ets new file mode 100644 index 0000000000..cba39feee7 --- /dev/null +++ b/ets2panda/test/runtime/ets/superCall2.ets @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2023-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 testInterface { + info(): string + get(): T + set(newValue: T): void +} + +class implements1 implements testInterface { + private name: string + private state: Value + + constructor(name: string, state: Value) { + this.name = name + this.state = state + } + + info(): string { + return this.name + } + + get(): Value { + return this.state + } + + set(value: Value): void { + this.state = value + } +} + +class extend1 extends implements1 { + constructor(name: string, value: Value) { + super(name, value) + } +} + +class extend2 extends extend1 { + private value: Value | undefined = undefined + private overType: string + + constructor(name: string, value: Value, overType: string = 'def') { + super(name, value) + this.overType = overType + } + + override get(): Value { + const state = super.get() + const value = this.value + return value ? value : state + } + + override set(value: Value): void { + super.set(value) + this.value = undefined + } + + update(value?: Value): void { + } + + getOverType(): string { + return this.overType; + } +} + +function main(): void { + let e1 = new extend2('e1','v1'); + let e2 = new extend2('e2','v2','custom'); + assertEQ(e1.getOverType(), 'def') + assertEQ(e2.getOverType(), 'custom') +} diff --git a/ets2panda/test/runtime/ets/superCall3.ets b/ets2panda/test/runtime/ets/superCall3.ets new file mode 100644 index 0000000000..789b3e39dd --- /dev/null +++ b/ets2panda/test/runtime/ets/superCall3.ets @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2023-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{ + public x:number; + constructor(n:number){ + this.x = n; + } +} +class B extends A { + public y: number = 0; + constructor() { + let foo = ()=>{ + this.x = 6 + } + super(8) + foo(); + } +} + +function main(): void { + let b = new B(); + assertEQ(b.x, 6); +} diff --git a/ets2panda/util/diagnostic/semantic.yaml b/ets2panda/util/diagnostic/semantic.yaml index e61143986b..f8970e0af9 100644 --- a/ets2panda/util/diagnostic/semantic.yaml +++ b/ets2panda/util/diagnostic/semantic.yaml @@ -807,10 +807,6 @@ semantic: id: 200 message: "Only integer type allowed for element access on tuples." -- name: CTOR_CLASS_NOT_FIRST - id: 201 - message: "Call to '{}' must be first statement in constructor" - - name: CTOR_REF_IN_STATIC_CTX id: 202 message: "'{}' cannot be referenced from a static context" @@ -1482,3 +1478,15 @@ semantic: - name: SUPER_NOT_ACCESSIBLE id: 372 message: "Class field '{}' defined by the parent class is not accessible in the child class via super." + +- name: SUPER_MUST_BE_IN_CONSTRUCTOR + id: 373 + message: "'{}()' calls are not permitted outside constructors." + +- name: SUPER_ONLY_ONCE + id: 374 + message: "Super constructor may only be called once." + +- name: SUPER_BEFORE_THIS + id: 375 + message: "'super()' must be called before accessing 'this' in the constructor of a derived class." \ No newline at end of file -- Gitee