diff --git a/ets2panda/checker/ETSAnalyzer.cpp b/ets2panda/checker/ETSAnalyzer.cpp index 4f35405def8737d07d309baeb3c00308f1f749d6..c4cd01f8e64a7dd4a3fc0dac3221b8e61809673e 100644 --- a/ets2panda/checker/ETSAnalyzer.cpp +++ b/ets2panda/checker/ETSAnalyzer.cpp @@ -1271,7 +1271,7 @@ checker::Type *ETSAnalyzer::Check(ir::MemberExpression *expr) const if (base_type->HasTypeFlag(checker::TypeFlag::ETS_PRIMITIVE)) { checker->Relation()->SetNode(expr); expr->SetObjectType(checker->PrimitiveTypeAsETSBuiltinType(base_type)->AsETSObjectType()); - checker->AddBoxingUnboxingFlagToNode(expr, expr->ObjType()); + checker->AddBoxingUnboxingFlagsToNode(expr, expr->ObjType()); auto [res_type, res_var] = expr->ResolveObjectMember(checker); expr->SetPropVar(res_var); return expr->AdjustOptional(checker, res_type); @@ -1564,7 +1564,7 @@ checker::Type *ETSAnalyzer::Check(ir::UnaryExpression *expr) const if (arg_type->IsETSObjectType() && (unboxed_operand_type != nullptr) && unboxed_operand_type->HasTypeFlag(checker::TypeFlag::ETS_PRIMITIVE)) { - expr->Argument()->AddBoxingUnboxingFlag(checker->GetUnboxingFlag(unboxed_operand_type)); + expr->Argument()->AddBoxingUnboxingFlags(checker->GetUnboxingFlag(unboxed_operand_type)); } return expr->TsType(); @@ -1599,8 +1599,8 @@ checker::Type *ETSAnalyzer::Check(ir::UpdateExpression *expr) const } if (operand_type->IsETSObjectType()) { - expr->Argument()->AddBoxingUnboxingFlag(checker->GetUnboxingFlag(unboxed_type) | - checker->GetBoxingFlag(unboxed_type)); + expr->Argument()->AddBoxingUnboxingFlags(checker->GetUnboxingFlag(unboxed_type) | + checker->GetBoxingFlag(unboxed_type)); } expr->SetTsType(operand_type); @@ -2388,6 +2388,14 @@ checker::Type *ETSAnalyzer::Check(ir::TSAsExpression *expr) const } auto *const source_type = expr->Expr()->Check(checker); + if (target_type->HasTypeFlag(checker::TypeFlag::ETS_PRIMITIVE) && + source_type->HasTypeFlag(checker::TypeFlag::ETS_ARRAY_OR_OBJECT)) { + auto *const boxed_target_type = checker->PrimitiveTypeAsETSBuiltinType(target_type); + if (!checker->Relation()->IsIdenticalTo(source_type, boxed_target_type)) { + expr->Expr()->AddAstNodeFlags(ir::AstNodeFlags::CHECKCAST); + } + } + const checker::CastingContext ctx(checker->Relation(), expr->Expr(), source_type, target_type, expr->Expr()->Start(), {"Cannot cast type '", source_type, "' to '", target_type, "'"}); diff --git a/ets2panda/checker/ETSchecker.h b/ets2panda/checker/ETSchecker.h index 56de58a6ffcc067de18de8dd87d81422cfa7d00b..d4970b6b87ab349a26d25e30b70212ee6d17152e 100644 --- a/ets2panda/checker/ETSchecker.h +++ b/ets2panda/checker/ETSchecker.h @@ -446,7 +446,7 @@ public: Type *ETSBuiltinTypeAsPrimitiveType(Type *object_type); Type *ETSBuiltinTypeAsConditionalType(Type *object_type); Type *PrimitiveTypeAsETSBuiltinType(Type *object_type); - void AddBoxingUnboxingFlagToNode(ir::AstNode *node, Type *boxing_unboxing_type); + void AddBoxingUnboxingFlagsToNode(ir::AstNode *node, Type *boxing_unboxing_type); ir::BoxingUnboxingFlags GetBoxingFlag(Type *boxing_type); ir::BoxingUnboxingFlags GetUnboxingFlag(Type const *unboxing_type) const; Type *MaybeBoxedType(const varbinder::Variable *var, ArenaAllocator *allocator) const; diff --git a/ets2panda/checker/ets/arithmetic.cpp b/ets2panda/checker/ets/arithmetic.cpp index de5372293cab0bdba6bb9bf061670bda96bc32d1..05de34bd7dc2da1fabf3c994ff6fb8aa969bfe1e 100644 --- a/ets2panda/checker/ets/arithmetic.cpp +++ b/ets2panda/checker/ets/arithmetic.cpp @@ -473,7 +473,7 @@ Type *ETSChecker::CheckBinaryOperatorNullishCoalescing(ir::Expression *right, le if (boxed_right_type == nullptr) { ThrowTypeError("Invalid right-hand side expression", pos); } - right->AddBoxingUnboxingFlag(GetBoxingFlag(boxed_right_type)); + right->AddBoxingUnboxingFlags(GetBoxingFlag(boxed_right_type)); return FindLeastUpperBound(non_nullish_left_type, boxed_right_type); } @@ -609,7 +609,7 @@ Type *ETSChecker::HandleArithmeticOperationOnTypes(Type *left, Type *right, lexe void ETSChecker::FlagExpressionWithUnboxing(Type *type, Type *unboxed_type, ir::Expression *type_expression) { if (type->IsETSObjectType() && (unboxed_type != nullptr)) { - type_expression->AddBoxingUnboxingFlag(GetUnboxingFlag(unboxed_type)); + type_expression->AddBoxingUnboxingFlags(GetUnboxingFlag(unboxed_type)); } } diff --git a/ets2panda/checker/ets/boxingConverter.h b/ets2panda/checker/ets/boxingConverter.h index 8c142419c6e028eef81219c6cf2b3fe6984ea08a..14ce94e657fde151c75363764af191cac8a1b979 100644 --- a/ets2panda/checker/ets/boxingConverter.h +++ b/ets2panda/checker/ets/boxingConverter.h @@ -25,8 +25,6 @@ public: BoxingConverter(ETSChecker *checker, TypeRelation *relation, Type *source) : TypeConverter(checker, relation, nullptr, source) { - ASSERT(relation->GetNode()); - if (!source->HasTypeFlag(TypeFlag::ETS_PRIMITIVE)) { Relation()->Result(false); return; @@ -39,8 +37,6 @@ public: BoxingConverter(ETSChecker *checker, TypeRelation *relation, Type *source, Type *target) : TypeConverter(checker, relation, target, source) { - ASSERT(relation->GetNode()); - if (!target->IsETSObjectType() || relation->IsTrue()) { return; } diff --git a/ets2panda/checker/ets/conversion.cpp b/ets2panda/checker/ets/conversion.cpp index 83d5c7ec9e8fc558b969303b32602d17def0809a..705422a3f09064da18190afd164aa58d20e8c445 100644 --- a/ets2panda/checker/ets/conversion.cpp +++ b/ets2panda/checker/ets/conversion.cpp @@ -248,7 +248,7 @@ ETSObjectType *Boxing(TypeRelation *const relation, Type *const source) return nullptr; } auto *const boxed_type = boxed.Result()->AsETSObjectType(); - relation->GetNode()->AddBoxingUnboxingFlag(ets_checker->GetBoxingFlag(boxed_type)); + relation->GetNode()->AddBoxingUnboxingFlags(ets_checker->GetBoxingFlag(boxed_type)); return boxed_type; } @@ -260,7 +260,7 @@ Type *Unboxing(TypeRelation *const relation, ETSObjectType *const source) return nullptr; } auto *const unboxed_type = unboxed.Result(); - relation->GetNode()->AddBoxingUnboxingFlag(ets_checker->GetUnboxingFlag(unboxed_type)); + relation->GetNode()->AddBoxingUnboxingFlags(ets_checker->GetUnboxingFlag(unboxed_type)); return unboxed_type; } diff --git a/ets2panda/checker/ets/helpers.cpp b/ets2panda/checker/ets/helpers.cpp index 5e11b6fe85bcd2bd0d6a3a5efd8020b9a5f2240a..07331030a8790add992e8a4f2f1d6f79ad802a45 100644 --- a/ets2panda/checker/ets/helpers.cpp +++ b/ets2panda/checker/ets/helpers.cpp @@ -153,7 +153,7 @@ Type *ETSChecker::CreateOptionalResultType(Type *type) if (type->HasTypeFlag(checker::TypeFlag::ETS_PRIMITIVE)) { type = PrimitiveTypeAsETSBuiltinType(type); ASSERT(type->IsETSObjectType()); - Relation()->GetNode()->AddBoxingUnboxingFlag(GetBoxingFlag(type)); + Relation()->GetNode()->AddBoxingUnboxingFlags(GetBoxingFlag(type)); } return CreateNullishType(type, checker::TypeFlag::UNDEFINED, Allocator(), Relation(), GetGlobalTypesHolder()); @@ -874,7 +874,7 @@ void ETSChecker::ResolveReturnStatement(checker::Type *func_return_type, checker if (argument_type == nullptr) { ThrowTypeError("Invalid return statement expression", st->Argument()->Start()); } - st->Argument()->AddBoxingUnboxingFlag(GetBoxingFlag(argument_type)); + st->Argument()->AddBoxingUnboxingFlags(GetBoxingFlag(argument_type)); } if (!func_return_type->HasTypeFlag(checker::TypeFlag::ETS_ARRAY_OR_OBJECT)) { @@ -1581,12 +1581,12 @@ Type *ETSChecker::PrimitiveTypeAsETSBuiltinType(Type *object_type) return converter.Result(); } -void ETSChecker::AddBoxingUnboxingFlagToNode(ir::AstNode *node, Type *boxing_unboxing_type) +void ETSChecker::AddBoxingUnboxingFlagsToNode(ir::AstNode *node, Type *boxing_unboxing_type) { if (boxing_unboxing_type->IsETSObjectType()) { - node->AddBoxingUnboxingFlag(GetBoxingFlag(boxing_unboxing_type)); + node->AddBoxingUnboxingFlags(GetBoxingFlag(boxing_unboxing_type)); } else { - node->AddBoxingUnboxingFlag(GetUnboxingFlag(boxing_unboxing_type)); + node->AddBoxingUnboxingFlags(GetUnboxingFlag(boxing_unboxing_type)); } } @@ -1836,7 +1836,7 @@ void ETSChecker::AddBoxingFlagToPrimitiveType(TypeRelation *relation, Type *targ { auto boxing_result = PrimitiveTypeAsETSBuiltinType(target); if (boxing_result != nullptr) { - relation->GetNode()->AddBoxingUnboxingFlag(GetBoxingFlag(boxing_result)); + relation->GetNode()->AddBoxingUnboxingFlags(GetBoxingFlag(boxing_result)); relation->Result(true); } } @@ -1845,7 +1845,7 @@ void ETSChecker::AddUnboxingFlagToPrimitiveType(TypeRelation *relation, Type *so { auto unboxing_result = UnboxingConverter(this, relation, source, self).Result(); if ((unboxing_result != nullptr) && relation->IsTrue()) { - relation->GetNode()->AddBoxingUnboxingFlag(GetUnboxingFlag(unboxing_result)); + relation->GetNode()->AddBoxingUnboxingFlags(GetUnboxingFlag(unboxing_result)); } } @@ -1874,7 +1874,7 @@ void ETSChecker::CheckUnboxedTypesAssignable(TypeRelation *relation, Type *sourc } relation->IsAssignableTo(unboxed_source_type, unboxed_target_type); if (relation->IsTrue()) { - relation->GetNode()->AddBoxingUnboxingFlag( + relation->GetNode()->AddBoxingUnboxingFlags( relation->GetChecker()->AsETSChecker()->GetUnboxingFlag(unboxed_source_type)); } } @@ -1918,7 +1918,7 @@ void ETSChecker::CheckUnboxedSourceTypeWithWideningAssignable(TypeRelation *rela relation->GetChecker()->AsETSChecker()->CheckUnboxedTypeWidenable(relation, target, unboxed_source_type); } if (!relation->OnlyCheckBoxingUnboxing()) { - relation->GetNode()->AddBoxingUnboxingFlag( + relation->GetNode()->AddBoxingUnboxingFlags( relation->GetChecker()->AsETSChecker()->GetUnboxingFlag(unboxed_source_type)); } } diff --git a/ets2panda/checker/ets/object.cpp b/ets2panda/checker/ets/object.cpp index fc8c0d4b9fa9bfa6a0c37becc693c575c0a61e35..7d57fb9ed7f4d3df71be8aa5fb78ddbea3e33984 100644 --- a/ets2panda/checker/ets/object.cpp +++ b/ets2panda/checker/ets/object.cpp @@ -929,7 +929,7 @@ void ETSChecker::ValidateArrayIndex(ir::Expression *const expr) Type const *const index_type = ApplyUnaryOperatorPromotion(expression_type); if (expression_type->IsETSObjectType() && (unboxed_expression_type != nullptr)) { - expr->AddBoxingUnboxingFlag(GetUnboxingFlag(unboxed_expression_type)); + expr->AddBoxingUnboxingFlags(GetUnboxingFlag(unboxed_expression_type)); } if (index_type == nullptr || !index_type->HasTypeFlag(TypeFlag::ETS_ARRAY_INDEX)) { diff --git a/ets2panda/compiler/core/ETSGen.cpp b/ets2panda/compiler/core/ETSGen.cpp index 7a01cdebcdbc12213119daf39c95dd14b1edfff2..87b84f050e41fb3577aeded43f0d770554c7f842 100644 --- a/ets2panda/compiler/core/ETSGen.cpp +++ b/ets2panda/compiler/core/ETSGen.cpp @@ -986,50 +986,60 @@ void ETSGen::ApplyCast(const ir::AstNode *node, const checker::Type *target_type void ETSGen::EmitUnboxingConversion(const ir::AstNode *node) { - auto unboxing_flag = + const auto unboxing_flag = static_cast(ir::BoxingUnboxingFlags::UNBOXING_FLAG & node->GetBoxingUnboxingFlags()); RegScope rs(this); + auto emit_unboxed_call = [this, &node](std::string_view signature_flag, const checker::Type *const target_type, + const checker::Type *const boxed_type) { + if (node->HasAstNodeFlags(ir::AstNodeFlags::CHECKCAST)) { + EmitCheckedNarrowingReferenceConversion(node, boxed_type); + } + + Ra().Emit(node, signature_flag, dummy_reg_, 0); + SetAccumulatorType(target_type); + }; + switch (unboxing_flag) { case ir::BoxingUnboxingFlags::UNBOX_TO_BOOLEAN: { - Ra().Emit(node, Signatures::BUILTIN_BOOLEAN_UNBOXED, dummy_reg_, 0); - SetAccumulatorType(Checker()->GlobalETSBooleanType()); + emit_unboxed_call(Signatures::BUILTIN_BOOLEAN_UNBOXED, Checker()->GlobalETSBooleanType(), + Checker()->GetGlobalTypesHolder()->GlobalETSBooleanBuiltinType()); break; } case ir::BoxingUnboxingFlags::UNBOX_TO_BYTE: { - Ra().Emit(node, Signatures::BUILTIN_BYTE_UNBOXED, dummy_reg_, 0); - SetAccumulatorType(Checker()->GlobalByteType()); + emit_unboxed_call(Signatures::BUILTIN_BYTE_UNBOXED, Checker()->GlobalByteType(), + Checker()->GetGlobalTypesHolder()->GlobalByteBuiltinType()); break; } case ir::BoxingUnboxingFlags::UNBOX_TO_CHAR: { - Ra().Emit(node, Signatures::BUILTIN_CHAR_UNBOXED, dummy_reg_, 0); - SetAccumulatorType(Checker()->GlobalCharType()); + emit_unboxed_call(Signatures::BUILTIN_CHAR_UNBOXED, Checker()->GlobalCharType(), + Checker()->GetGlobalTypesHolder()->GlobalCharBuiltinType()); break; } case ir::BoxingUnboxingFlags::UNBOX_TO_SHORT: { - Ra().Emit(node, Signatures::BUILTIN_SHORT_UNBOXED, dummy_reg_, 0); - SetAccumulatorType(Checker()->GlobalShortType()); + emit_unboxed_call(Signatures::BUILTIN_SHORT_UNBOXED, Checker()->GlobalShortType(), + Checker()->GetGlobalTypesHolder()->GlobalShortBuiltinType()); break; } case ir::BoxingUnboxingFlags::UNBOX_TO_INT: { - Ra().Emit(node, Signatures::BUILTIN_INT_UNBOXED, dummy_reg_, 0); - SetAccumulatorType(Checker()->GlobalIntType()); + emit_unboxed_call(Signatures::BUILTIN_INT_UNBOXED, Checker()->GlobalIntType(), + Checker()->GetGlobalTypesHolder()->GlobalIntegerBuiltinType()); break; } case ir::BoxingUnboxingFlags::UNBOX_TO_LONG: { - Ra().Emit(node, Signatures::BUILTIN_LONG_UNBOXED, dummy_reg_, 0); - SetAccumulatorType(Checker()->GlobalLongType()); + emit_unboxed_call(Signatures::BUILTIN_LONG_UNBOXED, Checker()->GlobalLongType(), + Checker()->GetGlobalTypesHolder()->GlobalLongBuiltinType()); break; } case ir::BoxingUnboxingFlags::UNBOX_TO_FLOAT: { - Ra().Emit(node, Signatures::BUILTIN_FLOAT_UNBOXED, dummy_reg_, 0); - SetAccumulatorType(Checker()->GlobalFloatType()); + emit_unboxed_call(Signatures::BUILTIN_FLOAT_UNBOXED, Checker()->GlobalFloatType(), + Checker()->GetGlobalTypesHolder()->GlobalFloatBuiltinType()); break; } case ir::BoxingUnboxingFlags::UNBOX_TO_DOUBLE: { - Ra().Emit(node, Signatures::BUILTIN_DOUBLE_UNBOXED, dummy_reg_, 0); - SetAccumulatorType(Checker()->GlobalDoubleType()); + emit_unboxed_call(Signatures::BUILTIN_DOUBLE_UNBOXED, Checker()->GlobalDoubleType(), + Checker()->GetGlobalTypesHolder()->GlobalDoubleBuiltinType()); break; } default: diff --git a/ets2panda/ir/astNode.h b/ets2panda/ir/astNode.h index 9a30d7bb961d069a6375c232c325bc4db228d4dd..be5e889d3e0718db49665cd5b0236af176f54c67 100644 --- a/ets2panda/ir/astNode.h +++ b/ets2panda/ir/astNode.h @@ -423,20 +423,35 @@ public: return flags_; } - void SetBoxingUnboxingFlags(BoxingUnboxingFlags const flags) const noexcept - { - boxing_unboxing_flags_ = flags; - } - - void AddBoxingUnboxingFlag(BoxingUnboxingFlags const flag) const noexcept - { - boxing_unboxing_flags_ |= flag; - } - - [[nodiscard]] BoxingUnboxingFlags GetBoxingUnboxingFlags() const noexcept - { - return boxing_unboxing_flags_; - } + // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define DECLARE_FLAG_OPERATIONS(flag_type, member_name) \ + void Set##flag_type(flag_type flags) const noexcept \ + { \ + member_name = flags; \ + } \ + \ + void Add##flag_type(flag_type flag) const noexcept \ + { \ + member_name |= flag; \ + } \ + \ + [[nodiscard]] flag_type Get##flag_type() const noexcept \ + { \ + return member_name; \ + } \ + \ + bool Has##flag_type(flag_type flag) const noexcept \ + { \ + return (member_name & flag) != 0U; \ + } \ + void Remove##flag_type(flag_type flag) noexcept \ + { \ + member_name &= ~flag; \ + } + + DECLARE_FLAG_OPERATIONS(BoxingUnboxingFlags, boxing_unboxing_flags_); + DECLARE_FLAG_OPERATIONS(AstNodeFlags, ast_node_flags_); +#undef DECLARE_FLAG_OPERATIONS ir::ClassElement *AsClassElement() { @@ -509,6 +524,7 @@ protected: AstNodeType type_; varbinder::Variable *variable_ {}; ModifierFlags flags_ {}; + mutable AstNodeFlags ast_node_flags_ {}; mutable BoxingUnboxingFlags boxing_unboxing_flags_ {}; // NOLINTEND(misc-non-private-member-variables-in-classes) }; diff --git a/ets2panda/ir/astNodeFlags.h b/ets2panda/ir/astNodeFlags.h index d22bc5d6e4df5bdb4426b01f564c86614ce0bdd6..f6420bb131169df655c2d9ce113a41c983aa62a6 100644 --- a/ets2panda/ir/astNodeFlags.h +++ b/ets2panda/ir/astNodeFlags.h @@ -21,8 +21,7 @@ namespace panda::es2panda::ir { enum class AstNodeFlags { NO_OPTS = 0, - STRICT = (1U << 0U), - PARAMETER = (1U << 1U), + CHECKCAST = 1U << 0U, }; enum class ModifierFlags : uint32_t { @@ -114,4 +113,4 @@ enum class BoxingUnboxingFlags : uint32_t { }; } // namespace panda::es2panda::ir -#endif \ No newline at end of file +#endif diff --git a/ets2panda/ir/statements/returnStatement.cpp b/ets2panda/ir/statements/returnStatement.cpp index fed52f836bfa29819bc1d99258983482caef2388..b658bd70f47658b4387bb63fd6ad81eea32ebabc 100644 --- a/ets2panda/ir/statements/returnStatement.cpp +++ b/ets2panda/ir/statements/returnStatement.cpp @@ -75,7 +75,7 @@ void ReturnStatement::SetReturnType(checker::ETSChecker *checker, checker::Type if (argument_type == nullptr) { checker->ThrowTypeError("Invalid return statement expression", argument_->Start()); } - argument_->AddBoxingUnboxingFlag(checker->GetBoxingFlag(argument_type)); + argument_->AddBoxingUnboxingFlags(checker->GetBoxingFlag(argument_type)); relation->SetNode(nullptr); } diff --git a/ets2panda/test/runtime/ets/UnboxingCheckcast.ets b/ets2panda/test/runtime/ets/UnboxingCheckcast.ets new file mode 100644 index 0000000000000000000000000000000000000000..1a5fdfb062f7c2cc477143a3182b0fb71754168c --- /dev/null +++ b/ets2panda/test/runtime/ets/UnboxingCheckcast.ets @@ -0,0 +1,40 @@ +/* + * Copyright (c) 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. + */ + +function main() { + let b: Object[] = [new Object()]; + b[0] = 2.0; + let myvar: double = b[0] as double; + + assert myvar == 2.0; + + let c: Object; + c = 2.0; + c as double; + + let k = 2; + if(2 == k){ + c = new Object(); + } else { + c = new Double(); + } + + try{ + c as double; + } catch (e: ClassCastException){ + } catch(other) { + assert 1 == 0 + } +}