diff --git a/ets2panda/compiler/lowering/ets/enumLowering.cpp b/ets2panda/compiler/lowering/ets/enumLowering.cpp index a36144f45d6b2f8c142c0f01cfdc65786e791fbf..a50af535e690920141d95a687ee68ac0970806f7 100644 --- a/ets2panda/compiler/lowering/ets/enumLowering.cpp +++ b/ets2panda/compiler/lowering/ets/enumLowering.cpp @@ -79,34 +79,50 @@ void EnumLoweringPhase::LogError(const diagnostic::DiagnosticKind &diagnostic, template bool EnumLoweringPhase::CheckEnumMemberType(const ArenaVector &enumMembers, bool &hasLoggedError, - bool &hasLongLiteral) + EnumType &actualEnumType) { for (auto *member : enumMembers) { auto *init = member->AsTSEnumMember()->Init(); - if constexpr (TYPE_NODE == EnumLoweringPhase::EnumType::INT) { + // General handling of the enum numeric type. + if constexpr (TYPE_NODE == EnumLoweringPhase::EnumType::NUMERIC) { + if (!init->IsNumberLiteral()) { + LogError(diagnostic::ERROR_ARKTS_NO_ENUM_MIXED_TYPES, {}, init->Start()); + hasLoggedError = true; + continue; + } + // Update actualEnumType + auto numInit = init->AsNumberLiteral()->Number(); + if (member->AsTSEnumMember()->IsGenerated() && numInit.IsLong() && + numInit.GetLong() == std::numeric_limits::min()) { + LogError(diagnostic::ERROR_ARKTS_NO_ENUM_MIXED_TYPES, {}, init->Start()); + hasLoggedError = true; + } + if (numInit.IsLong() && actualEnumType < EnumType::LONG) { + actualEnumType = EnumType::LONG; + } else if ((numInit.IsDouble() || numInit.IsFloat()) && actualEnumType < EnumType::DOUBLE) { + actualEnumType = EnumType::DOUBLE; + } + } else if constexpr (TYPE_NODE == EnumLoweringPhase::EnumType::INT) { if (!init->IsNumberLiteral() || !init->AsNumberLiteral()->Number().IsInteger()) { LogError(diagnostic::ERROR_ARKTS_NO_ENUM_MIXED_TYPES, {}, init->Start()); hasLoggedError = true; continue; } - if (!init->AsNumberLiteral()->Number().IsLong()) { continue; } - - hasLongLiteral = true; - // Invalid generated value. if (member->AsTSEnumMember()->IsGenerated() && init->AsNumberLiteral()->Number().GetLong() == std::numeric_limits::min()) { LogError(diagnostic::ERROR_ARKTS_NO_ENUM_MIXED_TYPES, {}, init->Start()); hasLoggedError = true; } + if (actualEnumType < EnumType::LONG) { + actualEnumType = EnumType::LONG; + } } else if constexpr (TYPE_NODE == EnumLoweringPhase::EnumType::DOUBLE) { - if (!init->IsNumberLiteral() || - !(init->AsNumberLiteral()->Number().IsDouble() || init->AsNumberLiteral()->Number().IsFloat())) { + if (!init->IsNumberLiteral()) { LogError(diagnostic::ERROR_ARKTS_NO_ENUM_MIXED_TYPES, {}, init->Start()); hasLoggedError = true; - continue; } if (member->AsTSEnumMember()->IsGenerated()) { @@ -123,8 +139,8 @@ bool EnumLoweringPhase::CheckEnumMemberType(const ArenaVector &en hasLoggedError = true; } } else { - static_assert(TYPE_NODE == - EnumLoweringPhase::EnumType::NOT_SPECIFIED); // Unsupported TypeNode in CheckEnumMemberType. + // Unsupported TypeNode in CheckEnumMemberType. + static_assert(TYPE_NODE == EnumType::NOT_SPECIFIED); } } return !hasLoggedError; @@ -201,7 +217,7 @@ ir::Expression *EnumLoweringPhase::CheckEnumTypeForItemFields(EnumType enumType, valueArgument = AllocNode(enumFieldValue); break; } - case EnumType::NOT_SPECIFIED: { + default: { LogError(diagnostic::ENUM_INVALID_INIT, {}, member->AsTSEnumMember()->Init()->Start()); break; } @@ -624,20 +640,19 @@ checker::AstNodePtr EnumLoweringPhase::TransformAnnotedEnumChildrenRecursively(c } bool hasLoggedError = false; - bool hasLongLiteral = false; + EnumType actualEnumType = EnumType::INT; auto *const itemInit = enumDecl->Members().front()->AsTSEnumMember()->Init(); ir::TypeNode *typeAnnotation = enumDecl->TypeNodes(); ir::PrimitiveType primitiveType = typeAnnotation->AsETSPrimitiveType()->GetPrimitiveType(); if ((primitiveType == ir::PrimitiveType::INT || primitiveType == ir::PrimitiveType::LONG) && - CheckEnumMemberType(enumDecl->Members(), hasLoggedError, hasLongLiteral)) { - auto res = hasLongLiteral ? CreateEnumIntClassFromEnumDeclaration(enumDecl, flags) - : CreateEnumIntClassFromEnumDeclaration(enumDecl, flags); - return res; + CheckEnumMemberType(enumDecl->Members(), hasLoggedError, actualEnumType)) { + ES2PANDA_ASSERT(actualEnumType == EnumType::LONG || actualEnumType == EnumType::INT); + return CreateEnumNumericClassFromEumDeclaration(enumDecl, flags, actualEnumType); } if ((primitiveType == ir::PrimitiveType::DOUBLE) && - CheckEnumMemberType(enumDecl->Members(), hasLoggedError, hasLongLiteral)) { + CheckEnumMemberType(enumDecl->Members(), hasLoggedError, actualEnumType)) { return CreateEnumFloatClassFromEnumDeclaration(enumDecl, flags); } @@ -648,6 +663,25 @@ checker::AstNodePtr EnumLoweringPhase::TransformAnnotedEnumChildrenRecursively(c return ast; } +ir::ClassDeclaration *EnumLoweringPhase::CreateEnumNumericClassFromEumDeclaration(ir::TSEnumDeclaration *const enumDecl, + const DeclarationFlags flags, + EnumType enumType) +{ + switch (enumType) { + case EnumType::INT: { + return CreateEnumIntClassFromEnumDeclaration(enumDecl, flags); + } + case EnumType::LONG: { + return CreateEnumIntClassFromEnumDeclaration(enumDecl, flags); + } + case EnumType::DOUBLE: { + return CreateEnumFloatClassFromEnumDeclaration(enumDecl, flags); + } + default: { + ES2PANDA_UNREACHABLE(); + } + } +} checker::AstNodePtr EnumLoweringPhase::TransformEnumChildrenRecursively(checker::AstNodePtr &ast) { auto *enumDecl = ast->AsTSEnumDeclaration(); @@ -661,24 +695,16 @@ checker::AstNodePtr EnumLoweringPhase::TransformEnumChildrenRecursively(checker: } bool hasLoggedError = false; - bool hasLongLiteral = false; + EnumType actualEnumType = EnumType::INT; auto *const itemInit = enumDecl->Members().front()->AsTSEnumMember()->Init(); if (itemInit->IsNumberLiteral() && - ((itemInit->AsNumberLiteral()->Number().IsInteger() || itemInit->AsNumberLiteral()->Number().IsLong()) && - CheckEnumMemberType(enumDecl->Members(), hasLoggedError, hasLongLiteral))) { - auto res = hasLongLiteral ? CreateEnumIntClassFromEnumDeclaration(enumDecl, flags) - : CreateEnumIntClassFromEnumDeclaration(enumDecl, flags); - return res; - } - if (itemInit->IsNumberLiteral() && - ((itemInit->AsNumberLiteral()->Number().IsDouble() || itemInit->AsNumberLiteral()->Number().IsFloat()) && - CheckEnumMemberType(enumDecl->Members(), hasLoggedError, hasLongLiteral))) { - return CreateEnumFloatClassFromEnumDeclaration(enumDecl, flags); + CheckEnumMemberType(enumDecl->Members(), hasLoggedError, actualEnumType)) { + return CreateEnumNumericClassFromEumDeclaration(enumDecl, flags, actualEnumType); } if (itemInit->IsStringLiteral() && - CheckEnumMemberType(enumDecl->Members(), hasLoggedError, hasLongLiteral)) { + CheckEnumMemberType(enumDecl->Members(), hasLoggedError, actualEnumType)) { return CreateEnumStringClassFromEnumDeclaration(enumDecl, flags); } diff --git a/ets2panda/compiler/lowering/ets/enumLowering.h b/ets2panda/compiler/lowering/ets/enumLowering.h index f95443c8ce783deb8a2c3d27d974a2d4dcf85424..7ba201d36c1212d71332fa000a0da453c44495d7 100644 --- a/ets2panda/compiler/lowering/ets/enumLowering.h +++ b/ets2panda/compiler/lowering/ets/enumLowering.h @@ -20,6 +20,7 @@ #include #include "compiler/lowering/phase.h" #include "checker/ETSchecker.h" +#include "ir/statements/classDeclaration.h" namespace ark::es2panda::compiler { @@ -38,7 +39,7 @@ public: static constexpr std::string_view ORDINAL_NAME {"#ordinal"}; static constexpr auto ORDINAL_TYPE {ir::PrimitiveType::INT}; - enum EnumType { NOT_SPECIFIED = 0, INT = 1, LONG = 2, DOUBLE = 3, STRING = 4 }; + enum EnumType { NOT_SPECIFIED = 0, INT = 1, LONG = 2, DOUBLE = 3, STRING = 4, NUMERIC = 5 }; struct DeclarationFlags { // NOLINTBEGIN(misc-non-private-member-variables-in-classes) @@ -84,7 +85,7 @@ private: // clang-format off template bool CheckEnumMemberType(const ArenaVector &enumMembers, bool &hasLoggedError, - bool &hasLongLiteral); + EnumType &actualEnumType); // clang-format on [[nodiscard]] ir::ScriptFunction *MakeFunction(FunctionInfo &&functionInfo); @@ -104,6 +105,8 @@ private: template ir::ClassDeclaration *CreateEnumFloatClassFromEnumDeclaration(ir::TSEnumDeclaration *const enumDecl, const DeclarationFlags flags); + ir::ClassDeclaration *CreateEnumNumericClassFromEumDeclaration(ir::TSEnumDeclaration *const enumDecl, + const DeclarationFlags flags, EnumType enumType); ir::ClassDeclaration *CreateEnumStringClassFromEnumDeclaration(ir::TSEnumDeclaration *const enumDecl, const DeclarationFlags flags); static void AppendParentNames(util::UString &qualifiedName, const ir::AstNode *const node); diff --git a/ets2panda/test/ast/compiler/ets/enum_anno_bad_0.ets b/ets2panda/test/ast/compiler/ets/enum_anno_bad_0.ets new file mode 100644 index 0000000000000000000000000000000000000000..44262e384ff77c0d00015febf6ae625078d0ddc7 --- /dev/null +++ b/ets2panda/test/ast/compiler/ets/enum_anno_bad_0.ets @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +enum Color : int { + Red = 1, + Blue = 2.0, + Yellow = 3.3f +} + +function foo(a: Color, b: number) { + arktest.assertEQ(a, b); +} + +function main() { + foo(Color.Red, 1.0) + foo(Color.Blue, 2.0) + foo(Color.Yellow, 3.3f) +} + +/* @@? 18:12 Error TypeError: Enumeration members can be initialized only by compile-time expressions and initializers must be of the same type. */ +/* @@? 19:14 Error TypeError: Enumeration members can be initialized only by compile-time expressions and initializers must be of the same type. */ diff --git a/ets2panda/test/ast/compiler/ets/enum_anno_bad_1.ets b/ets2panda/test/ast/compiler/ets/enum_anno_bad_1.ets new file mode 100644 index 0000000000000000000000000000000000000000..af7f84711e8129206bcad46aa011ad5d6875e2e7 --- /dev/null +++ b/ets2panda/test/ast/compiler/ets/enum_anno_bad_1.ets @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +enum Color : long { + Red = 1, + Blue = 2.0, + Yellow = 3.3f +} + +function foo(a: Color, b: number) { + arktest.assertEQ(a, b); +} + +function main() { + foo(Color.Red, 1.0) + foo(Color.Blue, 2.0) + foo(Color.Yellow, 3.3f) +} + +/* @@? 18:12 Error TypeError: Enumeration members can be initialized only by compile-time expressions and initializers must be of the same type. */ +/* @@? 19:14 Error TypeError: Enumeration members can be initialized only by compile-time expressions and initializers must be of the same type. */ diff --git a/ets2panda/test/ast/parser/ets/enum18.ets b/ets2panda/test/ast/parser/ets/enum18.ets index 8c34ab054c1f9a06c835f2351fa23d3412bc4f56..66e599c2742218506c2a111a3cd99db6a603fd5e 100644 --- a/ets2panda/test/ast/parser/ets/enum18.ets +++ b/ets2panda/test/ast/parser/ets/enum18.ets @@ -15,9 +15,6 @@ enum InvalidInitTypeEnum { Red, - Green = /* @@ label */1.0, + Green = 1.0, Blue } - -/* @@@ label Error TypeError: Enumeration members can be initialized only by compile-time expressions and initializers must be of the same type. */ -/* @@? 19:7 Error TypeError: Enumeration members can be initialized only by compile-time expressions and initializers must be of the same type. */ diff --git a/ets2panda/test/ast/parser/ets/enum34.ets b/ets2panda/test/ast/parser/ets/enum34.ets index 9fcdc33582450ae23757dd3ddf4d970e5d45f1f2..4cf9a17d7fcee11aaf16008f2528acf951c07456 100644 --- a/ets2panda/test/ast/parser/ets/enum34.ets +++ b/ets2panda/test/ast/parser/ets/enum34.ets @@ -13,10 +13,7 @@ * limitations under the License. */ -enum Fraction { +enum Fraction { A = 1, B = 0.1 } - - -/* @@? 18:9 Error TypeError: Enumeration members can be initialized only by compile-time expressions and initializers must be of the same type. */ diff --git a/ets2panda/test/ast/parser/ets/invalidEnums1.ets b/ets2panda/test/ast/parser/ets/invalidEnums1.ets index 2021d1a65fdf77e5989dc25f8827933ee4ddfeef..6d09272664465f281717d01aa04a540de2060846 100644 --- a/ets2panda/test/ast/parser/ets/invalidEnums1.ets +++ b/ets2panda/test/ast/parser/ets/invalidEnums1.ets @@ -25,8 +25,4 @@ enum EStrNotInit { Str2 } - -/* @@? 18:15 Error TypeError: Enumeration members can be initialized only by compile-time expressions and initializers must be of the same type. */ -/* @@? 19:12 Error TypeError: Enumeration members can be initialized only by compile-time expressions and initializers must be of the same type. */ -/* @@? 20:12 Error TypeError: Enumeration members can be initialized only by compile-time expressions and initializers must be of the same type. */ /* @@? 25:9 Error SyntaxError: All items of string-type enumeration should be explicitly initialized. */ diff --git a/ets2panda/test/runtime/ets/enum_double_0.ets b/ets2panda/test/runtime/ets/enum_double_0.ets new file mode 100644 index 0000000000000000000000000000000000000000..32c50c3056564b00acc14eec840f6d1805b7fc3b --- /dev/null +++ b/ets2panda/test/runtime/ets/enum_double_0.ets @@ -0,0 +1,30 @@ +/* + * Copyright (c) -2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +enum Color :double { + Red = 1, + Blue = 2.0, + Yellow = 3.3f +} + +function foo(a: Color, b: number) { + arktest.assertEQ(a, b); +} + +function main() { + foo(Color.Red, 1.0) + foo(Color.Blue, 2.0) + foo(Color.Yellow, 3.3f) +} diff --git a/ets2panda/test/runtime/ets/enum_double_1.ets b/ets2panda/test/runtime/ets/enum_double_1.ets new file mode 100644 index 0000000000000000000000000000000000000000..c6ebdf6891c889db23b2a31588de4137d973cf5c --- /dev/null +++ b/ets2panda/test/runtime/ets/enum_double_1.ets @@ -0,0 +1,30 @@ +/* + * Copyright (c) -2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +enum Color { + Red = 1, + Blue = 2.0, + Yellow = 3.3f +} + +function foo(a: Color, b: number) { + arktest.assertEQ(a, b); +} + +function main() { + foo(Color.Red, 1.0) + foo(Color.Blue, 2.0) + foo(Color.Yellow, 3.3f) +}