From c13b79bbce238d62f9c959fa3bb2b839f8df559f Mon Sep 17 00:00:00 2001 From: Robert Sipka Date: Thu, 11 Sep 2025 15:13:17 +0200 Subject: [PATCH] Disallow char types in shift operations Issue: ICXM15 Internal issue: 29895 Both operands of a shift expression must be of numeric types or type bigint Note: To achieve full support, char must be removed from ETS_CONVERTIBLE_TO_NUMERIC flags, but this leads to addressing a broader issue. Change-Id: I32cf6fe9b292a8c5977435402d3841d938e04e4e Signed-off-by: Robert Sipka --- ets2panda/checker/ets/arithmetic.cpp | 11 ++++++---- ets2panda/checker/ets/helpers.cpp | 5 +---- .../ets/explicit_cast_boxed_expressions.ets | 4 ++-- .../ets/implicit_cast_boxed_expressions.ets | 16 +++++++------- .../compiler/ets/shiftOperatorWithChar.ets | 21 +++++++++++++++++++ 5 files changed, 39 insertions(+), 18 deletions(-) create mode 100644 ets2panda/test/ast/compiler/ets/shiftOperatorWithChar.ets diff --git a/ets2panda/checker/ets/arithmetic.cpp b/ets2panda/checker/ets/arithmetic.cpp index cc7e14a1bb..1f0e7ff483 100644 --- a/ets2panda/checker/ets/arithmetic.cpp +++ b/ets2panda/checker/ets/arithmetic.cpp @@ -91,10 +91,13 @@ bool ETSChecker::CheckIfNumeric(Type *type) return false; } if (type->IsETSPrimitiveType()) { - return type->HasTypeFlag(TypeFlag::ETS_CONVERTIBLE_TO_NUMERIC); + // NOTE(rsipka): Deprecated operations on 'char' #28006, it should be removed from ETS_CONVERTIBLE_TO_NUMERIC + return type->HasTypeFlag(TypeFlag::ETS_CONVERTIBLE_TO_NUMERIC) && type->HasTypeFlag(TypeFlag::CHAR); } auto *unboxed = MaybeUnboxInRelation(type); - return (unboxed != nullptr) && unboxed->HasTypeFlag(TypeFlag::ETS_CONVERTIBLE_TO_NUMERIC); + // NOTE(rsipka): Deprecated operations on 'char' #28006, it should be removed from ETS_CONVERTIBLE_TO_NUMERIC + return (unboxed != nullptr) && unboxed->HasTypeFlag(TypeFlag::ETS_CONVERTIBLE_TO_NUMERIC) && + !unboxed->HasTypeFlag(TypeFlag::CHAR); } bool ETSChecker::CheckIfFloatingPoint(Type *type) @@ -489,7 +492,7 @@ checker::Type *ETSChecker::CheckBinaryOperatorShift( return isPrim ? GlobalLongType() : GetGlobalTypesHolder()->GlobalLongBuiltinType(); } - if (unboxedProm->IsByteType() || unboxedProm->IsShortType() || unboxedProm->IsCharType()) { + if (unboxedProm->IsByteType() || unboxedProm->IsShortType()) { return promotedLeftType; } @@ -539,7 +542,7 @@ checker::Type *ETSChecker::CheckBinaryOperatorBitwise( return isPrim ? GlobalLongType() : GetGlobalTypesHolder()->GlobalLongBuiltinType(); } - if (unboxedProm->IsByteType() || unboxedProm->IsShortType() || unboxedProm->IsCharType()) { + if (unboxedProm->IsByteType() || unboxedProm->IsShortType()) { return promotedType; } return nullptr; diff --git a/ets2panda/checker/ets/helpers.cpp b/ets2panda/checker/ets/helpers.cpp index dd5d023d62..b4823f9371 100644 --- a/ets2panda/checker/ets/helpers.cpp +++ b/ets2panda/checker/ets/helpers.cpp @@ -405,15 +405,12 @@ Type *ETSChecker::GetUnaryOperatorPromotedType(Type *type, const bool doPromotio auto globalTypesHolder = GetGlobalTypesHolder(); if (doPromotion) { - // NOTE(dkofanov): Deprecated operations on 'char' #28006 if (type == globalTypesHolder->GlobalByteBuiltinType() || type == globalTypesHolder->GlobalShortBuiltinType() || - type == globalTypesHolder->GlobalCharBuiltinType() || type == globalTypesHolder->GlobalIntegerBuiltinType()) { return GlobalIntBuiltinType(); } - // NOTE(dkofanov): Deprecated operations on 'char' #28006 - if (type->IsIntType() || type->IsByteType() || type->IsShortType() || type->IsCharType()) { + if (type->IsIntType() || type->IsByteType() || type->IsShortType()) { return GlobalIntBuiltinType(); } } diff --git a/ets2panda/test/ast/compiler/ets/explicit_cast_boxed_expressions.ets b/ets2panda/test/ast/compiler/ets/explicit_cast_boxed_expressions.ets index 52fee19f86..2389bbc234 100644 --- a/ets2panda/test/ast/compiler/ets/explicit_cast_boxed_expressions.ets +++ b/ets2panda/test/ast/compiler/ets/explicit_cast_boxed_expressions.ets @@ -19,7 +19,7 @@ let testInt: Int = new Int(42 as int); let testLong: Long = new Long(42 as long); let testFloat: Float = new Float(42 as float); let testDouble: Double = new Double(42 as double); -let testChar: Char = new Char(42 as char); +let testChar: Char = new Char(Int.toChar(42) as char); let testLongValue: Long = 9223372036854775807; function byte_test(): boolean { @@ -88,7 +88,7 @@ function short_test(): boolean { } function char_test(): boolean { - let Char_: Char = new Char(42 as char); + let Char_: Char = new Char(Int.toChar(42) as char); let char_byte = Char.toByte(Char_); let char_short = Char.toShort(Char_); diff --git a/ets2panda/test/ast/compiler/ets/implicit_cast_boxed_expressions.ets b/ets2panda/test/ast/compiler/ets/implicit_cast_boxed_expressions.ets index 8b80e53739..949a32855c 100644 --- a/ets2panda/test/ast/compiler/ets/implicit_cast_boxed_expressions.ets +++ b/ets2panda/test/ast/compiler/ets/implicit_cast_boxed_expressions.ets @@ -19,7 +19,7 @@ let testInt: Int = new Int(42 as int); let testLong: Long = new Long(42 as long); let testFloat: Float = new Float(42 as float); let testDouble: Double = new Double(42 as double); -let testChar: Char = new Char(42 as char); +let testChar: Char = new Char(Int.toChar(42)); let testLongValue: Long = 9223372036854775807; function byte_test(): boolean { @@ -30,7 +30,7 @@ function byte_test(): boolean { let byte_long = new Long(42 as long); let byte_float = new Float(42 as float); let byte_double = new Double(42 as double); - let byte_char = new Char(42 as char); + let byte_char = new Char(Int.toChar(42)); byte_byte = testByte; // Byte -> Byte byte_short = testByte; // Byte -> Short @@ -70,7 +70,7 @@ function short_test(): boolean { let short_long = new Long(42 as long); let short_float = new Float(42 as float); let short_double = new Double(42 as double); - let short_char = new Char(42 as char); + let short_char = new Char(Int.toChar(42)); // short_byte = testShort; // Short -> Byte is not available according 6.5.2 widening table and CTE happens short_short = testShort; // Short -> Short @@ -110,7 +110,7 @@ function char_test(): boolean { let char_long = new Long(42 as long); let char_float = new Float(42 as float); let char_double = new Double(42 as double); - let char_char = new Char(42 as char); + let char_char = new Char(Int.toChar(42)); // char_byte = testChar; // Char -> Byte is not available according 6.5.2 widening table and CTE happens // char_short = testChar; // Char -> Short is not available according 6.5.2 widening table and CTE happens @@ -157,7 +157,7 @@ function int_test(): boolean { let int_long = new Long(42 as long); let int_float = new Float(42 as float); let int_double = new Double(42 as double); - let int_char = new Char(42 as char); + let int_char = new Char(Int.toChar(42)); // int_byte = testInt; // Int -> Byte is not available according 6.5.2 widening table and CTE happens // int_short = testInt; // Int -> Short is not available according 6.5.2 widening table and CTE happens @@ -196,7 +196,7 @@ function long_test(): boolean { let long_long = new Long(42 as long); let long_float = new Float(42 as float); let long_double = new Double(42 as double); - let long_char = new Char(42 as char); + let long_char = new Char(Int.toChar(42)); // long_byte = testLong; // Long -> Byte is not available according 6.5.2 widening table and CTE happens // long_short = testLong; // Long -> Short is not available according 6.5.2 widening table and CTE happens @@ -235,7 +235,7 @@ function float_test(): boolean { let float_long = new Long(42 as long); let float_float = new Float(42 as float); let float_double = new Double(42 as double); - let float_char = new Char(42 as char); + let float_char = new Char(Int.toChar(42)); // float_byte = testFloat; // Float -> Byte is not available according 6.5.2 widening table and CTE happens // float_short = testFloat; // Float -> Short is not available according 6.5.2 widening table and CTE happens @@ -275,7 +275,7 @@ function double_test(): boolean { let double_long = new Long(42 as long); let double_float = new Float(42 as float); let double_double = new Double(42 as double); - let double_char = new Char(42 as char); + let double_char = new Char(Int.toChar(42) as char); // double_byte = testDouble; // Double -> Byte is not available according 6.5.2 widening table and CTE happens // double_short = testDouble; // Double -> Short is not available according 6.5.2 widening table and CTE happens diff --git a/ets2panda/test/ast/compiler/ets/shiftOperatorWithChar.ets b/ets2panda/test/ast/compiler/ets/shiftOperatorWithChar.ets new file mode 100644 index 0000000000..f1484a2e33 --- /dev/null +++ b/ets2panda/test/ast/compiler/ets/shiftOperatorWithChar.ets @@ -0,0 +1,21 @@ +/* + * 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. + */ + +function main(): void { + let result = c'a' >> 1 +} + +/* @@? 17:16 Error TypeError: Wrong type of operands for binary expression */ +/* @@? 17:16 Error TypeError: Bad operand type, the types of the operands must be numeric type. */ -- Gitee