From 243f7750876657bd9faefb0734b336d2738b353d Mon Sep 17 00:00:00 2001 From: Anna Antipina Date: Wed, 6 Dec 2023 18:35:05 +0300 Subject: [PATCH] i try Signed-off-by: Anna Antipina --- ets2panda/checker/ETSchecker.h | 3 + ets2panda/checker/ets/helpers.cpp | 89 ++++++++++-- ets2panda/checker/types/typeFlag.h | 2 +- ets2panda/compiler/core/ETSGen.h | 1 + .../compiler/lowering/ets/unionLowering.cpp | 134 +++++++++++++++++- .../compiler/lowering/ets/unionLowering.h | 1 + 6 files changed, 218 insertions(+), 12 deletions(-) diff --git a/ets2panda/checker/ETSchecker.h b/ets2panda/checker/ETSchecker.h index 1696ec817c..e176610c1b 100644 --- a/ets2panda/checker/ETSchecker.h +++ b/ets2panda/checker/ETSchecker.h @@ -416,6 +416,7 @@ public: 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); + Type *CreateBinaryOperationETSUnionType(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); @@ -447,7 +448,9 @@ public: Type *PrimitiveTypeAsETSBuiltinType(Type *object_type); void AddBoxingUnboxingFlagToNode(ir::AstNode *node, Type *boxing_unboxing_type); ir::BoxingUnboxingFlags GetBoxingFlag(Type *boxing_type); + ir::BoxingUnboxingFlags GetBoxingFlag(TypeFlag type_kind) const; ir::BoxingUnboxingFlags GetUnboxingFlag(Type *unboxing_type); + ir::BoxingUnboxingFlags GetUnboxingFlag(TypeFlag type_kind) const; Type *MaybeBoxedType(const varbinder::Variable *var, ArenaAllocator *allocator) const; Type *MaybeBoxedType(const varbinder::Variable *var) { diff --git a/ets2panda/checker/ets/helpers.cpp b/ets2panda/checker/ets/helpers.cpp index b662b6c757..6201f19b78 100644 --- a/ets2panda/checker/ets/helpers.cpp +++ b/ets2panda/checker/ets/helpers.cpp @@ -776,6 +776,21 @@ bool ETSChecker::IsNullLikeOrVoidExpression(const ir::Expression *expr) const return expr->TsType()->IsETSNullLike() || expr->TsType()->IsETSVoidType(); } +Type *ETSChecker::CreateBinaryOperationETSUnionType(Type *left_type, Type *right_type, ir::BinaryExpression *expr) +{ + ArenaVector types(Allocator()->Adapter()); + types.push_back(left_type); + types.push_back(right_type); + Relation()->SetNode(expr); + if (expr->Left()->TsType()->HasTypeFlag(checker::TypeFlag::ETS_PRIMITIVE)) { + expr->Left()->SetBoxingUnboxingFlags(GetBoxingFlag(expr->Left()->TsType())); + } + if (expr->Right()->TsType()->HasTypeFlag(checker::TypeFlag::ETS_PRIMITIVE)) { + expr->Right()->SetBoxingUnboxingFlags(GetBoxingFlag(expr->Right()->TsType())); + } + return CreateETSUnionType(std::move(types)); +} + Type *ETSChecker::HandleBooleanLogicalOperatorsExtended(Type *left_type, Type *right_type, ir::BinaryExpression *expr) { ASSERT(left_type->IsConditionalExprType() && right_type->IsConditionalExprType()); @@ -786,13 +801,7 @@ Type *ETSChecker::HandleBooleanLogicalOperatorsExtended(Type *left_type, Type *r IsNullLikeOrVoidExpression(expr->Right()) ? std::make_tuple(true, false) : right_type->ResolveConditionExpr(); if (!resolve_left) { - if (IsTypeIdenticalTo(left_type, right_type)) { - return left_type; - } - ArenaVector types(Allocator()->Adapter()); - types.push_back(left_type); - types.push_back(right_type); - return CreateETSUnionType(std::move(types)); + return IsTypeIdenticalTo(left_type, right_type) ? left_type : CreateBinaryOperationETSUnionType(left_type, right_type, expr); } switch (expr->OperatorType()) { @@ -1563,9 +1572,73 @@ ir::BoxingUnboxingFlags ETSChecker::GetBoxingFlag(Type *boxing_type) } } +ir::BoxingUnboxingFlags ETSChecker::GetBoxingFlag(TypeFlag type_kind) const +{ + switch (type_kind) { + case TypeFlag::ETS_BOOLEAN: { + return ir::BoxingUnboxingFlags::BOX_TO_BOOLEAN; + } + case TypeFlag::BYTE: { + return ir::BoxingUnboxingFlags::BOX_TO_BYTE; + } + case TypeFlag::CHAR: { + return ir::BoxingUnboxingFlags::BOX_TO_CHAR; + } + case TypeFlag::SHORT: { + return ir::BoxingUnboxingFlags::BOX_TO_SHORT; + } + case TypeFlag::INT: { + return ir::BoxingUnboxingFlags::BOX_TO_INT; + } + case TypeFlag::LONG: { + return ir::BoxingUnboxingFlags::BOX_TO_LONG; + } + case TypeFlag::FLOAT: { + return ir::BoxingUnboxingFlags::BOX_TO_FLOAT; + } + case TypeFlag::DOUBLE: { + return ir::BoxingUnboxingFlags::BOX_TO_DOUBLE; + } + default: + UNREACHABLE(); + } +} + ir::BoxingUnboxingFlags ETSChecker::GetUnboxingFlag(Type *unboxing_type) { - auto type_kind = TypeKind(unboxing_type); + auto type_kind = TypeKind(ETSBuiltinTypeAsPrimitiveType(unboxing_type)); + switch (type_kind) { + case TypeFlag::ETS_BOOLEAN: { + return ir::BoxingUnboxingFlags::UNBOX_TO_BOOLEAN; + } + case TypeFlag::BYTE: { + return ir::BoxingUnboxingFlags::UNBOX_TO_BYTE; + } + case TypeFlag::CHAR: { + return ir::BoxingUnboxingFlags::UNBOX_TO_CHAR; + } + case TypeFlag::SHORT: { + return ir::BoxingUnboxingFlags::UNBOX_TO_SHORT; + } + case TypeFlag::INT: { + return ir::BoxingUnboxingFlags::UNBOX_TO_INT; + } + case TypeFlag::LONG: { + return ir::BoxingUnboxingFlags::UNBOX_TO_LONG; + } + case TypeFlag::FLOAT: { + return ir::BoxingUnboxingFlags::UNBOX_TO_FLOAT; + } + case TypeFlag::DOUBLE: { + return ir::BoxingUnboxingFlags::UNBOX_TO_DOUBLE; + } + default: + UNREACHABLE(); + } +} + +ir::BoxingUnboxingFlags ETSChecker::GetUnboxingFlag(TypeFlag type_kind) const +{ switch (type_kind) { case TypeFlag::ETS_BOOLEAN: { return ir::BoxingUnboxingFlags::UNBOX_TO_BOOLEAN; diff --git a/ets2panda/checker/types/typeFlag.h b/ets2panda/checker/types/typeFlag.h index cfa698c62d..f15755bd3e 100644 --- a/ets2panda/checker/types/typeFlag.h +++ b/ets2panda/checker/types/typeFlag.h @@ -130,7 +130,7 @@ enum class TypeFlag : uint64_t { UNIT = LITERAL | UNIQUE_SYMBOL | NULLISH, GETTER_SETTER = GETTER | SETTER, CONDITION_EXPRESSION_TYPE = NULLISH | CONSTANT | ETS_OBJECT | BYTE | SHORT | INT | LONG | FLOAT | DOUBLE | - ETS_BOOLEAN | ETS_ARRAY | ETS_ENUM | ETS_STRING_ENUM + ETS_BOOLEAN | ETS_ARRAY | ETS_ENUM | ETS_STRING_ENUM | ETS_UNION }; DEFINE_BITOPS(TypeFlag) diff --git a/ets2panda/compiler/core/ETSGen.h b/ets2panda/compiler/core/ETSGen.h index 95aaa9528a..38783dc08a 100644 --- a/ets2panda/compiler/core/ETSGen.h +++ b/ets2panda/compiler/core/ETSGen.h @@ -22,6 +22,7 @@ #include "compiler/core/ETSfunction.h" #include "compiler/core/targetTypeContext.h" #include "checker/ETSchecker.h" +#include "checker/ets/unboxingConverter.h" #include "util/helpers.h" namespace panda::es2panda::compiler { diff --git a/ets2panda/compiler/lowering/ets/unionLowering.cpp b/ets2panda/compiler/lowering/ets/unionLowering.cpp index c9e0335ac9..2756caca15 100644 --- a/ets2panda/compiler/lowering/ets/unionLowering.cpp +++ b/ets2panda/compiler/lowering/ets/unionLowering.cpp @@ -153,6 +153,7 @@ ir::TSAsExpression *HandleUnionCastToPrimitive(checker::ETSChecker *checker, ir: source_type = union_type->AsETSUnionType()->FindTypeIsCastableToSomeType(expr->Expr(), checker->Relation(), expr->TsType()); } + if (source_type != nullptr && expr->Expr()->GetBoxingUnboxingFlags() != ir::BoxingUnboxingFlags::NONE) { if (expr->TsType()->HasTypeFlag(checker::TypeFlag::ETS_PRIMITIVE)) { auto *const boxed_expr_type = checker::BoxingConverter::ETSTypeFromSource(checker, expr->TsType()); @@ -228,7 +229,7 @@ ir::ExpressionStatement *GenExpressionStmtWithAssignment(checker::ETSChecker *ch return checker->AllocNode(assignment_for_binary); } -ir::BlockStatement *GenBlockStmtForAssignmentBinary(checker::ETSChecker *checker, ir::Identifier *var_decl_id, +ir::BlockStatement *GenBlockStmtForAssignment(checker::ETSChecker *checker, ir::Identifier *var_decl_id, ir::Expression *expr) { auto local_ctx = varbinder::LexicalScope(checker->VarBinder()); @@ -322,7 +323,7 @@ ir::BlockStatement *ReplaceBinaryExprInStmt(checker::ETSChecker *checker, ir::Ex auto *const test = GenInstanceofExpr(checker, union_node, u_type); auto *cloned_binary = expr->Clone(checker->Allocator(), expr->Parent())->AsBinaryExpression(); cloned_binary->Check(checker); - auto *const consequent = GenBlockStmtForAssignmentBinary( + auto *const consequent = GenBlockStmtForAssignment( checker, var_decl_id->AsIdentifier(), ProcessOperandsInBinaryExpr(checker, cloned_binary, u_type)); instanceof_tree = checker->Allocator()->New(test, consequent, instanceof_tree); test->SetParent(instanceof_tree); @@ -371,6 +372,10 @@ ir::BlockStatement *HandleBlockWithBinaryAndUnions(checker::ETSChecker *checker, bool UnionLowering::Perform(public_lib::Context *ctx, parser::Program *program) { + if (!PerformLogicalLowering(ctx, program)) { + return false; + } + for (auto &[_, ext_programs] : program->ExternalSources()) { (void)_; for (auto *ext_prog : ext_programs) { @@ -396,7 +401,7 @@ bool UnionLowering::Perform(public_lib::Context *ctx, parser::Program *program) auto handle_binary = [](const ir::AstNode *ast_node) { return ast_node->IsBinaryExpression() && ast_node->AsBinaryExpression()->OperationType() != nullptr && - ast_node->AsBinaryExpression()->OperationType()->IsETSUnionType(); + ast_node->AsBinaryExpression()->OperationType()->IsETSUnionType() && !ast_node->AsBinaryExpression()->IsLogicalExtended(); }; if (ast->IsBlockStatement() && ast->IsAnyChild(handle_binary)) { return HandleBlockWithBinaryAndUnions(checker, ast->AsBlockStatement(), handle_binary); @@ -408,6 +413,129 @@ bool UnionLowering::Perform(public_lib::Context *ctx, parser::Program *program) return true; } +ir::VariableDeclaration *GenVariableDeclForUnionBinaryExpr(checker::ETSChecker *checker, varbinder::Scope *scope, + ir::BinaryExpression *expr) +{ + ASSERT(expr->OperatorType() == lexer::TokenType::PUNCTUATOR_LOGICAL_AND || + expr->OperatorType() == lexer::TokenType::PUNCTUATOR_LOGICAL_OR); + auto *var_id = Gensym(checker->Allocator()); + auto *var = scope->AddDecl(checker->Allocator(), var_id->Name(), + varbinder::VariableFlags::LOCAL); + var->SetTsType(expr->OperationType()); + var_id->SetVariable(var); + var_id->SetTsType(var->TsType()); + + auto declarator = checker->AllocNode(var_id); + ArenaVector declarators(checker->Allocator()->Adapter()); + declarators.push_back(declarator); + + auto var_kind = ir::VariableDeclaration::VariableDeclarationKind::LET; + auto *binary_var_decl = + checker->AllocNode(var_kind, checker->Allocator(), std::move(declarators), false); + binary_var_decl->SetRange({expr->Start(), expr->End()}); + return binary_var_decl; +} + +void InsertTruthyCheckBeforeStmt(ir::Statement *stmt, ir::VariableDeclaration *binary_var_decl, + ir::IfStatement *truthy_check) +{ + if (stmt->IsVariableDeclarator()) { + ASSERT(stmt->Parent()->IsVariableDeclaration()); + stmt = stmt->Parent()->AsVariableDeclaration(); + } + ASSERT(stmt->Parent()->IsBlockStatement()); + auto *block = stmt->Parent()->AsBlockStatement(); + binary_var_decl->SetParent(block); + truthy_check->SetParent(block); + auto it_stmt = std::find(block->Statements().begin(), block->Statements().end(), stmt); + block->Statements().insert(it_stmt, {binary_var_decl, truthy_check}); +} + +ir::BlockStatement *ReplaceLogicalBinaryExprInStmt(checker::ETSChecker *checker, ir::BlockStatement *block, ir::BinaryExpression *expr) +{ + auto *stmt = FindStatementFromNode(expr); + ASSERT(stmt->IsVariableDeclarator() || block == stmt->Parent()); // statement with union + auto *const binary_var_decl = GenVariableDeclForUnionBinaryExpr(checker, NearestScope(stmt), expr); + auto *const var_decl_id = binary_var_decl->Declarators().front()->Id(); // only one declarator was generated + ir::IfStatement *truthy_check_stmt = nullptr; + auto *cloned_left = expr->Left()->Clone(checker->Allocator(), expr->Left()->Parent())->AsExpression(); + cloned_left->Check(checker); + auto *const left_assignment = GenBlockStmtForAssignment(checker, var_decl_id->AsIdentifier(), expr->Left()); + auto *const right_assignment = GenBlockStmtForAssignment(checker, var_decl_id->AsIdentifier(), expr->Right()); + if (expr->Left()->TsType()->HasTypeFlag(checker::TypeFlag::ETS_PRIMITIVE)) { + const auto boxing_flags = + static_cast(expr->Left()->GetBoxingUnboxingFlags() & ir::BoxingUnboxingFlags::BOXING_FLAG); + cloned_left->SetBoxingUnboxingFlags( + static_cast(expr->Left()->GetBoxingUnboxingFlags() & ~boxing_flags)); + } + if (expr->OperatorType() == lexer::TokenType::PUNCTUATOR_LOGICAL_AND) { + truthy_check_stmt = checker->Allocator()->New(cloned_left, right_assignment, left_assignment); + } else { + truthy_check_stmt = checker->Allocator()->New(cloned_left, left_assignment, right_assignment); + } + + // Replacing a binary expression with an identifier + // that was set in one of the branches of the `instanceof_tree` tree + stmt->TransformChildrenRecursively([var_decl_id](ir::AstNode *ast) -> ir::AstNode * { + if (ast->IsBinaryExpression() && ast->AsBinaryExpression()->OperationType() != nullptr && + ast->AsBinaryExpression()->OperationType()->IsETSUnionType() && ast->AsBinaryExpression()->IsLogicalExtended()) { + return var_decl_id; + } + + return ast; + }); + InsertTruthyCheckBeforeStmt(stmt, binary_var_decl, truthy_check_stmt); + return block; +} + +ir::BlockStatement *HandleBlockWithUnionBinary(checker::ETSChecker *checker, ir::BlockStatement *block, + ir::BinaryExpression *bin_expr) +{ + if (bin_expr->OperatorType() != lexer::TokenType::PUNCTUATOR_LOGICAL_AND && + bin_expr->OperatorType() != lexer::TokenType::PUNCTUATOR_LOGICAL_OR) { + checker->ThrowTypeError("Bad operand type, unions are not allowed in binary expressions except equality.", + bin_expr->Start()); + } + return ReplaceLogicalBinaryExprInStmt(checker, block, bin_expr); +} + +ir::BlockStatement *HandleBlockWithUnionBinaryExprs(checker::ETSChecker *checker, ir::BlockStatement *block, + const ir::NodePredicate &handle_binary) +{ + ir::BlockStatement *modified_ast_block = block; + while (modified_ast_block->IsAnyChild(handle_binary)) { + modified_ast_block = HandleBlockWithUnionBinary( + checker, modified_ast_block, modified_ast_block->FindChild(handle_binary)->AsBinaryExpression()); + } + return modified_ast_block; +} + +bool UnionLowering::PerformLogicalLowering(public_lib::Context *ctx, parser::Program *program) +{ + for (auto &[_, ext_programs] : program->ExternalSources()) { + (void)_; + for (auto *ext_prog : ext_programs) { + PerformLogicalLowering(ctx, ext_prog); + } + } + + checker::ETSChecker *checker = ctx->checker->AsETSChecker(); + + program->Ast()->TransformChildrenRecursively([checker](ir::AstNode *ast) -> ir::AstNode * { + auto handle_binary = [](const ir::AstNode *ast_node) { + return ast_node->IsBinaryExpression() && ast_node->AsBinaryExpression()->OperationType() != nullptr && + ast_node->AsBinaryExpression()->OperationType()->IsETSUnionType() && ast_node->AsBinaryExpression()->IsLogicalExtended(); + }; + if (ast->IsBlockStatement() && ast->IsAnyChild(handle_binary)) { + return HandleBlockWithUnionBinaryExprs(checker, ast->AsBlockStatement(), handle_binary); + } + + return ast; + }); + + return true; +} + bool UnionLowering::Postcondition(public_lib::Context *ctx, const parser::Program *program) { bool current = !program->Ast()->IsAnyChild([](const ir::AstNode *ast) { diff --git a/ets2panda/compiler/lowering/ets/unionLowering.h b/ets2panda/compiler/lowering/ets/unionLowering.h index efc0bf6bff..97c3927443 100644 --- a/ets2panda/compiler/lowering/ets/unionLowering.h +++ b/ets2panda/compiler/lowering/ets/unionLowering.h @@ -24,6 +24,7 @@ class UnionLowering : public Phase { public: std::string_view Name() override; bool Perform(public_lib::Context *ctx, parser::Program *program) override; + bool PerformLogicalLowering(public_lib::Context *ctx, parser::Program *program); bool Postcondition(public_lib::Context *ctx, const parser::Program *program) override; }; -- Gitee