From 8bb1d8a98beb9f8b4e1ef139c619bbdd9c66ba96 Mon Sep 17 00:00:00 2001 From: Tatiana Date: Thu, 12 Oct 2023 16:36:22 +0300 Subject: [PATCH 1/4] Add AST Verifier feature Signed-off-by: Tatiana --- BUILD.gn | 1 + CMakeLists.txt | 1 + compiler/core/ASTVerifier.cpp | 409 ++++++++++++++++++++++++++++++ compiler/core/ASTVerifier.h | 55 ++++ compiler/lowering/phase.cpp | 9 + ir/astNode.h | 10 + test/CMakeLists.txt | 12 + test/public/ast_verifier_test.cpp | 71 ++++++ 8 files changed, 568 insertions(+) create mode 100644 compiler/core/ASTVerifier.cpp create mode 100644 compiler/core/ASTVerifier.h create mode 100644 test/public/ast_verifier_test.cpp diff --git a/BUILD.gn b/BUILD.gn index 99b4a26ef..8663df872 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -138,6 +138,7 @@ libes2panda_sources = [ "compiler/core/JSCompiler.cpp", "compiler/core/JSemitter.cpp", "compiler/core/codeGen.cpp", + "compiler/core/ASTVerifier.cpp", "compiler/core/compileJob.cpp", "compiler/core/compileQueue.cpp", "compiler/core/compilerContext.cpp", diff --git a/CMakeLists.txt b/CMakeLists.txt index e49880048..b34882d04 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -102,6 +102,7 @@ set(ES2PANDA_LIB_SRC compiler/base/literals.cpp compiler/base/lreference.cpp compiler/base/optionalChain.cpp + compiler/core/ASTVerifier.cpp compiler/core/codeGen.cpp compiler/core/compileJob.cpp compiler/core/compileQueue.cpp diff --git a/compiler/core/ASTVerifier.cpp b/compiler/core/ASTVerifier.cpp new file mode 100644 index 000000000..f59438c30 --- /dev/null +++ b/compiler/core/ASTVerifier.cpp @@ -0,0 +1,409 @@ +/** + * Copyright (c) 2021-2023 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. + */ + +#include "ASTVerifier.h" + +#include "es2panda.h" +#include "binder/variableFlags.h" +#include "binder/scope.h" +#include "ir/astNode.h" +#include "ir/base/catchClause.h" +#include "ir/base/classDefinition.h" +#include "ir/base/classStaticBlock.h" +#include "ir/base/methodDefinition.h" +#include "ir/base/scriptFunction.h" +#include "ir/ets/etsFunctionType.h" +#include "ir/ets/etsNewClassInstanceExpression.h" +#include "ir/ets/etsPackageDeclaration.h" +#include "ir/ets/etsParameterExpression.h" +#include "ir/ets/etsTypeReference.h" +#include "ir/ets/etsTypeReferencePart.h" +#include "ir/expressions/callExpression.h" +#include "ir/expressions/functionExpression.h" +#include "ir/expressions/identifier.h" +#include "ir/expressions/memberExpression.h" +#include "ir/expressions/literals/numberLiteral.h" +#include "ir/expressions/literals/stringLiteral.h" +#include "ir/statements/blockStatement.h" +#include "ir/statements/classDeclaration.h" +#include "ir/statements/expressionStatement.h" +#include "ir/statements/throwStatement.h" +#include "ir/statements/tryStatement.h" +#include "ir/statements/variableDeclaration.h" +#include "ir/statements/variableDeclarator.h" +#include "ir/ts/tsClassImplements.h" +#include "ir/ts/tsTypeParameter.h" +#include "ir/ts/tsTypeParameterDeclaration.h" +#include "ir/ts/tsTypeParameterInstantiation.h" + +namespace panda::es2panda::compiler { + +bool ASTVerifier::IsCorrectProgram(const parser::Program *program) +{ + bool is_correct = true; + error_messages_.clear(); + + for (auto *statement : program->Ast()->Statements()) { + is_correct &= HaveParents(statement); + } + is_correct &= HaveParents(program->GlobalClass()); + + for (auto *statement : program->Ast()->Statements()) { + is_correct &= HaveTypes(statement); + } + is_correct &= HaveTypes(program->GlobalClass()); + + for (auto *statement : program->Ast()->Statements()) { + is_correct &= HaveVariables(statement); + } + is_correct &= HaveVariables(program->GlobalClass()); + + for (auto *statement : program->Ast()->Statements()) { + is_correct &= HaveScopes(statement); + } + is_correct &= HaveScopes(program->GlobalClass()); + +#ifndef NDEBUG + std::for_each(error_messages_.begin(), error_messages_.end(), [](auto const msg) { LOG(INFO, COMMON) << msg; }); +#endif // NDEBUG + return is_correct; +} + +std::string ToStringHelper(const binder::ScopeType type) +{ + switch (type) { + case binder::ScopeType::CATCH: { + return "CATCH"; + } + case binder::ScopeType::CATCH_PARAM: { + return "CATCH_PARAM"; + } + case binder::ScopeType::CLASS: { + return "CLASS"; + } + case binder::ScopeType::FUNCTION: { + return "FUNCTION"; + } + case binder::ScopeType::FUNCTION_PARAM: { + return "FUNCTION_PARAM"; + } + case binder::ScopeType::GLOBAL: { + return "GLOBAL"; + } + case binder::ScopeType::LOCAL: { + return "LOCAL"; + } + case binder::ScopeType::LOOP: { + return "LOOP"; + } + case binder::ScopeType::LOOP_DECL: { + return "LOOP_DECL"; + } + case binder::ScopeType::MODULE: { + return "MODULE"; + } + case binder::ScopeType::PARAM: { + return "PARAM"; + } + default: { + return "MUST BE UNREACHABLE"; + } + } +} + +std::string ToStringHelper(const util::StringView &name) +{ + return name == nullptr ? "" : name.Mutf8(); +} + +std::string ToStringHelper(const binder::Scope *scope) +{ + if (scope == nullptr) { + return ""; + } + + switch (scope->Type()) { + case binder::ScopeType::FUNCTION: { + return "FUNC_SCOPE " + ToStringHelper(scope->AsFunctionScope()->Name()); + } + case binder::ScopeType::LOCAL: { + return "LOCAL_SCOPE "; + } + case binder::ScopeType::CATCH: { + return "CATCH_SCOPE "; + } + default: { + return "MUST BE UNREACHABLE"; + } + } +} + +std::string ToStringHelper(const binder::Variable *var) +{ + if (var == nullptr) { + return ""; + } + + switch (var->Type()) { + case binder::VariableType::LOCAL: { + return "LOCAL_VAR " + ToStringHelper(var->Name()); + } + case binder::VariableType::MODULE: { + return "MODULE_VAR " + ToStringHelper(var->Name()); + } + case binder::VariableType::GLOBAL: { + return "GLOBAL_VAR " + ToStringHelper(var->Name()); + } + case binder::VariableType::ENUM: { + return "ENUM_VAR " + ToStringHelper(var->Name()); + } + default: { + return "MUST BE UNREACHABLE"; + } + } +} + +template +std::string ToStringParamsHelper(const ir::AstNode *parent, const ArenaVector ¶ms) +{ + std::string name; + if (parent != nullptr) { + name = ToStringHelper(parent) + " "; + } + + name += "("; + for (auto const *param : params) { + name += ToStringHelper(param); + } + + return name + ")"; +} + +std::string ToStringHelper(const ir::AstNode *ast) +{ + if (ast == nullptr) { + return ""; + } + + switch (ast->Type()) { + case ir::AstNodeType::IDENTIFIER: { + return "ID " + ToStringHelper(ast->AsIdentifier()->Name()); + } + case ir::AstNodeType::CLASS_DEFINITION: { + return "CLS_DEF " + ToStringHelper(ast->AsClassDefinition()->Ident()); + } + case ir::AstNodeType::CLASS_DECLARATION: { + return "CLS_DECL " + ToStringHelper(ast->AsClassDeclaration()->Definition()); + } + case ir::AstNodeType::BLOCK_STATEMENT: { + return "BLOCK " + ToStringHelper(ast->AsBlockStatement()->Scope()); + } + case ir::AstNodeType::SCRIPT_FUNCTION: { + auto const *sf = ast->AsScriptFunction(); + return "SCRIPT_FUN " + ToStringHelper(sf->Scope()) + "::" + ToStringHelper(sf->Id()); + } + case ir::AstNodeType::FUNCTION_EXPRESSION: { + return "FUN_EXPR " + ToStringHelper(ast->AsFunctionExpression()->Function()); + } + case ir::AstNodeType::METHOD_DEFINITION: { + return "METHOD_DEF " + ToStringHelper(ast->AsMethodDefinition()->Value()); + } + case ir::AstNodeType::ETS_TYPE_REFERENCE_PART: { + return "TYPE_REF_PART " + ToStringHelper(ast->AsETSTypeReferencePart()->Name()); + } + case ir::AstNodeType::ETS_TYPE_REFERENCE: { + return "TYPE_REF " + ToStringHelper(ast->AsETSTypeReference()->Part()); + } + case ir::AstNodeType::VARIABLE_DECLARATOR: { + return "VAR_DECLARATOR " + ToStringHelper(ast->AsVariableDeclarator()->Id()); + } + case ir::AstNodeType::VARIABLE_DECLARATION: { + if (ast->AsVariableDeclaration()->Declarators().empty()) { + return "VAR_DECLARATION "; + } + return "VAR_DECLARATION " + ToStringHelper(ast->AsVariableDeclaration()->Declarators().at(0)); + } + case ir::AstNodeType::CALL_EXPRESSION: { + return "CALL_EXPR " + ToStringHelper(ast->AsCallExpression()->Callee()) + "(...)"; + } + case ir::AstNodeType::EXPRESSION_STATEMENT: { + return "EXPR_STMT " + ToStringHelper(ast->AsExpressionStatement()->GetExpression()); + } + case ir::AstNodeType::MEMBER_EXPRESSION: { + auto const *me = ast->AsMemberExpression(); + return "MEMBER_EXPR " + ToStringHelper(me->Object()) + "." + ToStringHelper(me->Property()); + } + case ir::AstNodeType::CLASS_STATIC_BLOCK: { + return "CLS_STATIC_BLOCK " + ToStringHelper(ast->AsClassStaticBlock()->Function()); + } + case ir::AstNodeType::ETS_PACKAGE_DECLARATION: { + return "PKG_DECL "; + } + case ir::AstNodeType::TS_TYPE_PARAMETER_DECLARATION: { + return "PARAM_DECL " + ToStringParamsHelper( + ast->Parent(), ast->AsTSTypeParameterDeclaration()->Params()); + } + case ir::AstNodeType::TS_TYPE_PARAMETER: { + return "TYPE_PARAM " + ToStringHelper(ast->AsTSTypeParameter()->Name()); + } + case ir::AstNodeType::TS_TYPE_PARAMETER_INSTANTIATION: { + return "PARAM_INSTANTIATION " + + ToStringParamsHelper(ast->Parent(), ast->AsTSTypeParameterInstantiation()->Params()); + } + case ir::AstNodeType::THROW_STATEMENT: { + return "THROW_STMT " + ToStringHelper(ast->AsThrowStatement()->Argument()); + } + case ir::AstNodeType::ETS_NEW_CLASS_INSTANCE_EXPRESSION: { + return "NEW_CLS_INSTANCE " + ToStringHelper(ast->AsETSNewClassInstanceExpression()->GetTypeRef()); + } + case ir::AstNodeType::STRING_LITERAL: { + return "STR_LITERAL " + ToStringHelper(ast->AsStringLiteral()->Str()); + } + case ir::AstNodeType::TRY_STATEMENT: { + return "TRY_STMT " + ToStringHelper(ast->AsTryStatement()->Block()); + } + case ir::AstNodeType::CATCH_CLAUSE: { + return "CATCH_CLAUSE "; + } + case ir::AstNodeType::NUMBER_LITERAL: { + return "NUMBER_LITERAL " + ToStringHelper(ast->AsNumberLiteral()->Str()); + } + case ir::AstNodeType::ETS_PARAMETER_EXPRESSION: { + return "ETS_PARAM_EXPR " + ToStringHelper(ast->AsETSParameterExpression()->Ident()); + } + case ir::AstNodeType::TS_INTERFACE_DECLARATION: { + return "TS_INTERFACE_DECL " + ToStringHelper(ast->AsTSInterfaceDeclaration()->Id()); + } + case ir::AstNodeType::TS_INTERFACE_BODY: { + return "TS_INTERFACE_BODY "; + } + case ir::AstNodeType::ETS_FUNCTION_TYPE: { + return "ETS_FUNC_TYPE " + + ToStringParamsHelper(ast->Parent(), ast->AsETSFunctionType()->Params()); + } + case ir::AstNodeType::TS_CLASS_IMPLEMENTS: { + return "TS_CLASS_IMPL " + ToStringHelper(ast->AsTSClassImplements()->Expr()); + } + default: { + return "MUST BE UNREACHABLE"; + } + } +} + +bool ASTVerifier::HasParent(const ir::AstNode *ast) +{ + if (ast == nullptr) { + return false; + } + + if (ast->Parent() == nullptr) { + error_messages_.push_back("NULL_PARENT: " + ToStringHelper(ast)); + return false; + } + + return true; +} + +bool ASTVerifier::HaveParents(const ir::AstNode *ast) +{ + if (ast == nullptr) { + return false; + } + + bool has_parent = HasParent(ast); + ast->IterateRecursively([this, &has_parent](ir::AstNode *child) { has_parent &= HasParent(child); }); + return has_parent; +} + +bool ASTVerifier::HasType(const ir::AstNode *ast) +{ + if (ast == nullptr) { + return false; + } + + if (ast->IsTyped() && static_cast(ast)->TsType() == nullptr) { + error_messages_.push_back("NULL_TS_TYPE: " + ToStringHelper(ast)); + return false; + } + return true; +} + +bool ASTVerifier::HaveTypes(const ir::AstNode *ast) +{ + if (ast == nullptr) { + return false; + } + + bool has_type = HasType(ast); + ast->IterateRecursively([this, &has_type](ir::AstNode *child) { has_type &= HasType(child); }); + return has_type; +} + +bool ASTVerifier::HasVariable(const ir::AstNode *ast) +{ + if (ast == nullptr) { + return false; + } + + if (!ast->IsIdentifier() || ast->AsIdentifier()->Variable() != nullptr) { + return true; + } + + error_messages_.push_back("NULL_VARIABLE: " + ToStringHelper(ast->AsIdentifier())); + return false; +} + +bool ASTVerifier::HaveVariables(const ir::AstNode *ast) +{ + if (ast == nullptr) { + return false; + } + + bool has_variable = HasVariable(ast); + ast->IterateRecursively([this, &has_variable](ir::AstNode *child) { has_variable &= HasVariable(child); }); + return has_variable; +} + +bool ASTVerifier::HasScope(const ir::AstNode *ast) +{ + if (ast == nullptr) { + return false; + } + + if (!ast->IsIdentifier()) { + return true; // we will check only Identifier + } + // we will check only local variables of identifiers + if (HasVariable(ast) && ast->AsIdentifier()->Variable()->IsLocalVariable() && + ast->AsIdentifier()->Variable()->AsLocalVariable()->GetScope() == nullptr) { + error_messages_.push_back("NULL_SCOPE_LOCAL_VAR: " + ToStringHelper(ast)); + return false; + } + // TODO(tatiana): Add check that the scope enclose this identifier + return true; +} + +bool ASTVerifier::HaveScopes(const ir::AstNode *ast) +{ + if (ast == nullptr) { + return false; + } + + bool has_scope = HasScope(ast); + ast->IterateRecursively([this, &has_scope](ir::AstNode *child) { has_scope &= HasScope(child); }); + return has_scope; +} + +} // namespace panda::es2panda::compiler diff --git a/compiler/core/ASTVerifier.h b/compiler/core/ASTVerifier.h new file mode 100644 index 000000000..d9bdba118 --- /dev/null +++ b/compiler/core/ASTVerifier.h @@ -0,0 +1,55 @@ +/** + * Copyright (c) 2021-2022 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. + */ + +#ifndef ES2PANDA_COMPILER_CORE_ASTVERIFIER_H +#define ES2PANDA_COMPILER_CORE_ASTVERIFIER_H + +#include "parser/program/program.h" + +namespace panda::es2panda::compiler { + +class ASTVerifier { +public: + using ErrorMessages = std::vector; + NO_COPY_SEMANTIC(ASTVerifier); + NO_MOVE_SEMANTIC(ASTVerifier); + + ASTVerifier() = default; + ~ASTVerifier() = default; + + bool IsCorrectProgram(const parser::Program *program); + bool HaveParents(const ir::AstNode *ast); + bool HasParent(const ir::AstNode *ast); + bool HaveTypes(const ir::AstNode *ast); + bool HasType(const ir::AstNode *ast); + bool HaveVariables(const ir::AstNode *ast); + bool HasVariable(const ir::AstNode *ast); + bool HasScope(const ir::AstNode *ast); + bool HaveScopes(const ir::AstNode *ast); + + ErrorMessages GetErrorMessages() + { + return error_messages_; + } + +private: + ErrorMessages error_messages_; +}; + +std::string ToStringHelper(const ir::AstNode *ast); + +} // namespace panda::es2panda::compiler + +#endif // ES2PANDA_COMPILER_CORE_ASTVERIFIER_H diff --git a/compiler/lowering/phase.cpp b/compiler/lowering/phase.cpp index 5877c6e49..0b7c30057 100644 --- a/compiler/lowering/phase.cpp +++ b/compiler/lowering/phase.cpp @@ -15,6 +15,7 @@ #include "phase.h" #include "checker/checker.h" +#include "compiler/core/ASTVerifier.h" #include "compiler/core/compilerContext.h" #include "lexer/token/sourceLocation.h" #include "compiler/lowering/checkerPhase.h" @@ -57,6 +58,10 @@ bool Phase::Apply(CompilerContext *ctx, parser::Program *program) } #ifndef NDEBUG + ASTVerifier ast_before; + if (!ast_before.IsCorrectProgram(program)) { + // TODO(tatiana): Add some error processing + } if (!Precondition(ctx, program)) { ctx->Checker()->ThrowTypeError({"Precondition check failed for ", Name()}, lexer::SourcePosition {}); } @@ -72,6 +77,10 @@ bool Phase::Apply(CompilerContext *ctx, parser::Program *program) } #ifndef NDEBUG + ASTVerifier ast_after; + if (!ast_after.IsCorrectProgram(program)) { + // TODO(tatiana): Add some error processing + } if (!Postcondition(ctx, program)) { ctx->Checker()->ThrowTypeError({"Postcondition check failed for ", Name()}, lexer::SourcePosition {}); } diff --git a/ir/astNode.h b/ir/astNode.h index b9558663a..5accd1e72 100644 --- a/ir/astNode.h +++ b/ir/astNode.h @@ -227,6 +227,11 @@ public: return false; } + virtual bool IsTyped() const + { + return false; + } + // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) #define DECLARE_AS_CASTS(nodeType, className) \ className *As##className() \ @@ -587,6 +592,11 @@ public: ts_type_ = ts_type; } + bool IsTyped() const override + { + return true; + } + protected: explicit Typed(AstNodeType const type) : T(type) {} explicit Typed(AstNodeType const type, ModifierFlags const flags) : T(type, flags) {} diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index f40aed24b..ff14df7c4 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -92,5 +92,17 @@ if(PANDA_WITH_ETS) add_dependencies(ets_tests es2panda_tests) endif() + panda_add_gtest( + NAME es2panda_astverifier_tests + SOURCES + public/ast_verifier_test.cpp + LIBRARIES + es2panda-lib + INCLUDE_DIRS + ${ES2PANDA_PATH} + SANITIZERS + ${PANDA_SANITIZERS_LIST} + ) + add_subdirectory(tsconfig) endif() diff --git a/test/public/ast_verifier_test.cpp b/test/public/ast_verifier_test.cpp new file mode 100644 index 000000000..dd36a1937 --- /dev/null +++ b/test/public/ast_verifier_test.cpp @@ -0,0 +1,71 @@ +/** + * Copyright (c) 2021-2023 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. + */ + +#include +#include +#include "macros.h" + +#include "compiler/core/ASTVerifier.h" +#include "ir/astDump.h" +#include "ir/expressions/literals/stringLiteral.h" + +class ASTVerifierTest : public testing::Test { +public: + ASTVerifierTest() = default; + ~ASTVerifierTest() override = default; + + NO_COPY_SEMANTIC(ASTVerifierTest); + NO_MOVE_SEMANTIC(ASTVerifierTest); + +private: +}; + +TEST_F(ASTVerifierTest, NullParent) +{ + panda::es2panda::compiler::ASTVerifier verifier {}; + panda::es2panda::ir::StringLiteral empty_node; + + bool has_parent = verifier.HasParent(&empty_node); + auto messages = verifier.GetErrorMessages(); + + ASSERT_EQ(has_parent, false); + ASSERT_NE(messages.size(), 0); + ASSERT_EQ(messages[0], "NULL_PARENT: STR_LITERAL "); +} + +TEST_F(ASTVerifierTest, NullType) +{ + panda::es2panda::compiler::ASTVerifier verifier {}; + panda::es2panda::ir::StringLiteral empty_node; + + bool has_type = verifier.HasType(&empty_node); + auto messages = verifier.GetErrorMessages(); + + ASSERT_EQ(has_type, false); + ASSERT_NE(messages.size(), 0); + ASSERT_EQ(messages[0], "NULL_TS_TYPE: STR_LITERAL "); +} + +TEST_F(ASTVerifierTest, WithoutScope) +{ + panda::es2panda::compiler::ASTVerifier verifier {}; + panda::es2panda::ir::StringLiteral empty_node; + + bool has_scope = verifier.HasScope(&empty_node); + auto messages = verifier.GetErrorMessages(); + + ASSERT_EQ(has_scope, true); + ASSERT_EQ(messages.size(), 0); +} -- Gitee From 3bbd75fe73c352d5a5ee373b596440cd1d16af54 Mon Sep 17 00:00:00 2001 From: Csaba Hurton Date: Fri, 13 Oct 2023 16:02:57 +0200 Subject: [PATCH 2/4] Move Compile and Check Logic from ASTNode classes Round #4 Linked Internal Issue 13840 Signed-off-by: Csaba Hurton --- checker/ETSAnalyzer.cpp | 112 +++++++++++++--- checker/SemanticAnalyzer.h | 146 +++++++++++++++++++++ checker/TSAnalyzer.cpp | 56 +++++--- compiler/core/ASTCompiler.h | 146 +++++++++++++++++++++ compiler/core/ETSCompiler.cpp | 51 ++++--- compiler/core/JSCompiler.cpp | 34 ++--- ir/ets/etsParameterExpression.cpp | 37 ++---- ir/ets/etsParameterExpression.h | 7 + ir/ets/etsPrimitiveType.cpp | 20 +-- ir/ets/etsPrimitiveType.h | 8 +- ir/ets/etsStructDeclaration.cpp | 16 +-- ir/ets/etsStructDeclaration.h | 8 +- ir/ets/etsTypeReference.cpp | 21 +-- ir/ets/etsTypeReference.h | 8 +- ir/ets/etsTypeReferencePart.cpp | 22 ++-- ir/ets/etsTypeReferencePart.h | 8 +- ir/ets/etsWildcardType.cpp | 24 ++-- ir/ets/etsWildcardType.h | 8 +- ir/expressions/arrowFunctionExpression.cpp | 111 +--------------- ir/expressions/arrowFunctionExpression.h | 12 +- ir/statements/tryStatement.h | 2 +- 21 files changed, 576 insertions(+), 281 deletions(-) diff --git a/checker/ETSAnalyzer.cpp b/checker/ETSAnalyzer.cpp index 2e05e528f..33a317b03 100644 --- a/checker/ETSAnalyzer.cpp +++ b/checker/ETSAnalyzer.cpp @@ -16,6 +16,8 @@ #include "binder/binder.h" #include "binder/ETSBinder.h" +#include "checker/ETSchecker.h" +#include "checker/ets/castingContext.h" #include "checker/ets/typeRelationContext.h" #include "ir/base/catchClause.h" #include "ir/base/classProperty.h" @@ -233,37 +235,55 @@ checker::Type *ETSAnalyzer::Check(ir::ETSPackageDeclaration *st) const checker::Type *ETSAnalyzer::Check(ir::ETSParameterExpression *expr) const { - (void)expr; - UNREACHABLE(); + ETSChecker *checker = GetETSChecker(); + if (expr->TsType() == nullptr) { + checker::Type *param_type; + + if (expr->Ident()->TsType() != nullptr) { + param_type = expr->Ident()->TsType(); + } else { + param_type = !expr->IsRestParameter() ? expr->Ident()->Check(checker) : expr->spread_->Check(checker); + if (expr->IsDefault()) { + [[maybe_unused]] auto *const init_type = expr->Initializer()->Check(checker); + // TODO(ttamas) : fix this aftet nullable fix + // const checker::AssignmentContext ctx(checker->Relation(), initializer_, init_type, name_type, + // initializer_->Start(), + // {"Initializers type is not assignable to the target type"}); + } + } + + expr->SetTsType(param_type); + } + + return expr->TsType(); } -checker::Type *ETSAnalyzer::Check(ir::ETSPrimitiveType *node) const +checker::Type *ETSAnalyzer::Check([[maybe_unused]] ir::ETSPrimitiveType *node) const { - (void)node; - UNREACHABLE(); + return nullptr; } checker::Type *ETSAnalyzer::Check(ir::ETSStructDeclaration *node) const { - (void)node; - UNREACHABLE(); + ETSChecker *checker = GetETSChecker(); + node->Definition()->Check(checker); + return nullptr; } checker::Type *ETSAnalyzer::Check(ir::ETSTypeReference *node) const { - (void)node; - UNREACHABLE(); + ETSChecker *checker = GetETSChecker(); + return node->GetType(checker); } checker::Type *ETSAnalyzer::Check(ir::ETSTypeReferencePart *node) const { - (void)node; - UNREACHABLE(); + ETSChecker *checker = GetETSChecker(); + return node->GetType(checker); } -checker::Type *ETSAnalyzer::Check(ir::ETSWildcardType *node) const +checker::Type *ETSAnalyzer::Check([[maybe_unused]] ir::ETSWildcardType *node) const { - (void)node; UNREACHABLE(); } // compile methods for EXPRESSIONS in alphabetical order @@ -275,8 +295,70 @@ checker::Type *ETSAnalyzer::Check(ir::ArrayExpression *expr) const checker::Type *ETSAnalyzer::Check(ir::ArrowFunctionExpression *expr) const { - (void)expr; - UNREACHABLE(); + ETSChecker *checker = GetETSChecker(); + if (expr->TsType() != nullptr) { + return expr->TsType(); + } + + auto *func_type = checker->BuildFunctionSignature(expr->Function(), false); + + if (expr->Function()->IsAsyncFunc()) { + auto *ret_type = static_cast(expr->Function()->Signature()->ReturnType()); + if (ret_type->AssemblerName() != checker->GlobalBuiltinPromiseType()->AssemblerName()) { + checker->ThrowTypeError("Return type of async lambda must be 'Promise'", expr->Function()->Start()); + } + } + + checker::ScopeContext scope_ctx(checker, expr->Function()->Scope()); + + if (checker->HasStatus(checker::CheckerStatus::IN_INSTANCE_EXTENSION_METHOD)) { + /* + example code: + ``` + class A { + prop:number + } + function A.method() { + let a = () => { + console.println(this.prop) + } + } + ``` + here the enclosing class of arrow function should be Class A + */ + checker->Context().SetContainingClass( + checker->Scope()->Find(binder::Binder::MANDATORY_PARAM_THIS).variable->TsType()->AsETSObjectType()); + } + + checker::SavedCheckerContext saved_context(checker, checker->Context().Status(), + checker->Context().ContainingClass()); + checker->AddStatus(checker::CheckerStatus::IN_LAMBDA); + checker->Context().SetContainingSignature(func_type->CallSignatures()[0]); + + auto *body_type = expr->Function()->Body()->Check(checker); + + if (expr->Function()->Body()->IsExpression()) { + if (expr->Function()->ReturnTypeAnnotation() == nullptr) { + func_type->CallSignatures()[0]->SetReturnType(body_type); + } + + checker::AssignmentContext( + checker->Relation(), expr->Function()->Body()->AsExpression(), body_type, + func_type->CallSignatures()[0]->ReturnType(), expr->Function()->Start(), + {"Return statements return type is not compatible with the containing functions return type"}, + checker::TypeRelationFlag::DIRECT_RETURN); + } + + checker->Context().SetContainingSignature(nullptr); + checker->CheckCapturedVariables(); + + for (auto [var, _] : checker->Context().CapturedVars()) { + (void)_; + expr->CapturedVars().push_back(var); + } + + expr->SetTsType(func_type); + return expr->TsType(); } checker::Type *ETSAnalyzer::Check(ir::AssignmentExpression *expr) const diff --git a/checker/SemanticAnalyzer.h b/checker/SemanticAnalyzer.h index 72cfe3735..8cc27451f 100644 --- a/checker/SemanticAnalyzer.h +++ b/checker/SemanticAnalyzer.h @@ -17,6 +17,152 @@ #define ES2PANDA_CHECKER_SEMANTICANALYZER_H #include "compiler/core/dynamicContext.h" +#include "ir/opaqueTypeNode.h" +#include "ir/as/namedType.h" +#include "ir/as/prefixAssertionExpression.h" +#include "ir/base/catchClause.h" +#include "ir/base/classDefinition.h" +#include "ir/base/classProperty.h" +#include "ir/base/classStaticBlock.h" +#include "ir/base/decorator.h" +#include "ir/base/metaProperty.h" +#include "ir/base/methodDefinition.h" +#include "ir/base/property.h" +#include "ir/base/scriptFunction.h" +#include "ir/base/spreadElement.h" +#include "ir/base/templateElement.h" +#include "ir/base/tsIndexSignature.h" +#include "ir/base/tsMethodSignature.h" +#include "ir/base/tsPropertySignature.h" +#include "ir/base/tsSignatureDeclaration.h" +#include "ir/ets/etsClassLiteral.h" +#include "ir/ets/etsFunctionType.h" +#include "ir/ets/etsImportDeclaration.h" +#include "ir/ets/etsLaunchExpression.h" +#include "ir/ets/etsNewArrayInstanceExpression.h" +#include "ir/ets/etsNewClassInstanceExpression.h" +#include "ir/ets/etsNewMultiDimArrayInstanceExpression.h" +#include "ir/ets/etsPackageDeclaration.h" +#include "ir/ets/etsParameterExpression.h" +#include "ir/ets/etsPrimitiveType.h" +#include "ir/ets/etsScript.h" +#include "ir/ets/etsStructDeclaration.h" +#include "ir/ets/etsTypeReference.h" +#include "ir/ets/etsTypeReferencePart.h" +#include "ir/ets/etsWildcardType.h" +#include "ir/expressions/arrayExpression.h" +#include "ir/expressions/arrowFunctionExpression.h" +#include "ir/expressions/assignmentExpression.h" +#include "ir/expressions/awaitExpression.h" +#include "ir/expressions/binaryExpression.h" +#include "ir/expressions/callExpression.h" +#include "ir/expressions/chainExpression.h" +#include "ir/expressions/classExpression.h" +#include "ir/expressions/conditionalExpression.h" +#include "ir/expressions/directEvalExpression.h" +#include "ir/expressions/functionExpression.h" +#include "ir/expressions/identifier.h" +#include "ir/expressions/importExpression.h" +#include "ir/expressions/memberExpression.h" +#include "ir/expressions/newExpression.h" +#include "ir/expressions/objectExpression.h" +#include "ir/expressions/omittedExpression.h" +#include "ir/expressions/sequenceExpression.h" +#include "ir/expressions/superExpression.h" +#include "ir/expressions/taggedTemplateExpression.h" +#include "ir/expressions/templateLiteral.h" +#include "ir/expressions/thisExpression.h" +#include "ir/expressions/unaryExpression.h" +#include "ir/expressions/updateExpression.h" +#include "ir/expressions/yieldExpression.h" +#include "ir/expressions/literals/bigIntLiteral.h" +#include "ir/expressions/literals/booleanLiteral.h" +#include "ir/expressions/literals/charLiteral.h" +#include "ir/expressions/literals/nullLiteral.h" +#include "ir/expressions/literals/numberLiteral.h" +#include "ir/expressions/literals/regExpLiteral.h" +#include "ir/expressions/literals/stringLiteral.h" +#include "ir/module/exportAllDeclaration.h" +#include "ir/module/exportDefaultDeclaration.h" +#include "ir/module/exportNamedDeclaration.h" +#include "ir/module/exportSpecifier.h" +#include "ir/module/importDeclaration.h" +#include "ir/module/importDefaultSpecifier.h" +#include "ir/module/importNamespaceSpecifier.h" +#include "ir/module/importSpecifier.h" +#include "ir/statements/assertStatement.h" +#include "ir/statements/blockStatement.h" +#include "ir/statements/breakStatement.h" +#include "ir/statements/classDeclaration.h" +#include "ir/statements/continueStatement.h" +#include "ir/statements/debuggerStatement.h" +#include "ir/statements/doWhileStatement.h" +#include "ir/statements/emptyStatement.h" +#include "ir/statements/expressionStatement.h" +#include "ir/statements/forInStatement.h" +#include "ir/statements/forOfStatement.h" +#include "ir/statements/forUpdateStatement.h" +#include "ir/statements/functionDeclaration.h" +#include "ir/statements/ifStatement.h" +#include "ir/statements/labelledStatement.h" +#include "ir/statements/returnStatement.h" +#include "ir/statements/switchCaseStatement.h" +#include "ir/statements/switchStatement.h" +#include "ir/statements/throwStatement.h" +#include "ir/statements/tryStatement.h" +#include "ir/statements/variableDeclaration.h" +#include "ir/statements/variableDeclarator.h" +#include "ir/statements/whileStatement.h" +#include "ir/ts/tsAnyKeyword.h" +#include "ir/ts/tsArrayType.h" +#include "ir/ts/tsAsExpression.h" +#include "ir/ts/tsBigintKeyword.h" +#include "ir/ts/tsBooleanKeyword.h" +#include "ir/ts/tsClassImplements.h" +#include "ir/ts/tsConditionalType.h" +#include "ir/ts/tsConstructorType.h" +#include "ir/ts/tsEnumDeclaration.h" +#include "ir/ts/tsEnumMember.h" +#include "ir/ts/tsExternalModuleReference.h" +#include "ir/ts/tsFunctionType.h" +#include "ir/ts/tsImportEqualsDeclaration.h" +#include "ir/ts/tsImportType.h" +#include "ir/ts/tsIndexedAccessType.h" +#include "ir/ts/tsInferType.h" +#include "ir/ts/tsInterfaceBody.h" +#include "ir/ts/tsInterfaceDeclaration.h" +#include "ir/ts/tsInterfaceHeritage.h" +#include "ir/ts/tsIntersectionType.h" +#include "ir/ts/tsLiteralType.h" +#include "ir/ts/tsMappedType.h" +#include "ir/ts/tsModuleBlock.h" +#include "ir/ts/tsModuleDeclaration.h" +#include "ir/ts/tsNamedTupleMember.h" +#include "ir/ts/tsNeverKeyword.h" +#include "ir/ts/tsNonNullExpression.h" +#include "ir/ts/tsNullKeyword.h" +#include "ir/ts/tsNumberKeyword.h" +#include "ir/ts/tsObjectKeyword.h" +#include "ir/ts/tsParameterProperty.h" +#include "ir/ts/tsParenthesizedType.h" +#include "ir/ts/tsQualifiedName.h" +#include "ir/ts/tsStringKeyword.h" +#include "ir/ts/tsThisType.h" +#include "ir/ts/tsTupleType.h" +#include "ir/ts/tsTypeAliasDeclaration.h" +#include "ir/ts/tsTypeAssertion.h" +#include "ir/ts/tsTypeLiteral.h" +#include "ir/ts/tsTypeOperator.h" +#include "ir/ts/tsTypeParameterDeclaration.h" +#include "ir/ts/tsTypeParameter.h" +#include "ir/ts/tsTypeParameterInstantiation.h" +#include "ir/ts/tsTypePredicate.h" +#include "ir/ts/tsTypeQuery.h" +#include "ir/ts/tsTypeReference.h" +#include "ir/ts/tsUndefinedKeyword.h" +#include "ir/ts/tsUnionType.h" +#include "ir/ts/tsUnknownKeyword.h" +#include "ir/ts/tsVoidKeyword.h" namespace panda::es2panda::checker { class Checker; diff --git a/checker/TSAnalyzer.cpp b/checker/TSAnalyzer.cpp index f5bfa79b2..62f2f79a9 100644 --- a/checker/TSAnalyzer.cpp +++ b/checker/TSAnalyzer.cpp @@ -16,12 +16,7 @@ #include "TSAnalyzer.h" #include "checker/TSchecker.h" -#include "ir/base/catchClause.h" -#include "ir/base/methodDefinition.h" -#include "ir/base/scriptFunction.h" -#include "ir/statements/blockStatement.h" -#include "ir/statements/returnStatement.h" -#include "ir/typeNode.h" +#include "checker/ts/destructuringContext.h" #include "util/helpers.h" namespace panda::es2panda::checker { @@ -191,39 +186,33 @@ checker::Type *TSAnalyzer::Check(ir::ETSPackageDeclaration *st) const UNREACHABLE(); } -checker::Type *TSAnalyzer::Check(ir::ETSParameterExpression *expr) const +checker::Type *TSAnalyzer::Check([[maybe_unused]] ir::ETSParameterExpression *expr) const { - (void)expr; UNREACHABLE(); } -checker::Type *TSAnalyzer::Check(ir::ETSPrimitiveType *node) const +checker::Type *TSAnalyzer::Check([[maybe_unused]] ir::ETSPrimitiveType *node) const { - (void)node; UNREACHABLE(); } -checker::Type *TSAnalyzer::Check(ir::ETSStructDeclaration *node) const +checker::Type *TSAnalyzer::Check([[maybe_unused]] ir::ETSStructDeclaration *node) const { - (void)node; UNREACHABLE(); } -checker::Type *TSAnalyzer::Check(ir::ETSTypeReference *node) const +checker::Type *TSAnalyzer::Check([[maybe_unused]] ir::ETSTypeReference *node) const { - (void)node; UNREACHABLE(); } -checker::Type *TSAnalyzer::Check(ir::ETSTypeReferencePart *node) const +checker::Type *TSAnalyzer::Check([[maybe_unused]] ir::ETSTypeReferencePart *node) const { - (void)node; UNREACHABLE(); } -checker::Type *TSAnalyzer::Check(ir::ETSWildcardType *node) const +checker::Type *TSAnalyzer::Check([[maybe_unused]] ir::ETSWildcardType *node) const { - (void)node; UNREACHABLE(); } // compile methods for EXPRESSIONS in alphabetical order @@ -235,8 +224,35 @@ checker::Type *TSAnalyzer::Check(ir::ArrayExpression *expr) const checker::Type *TSAnalyzer::Check(ir::ArrowFunctionExpression *expr) const { - (void)expr; - UNREACHABLE(); + TSChecker *checker = GetTSChecker(); + binder::Variable *func_var = nullptr; + + if (expr->Function()->Parent()->Parent() != nullptr && + expr->Function()->Parent()->Parent()->IsVariableDeclarator() && + expr->Function()->Parent()->Parent()->AsVariableDeclarator()->Id()->IsIdentifier()) { + func_var = expr->Function()->Parent()->Parent()->AsVariableDeclarator()->Id()->AsIdentifier()->Variable(); + } + + checker::ScopeContext scope_ctx(checker, expr->Function()->Scope()); + + auto *signature_info = checker->Allocator()->New(checker->Allocator()); + checker->CheckFunctionParameterDeclarations(expr->Function()->Params(), signature_info); + + auto *signature = checker->Allocator()->New( + signature_info, checker->GlobalResolvingReturnType(), expr->Function()); + checker::Type *func_type = checker->CreateFunctionTypeWithSignature(signature); + + if (func_var != nullptr && func_var->TsType() == nullptr) { + func_var->SetTsType(func_type); + } + + signature->SetReturnType(checker->HandleFunctionReturn(expr->Function())); + + if (!expr->Function()->Body()->IsExpression()) { + expr->Function()->Body()->Check(checker); + } + + return func_type; } checker::Type *TSAnalyzer::Check(ir::AssignmentExpression *expr) const diff --git a/compiler/core/ASTCompiler.h b/compiler/core/ASTCompiler.h index b5bd66617..03fdde033 100644 --- a/compiler/core/ASTCompiler.h +++ b/compiler/core/ASTCompiler.h @@ -17,6 +17,152 @@ #define ES2PANDA_COMPILER_CORE_ASTCOMPILER_H #include "compiler/core/dynamicContext.h" +#include "ir/opaqueTypeNode.h" +#include "ir/as/namedType.h" +#include "ir/as/prefixAssertionExpression.h" +#include "ir/base/catchClause.h" +#include "ir/base/classDefinition.h" +#include "ir/base/classProperty.h" +#include "ir/base/classStaticBlock.h" +#include "ir/base/decorator.h" +#include "ir/base/metaProperty.h" +#include "ir/base/methodDefinition.h" +#include "ir/base/property.h" +#include "ir/base/scriptFunction.h" +#include "ir/base/spreadElement.h" +#include "ir/base/templateElement.h" +#include "ir/base/tsIndexSignature.h" +#include "ir/base/tsMethodSignature.h" +#include "ir/base/tsPropertySignature.h" +#include "ir/base/tsSignatureDeclaration.h" +#include "ir/ets/etsClassLiteral.h" +#include "ir/ets/etsFunctionType.h" +#include "ir/ets/etsImportDeclaration.h" +#include "ir/ets/etsLaunchExpression.h" +#include "ir/ets/etsNewArrayInstanceExpression.h" +#include "ir/ets/etsNewClassInstanceExpression.h" +#include "ir/ets/etsNewMultiDimArrayInstanceExpression.h" +#include "ir/ets/etsPackageDeclaration.h" +#include "ir/ets/etsParameterExpression.h" +#include "ir/ets/etsPrimitiveType.h" +#include "ir/ets/etsScript.h" +#include "ir/ets/etsStructDeclaration.h" +#include "ir/ets/etsTypeReference.h" +#include "ir/ets/etsTypeReferencePart.h" +#include "ir/ets/etsWildcardType.h" +#include "ir/expressions/arrayExpression.h" +#include "ir/expressions/arrowFunctionExpression.h" +#include "ir/expressions/assignmentExpression.h" +#include "ir/expressions/awaitExpression.h" +#include "ir/expressions/binaryExpression.h" +#include "ir/expressions/callExpression.h" +#include "ir/expressions/chainExpression.h" +#include "ir/expressions/classExpression.h" +#include "ir/expressions/conditionalExpression.h" +#include "ir/expressions/directEvalExpression.h" +#include "ir/expressions/functionExpression.h" +#include "ir/expressions/identifier.h" +#include "ir/expressions/importExpression.h" +#include "ir/expressions/memberExpression.h" +#include "ir/expressions/newExpression.h" +#include "ir/expressions/objectExpression.h" +#include "ir/expressions/omittedExpression.h" +#include "ir/expressions/sequenceExpression.h" +#include "ir/expressions/superExpression.h" +#include "ir/expressions/taggedTemplateExpression.h" +#include "ir/expressions/templateLiteral.h" +#include "ir/expressions/thisExpression.h" +#include "ir/expressions/unaryExpression.h" +#include "ir/expressions/updateExpression.h" +#include "ir/expressions/yieldExpression.h" +#include "ir/expressions/literals/bigIntLiteral.h" +#include "ir/expressions/literals/booleanLiteral.h" +#include "ir/expressions/literals/charLiteral.h" +#include "ir/expressions/literals/nullLiteral.h" +#include "ir/expressions/literals/numberLiteral.h" +#include "ir/expressions/literals/regExpLiteral.h" +#include "ir/expressions/literals/stringLiteral.h" +#include "ir/module/exportAllDeclaration.h" +#include "ir/module/exportDefaultDeclaration.h" +#include "ir/module/exportNamedDeclaration.h" +#include "ir/module/exportSpecifier.h" +#include "ir/module/importDeclaration.h" +#include "ir/module/importDefaultSpecifier.h" +#include "ir/module/importNamespaceSpecifier.h" +#include "ir/module/importSpecifier.h" +#include "ir/statements/assertStatement.h" +#include "ir/statements/blockStatement.h" +#include "ir/statements/breakStatement.h" +#include "ir/statements/classDeclaration.h" +#include "ir/statements/continueStatement.h" +#include "ir/statements/debuggerStatement.h" +#include "ir/statements/doWhileStatement.h" +#include "ir/statements/emptyStatement.h" +#include "ir/statements/expressionStatement.h" +#include "ir/statements/forInStatement.h" +#include "ir/statements/forOfStatement.h" +#include "ir/statements/forUpdateStatement.h" +#include "ir/statements/functionDeclaration.h" +#include "ir/statements/ifStatement.h" +#include "ir/statements/labelledStatement.h" +#include "ir/statements/returnStatement.h" +#include "ir/statements/switchCaseStatement.h" +#include "ir/statements/switchStatement.h" +#include "ir/statements/throwStatement.h" +#include "ir/statements/tryStatement.h" +#include "ir/statements/variableDeclaration.h" +#include "ir/statements/variableDeclarator.h" +#include "ir/statements/whileStatement.h" +#include "ir/ts/tsAnyKeyword.h" +#include "ir/ts/tsArrayType.h" +#include "ir/ts/tsAsExpression.h" +#include "ir/ts/tsBigintKeyword.h" +#include "ir/ts/tsBooleanKeyword.h" +#include "ir/ts/tsClassImplements.h" +#include "ir/ts/tsConditionalType.h" +#include "ir/ts/tsConstructorType.h" +#include "ir/ts/tsEnumDeclaration.h" +#include "ir/ts/tsEnumMember.h" +#include "ir/ts/tsExternalModuleReference.h" +#include "ir/ts/tsFunctionType.h" +#include "ir/ts/tsImportEqualsDeclaration.h" +#include "ir/ts/tsImportType.h" +#include "ir/ts/tsIndexedAccessType.h" +#include "ir/ts/tsInferType.h" +#include "ir/ts/tsInterfaceBody.h" +#include "ir/ts/tsInterfaceDeclaration.h" +#include "ir/ts/tsInterfaceHeritage.h" +#include "ir/ts/tsIntersectionType.h" +#include "ir/ts/tsLiteralType.h" +#include "ir/ts/tsMappedType.h" +#include "ir/ts/tsModuleBlock.h" +#include "ir/ts/tsModuleDeclaration.h" +#include "ir/ts/tsNamedTupleMember.h" +#include "ir/ts/tsNeverKeyword.h" +#include "ir/ts/tsNonNullExpression.h" +#include "ir/ts/tsNullKeyword.h" +#include "ir/ts/tsNumberKeyword.h" +#include "ir/ts/tsObjectKeyword.h" +#include "ir/ts/tsParameterProperty.h" +#include "ir/ts/tsParenthesizedType.h" +#include "ir/ts/tsQualifiedName.h" +#include "ir/ts/tsStringKeyword.h" +#include "ir/ts/tsThisType.h" +#include "ir/ts/tsTupleType.h" +#include "ir/ts/tsTypeAliasDeclaration.h" +#include "ir/ts/tsTypeAssertion.h" +#include "ir/ts/tsTypeLiteral.h" +#include "ir/ts/tsTypeOperator.h" +#include "ir/ts/tsTypeParameterDeclaration.h" +#include "ir/ts/tsTypeParameter.h" +#include "ir/ts/tsTypeParameterInstantiation.h" +#include "ir/ts/tsTypePredicate.h" +#include "ir/ts/tsTypeQuery.h" +#include "ir/ts/tsTypeReference.h" +#include "ir/ts/tsUndefinedKeyword.h" +#include "ir/ts/tsUnionType.h" +#include "ir/ts/tsUnknownKeyword.h" +#include "ir/ts/tsVoidKeyword.h" namespace panda::es2panda::compiler { class CodeGen; diff --git a/compiler/core/ETSCompiler.cpp b/compiler/core/ETSCompiler.cpp index c7f91c0d7..ffff9fe9d 100644 --- a/compiler/core/ETSCompiler.cpp +++ b/compiler/core/ETSCompiler.cpp @@ -15,13 +15,11 @@ #include "ETSCompiler.h" +#include "checker/types/ets/etsDynamicFunctionType.h" +#include "compiler/base/condition.h" #include "compiler/base/lreference.h" #include "compiler/core/ETSGen.h" -#include "ir/base/catchClause.h" -#include "ir/base/classProperty.h" -#include "ir/expressions/identifier.h" -#include "ir/statements/blockStatement.h" -#include "ir/statements/returnStatement.h" +#include "compiler/function/functionBuilder.h" namespace panda::es2panda::compiler { @@ -198,38 +196,36 @@ void ETSCompiler::Compile(const ir::ETSPackageDeclaration *st) const void ETSCompiler::Compile(const ir::ETSParameterExpression *expr) const { - (void)expr; - UNREACHABLE(); + ETSGen *etsg = GetETSGen(); + expr->Ident()->Identifier::Compile(etsg); } -void ETSCompiler::Compile(const ir::ETSPrimitiveType *node) const +void ETSCompiler::Compile([[maybe_unused]] const ir::ETSPrimitiveType *node) const { - (void)node; UNREACHABLE(); } -void ETSCompiler::Compile(const ir::ETSStructDeclaration *node) const +void ETSCompiler::Compile([[maybe_unused]] const ir::ETSStructDeclaration *node) const { - (void)node; UNREACHABLE(); } void ETSCompiler::Compile(const ir::ETSTypeReference *node) const { - (void)node; - UNREACHABLE(); + ETSGen *etsg = GetETSGen(); + node->Part()->Compile(etsg); } void ETSCompiler::Compile(const ir::ETSTypeReferencePart *node) const { - (void)node; - UNREACHABLE(); + ETSGen *etsg = GetETSGen(); + node->Name()->Compile(etsg); } -void ETSCompiler::Compile(const ir::ETSWildcardType *node) const +void ETSCompiler::Compile([[maybe_unused]] const ir::ETSWildcardType *node) const { - (void)node; - UNREACHABLE(); + ETSGen *etsg = GetETSGen(); + etsg->Unimplemented(); } // compile methods for EXPRESSIONS in alphabetical order void ETSCompiler::Compile(const ir::ArrayExpression *expr) const @@ -240,8 +236,23 @@ void ETSCompiler::Compile(const ir::ArrayExpression *expr) const void ETSCompiler::Compile(const ir::ArrowFunctionExpression *expr) const { - (void)expr; - UNREACHABLE(); + ETSGen *etsg = GetETSGen(); + ASSERT(expr->ResolvedLambda() != nullptr); + auto *ctor = expr->ResolvedLambda()->TsType()->AsETSObjectType()->ConstructSignatures()[0]; + std::vector arguments; + + for (auto *it : expr->CapturedVars()) { + if (it->HasFlag(binder::VariableFlags::LOCAL)) { + arguments.push_back(it->AsLocalVariable()->Vreg()); + } + } + + if (expr->propagate_this_) { + arguments.push_back(etsg->GetThisReg()); + } + + etsg->InitLambdaObject(expr, ctor, arguments); + etsg->SetAccumulatorType(expr->resolved_lambda_->TsType()); } void ETSCompiler::Compile(const ir::AssignmentExpression *expr) const diff --git a/compiler/core/JSCompiler.cpp b/compiler/core/JSCompiler.cpp index a0b481589..5be37688d 100644 --- a/compiler/core/JSCompiler.cpp +++ b/compiler/core/JSCompiler.cpp @@ -15,18 +15,10 @@ #include "JSCompiler.h" +#include "compiler/base/condition.h" #include "compiler/base/lreference.h" #include "compiler/core/pandagen.h" -#include "ir/base/catchClause.h" -#include "ir/base/classDefinition.h" -#include "ir/base/classProperty.h" -#include "ir/base/classStaticBlock.h" -#include "ir/base/methodDefinition.h" -#include "ir/base/scriptFunction.h" -#include "ir/expressions/functionExpression.h" -#include "ir/expressions/identifier.h" -#include "ir/statements/blockStatement.h" -#include "ir/statements/returnStatement.h" +#include "compiler/function/functionBuilder.h" #include "util/helpers.h" namespace panda::es2panda::compiler { @@ -523,39 +515,33 @@ void JSCompiler::Compile(const ir::ETSPackageDeclaration *expr) const UNREACHABLE(); } -void JSCompiler::Compile(const ir::ETSParameterExpression *expr) const +void JSCompiler::Compile([[maybe_unused]] const ir::ETSParameterExpression *expr) const { - (void)expr; UNREACHABLE(); } -void JSCompiler::Compile(const ir::ETSPrimitiveType *expr) const +void JSCompiler::Compile([[maybe_unused]] const ir::ETSPrimitiveType *expr) const { - (void)expr; UNREACHABLE(); } -void JSCompiler::Compile(const ir::ETSStructDeclaration *node) const +void JSCompiler::Compile([[maybe_unused]] const ir::ETSStructDeclaration *node) const { - (void)node; UNREACHABLE(); } -void JSCompiler::Compile(const ir::ETSTypeReference *expr) const +void JSCompiler::Compile([[maybe_unused]] const ir::ETSTypeReference *expr) const { - (void)expr; UNREACHABLE(); } -void JSCompiler::Compile(const ir::ETSTypeReferencePart *expr) const +void JSCompiler::Compile([[maybe_unused]] const ir::ETSTypeReferencePart *expr) const { - (void)expr; UNREACHABLE(); } -void JSCompiler::Compile(const ir::ETSWildcardType *expr) const +void JSCompiler::Compile([[maybe_unused]] const ir::ETSWildcardType *expr) const { - (void)expr; UNREACHABLE(); } @@ -568,8 +554,8 @@ void JSCompiler::Compile(const ir::ArrayExpression *expr) const void JSCompiler::Compile(const ir::ArrowFunctionExpression *expr) const { - (void)expr; - UNREACHABLE(); + PandaGen *pg = GetPandaGen(); + pg->DefineFunction(expr->Function(), expr->Function(), expr->Function()->Scope()->InternalName()); } void JSCompiler::Compile(const ir::AssignmentExpression *expr) const diff --git a/ir/ets/etsParameterExpression.cpp b/ir/ets/etsParameterExpression.cpp index c862ffe69..30fee78aa 100644 --- a/ir/ets/etsParameterExpression.cpp +++ b/ir/ets/etsParameterExpression.cpp @@ -15,9 +15,11 @@ #include "etsParameterExpression.h" -#include "compiler/core/pandagen.h" #include "checker/ETSchecker.h" #include "checker/ets/typeRelationContext.h" +#include "checker/TSchecker.h" +#include "compiler/core/ETSGen.h" +#include "compiler/core/pandagen.h" #include "ir/astDump.h" #include "ir/typeNode.h" #include "ir/expressions/identifier.h" @@ -140,43 +142,24 @@ void ETSParameterExpression::Dump(ir::AstDumper *const dumper) const } } -void ETSParameterExpression::Compile([[maybe_unused]] compiler::PandaGen *const pg) const +void ETSParameterExpression::Compile(compiler::PandaGen *const pg) const { - UNREACHABLE(); + pg->GetAstCompiler()->Compile(this); } -void ETSParameterExpression::Compile([[maybe_unused]] compiler::ETSGen *const etsg) const +void ETSParameterExpression::Compile(compiler::ETSGen *const etsg) const { - ident_->Identifier::Compile(etsg); + etsg->GetAstCompiler()->Compile(this); } -checker::Type *ETSParameterExpression::Check([[maybe_unused]] checker::TSChecker *const checker) +checker::Type *ETSParameterExpression::Check(checker::TSChecker *const checker) { - UNREACHABLE(); + return checker->GetAnalyzer()->Check(this); } checker::Type *ETSParameterExpression::Check(checker::ETSChecker *const checker) { - if (TsType() == nullptr) { - checker::Type *param_type; - - if (ident_->TsType() != nullptr) { - param_type = ident_->TsType(); - } else { - param_type = !IsRestParameter() ? ident_->Check(checker) : spread_->Check(checker); - if (IsDefault()) { - [[maybe_unused]] auto *const init_type = initializer_->Check(checker); - // TODO(ttamas) : fix this aftet nullable fix - // const checker::AssignmentContext ctx(checker->Relation(), initializer_, init_type, name_type, - // initializer_->Start(), - // {"Initializers type is not assignable to the target type"}); - } - } - - SetTsType(param_type); - } - - return TsType(); + return checker->GetAnalyzer()->Check(this); } // NOLINTNEXTLINE(google-default-arguments) diff --git a/ir/ets/etsParameterExpression.h b/ir/ets/etsParameterExpression.h index d63a3dd88..1160289e8 100644 --- a/ir/ets/etsParameterExpression.h +++ b/ir/ets/etsParameterExpression.h @@ -18,6 +18,10 @@ #include "ir/expression.h" +namespace panda::es2panda::checker { +class ETSAnalyzer; +} // namespace panda::es2panda::checker + namespace panda::es2panda::ir { class ETSParameterExpression final : public Expression { public: @@ -29,6 +33,9 @@ public: explicit ETSParameterExpression(AnnotatedExpression *ident_or_spread, Expression *initializer); + // TODO (csabahurton): friend relationship can be removed once there are getters for private fields + friend class checker::ETSAnalyzer; + [[nodiscard]] const Identifier *Ident() const noexcept; [[nodiscard]] Identifier *Ident() noexcept; diff --git a/ir/ets/etsPrimitiveType.cpp b/ir/ets/etsPrimitiveType.cpp index 820b6fb54..7e722d78c 100644 --- a/ir/ets/etsPrimitiveType.cpp +++ b/ir/ets/etsPrimitiveType.cpp @@ -15,10 +15,11 @@ #include "etsPrimitiveType.h" -#include "ir/astDump.h" #include "checker/TSchecker.h" #include "checker/ETSchecker.h" #include "compiler/core/ETSGen.h" +#include "compiler/core/pandagen.h" +#include "ir/astDump.h" namespace panda::es2panda::ir { void ETSPrimitiveType::TransformChildren([[maybe_unused]] const NodeTransformer &cb) {} @@ -29,16 +30,19 @@ void ETSPrimitiveType::Dump(ir::AstDumper *dumper) const dumper->Add({{"type", "ETSPrimitiveType"}}); } -void ETSPrimitiveType::Compile([[maybe_unused]] compiler::PandaGen *pg) const {} +void ETSPrimitiveType::Compile(compiler::PandaGen *pg) const +{ + pg->GetAstCompiler()->Compile(this); +} -void ETSPrimitiveType::Compile([[maybe_unused]] compiler::ETSGen *etsg) const +void ETSPrimitiveType::Compile(compiler::ETSGen *etsg) const { - UNREACHABLE(); + etsg->GetAstCompiler()->Compile(this); } -checker::Type *ETSPrimitiveType::Check([[maybe_unused]] checker::TSChecker *checker) +checker::Type *ETSPrimitiveType::Check(checker::TSChecker *checker) { - return nullptr; + return checker->GetAnalyzer()->Check(this); } checker::Type *ETSPrimitiveType::GetType([[maybe_unused]] checker::TSChecker *checker) @@ -46,9 +50,9 @@ checker::Type *ETSPrimitiveType::GetType([[maybe_unused]] checker::TSChecker *ch return checker->GlobalAnyType(); } -checker::Type *ETSPrimitiveType::Check([[maybe_unused]] checker::ETSChecker *checker) +checker::Type *ETSPrimitiveType::Check(checker::ETSChecker *checker) { - return nullptr; + return checker->GetAnalyzer()->Check(this); } checker::Type *ETSPrimitiveType::GetType([[maybe_unused]] checker::ETSChecker *checker) diff --git a/ir/ets/etsPrimitiveType.h b/ir/ets/etsPrimitiveType.h index c8ae085f4..c394b0019 100644 --- a/ir/ets/etsPrimitiveType.h +++ b/ir/ets/etsPrimitiveType.h @@ -33,11 +33,11 @@ public: void TransformChildren(const NodeTransformer &cb) override; void Iterate(const NodeTraverser &cb) const override; void Dump(ir::AstDumper *dumper) const override; - void Compile([[maybe_unused]] compiler::PandaGen *pg) const override; - void Compile([[maybe_unused]] compiler::ETSGen *etsg) const override; - checker::Type *Check([[maybe_unused]] checker::TSChecker *checker) override; + void Compile(compiler::PandaGen *pg) const override; + void Compile(compiler::ETSGen *etsg) const override; + checker::Type *Check(checker::TSChecker *checker) override; checker::Type *GetType([[maybe_unused]] checker::TSChecker *checker) override; - checker::Type *Check([[maybe_unused]] checker::ETSChecker *checker) override; + checker::Type *Check(checker::ETSChecker *checker) override; checker::Type *GetType([[maybe_unused]] checker::ETSChecker *checker) override; private: diff --git a/ir/ets/etsStructDeclaration.cpp b/ir/ets/etsStructDeclaration.cpp index 62f8b74cd..3b05f2717 100644 --- a/ir/ets/etsStructDeclaration.cpp +++ b/ir/ets/etsStructDeclaration.cpp @@ -15,6 +15,7 @@ #include "etsStructDeclaration.h" +#include "checker/TSchecker.h" #include "compiler/base/lreference.h" #include "compiler/core/pandagen.h" #include "compiler/core/ETSGen.h" @@ -48,24 +49,23 @@ void ETSStructDeclaration::Dump(ir::AstDumper *dumper) const {{"type", "ETSStructDeclaration"}, {"definition", def_}, {"decorators", AstDumper::Optional(decorators_)}}); } -void ETSStructDeclaration::Compile([[maybe_unused]] compiler::PandaGen *pg) const +void ETSStructDeclaration::Compile(compiler::PandaGen *pg) const { - UNREACHABLE(); + pg->GetAstCompiler()->Compile(this); } -void ETSStructDeclaration::Compile([[maybe_unused]] compiler::ETSGen *etsg) const +void ETSStructDeclaration::Compile(compiler::ETSGen *etsg) const { - UNREACHABLE(); + etsg->GetAstCompiler()->Compile(this); } -checker::Type *ETSStructDeclaration::Check([[maybe_unused]] checker::TSChecker *checker) +checker::Type *ETSStructDeclaration::Check(checker::TSChecker *checker) { - return nullptr; + return checker->GetAnalyzer()->Check(this); } checker::Type *ETSStructDeclaration::Check(checker::ETSChecker *checker) { - def_->Check(checker); - return nullptr; + return checker->GetAnalyzer()->Check(this); } } // namespace panda::es2panda::ir diff --git a/ir/ets/etsStructDeclaration.h b/ir/ets/etsStructDeclaration.h index 7a52e9b80..10a2201ab 100644 --- a/ir/ets/etsStructDeclaration.h +++ b/ir/ets/etsStructDeclaration.h @@ -54,11 +54,11 @@ public: void TransformChildren(const NodeTransformer &cb) override; void Iterate(const NodeTraverser &cb) const override; void Dump(ir::AstDumper *dumper) const override; - void Compile([[maybe_unused]] compiler::PandaGen *pg) const override; - void Compile([[maybe_unused]] compiler::ETSGen *etsg) const override; + void Compile(compiler::PandaGen *pg) const override; + void Compile(compiler::ETSGen *etsg) const override; - checker::Type *Check([[maybe_unused]] checker::TSChecker *checker) override; - checker::Type *Check([[maybe_unused]] checker::ETSChecker *checker) override; + checker::Type *Check(checker::TSChecker *checker) override; + checker::Type *Check(checker::ETSChecker *checker) override; private: ClassDefinition *def_; diff --git a/ir/ets/etsTypeReference.cpp b/ir/ets/etsTypeReference.cpp index 849f52346..7ac21bd51 100644 --- a/ir/ets/etsTypeReference.cpp +++ b/ir/ets/etsTypeReference.cpp @@ -15,11 +15,13 @@ #include "etsTypeReference.h" +#include "checker/ETSchecker.h" +#include "checker/TSchecker.h" +#include "compiler/core/ETSGen.h" +#include "compiler/core/pandagen.h" #include "ir/astDump.h" #include "ir/ts/tsQualifiedName.h" #include "ir/ets/etsTypeReferencePart.h" -#include "checker/ETSchecker.h" -#include "compiler/core/ETSGen.h" namespace panda::es2panda::ir { void ETSTypeReference::TransformChildren(const NodeTransformer &cb) @@ -60,20 +62,23 @@ void ETSTypeReference::Dump(ir::AstDumper *dumper) const dumper->Add({{"type", "ETSTypeReference"}, {"part", part_}}); } -void ETSTypeReference::Compile([[maybe_unused]] compiler::PandaGen *pg) const {} -void ETSTypeReference::Compile([[maybe_unused]] compiler::ETSGen *etsg) const +void ETSTypeReference::Compile(compiler::PandaGen *pg) const +{ + pg->GetAstCompiler()->Compile(this); +} +void ETSTypeReference::Compile(compiler::ETSGen *etsg) const { - part_->Compile(etsg); + etsg->GetAstCompiler()->Compile(this); } -checker::Type *ETSTypeReference::Check([[maybe_unused]] checker::TSChecker *checker) +checker::Type *ETSTypeReference::Check(checker::TSChecker *checker) { - return nullptr; + return checker->GetAnalyzer()->Check(this); } checker::Type *ETSTypeReference::Check(checker::ETSChecker *checker) { - return GetType(checker); + return checker->GetAnalyzer()->Check(this); } checker::Type *ETSTypeReference::GetType(checker::ETSChecker *checker) diff --git a/ir/ets/etsTypeReference.h b/ir/ets/etsTypeReference.h index 17b8ef6ce..d771dcceb 100644 --- a/ir/ets/etsTypeReference.h +++ b/ir/ets/etsTypeReference.h @@ -41,10 +41,10 @@ public: void TransformChildren(const NodeTransformer &cb) override; void Iterate(const NodeTraverser &cb) const override; void Dump(ir::AstDumper *dumper) const override; - void Compile([[maybe_unused]] compiler::PandaGen *pg) const override; - void Compile([[maybe_unused]] compiler::ETSGen *etsg) const override; - checker::Type *Check([[maybe_unused]] checker::TSChecker *checker) override; - checker::Type *Check([[maybe_unused]] checker::ETSChecker *checker) override; + void Compile(compiler::PandaGen *pg) const override; + void Compile(compiler::ETSGen *etsg) const override; + checker::Type *Check(checker::TSChecker *checker) override; + checker::Type *Check(checker::ETSChecker *checker) override; checker::Type *GetType([[maybe_unused]] checker::ETSChecker *checker) override; private: diff --git a/ir/ets/etsTypeReferencePart.cpp b/ir/ets/etsTypeReferencePart.cpp index 4d86e0905..79a2b4e97 100644 --- a/ir/ets/etsTypeReferencePart.cpp +++ b/ir/ets/etsTypeReferencePart.cpp @@ -15,13 +15,12 @@ #include "etsTypeReferencePart.h" -#include "ir/astDump.h" -#include "ir/expressions/identifier.h" -#include "ir/ts/tsTypeParameterInstantiation.h" -#include "checker/TSchecker.h" #include "checker/ETSchecker.h" #include "checker/ets/typeRelationContext.h" +#include "checker/TSchecker.h" #include "compiler/core/ETSGen.h" +#include "compiler/core/pandagen.h" +#include "ir/astDump.h" namespace panda::es2panda::ir { void ETSTypeReferencePart::TransformChildren(const NodeTransformer &cb) @@ -58,20 +57,23 @@ void ETSTypeReferencePart::Dump(ir::AstDumper *dumper) const {"previous", AstDumper::Optional(prev_)}}); } -void ETSTypeReferencePart::Compile([[maybe_unused]] compiler::PandaGen *pg) const {} -void ETSTypeReferencePart::Compile([[maybe_unused]] compiler::ETSGen *etsg) const +void ETSTypeReferencePart::Compile(compiler::PandaGen *pg) const +{ + pg->GetAstCompiler()->Compile(this); +} +void ETSTypeReferencePart::Compile(compiler::ETSGen *etsg) const { - name_->Compile(etsg); + etsg->GetAstCompiler()->Compile(this); } -checker::Type *ETSTypeReferencePart::Check([[maybe_unused]] checker::TSChecker *checker) +checker::Type *ETSTypeReferencePart::Check(checker::TSChecker *checker) { - return nullptr; + return checker->GetAnalyzer()->Check(this); } checker::Type *ETSTypeReferencePart::Check(checker::ETSChecker *checker) { - return GetType(checker); + return checker->GetAnalyzer()->Check(this); } checker::Type *ETSTypeReferencePart::GetType(checker::ETSChecker *checker) diff --git a/ir/ets/etsTypeReferencePart.h b/ir/ets/etsTypeReferencePart.h index f8d82bcf2..f3e4b8aaf 100644 --- a/ir/ets/etsTypeReferencePart.h +++ b/ir/ets/etsTypeReferencePart.h @@ -58,10 +58,10 @@ public: void TransformChildren(const NodeTransformer &cb) override; void Iterate(const NodeTraverser &cb) const override; void Dump(ir::AstDumper *dumper) const override; - void Compile([[maybe_unused]] compiler::PandaGen *pg) const override; - void Compile([[maybe_unused]] compiler::ETSGen *etsg) const override; - checker::Type *Check([[maybe_unused]] checker::TSChecker *checker) override; - checker::Type *Check([[maybe_unused]] checker::ETSChecker *checker) override; + void Compile(compiler::PandaGen *pg) const override; + void Compile(compiler::ETSGen *etsg) const override; + checker::Type *Check(checker::TSChecker *checker) override; + checker::Type *Check(checker::ETSChecker *checker) override; checker::Type *GetType([[maybe_unused]] checker::ETSChecker *checker) override; private: diff --git a/ir/ets/etsWildcardType.cpp b/ir/ets/etsWildcardType.cpp index 07db87b9c..76c5ea1cc 100644 --- a/ir/ets/etsWildcardType.cpp +++ b/ir/ets/etsWildcardType.cpp @@ -15,11 +15,12 @@ #include "etsWildcardType.h" -#include "ir/astDump.h" -#include "ir/ets/etsTypeReference.h" -#include "checker/TSchecker.h" #include "checker/ETSchecker.h" +#include "checker/TSchecker.h" #include "compiler/core/ETSGen.h" +#include "compiler/core/pandagen.h" +#include "ir/astDump.h" +#include "ir/ets/etsTypeReference.h" namespace panda::es2panda::ir { void ETSWildcardType::TransformChildren(const NodeTransformer &cb) @@ -44,16 +45,19 @@ void ETSWildcardType::Dump(ir::AstDumper *dumper) const {"out", AstDumper::Optional(IsOut())}}); } -void ETSWildcardType::Compile([[maybe_unused]] compiler::PandaGen *pg) const {} +void ETSWildcardType::Compile(compiler::PandaGen *pg) const +{ + pg->GetAstCompiler()->Compile(this); +} -void ETSWildcardType::Compile([[maybe_unused]] compiler::ETSGen *etsg) const +void ETSWildcardType::Compile(compiler::ETSGen *etsg) const { - etsg->Unimplemented(); + etsg->GetAstCompiler()->Compile(this); } -checker::Type *ETSWildcardType::Check([[maybe_unused]] checker::TSChecker *checker) +checker::Type *ETSWildcardType::Check(checker::TSChecker *checker) { - return nullptr; + return checker->GetAnalyzer()->Check(this); } checker::Type *ETSWildcardType::GetType([[maybe_unused]] checker::TSChecker *checker) @@ -61,9 +65,9 @@ checker::Type *ETSWildcardType::GetType([[maybe_unused]] checker::TSChecker *che return nullptr; } -checker::Type *ETSWildcardType::Check([[maybe_unused]] checker::ETSChecker *checker) +checker::Type *ETSWildcardType::Check(checker::ETSChecker *checker) { - return nullptr; + return checker->GetAnalyzer()->Check(this); } checker::Type *ETSWildcardType::GetType([[maybe_unused]] checker::ETSChecker *checker) diff --git a/ir/ets/etsWildcardType.h b/ir/ets/etsWildcardType.h index 4ce8ce2ad..ca14342e0 100644 --- a/ir/ets/etsWildcardType.h +++ b/ir/ets/etsWildcardType.h @@ -36,11 +36,11 @@ public: void TransformChildren(const NodeTransformer &cb) override; void Iterate(const NodeTraverser &cb) const override; void Dump(ir::AstDumper *dumper) const override; - void Compile([[maybe_unused]] compiler::PandaGen *pg) const override; - void Compile([[maybe_unused]] compiler::ETSGen *etsg) const override; - checker::Type *Check([[maybe_unused]] checker::TSChecker *checker) override; + void Compile(compiler::PandaGen *pg) const override; + void Compile(compiler::ETSGen *etsg) const override; + checker::Type *Check(checker::TSChecker *checker) override; checker::Type *GetType([[maybe_unused]] checker::TSChecker *checker) override; - checker::Type *Check([[maybe_unused]] checker::ETSChecker *checker) override; + checker::Type *Check(checker::ETSChecker *checker) override; checker::Type *GetType([[maybe_unused]] checker::ETSChecker *checker) override; private: diff --git a/ir/expressions/arrowFunctionExpression.cpp b/ir/expressions/arrowFunctionExpression.cpp index a7ceaf07c..0cb1b3b65 100644 --- a/ir/expressions/arrowFunctionExpression.cpp +++ b/ir/expressions/arrowFunctionExpression.cpp @@ -44,124 +44,21 @@ void ArrowFunctionExpression::Dump(ir::AstDumper *dumper) const void ArrowFunctionExpression::Compile(compiler::PandaGen *pg) const { - pg->DefineFunction(func_, func_, func_->Scope()->InternalName()); + pg->GetAstCompiler()->Compile(this); } void ArrowFunctionExpression::Compile(compiler::ETSGen *etsg) const { - ASSERT(resolved_lambda_ != nullptr); - auto *ctor = resolved_lambda_->TsType()->AsETSObjectType()->ConstructSignatures()[0]; - std::vector arguments; - - for (auto *it : captured_vars_) { - if (it->HasFlag(binder::VariableFlags::LOCAL)) { - arguments.push_back(it->AsLocalVariable()->Vreg()); - } - } - - if (propagate_this_) { - arguments.push_back(etsg->GetThisReg()); - } - - etsg->InitLambdaObject(this, ctor, arguments); - etsg->SetAccumulatorType(resolved_lambda_->TsType()); + etsg->GetAstCompiler()->Compile(this); } checker::Type *ArrowFunctionExpression::Check(checker::TSChecker *checker) { - binder::Variable *func_var = nullptr; - - if (func_->Parent()->Parent() != nullptr && func_->Parent()->Parent()->IsVariableDeclarator() && - func_->Parent()->Parent()->AsVariableDeclarator()->Id()->IsIdentifier()) { - func_var = func_->Parent()->Parent()->AsVariableDeclarator()->Id()->AsIdentifier()->Variable(); - } - - checker::ScopeContext scope_ctx(checker, func_->Scope()); - - auto *signature_info = checker->Allocator()->New(checker->Allocator()); - checker->CheckFunctionParameterDeclarations(func_->Params(), signature_info); - - auto *signature = - checker->Allocator()->New(signature_info, checker->GlobalResolvingReturnType(), func_); - checker::Type *func_type = checker->CreateFunctionTypeWithSignature(signature); - - if (func_var != nullptr && func_var->TsType() == nullptr) { - func_var->SetTsType(func_type); - } - - signature->SetReturnType(checker->HandleFunctionReturn(func_)); - - if (!func_->Body()->IsExpression()) { - func_->Body()->Check(checker); - } - - return func_type; + return checker->GetAnalyzer()->Check(this); } checker::Type *ArrowFunctionExpression::Check(checker::ETSChecker *checker) { - if (TsType() != nullptr) { - return TsType(); - } - - auto *func_type = checker->BuildFunctionSignature(func_, false); - - if (Function()->IsAsyncFunc()) { - auto *ret_type = static_cast(Function()->Signature()->ReturnType()); - if (ret_type->AssemblerName() != checker->GlobalBuiltinPromiseType()->AssemblerName()) { - checker->ThrowTypeError("Return type of async lambda must be 'Promise'", Function()->Start()); - } - } - - checker::ScopeContext scope_ctx(checker, func_->Scope()); - - if (checker->HasStatus(checker::CheckerStatus::IN_INSTANCE_EXTENSION_METHOD)) { - /* - example code: - ``` - class A { - prop:number - } - function A.method() { - let a = () => { - console.println(this.prop) - } - } - ``` - here the enclosing class of arrow function should be Class A - */ - checker->Context().SetContainingClass( - checker->Scope()->Find(binder::Binder::MANDATORY_PARAM_THIS).variable->TsType()->AsETSObjectType()); - } - - checker::SavedCheckerContext saved_context(checker, checker->Context().Status(), - checker->Context().ContainingClass()); - checker->AddStatus(checker::CheckerStatus::IN_LAMBDA); - checker->Context().SetContainingSignature(func_type->CallSignatures()[0]); - - auto *body_type = func_->Body()->Check(checker); - - if (func_->Body()->IsExpression()) { - if (func_->ReturnTypeAnnotation() == nullptr) { - func_type->CallSignatures()[0]->SetReturnType(body_type); - } - - checker::AssignmentContext( - checker->Relation(), func_->Body()->AsExpression(), body_type, func_type->CallSignatures()[0]->ReturnType(), - func_->Start(), - {"Return statements return type is not compatible with the containing functions return type"}, - checker::TypeRelationFlag::DIRECT_RETURN); - } - - checker->Context().SetContainingSignature(nullptr); - checker->CheckCapturedVariables(); - - for (auto [var, _] : checker->Context().CapturedVars()) { - (void)_; - captured_vars_.push_back(var); - } - - SetTsType(func_type); - return TsType(); + return checker->GetAnalyzer()->Check(this); } } // namespace panda::es2panda::ir diff --git a/ir/expressions/arrowFunctionExpression.h b/ir/expressions/arrowFunctionExpression.h index 5bcefdb81..204ce84ac 100644 --- a/ir/expressions/arrowFunctionExpression.h +++ b/ir/expressions/arrowFunctionExpression.h @@ -18,6 +18,10 @@ #include "ir/expression.h" +namespace panda::es2panda::compiler { +class ETSCompiler; +} // namespace panda::es2panda::compiler + namespace panda::es2panda::ir { class ScriptFunction; @@ -27,6 +31,8 @@ public: : Expression(AstNodeType::ARROW_FUNCTION_EXPRESSION), func_(func), captured_vars_(allocator->Adapter()) { } + // TODO (csabahurton): friend relationship can be removed once there are getters for private fields + friend class compiler::ETSCompiler; const ScriptFunction *Function() const { @@ -71,10 +77,10 @@ public: void TransformChildren(const NodeTransformer &cb) override; void Iterate(const NodeTraverser &cb) const override; void Dump(ir::AstDumper *dumper) const override; - void Compile([[maybe_unused]] compiler::PandaGen *pg) const override; + void Compile(compiler::PandaGen *pg) const override; void Compile(compiler::ETSGen *etsg) const override; - checker::Type *Check([[maybe_unused]] checker::TSChecker *checker) override; - checker::Type *Check([[maybe_unused]] checker::ETSChecker *checker) override; + checker::Type *Check(checker::TSChecker *checker) override; + checker::Type *Check(checker::ETSChecker *checker) override; private: ScriptFunction *func_; diff --git a/ir/statements/tryStatement.h b/ir/statements/tryStatement.h index 74b955c87..af69c1eb9 100644 --- a/ir/statements/tryStatement.h +++ b/ir/statements/tryStatement.h @@ -16,7 +16,7 @@ #ifndef ES2PANDA_IR_STATEMENT_TRY_STATEMENT_H #define ES2PANDA_IR_STATEMENT_TRY_STATEMENT_H -#include "compiler/core/ETSGen.h" +#include "compiler/core/labelPair.h" #include "ir/statement.h" namespace panda::es2panda::compiler { -- Gitee From 9106eab58ae0fe8d6bdf49c243b40f8e73c3fe5e Mon Sep 17 00:00:00 2001 From: Anna Antipina Date: Fri, 27 Oct 2023 11:41:46 +0300 Subject: [PATCH 3/4] Implement extended conditional expressions Signed-off-by: Anna Antipina --- checker/ETSchecker.h | 8 +- checker/ets/aliveAnalyzer.cpp | 32 ++-- checker/ets/arithmetic.cpp | 34 +++-- checker/ets/helpers.cpp | 88 ++++++++++- checker/types/ets/byteType.h | 5 + checker/types/ets/charType.h | 5 + checker/types/ets/doubleType.h | 5 + checker/types/ets/etsArrayType.h | 5 + checker/types/ets/etsBooleanType.h | 5 + checker/types/ets/etsObjectType.h | 9 ++ checker/types/ets/etsStringType.h | 9 ++ checker/types/ets/floatType.h | 5 + checker/types/ets/intType.h | 5 + checker/types/ets/longType.h | 5 + checker/types/ets/shortType.h | 5 + checker/types/type.h | 15 ++ checker/types/typeFlag.h | 4 +- compiler/base/condition.cpp | 41 ++++-- compiler/base/condition.h | 2 +- compiler/core/ETSGen.cpp | 16 +- compiler/core/ETSGen.h | 137 +++++++++++++++++- compiler/scripts/signatures.yaml | 12 ++ ir/expressions/assignmentExpression.cpp | 2 +- ir/expressions/assignmentExpression.h | 23 +++ ir/expressions/binaryExpression.cpp | 48 ++++-- ir/expressions/binaryExpression.h | 23 +++ ir/expressions/unaryExpression.cpp | 26 +++- ir/statements/assertStatement.cpp | 2 +- ir/statements/ifStatement.cpp | 2 +- .../voidTypeInBinaryOperation-expected.txt | 1 - 30 files changed, 508 insertions(+), 71 deletions(-) diff --git a/checker/ETSchecker.h b/checker/ETSchecker.h index 1cdd1f169..e3553c527 100644 --- a/checker/ETSchecker.h +++ b/checker/ETSchecker.h @@ -250,7 +250,7 @@ public: // Arithmetic Type *NegateNumericType(Type *type, ir::Expression *node); Type *BitwiseNegateIntegralType(Type *type, ir::Expression *node); - std::tuple CheckBinaryOperator(ir::Expression *left, ir::Expression *right, + std::tuple CheckBinaryOperator(ir::Expression *left, ir::Expression *right, ir::Expression *expr, lexer::TokenType operation_type, lexer::SourcePosition pos, bool force_promotion = false); Type *HandleArithmeticOperationOnTypes(Type *left, Type *right, lexer::TokenType operation_type); @@ -399,14 +399,17 @@ public: Type *GetTypeFromEnumReference(binder::Variable *var); Type *GetTypeFromTypeParameterReference(binder::LocalVariable *var, const lexer::SourcePosition &pos); Type *GetNonConstantTypeFromPrimitiveType(Type *type); + bool IsNullOrVoidExpression(const ir::Expression *expr) const; bool IsConstantExpression(ir::Expression *expr, Type *type); void ValidateUnaryOperatorOperand(binder::Variable *variable); std::tuple ApplyBinaryOperatorPromotion(Type *left, Type *right, TypeFlag test, bool do_promotion = true); checker::Type *ApplyConditionalOperatorPromotion(checker::ETSChecker *checker, checker::Type *unboxed_l, checker::Type *unboxed_r); - Type *ApplyUnaryOperatorPromotion(Type *type, bool create_const = true, bool do_promotion = true); + Type *ApplyUnaryOperatorPromotion(Type *type, bool create_const = true, bool do_promotion = true, + bool is_cond_expr = false); Type *HandleBooleanLogicalOperators(Type *left_type, Type *right_type, lexer::TokenType token_type); + Type *HandleBooleanLogicalOperatorsExtended(Type *left_type, Type *right_type, ir::BinaryExpression *expr); checker::Type *CheckVariableDeclaration(ir::Identifier *ident, ir::TypeNode *type_annotation, ir::Expression *init, ir::ModifierFlags flags); void CheckTruthinessOfType(ir::Expression *expr); @@ -428,6 +431,7 @@ public: binder::VariableFlags GetAccessFlagFromNode(const ir::AstNode *node); void CheckSwitchDiscriminant(ir::Expression *discriminant); Type *ETSBuiltinTypeAsPrimitiveType(Type *object_type); + Type *ETSBuiltinTypeAsConditionalType(Type *object_type); Type *PrimitiveTypeAsETSBuiltinType(Type *object_type); void AddBoxingUnboxingFlagToNode(ir::AstNode *node, Type *boxing_unboxing_type); ir::BoxingUnboxingFlags GetBoxingFlag(Type *boxing_type); diff --git a/checker/ets/aliveAnalyzer.cpp b/checker/ets/aliveAnalyzer.cpp index c47f5915e..e0e22a14a 100644 --- a/checker/ets/aliveAnalyzer.cpp +++ b/checker/ets/aliveAnalyzer.cpp @@ -300,10 +300,9 @@ void AliveAnalyzer::AnalyzeDoLoop(const ir::DoWhileStatement *do_while) AnalyzeStat(do_while->Body()); status_ = Or(status_, ResolveContinues(do_while)); AnalyzeNode(do_while->Test()); - ASSERT(do_while->Test()->TsType() && do_while->Test()->TsType()->IsETSBooleanType()); - auto *cond_type = do_while->Test()->TsType()->AsETSBooleanType(); - status_ = And(status_, - static_cast(!(cond_type->HasTypeFlag(TypeFlag::CONSTANT) && cond_type->GetValue()))); + ASSERT(do_while->Test()->TsType() && do_while->Test()->TsType()->IsConditionalExprType()); + const auto expr_res = do_while->Test()->TsType()->ResolveConditionExpr(); + status_ = And(status_, static_cast(!std::get<0>(expr_res) || !std::get<1>(expr_res))); status_ = Or(status_, ResolveBreaks(do_while)); } @@ -311,26 +310,28 @@ void AliveAnalyzer::AnalyzeWhileLoop(const ir::WhileStatement *while_stmt) { SetOldPendingExits(PendingExits()); AnalyzeNode(while_stmt->Test()); - ASSERT(while_stmt->Test()->TsType() && while_stmt->Test()->TsType()->IsETSBooleanType()); - auto *cond_type = while_stmt->Test()->TsType()->AsETSBooleanType(); - status_ = And(status_, - static_cast(!(cond_type->HasTypeFlag(TypeFlag::CONSTANT) && !cond_type->GetValue()))); + ASSERT(while_stmt->Test()->TsType() && while_stmt->Test()->TsType()->IsConditionalExprType()); + const auto expr_res = while_stmt->Test()->TsType()->ResolveConditionExpr(); + status_ = And(status_, static_cast(!std::get<0>(expr_res) || std::get<1>(expr_res))); AnalyzeStat(while_stmt->Body()); status_ = Or(status_, ResolveContinues(while_stmt)); - status_ = - Or(ResolveBreaks(while_stmt), From(!(cond_type->HasTypeFlag(TypeFlag::CONSTANT) && cond_type->GetValue()))); + status_ = Or(ResolveBreaks(while_stmt), From(!std::get<0>(expr_res) || !std::get<1>(expr_res))); } void AliveAnalyzer::AnalyzeForLoop(const ir::ForUpdateStatement *for_stmt) { AnalyzeNode(for_stmt->Init()); SetOldPendingExits(PendingExits()); - const ETSBooleanType *cond_type {}; + const Type *cond_type {}; + bool resolve_type = false; + bool res = false; + if (for_stmt->Test() != nullptr) { AnalyzeNode(for_stmt->Test()); - ASSERT(for_stmt->Test()->TsType() && for_stmt->Test()->TsType()->IsETSBooleanType()); - cond_type = for_stmt->Test()->TsType()->AsETSBooleanType(); - status_ = From(!(cond_type->HasTypeFlag(TypeFlag::CONSTANT) && !cond_type->GetValue())); + ASSERT(for_stmt->Test()->TsType() && for_stmt->Test()->TsType()->IsConditionalExprType()); + cond_type = for_stmt->Test()->TsType(); + std::tie(resolve_type, res) = for_stmt->Test()->TsType()->ResolveConditionExpr(); + status_ = From(!resolve_type || res); } else { status_ = LivenessStatus::ALIVE; } @@ -338,8 +339,7 @@ void AliveAnalyzer::AnalyzeForLoop(const ir::ForUpdateStatement *for_stmt) AnalyzeStat(for_stmt->Body()); status_ = Or(status_, ResolveContinues(for_stmt)); AnalyzeNode(for_stmt->Update()); - status_ = Or(ResolveBreaks(for_stmt), - From(cond_type != nullptr && !(cond_type->HasTypeFlag(TypeFlag::CONSTANT) && cond_type->GetValue()))); + status_ = Or(ResolveBreaks(for_stmt), From(cond_type != nullptr && (!resolve_type || !res))); } void AliveAnalyzer::AnalyzeForOfLoop(const ir::ForOfStatement *for_of_stmt) diff --git a/checker/ets/arithmetic.cpp b/checker/ets/arithmetic.cpp index d5fd4872f..e2470501e 100644 --- a/checker/ets/arithmetic.cpp +++ b/checker/ets/arithmetic.cpp @@ -128,13 +128,18 @@ Type *ETSChecker::HandleRelationOperationOnTypes(Type *left, Type *right, lexer: // NOLINTNEXTLINE(readability-function-size) std::tuple ETSChecker::CheckBinaryOperator(ir::Expression *left, ir::Expression *right, - lexer::TokenType operation_type, lexer::SourcePosition pos, - bool force_promotion) + ir::Expression *expr, lexer::TokenType operation_type, + lexer::SourcePosition pos, bool force_promotion) { checker::Type *const left_type = left->Check(this); checker::Type *const right_type = right->Check(this); - Type *unboxed_l = ETSBuiltinTypeAsPrimitiveType(left_type); - Type *unboxed_r = ETSBuiltinTypeAsPrimitiveType(right_type); + const bool is_logical_extended_operator = (operation_type == lexer::TokenType::PUNCTUATOR_LOGICAL_AND) || + (operation_type == lexer::TokenType::PUNCTUATOR_LOGICAL_OR); + Type *unboxed_l = is_logical_extended_operator ? ETSBuiltinTypeAsConditionalType(left_type) + : ETSBuiltinTypeAsPrimitiveType(left_type); + Type *unboxed_r = is_logical_extended_operator ? ETSBuiltinTypeAsConditionalType(right_type) + : ETSBuiltinTypeAsPrimitiveType(right_type); + checker::Type *ts_type {}; bool is_equal_op = (operation_type > lexer::TokenType::PUNCTUATOR_SUBSTITUTION && operation_type < lexer::TokenType::PUNCTUATOR_ARROW) && @@ -283,15 +288,24 @@ std::tuple ETSChecker::CheckBinaryOperator(ir::Expression *left, } case lexer::TokenType::PUNCTUATOR_LOGICAL_AND: case lexer::TokenType::PUNCTUATOR_LOGICAL_OR: { - if (unboxed_l == nullptr || !unboxed_l->HasTypeFlag(checker::TypeFlag::ETS_BOOLEAN) || - unboxed_r == nullptr || !unboxed_r->HasTypeFlag(checker::TypeFlag::ETS_BOOLEAN)) { - ThrowTypeError("Bad operand type, the types of the operands must be boolean type.", pos); + if (unboxed_l == nullptr || !unboxed_l->IsConditionalExprType() || unboxed_r == nullptr || + !unboxed_r->IsConditionalExprType()) { + ThrowTypeError("Bad operand type, the types of the operands must be of possible condition type.", pos); } - FlagExpressionWithUnboxing(left_type, unboxed_l, left); - FlagExpressionWithUnboxing(right_type, unboxed_r, right); + if (unboxed_l->HasTypeFlag(checker::TypeFlag::ETS_PRIMITIVE)) { + FlagExpressionWithUnboxing(left_type, unboxed_l, left); + } - ts_type = HandleBooleanLogicalOperators(unboxed_l, unboxed_r, operation_type); + if (unboxed_r->HasTypeFlag(checker::TypeFlag::ETS_PRIMITIVE)) { + FlagExpressionWithUnboxing(right_type, unboxed_r, right); + } + + if (expr->IsBinaryExpression()) { + ts_type = HandleBooleanLogicalOperatorsExtended(unboxed_l, unboxed_r, expr->AsBinaryExpression()); + } else { + UNREACHABLE(); + } break; } case lexer::TokenType::PUNCTUATOR_STRICT_EQUAL: diff --git a/checker/ets/helpers.cpp b/checker/ets/helpers.cpp index d8417228b..bb87e7d55 100644 --- a/checker/ets/helpers.cpp +++ b/checker/ets/helpers.cpp @@ -72,13 +72,15 @@ namespace panda::es2panda::checker { void ETSChecker::CheckTruthinessOfType(ir::Expression *expr) { checker::Type *type = expr->Check(this); - auto *unboxed_type = ETSBuiltinTypeAsPrimitiveType(type); + auto *unboxed_type = ETSBuiltinTypeAsConditionalType(type); - if (unboxed_type == nullptr || !unboxed_type->IsETSBooleanType()) { - ThrowTypeError("Condition must be of type boolean", expr->Start()); + if (unboxed_type != nullptr && !unboxed_type->IsConditionalExprType()) { + ThrowTypeError("Condition must be of possible condition type", expr->Start()); } - FlagExpressionWithUnboxing(type, unboxed_type, expr); + if (unboxed_type != nullptr && unboxed_type->HasTypeFlag(TypeFlag::ETS_PRIMITIVE)) { + FlagExpressionWithUnboxing(type, unboxed_type, expr); + } expr->SetTsType(unboxed_type); } @@ -626,9 +628,10 @@ checker::Type *ETSChecker::ApplyConditionalOperatorPromotion(checker::ETSChecker UNREACHABLE(); } -Type *ETSChecker::ApplyUnaryOperatorPromotion(Type *type, bool create_const, bool do_promotion) +Type *ETSChecker::ApplyUnaryOperatorPromotion(Type *type, const bool create_const, const bool do_promotion, + const bool is_cond_expr) { - Type *unboxed_type = ETSBuiltinTypeAsPrimitiveType(type); + Type *unboxed_type = is_cond_expr ? ETSBuiltinTypeAsConditionalType(type) : ETSBuiltinTypeAsPrimitiveType(type); if (unboxed_type == nullptr) { return nullptr; @@ -652,6 +655,58 @@ Type *ETSChecker::ApplyUnaryOperatorPromotion(Type *type, bool create_const, boo return unboxed_type; } +bool ETSChecker::IsNullOrVoidExpression(const ir::Expression *expr) const +{ + return (expr->IsLiteral() && expr->AsLiteral()->IsNullLiteral()) || + (expr->IsCallExpression() && + (expr->AsCallExpression()->Signature()->ReturnType() == GlobalBuiltinVoidType())); +} + +Type *ETSChecker::HandleBooleanLogicalOperatorsExtended(Type *left_type, Type *right_type, ir::BinaryExpression *expr) +{ + ASSERT(left_type->IsConditionalExprType() && right_type->IsConditionalExprType()); + + bool resolve_left = false; + bool left_value = false; + bool resolve_right = false; + bool right_value = false; + std::tie(resolve_left, left_value) = + IsNullOrVoidExpression(expr->Left()) ? std::make_tuple(true, false) : left_type->ResolveConditionExpr(); + std::tie(resolve_right, right_value) = + IsNullOrVoidExpression(expr->Right()) ? std::make_tuple(true, false) : right_type->ResolveConditionExpr(); + + if (!resolve_left) { + // return the UNION type when it is implemented + return IsTypeIdenticalTo(left_type, right_type) ? left_type : GlobalETSBooleanType(); + } + + switch (expr->OperatorType()) { + case lexer::TokenType::PUNCTUATOR_LOGICAL_OR: { + if (left_value) { + expr->SetResult(expr->Left()); + return left_type->IsETSBooleanType() ? CreateETSBooleanType(true) : left_type; + } + + expr->SetResult(expr->Right()); + return right_type->IsETSBooleanType() && resolve_right ? CreateETSBooleanType(right_value) : right_type; + } + case lexer::TokenType::PUNCTUATOR_LOGICAL_AND: { + if (left_value) { + expr->SetResult(expr->Right()); + return right_type->IsETSBooleanType() && resolve_right ? CreateETSBooleanType(right_value) : right_type; + } + + expr->SetResult(expr->Left()); + return left_type->IsETSBooleanType() ? CreateETSBooleanType(false) : left_type; + } + default: { + break; + } + } + + UNREACHABLE(); +} + Type *ETSChecker::HandleBooleanLogicalOperators(Type *left_type, Type *right_type, lexer::TokenType token_type) { using UType = typename ETSBooleanType::UType; @@ -1309,6 +1364,27 @@ Type *ETSChecker::ETSBuiltinTypeAsPrimitiveType(Type *object_type) return converter.Result(); } +Type *ETSChecker::ETSBuiltinTypeAsConditionalType(Type *object_type) +{ + if ((object_type == nullptr) || !object_type->IsConditionalExprType()) { + return nullptr; + } + + if (object_type->IsETSObjectType()) { + if (!object_type->AsETSObjectType()->HasObjectFlag(ETSObjectFlags::UNBOXABLE_TYPE)) { + return object_type; + } + auto saved_result = Relation()->IsTrue(); + Relation()->Result(false); + + UnboxingConverter converter = UnboxingConverter(AsETSChecker(), Relation(), object_type, object_type); + Relation()->Result(saved_result); + return converter.Result(); + } + + return object_type; +} + Type *ETSChecker::PrimitiveTypeAsETSBuiltinType(Type *object_type) { if (object_type == nullptr) { diff --git a/checker/types/ets/byteType.h b/checker/types/ets/byteType.h index ecbf47d88..1a1ea05d1 100644 --- a/checker/types/ets/byteType.h +++ b/checker/types/ets/byteType.h @@ -52,6 +52,11 @@ public: ss << compiler::Signatures::TYPE_DESCRIPTOR_BYTE; } + std::tuple ResolveConditionExpr() const override + { + return {IsConstantType(), value_ != 0}; + } + private: UType value_ {0}; }; diff --git a/checker/types/ets/charType.h b/checker/types/ets/charType.h index 6f7dc11b8..763a43354 100644 --- a/checker/types/ets/charType.h +++ b/checker/types/ets/charType.h @@ -52,6 +52,11 @@ public: ss << compiler::Signatures::TYPE_DESCRIPTOR_CHAR; } + std::tuple ResolveConditionExpr() const override + { + return {IsConstantType(), value_ != '\0'}; + } + private: UType value_ {'\0'}; }; diff --git a/checker/types/ets/doubleType.h b/checker/types/ets/doubleType.h index 07365fa67..ec7ebce23 100644 --- a/checker/types/ets/doubleType.h +++ b/checker/types/ets/doubleType.h @@ -52,6 +52,11 @@ public: ss << compiler::Signatures::TYPE_DESCRIPTOR_DOUBLE; } + std::tuple ResolveConditionExpr() const override + { + return {IsConstantType(), value_ != 0}; + } + private: UType value_ {0.0}; }; diff --git a/checker/types/ets/etsArrayType.h b/checker/types/ets/etsArrayType.h index 323f3df40..e19ceccd5 100644 --- a/checker/types/ets/etsArrayType.h +++ b/checker/types/ets/etsArrayType.h @@ -33,6 +33,11 @@ public: return element_; } + std::tuple ResolveConditionExpr() const override + { + return {false, false}; + } + void ToString(std::stringstream &ss) const override; void ToAssemblerType(std::stringstream &ss) const override; void ToAssemblerTypeWithRank(std::stringstream &ss) const override; diff --git a/checker/types/ets/etsBooleanType.h b/checker/types/ets/etsBooleanType.h index f842f762f..2d35d71ef 100644 --- a/checker/types/ets/etsBooleanType.h +++ b/checker/types/ets/etsBooleanType.h @@ -51,6 +51,11 @@ public: ss << compiler::Signatures::TYPE_DESCRIPTOR_BOOLEAN; } + std::tuple ResolveConditionExpr() const override + { + return {IsConstantType(), value_}; + } + private: UType value_ {false}; }; diff --git a/checker/types/ets/etsObjectType.h b/checker/types/ets/etsObjectType.h index 45232d5c8..4821d6b00 100644 --- a/checker/types/ets/etsObjectType.h +++ b/checker/types/ets/etsObjectType.h @@ -508,6 +508,15 @@ public: return allocator_; } + std::tuple ResolveConditionExpr() const override + { + if (IsNullableType() || IsETSStringType()) { + return {false, false}; + } + + return {true, true}; + } + protected: virtual ETSFunctionType *CreateETSFunctionType(const util::StringView &name) const; diff --git a/checker/types/ets/etsStringType.h b/checker/types/ets/etsStringType.h index 4a516db40..1cb059c48 100644 --- a/checker/types/ets/etsStringType.h +++ b/checker/types/ets/etsStringType.h @@ -55,6 +55,15 @@ public: return value_; } + std::tuple ResolveConditionExpr() const override + { + if (IsNullableType()) { + return {false, false}; + } + + return {IsConstantType(), IsConstantType() ? (GetValue().Length() != 0) : false}; + } + private: util::StringView value_ {}; }; diff --git a/checker/types/ets/floatType.h b/checker/types/ets/floatType.h index 513c9b325..a3b8ffb5a 100644 --- a/checker/types/ets/floatType.h +++ b/checker/types/ets/floatType.h @@ -52,6 +52,11 @@ public: ss << compiler::Signatures::TYPE_DESCRIPTOR_FLOAT; } + std::tuple ResolveConditionExpr() const override + { + return {IsConstantType(), value_ != 0}; + } + private: UType value_ {0.0}; }; diff --git a/checker/types/ets/intType.h b/checker/types/ets/intType.h index df8f6af99..ec411394f 100644 --- a/checker/types/ets/intType.h +++ b/checker/types/ets/intType.h @@ -52,6 +52,11 @@ public: ss << compiler::Signatures::TYPE_DESCRIPTOR_INT; } + std::tuple ResolveConditionExpr() const override + { + return {IsConstantType(), value_ != 0}; + } + private: UType value_ {0}; }; diff --git a/checker/types/ets/longType.h b/checker/types/ets/longType.h index 27a2deca6..8d89b3be8 100644 --- a/checker/types/ets/longType.h +++ b/checker/types/ets/longType.h @@ -52,6 +52,11 @@ public: ss << compiler::Signatures::TYPE_DESCRIPTOR_LONG; } + std::tuple ResolveConditionExpr() const override + { + return {IsConstantType(), value_ != 0}; + } + private: UType value_ {0}; }; diff --git a/checker/types/ets/shortType.h b/checker/types/ets/shortType.h index 287c04242..e0848cc72 100644 --- a/checker/types/ets/shortType.h +++ b/checker/types/ets/shortType.h @@ -52,6 +52,11 @@ public: ss << compiler::Signatures::TYPE_DESCRIPTOR_SHORT; } + std::tuple ResolveConditionExpr() const override + { + return {IsConstantType(), value_ != 0}; + } + private: UType value_ {0}; }; diff --git a/checker/types/type.h b/checker/types/type.h index 2c6d11a78..77b74baec 100644 --- a/checker/types/type.h +++ b/checker/types/type.h @@ -145,6 +145,16 @@ public: return reinterpret_cast(this); } + bool IsConditionalExprType() const + { + return HasTypeFlag(TypeFlag::CONDITION_EXPRESSION_TYPE); + } + + bool IsConstantType() const + { + return HasTypeFlag(checker::TypeFlag::CONSTANT); + } + TypeFlag TypeFlags() const { return type_flags_; @@ -208,6 +218,11 @@ public: return 0; } + virtual std::tuple ResolveConditionExpr() const + { + UNREACHABLE(); + }; + virtual void Identical(TypeRelation *relation, Type *other); virtual void AssignmentTarget(TypeRelation *relation, Type *source) = 0; virtual bool AssignmentSource(TypeRelation *relation, Type *target); diff --git a/checker/types/typeFlag.h b/checker/types/typeFlag.h index 5bc36dad5..aaa4fb759 100644 --- a/checker/types/typeFlag.h +++ b/checker/types/typeFlag.h @@ -127,7 +127,9 @@ enum class TypeFlag : uint64_t { POSSIBLY_FALSY = DEFINITELY_FALSY | STRING | NUMBER | BOOLEAN | BIGINT, VALID_ARITHMETIC_TYPE = ANY | NUMBER_LIKE | BIGINT_LIKE | ENUM, UNIT = LITERAL | UNIQUE_SYMBOL | NULLABLE, - GETTER_SETTER = GETTER | SETTER + GETTER_SETTER = GETTER | SETTER, + CONDITION_EXPRESSION_TYPE = + NULLABLE | CONSTANT | ETS_OBJECT | BYTE | SHORT | INT | LONG | FLOAT | DOUBLE | ETS_BOOLEAN | ETS_ARRAY }; DEFINE_BITOPS(TypeFlag) diff --git a/compiler/base/condition.cpp b/compiler/base/condition.cpp index 7ebaf1929..a90b5f020 100644 --- a/compiler/base/condition.cpp +++ b/compiler/base/condition.cpp @@ -17,7 +17,9 @@ #include "compiler/core/pandagen.h" #include "compiler/core/ETSGen.h" +#include "ir/expressions/assignmentExpression.h" #include "ir/expressions/binaryExpression.h" +#include "ir/expressions/callExpression.h" #include "ir/expressions/unaryExpression.h" namespace panda::es2panda::compiler { @@ -91,11 +93,29 @@ void Condition::Compile(PandaGen *pg, const ir::Expression *expr, Label *false_l pg->BranchIfFalse(expr, false_label); } -Condition::Result Condition::CheckConstantExpr(const ir::Expression *expr) +Condition::Result Condition::CheckConstantExpr(ETSGen *etsg, const ir::Expression *expr) { - if (expr->TsType()->HasTypeFlag(checker::TypeFlag::CONSTANT)) { - auto res = expr->TsType()->AsETSBooleanType()->GetValue(); - return res ? Result::CONST_TRUE : Result::CONST_FALSE; + const auto resulting_expression = [](const ir::Expression *e) { + if (e->IsBinaryExpression() && e->AsBinaryExpression()->IsLogicalExtended()) { + return e->AsBinaryExpression()->Result(); + } + if (e->IsAssignmentExpression() && e->AsAssignmentExpression()->IsLogicalExtended()) { + return e->AsAssignmentExpression()->Result(); + } + return e; + }(expr); + + if (resulting_expression == nullptr) { + return Result::UNKNOWN; + } + + if (etsg->Checker()->IsNullOrVoidExpression(resulting_expression)) { + return Result::CONST_FALSE; + } + + auto expr_res = resulting_expression->TsType()->ResolveConditionExpr(); + if (std::get<0>(expr_res)) { + return std::get<1>(expr_res) ? Result::CONST_TRUE : Result::CONST_FALSE; } return Result::UNKNOWN; @@ -129,10 +149,12 @@ void Condition::Compile(ETSGen *etsg, const ir::Expression *expr, Label *false_l case lexer::TokenType::PUNCTUATOR_LOGICAL_AND: { bin_expr->Left()->Compile(etsg); etsg->ApplyConversion(bin_expr->Left(), bin_expr->OperationType()); + etsg->ResolveConditionalResultIfFalse(bin_expr->Left(), false_label); etsg->BranchIfFalse(bin_expr, false_label); bin_expr->Right()->Compile(etsg); etsg->ApplyConversion(bin_expr->Right(), bin_expr->OperationType()); + etsg->ResolveConditionalResultIfFalse(bin_expr->Right(), false_label); etsg->BranchIfFalse(bin_expr, false_label); return; } @@ -141,10 +163,12 @@ void Condition::Compile(ETSGen *etsg, const ir::Expression *expr, Label *false_l bin_expr->Left()->Compile(etsg); etsg->ApplyConversion(bin_expr->Left(), bin_expr->OperationType()); + etsg->ResolveConditionalResultIfTrue(bin_expr->Left(), end_label); etsg->BranchIfTrue(bin_expr, end_label); bin_expr->Right()->Compile(etsg); etsg->ApplyConversion(bin_expr->Right(), bin_expr->OperationType()); + etsg->ResolveConditionalResultIfFalse(bin_expr->Right(), false_label); etsg->BranchIfFalse(bin_expr, false_label); etsg->SetLabel(bin_expr, end_label); return; @@ -157,17 +181,14 @@ void Condition::Compile(ETSGen *etsg, const ir::Expression *expr, Label *false_l expr->AsUnaryExpression()->OperatorType() == lexer::TokenType::PUNCTUATOR_EXCLAMATION_MARK) { expr->AsUnaryExpression()->Argument()->Compile(etsg); etsg->ApplyConversion(expr->AsUnaryExpression()->Argument(), etsg->Checker()->GlobalETSBooleanType()); + etsg->ResolveConditionalResultIfTrue(expr, false_label); etsg->BranchIfTrue(expr, false_label); return; } - - // TODO(user): Handle implicit bool conversion: not zero int == true, not null obj ref == true, otherwise false - ASSERT(expr->TsType()->IsETSBooleanType() || - (expr->TsType()->IsETSObjectType() && - expr->TsType()->AsETSObjectType()->HasObjectFlag( - checker::ETSObjectFlags::BUILTIN_BOOLEAN))); // already checked by checker::CheckTruthinessOfType() + ASSERT(expr->TsType()->IsConditionalExprType()); expr->Compile(etsg); etsg->ApplyConversion(expr, etsg->Checker()->GlobalETSBooleanType()); + etsg->ResolveConditionalResultIfFalse(expr, false_label); etsg->BranchIfFalse(expr, false_label); } } // namespace panda::es2panda::compiler diff --git a/compiler/base/condition.h b/compiler/base/condition.h index 85071b3a4..0fe3e9803 100644 --- a/compiler/base/condition.h +++ b/compiler/base/condition.h @@ -35,7 +35,7 @@ public: static void Compile(PandaGen *pg, const ir::Expression *expr, Label *false_label); static void Compile(ETSGen *etsg, const ir::Expression *expr, Label *false_label); - static Result CheckConstantExpr(const ir::Expression *expr); + static Result CheckConstantExpr(ETSGen *etsg, const ir::Expression *expr); }; } // namespace panda::es2panda::compiler diff --git a/compiler/core/ETSGen.cpp b/compiler/core/ETSGen.cpp index 04fa62fea..d39346d6d 100644 --- a/compiler/core/ETSGen.cpp +++ b/compiler/core/ETSGen.cpp @@ -21,6 +21,7 @@ #include "ir/expressions/assignmentExpression.h" #include "ir/expressions/identifier.h" #include "ir/expressions/binaryExpression.h" +#include "ir/expressions/callExpression.h" #include "ir/expressions/memberExpression.h" #include "ir/expressions/templateLiteral.h" #include "ir/statements/breakStatement.h" @@ -1878,7 +1879,8 @@ void ETSGen::Negate(const ir::AstNode *node) void ETSGen::LogicalNot(const ir::AstNode *node) { - ASSERT(GetAccumulatorType()->IsETSBooleanType()); + ASSERT(GetAccumulatorType()->IsConditionalExprType()); + ResolveConditionalResultIfFalse(node); Sa().Emit(node, 1); } @@ -2237,6 +2239,18 @@ void ETSGen::LoadStringLength(const ir::AstNode *node) SetAccumulatorType(Checker()->GlobalIntType()); } +void ETSGen::FloatIsNaN(const ir::AstNode *node) +{ + Ra().Emit(node, Signatures::BUILTIN_FLOAT_IS_NAN, dummy_reg_, 0); + SetAccumulatorType(Checker()->GlobalETSBooleanType()); +} + +void ETSGen::DoubleIsNaN(const ir::AstNode *node) +{ + Ra().Emit(node, Signatures::BUILTIN_DOUBLE_IS_NAN, dummy_reg_, 0); + SetAccumulatorType(Checker()->GlobalETSBooleanType()); +} + void ETSGen::LoadStringChar(const ir::AstNode *node, const VReg string_obj, const VReg char_index) { Ra().Emit(node, Signatures::BUILTIN_STRING_CHAR_AT, string_obj, char_index); diff --git a/compiler/core/ETSGen.h b/compiler/core/ETSGen.h index c70e92e37..fcc7e6571 100644 --- a/compiler/core/ETSGen.h +++ b/compiler/core/ETSGen.h @@ -96,6 +96,132 @@ public: bool TryLoadConstantExpression(const ir::Expression *node); void Condition(const ir::AstNode *node, lexer::TokenType op, VReg lhs, Label *if_false); + template + void ResolveConditionalResult(const ir::AstNode *node, [[maybe_unused]] Label *if_false) + { + auto type = node->IsExpression() ? node->AsExpression()->TsType() : GetAccumulatorType(); + if (type->IsETSObjectType() && + type->AsETSObjectType()->HasObjectFlag(panda::es2panda::checker::ETSObjectFlags::UNBOXABLE_TYPE)) { + type = GetAccumulatorType(); + } + if (type->IsETSBooleanType()) { + return; + } + + if (node->IsExpression()) { + auto expr_node = node->AsExpression(); + if (Checker()->IsNullOrVoidExpression(expr_node)) { + if constexpr (USE_FALSE_LABEL) { + Branch(node, if_false); + } else { + Sa().Emit(node, 0); + } + return; + } + } + Label *if_nullable {nullptr}; + Label *end {nullptr}; + if (type->IsNullableType()) { + if constexpr (USE_FALSE_LABEL) { + BranchIfNull(node, if_false); + } else { + if_nullable = AllocLabel(); + end = AllocLabel(); + BranchIfNull(node, if_nullable); + } + } + if (type->IsETSArrayType()) { + compiler::VReg obj_reg = AllocReg(); + StoreAccumulator(node, obj_reg); + LoadArrayLength(node, obj_reg); + } else if (type->IsETSObjectType()) { + if (type->IsETSStringType()) { + LoadStringLength(node); + if constexpr (BEFORE_LOGICAL_NOT) { + Label *zero_lenth = AllocLabel(); + BranchIfFalse(node, zero_lenth); + ToBinaryResult(node, zero_lenth); + } + } else { + Sa().Emit(node, 1); + } + } else { + switch (type->TypeFlags()) { + case checker::TypeFlag::LONG: { + CastToInt(node); + [[fallthrough]]; + } + case checker::TypeFlag::BYTE: + case checker::TypeFlag::CHAR: + case checker::TypeFlag::SHORT: + case checker::TypeFlag::INT: { + if constexpr (BEFORE_LOGICAL_NOT) { + Label *zero_primitive = AllocLabel(); + BranchIfFalse(node, zero_primitive); + ToBinaryResult(node, zero_primitive); + } + break; + } + case checker::TypeFlag::DOUBLE: + case checker::TypeFlag::FLOAT: { + VReg tmp_reg = AllocReg(); + StoreAccumulator(node, tmp_reg); + if (type->IsFloatType()) { + FloatIsNaN(node); + } else { + DoubleIsNaN(node); + } + Sa().Emit(node, 1); + + auto real_end_label = [](Label *end_label, Label *if_false_label, ETSGen *etsgn, + bool use_false_label) { + if (use_false_label) { + return if_false_label; + } + if (end_label == nullptr) { + end_label = etsgn->AllocLabel(); + } + return end_label; + }(end, if_false, this, USE_FALSE_LABEL); + BranchIfFalse(node, real_end_label); + + LoadAccumulator(node, tmp_reg); + VReg zero_reg = AllocReg(); + if (type->IsFloatType()) { + MoveImmediateToRegister(node, zero_reg, checker::TypeFlag::FLOAT, 0); + BinaryNumberComparison(node, zero_reg, real_end_label); + } else { + MoveImmediateToRegister(node, zero_reg, checker::TypeFlag::DOUBLE, 0); + BinaryNumberComparison(node, zero_reg, real_end_label); + } + break; + } + default: + break; + } + } + if (if_nullable != nullptr) { + Branch(node, end); + SetLabel(node, if_nullable); + Sa().Emit(node, 0); + } + if (end != nullptr) { + SetLabel(node, end); + } + } + + template + void ResolveConditionalResultIfFalse(const ir::AstNode *node, Label *if_false = nullptr) + { + ResolveConditionalResult(node, if_false); + } + + template + void ResolveConditionalResultIfTrue(const ir::AstNode *node, Label *if_false = nullptr) + { + ResolveConditionalResult(node, if_false); + } + void BranchIfFalse(const ir::AstNode *node, Label *if_false) { Sa().Emit(node, if_false); @@ -305,6 +431,9 @@ public: void LoadStringLength(const ir::AstNode *node); void LoadStringChar(const ir::AstNode *node, VReg string_obj, VReg char_index); + void FloatIsNaN(const ir::AstNode *node); + void DoubleIsNaN(const ir::AstNode *node); + void CompileStatements(const ArenaVector &statements); // Cast @@ -500,9 +629,12 @@ private: template void ConditionalBranching(const ir::AstNode *node, Label *if_false) { - SetAccumulatorType(Checker()->GlobalETSBooleanType()); if constexpr (std::is_same_v) { - LogicalNot(node); + ResolveConditionalResultIfTrue(node, if_false); + Sa().Emit(node); + Sa().Emit(node, 1); + } else { + ResolveConditionalResultIfFalse(node, if_false); } BranchIfFalse(node, if_false); } @@ -519,6 +651,7 @@ private: } else { CallThisVirtual1(node, lhs, Signatures::BUILTIN_OBJECT_EQUALS, arg0); } + SetAccumulatorType(Checker()->GlobalETSBooleanType()); ConditionalBranching(node, if_false); JumpTo(node, if_true); diff --git a/compiler/scripts/signatures.yaml b/compiler/scripts/signatures.yaml index 35f30fef0..0f2a2e8dc 100644 --- a/compiler/scripts/signatures.yaml +++ b/compiler/scripts/signatures.yaml @@ -451,6 +451,18 @@ signatures: return_type: BUILTIN_DOUBLE ref: BUILTIN_DOUBLE_VALUE_OF + - callee: BUILTIN_FLOAT + method_name: isNaN + params: [PRIMITIVE_FLOAT] + return_type: PRIMITIVE_BOOLEAN + ref: BUILTIN_FLOAT_IS_NAN + + - callee: BUILTIN_DOUBLE + method_name: isNaN + params: [PRIMITIVE_DOUBLE] + return_type: PRIMITIVE_BOOLEAN + ref: BUILTIN_DOUBLE_IS_NAN + - callee: BUILTIN_BOOLEAN method_name: unboxed params: [] diff --git a/ir/expressions/assignmentExpression.cpp b/ir/expressions/assignmentExpression.cpp index bd1d436bc..b1e58362d 100644 --- a/ir/expressions/assignmentExpression.cpp +++ b/ir/expressions/assignmentExpression.cpp @@ -274,7 +274,7 @@ checker::Type *AssignmentExpression::Check([[maybe_unused]] checker::ETSChecker case lexer::TokenType::PUNCTUATOR_BITWISE_OR_EQUAL: case lexer::TokenType::PUNCTUATOR_PLUS_EQUAL: { std::tie(std::ignore, operation_type_) = - checker->CheckBinaryOperator(left_, right_, operator_, Start(), true); + checker->CheckBinaryOperator(left_, right_, this, operator_, Start(), true); auto unboxed_left = checker->ETSBuiltinTypeAsPrimitiveType(left_type); source_type = unboxed_left == nullptr ? left_type : unboxed_left; diff --git a/ir/expressions/assignmentExpression.h b/ir/expressions/assignmentExpression.h index c16b3780c..0bbef758c 100644 --- a/ir/expressions/assignmentExpression.h +++ b/ir/expressions/assignmentExpression.h @@ -65,6 +65,16 @@ public: return right_; } + [[nodiscard]] const Expression *Result() const noexcept + { + return result_; + } + + [[nodiscard]] Expression *Result() noexcept + { + return result_; + } + [[nodiscard]] lexer::TokenType OperatorType() const noexcept { return operator_; @@ -75,6 +85,18 @@ public: return operator_ = token_type; } + void SetResult(Expression *expr) noexcept + { + left_ = expr; + SetStart(left_->Start()); + } + + [[nodiscard]] bool IsLogicalExtended() const noexcept + { + return operator_ == lexer::TokenType::PUNCTUATOR_LOGICAL_AND || + operator_ == lexer::TokenType::PUNCTUATOR_LOGICAL_OR; + } + [[nodiscard]] binder::Variable *Target() noexcept { return target_; @@ -110,6 +132,7 @@ protected: private: Expression *left_ = nullptr; Expression *right_ = nullptr; + Expression *result_ = nullptr; lexer::TokenType operator_; binder::Variable *target_ {}; checker::Type *operation_type_ {}; diff --git a/ir/expressions/binaryExpression.cpp b/ir/expressions/binaryExpression.cpp index 5c041a13f..5b2975773 100644 --- a/ir/expressions/binaryExpression.cpp +++ b/ir/expressions/binaryExpression.cpp @@ -20,11 +20,9 @@ #include "compiler/core/pandagen.h" #include "compiler/core/ETSGen.h" #include "compiler/core/regScope.h" -#include "checker/ETSchecker.h" #include "checker/TSchecker.h" #include "ir/astDump.h" #include "ir/expressions/identifier.h" -#include "lexer/token/tokenType.h" namespace panda::es2panda::ir { void BinaryExpression::TransformChildren(const NodeTransformer &cb) @@ -149,20 +147,46 @@ void BinaryExpression::CompileLogical(compiler::ETSGen *etsg) const { auto *end_label = etsg->AllocLabel(); + if (operator_ == lexer::TokenType::PUNCTUATOR_NULLISH_COALESCING) { + left_->Compile(etsg); + etsg->ApplyConversion(left_, operation_type_); + etsg->BranchIfNotNull(this, end_label); + right_->Compile(etsg); + etsg->ApplyConversion(right_, operation_type_); + etsg->SetLabel(this, end_label); + return; + } + + ASSERT(IsLogicalExtended()); + auto ttctx = compiler::TargetTypeContext(etsg, OperationType()); + compiler::RegScope rs(etsg); + auto lhs = etsg->AllocReg(); + auto rhs = etsg->AllocReg(); left_->Compile(etsg); - etsg->ApplyConversion(left_, operation_type_); + etsg->ApplyConversionAndStoreAccumulator(left_, lhs, OperationType()); + auto left_false_label = etsg->AllocLabel(); if (operator_ == lexer::TokenType::PUNCTUATOR_LOGICAL_AND) { - etsg->BranchIfFalse(this, end_label); - } else if (operator_ == lexer::TokenType::PUNCTUATOR_LOGICAL_OR) { - etsg->BranchIfTrue(this, end_label); + etsg->ResolveConditionalResultIfFalse(left_, left_false_label); + etsg->BranchIfFalse(this, left_false_label); + + right_->Compile(etsg); + etsg->ApplyConversionAndStoreAccumulator(right_, rhs, OperationType()); + etsg->Branch(this, end_label); + + etsg->SetLabel(this, left_false_label); + etsg->LoadAccumulator(this, lhs); } else { - ASSERT(operator_ == lexer::TokenType::PUNCTUATOR_NULLISH_COALESCING); - etsg->BranchIfNotNull(this, end_label); - } + etsg->ResolveConditionalResultIfFalse(left_, left_false_label); + etsg->BranchIfFalse(this, left_false_label); - right_->Compile(etsg); - etsg->ApplyConversion(right_, operation_type_); + etsg->LoadAccumulator(this, lhs); + etsg->Branch(this, end_label); + + etsg->SetLabel(this, left_false_label); + right_->Compile(etsg); + etsg->ApplyConversionAndStoreAccumulator(right_, rhs, OperationType()); + } etsg->SetLabel(this, end_label); } @@ -239,7 +263,7 @@ checker::Type *BinaryExpression::Check(checker::ETSChecker *checker) return TsType(); } checker::Type *new_ts_type {nullptr}; - std::tie(new_ts_type, operation_type_) = checker->CheckBinaryOperator(left_, right_, operator_, Start()); + std::tie(new_ts_type, operation_type_) = checker->CheckBinaryOperator(left_, right_, this, operator_, Start()); SetTsType(new_ts_type); return TsType(); } diff --git a/ir/expressions/binaryExpression.h b/ir/expressions/binaryExpression.h index de4488f0c..c2a1a47bb 100644 --- a/ir/expressions/binaryExpression.h +++ b/ir/expressions/binaryExpression.h @@ -53,6 +53,16 @@ public: return right_; } + [[nodiscard]] const Expression *Result() const noexcept + { + return result_; + } + + [[nodiscard]] Expression *Result() noexcept + { + return result_; + } + [[nodiscard]] lexer::TokenType OperatorType() const noexcept { return operator_; @@ -63,12 +73,24 @@ public: return operator_ <= lexer::TokenType::PUNCTUATOR_LOGICAL_AND; } + [[nodiscard]] bool IsLogicalExtended() const noexcept + { + return operator_ == lexer::TokenType::PUNCTUATOR_LOGICAL_AND || + operator_ == lexer::TokenType::PUNCTUATOR_LOGICAL_OR; + } + void SetLeft(Expression *expr) noexcept { left_ = expr; SetStart(left_->Start()); } + void SetResult(Expression *expr) noexcept + { + left_ = expr; + SetStart(left_->Start()); + } + void SetOperator(lexer::TokenType operator_type) noexcept { operator_ = operator_type; @@ -106,6 +128,7 @@ public: private: Expression *left_ = nullptr; Expression *right_ = nullptr; + Expression *result_ = nullptr; lexer::TokenType operator_; checker::Type *operation_type_ {}; }; diff --git a/ir/expressions/unaryExpression.cpp b/ir/expressions/unaryExpression.cpp index 4570f0e2b..677b1886a 100644 --- a/ir/expressions/unaryExpression.cpp +++ b/ir/expressions/unaryExpression.cpp @@ -24,6 +24,7 @@ #include "ir/expressions/identifier.h" #include "ir/expressions/literals/bigIntLiteral.h" #include "ir/expressions/literals/numberLiteral.h" +#include "ir/expressions/callExpression.h" #include "ir/expressions/memberExpression.h" namespace panda::es2panda::ir { @@ -216,8 +217,10 @@ checker::Type *UnaryExpression::Check(checker::ETSChecker *checker) } auto arg_type = argument_->Check(checker); - checker::Type *operand_type = checker->ApplyUnaryOperatorPromotion(arg_type); - auto unboxed_operand_type = checker->ETSBuiltinTypeAsPrimitiveType(arg_type); + const auto is_cond_expr = operator_ == lexer::TokenType::PUNCTUATOR_EXCLAMATION_MARK; + checker::Type *operand_type = checker->ApplyUnaryOperatorPromotion(arg_type, true, true, is_cond_expr); + auto unboxed_operand_type = is_cond_expr ? checker->ETSBuiltinTypeAsConditionalType(arg_type) + : checker->ETSBuiltinTypeAsPrimitiveType(arg_type); switch (operator_) { case lexer::TokenType::PUNCTUATOR_MINUS: @@ -251,13 +254,23 @@ checker::Type *UnaryExpression::Check(checker::ETSChecker *checker) break; } case lexer::TokenType::PUNCTUATOR_EXCLAMATION_MARK: { - if (operand_type == nullptr || !operand_type->HasTypeFlag(checker::TypeFlag::ETS_BOOLEAN)) { + if (checker->IsNullOrVoidExpression(argument_)) { + auto ts_type = checker->CreateETSBooleanType(true); + ts_type->AddTypeFlag(checker::TypeFlag::CONSTANT); + SetTsType(ts_type); + break; + } + + if (operand_type == nullptr || !operand_type->IsConditionalExprType()) { checker->ThrowTypeError("Bad operand type, the type of the operand must be boolean type.", argument_->Start()); } - if (operand_type->HasTypeFlag(checker::TypeFlag::CONSTANT)) { - SetTsType(checker->CreateETSBooleanType(!operand_type->AsETSBooleanType()->GetValue())); + auto expr_res = operand_type->ResolveConditionExpr(); + if (std::get<0>(expr_res)) { + auto ts_type = checker->CreateETSBooleanType(!std::get<1>(expr_res)); + ts_type->AddTypeFlag(checker::TypeFlag::CONSTANT); + SetTsType(ts_type); break; } @@ -274,7 +287,8 @@ checker::Type *UnaryExpression::Check(checker::ETSChecker *checker) } } - if (arg_type->IsETSObjectType() && (unboxed_operand_type != nullptr)) { + if (arg_type->IsETSObjectType() && (unboxed_operand_type != nullptr) && + unboxed_operand_type->HasTypeFlag(checker::TypeFlag::ETS_PRIMITIVE)) { argument_->AddBoxingUnboxingFlag(checker->GetUnboxingFlag(unboxed_operand_type)); } diff --git a/ir/statements/assertStatement.cpp b/ir/statements/assertStatement.cpp index 01181345a..d327b03b0 100644 --- a/ir/statements/assertStatement.cpp +++ b/ir/statements/assertStatement.cpp @@ -71,7 +71,7 @@ void AssertStatement::ThrowError(compiler::ETSGen *const etsg) const void AssertStatement::Compile([[maybe_unused]] compiler::ETSGen *etsg) const { - auto res = compiler::Condition::CheckConstantExpr(test_); + auto res = compiler::Condition::CheckConstantExpr(etsg, test_); if (res == compiler::Condition::Result::CONST_TRUE) { return; diff --git a/ir/statements/ifStatement.cpp b/ir/statements/ifStatement.cpp index b931fe18b..91736420b 100644 --- a/ir/statements/ifStatement.cpp +++ b/ir/statements/ifStatement.cpp @@ -72,7 +72,7 @@ void IfStatement::Compile([[maybe_unused]] compiler::PandaGen *pg) const void IfStatement::Compile([[maybe_unused]] compiler::ETSGen *etsg) const { - auto res = compiler::Condition::CheckConstantExpr(test_); + auto res = compiler::Condition::CheckConstantExpr(etsg, test_); if (res == compiler::Condition::Result::CONST_TRUE) { consequent_->Compile(etsg); diff --git a/test/compiler/ets/voidTypeInBinaryOperation-expected.txt b/test/compiler/ets/voidTypeInBinaryOperation-expected.txt index aa1df0dca..9447d6cad 100644 --- a/test/compiler/ets/voidTypeInBinaryOperation-expected.txt +++ b/test/compiler/ets/voidTypeInBinaryOperation-expected.txt @@ -494,4 +494,3 @@ } } } -TypeError: Bad operand type, the types of the operands must be boolean type. [voidTypeInBinaryOperation.ets:20:10] -- Gitee From 45fd578af0ed840534d03fc21308c2ddd0516080 Mon Sep 17 00:00:00 2001 From: Amosov Alexey Date: Fri, 27 Oct 2023 19:33:47 +0300 Subject: [PATCH 4/4] Rename IsOptional() in AstNode Signed-off-by: Amosov Alexey --- ir/astNode.h | 2 +- ir/base/classProperty.cpp | 2 +- ir/base/methodDefinition.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ir/astNode.h b/ir/astNode.h index 5accd1e72..7e63089f0 100644 --- a/ir/astNode.h +++ b/ir/astNode.h @@ -371,7 +371,7 @@ public: return (flags_ & ModifierFlags::READONLY) != 0; } - [[nodiscard]] bool IsOptional() const noexcept + [[nodiscard]] bool IsOptionalDeclaration() const noexcept { return (flags_ & ModifierFlags::OPTIONAL) != 0; } diff --git a/ir/base/classProperty.cpp b/ir/base/classProperty.cpp index 24bfd844c..ecc55d923 100644 --- a/ir/base/classProperty.cpp +++ b/ir/base/classProperty.cpp @@ -74,7 +74,7 @@ void ClassProperty::Dump(ir::AstDumper *dumper) const {"static", IsStatic()}, {"readonly", IsReadonly()}, {"declare", IsDeclare()}, - {"optional", IsOptional()}, + {"optional", IsOptionalDeclaration()}, {"computed", is_computed_}, {"typeAnnotation", AstDumper::Optional(type_annotation_)}, {"definite", IsDefinite()}, diff --git a/ir/base/methodDefinition.cpp b/ir/base/methodDefinition.cpp index 92022432c..7acf34926 100644 --- a/ir/base/methodDefinition.cpp +++ b/ir/base/methodDefinition.cpp @@ -124,7 +124,7 @@ void MethodDefinition::Dump(ir::AstDumper *dumper) const {"kind", kind}, {"accessibility", AstDumper::Optional(AstDumper::ModifierToString(flags_))}, {"static", IsStatic()}, - {"optional", IsOptional()}, + {"optional", IsOptionalDeclaration()}, {"computed", is_computed_}, {"value", value_}, {"overloads", overloads_}, -- Gitee