diff --git a/integration_tests/CMakeLists.txt b/integration_tests/CMakeLists.txt index dc17f72d8c..8535c83194 100644 --- a/integration_tests/CMakeLists.txt +++ b/integration_tests/CMakeLists.txt @@ -570,11 +570,9 @@ RUN(NAME test_unary_op_01 LABELS cpython llvm c) # unary minus RUN(NAME test_unary_op_02 LABELS cpython llvm c) # unary plus RUN(NAME test_unary_op_03 LABELS cpython llvm c wasm) # unary bitinvert RUN(NAME test_unary_op_04 LABELS cpython llvm c) # unary bitinvert -# Unsigned unary minus is not supported in CPython -# RUN(NAME test_unary_op_05 LABELS cpython llvm c) # unsigned unary minus, plus +RUN(NAME test_unary_op_05 LABELS cpython llvm c) # unsigned unary minus, plus RUN(NAME test_unary_op_06 LABELS cpython llvm c) # unsigned unary bitnot -# The value after shift overflows in CPython -# RUN(NAME test_unsigned_01 LABELS cpython llvm c) # unsigned bitshift left, right +RUN(NAME test_unsigned_01 LABELS cpython llvm c) # unsigned bitshift left, right RUN(NAME test_unsigned_02 LABELS cpython llvm c) RUN(NAME test_unsigned_03 LABELS cpython llvm c) RUN(NAME test_bool_binop LABELS cpython llvm c) diff --git a/integration_tests/cast_02.py b/integration_tests/cast_02.py index dd2d8ddbb0..f984e5730c 100644 --- a/integration_tests/cast_02.py +++ b/integration_tests/cast_02.py @@ -34,50 +34,8 @@ def test_02(): print(w) assert w == u32(11) -# Disable following tests -# Negative numbers in unsigned should throw errors -# TODO: Add these tests as error reference tests - -# def test_03(): -# x : u32 = u32(-10) -# print(x) -# assert x == u32(4294967286) - -# y: u16 = u16(x) -# print(y) -# assert y == u16(65526) - -# z: u64 = u64(y) -# print(z) -# assert z == u64(65526) - -# w: u8 = u8(z) -# print(w) -# assert w == u8(246) - -# def test_04(): -# x : u64 = u64(-11) -# print(x) -# # TODO: We are unable to store the following u64 in AST/R -# # assert x == u64(18446744073709551605) - -# y: u8 = u8(x) -# print(y) -# assert y == u8(245) - -# z: u16 = u16(y) -# print(z) -# assert z == u16(245) - -# w: u32 = u32(z) -# print(w) -# assert w == u32(245) - - def main0(): test_01() test_02() - # test_03() - # test_04() main0() diff --git a/integration_tests/test_unary_op_05.py b/integration_tests/test_unary_op_05.py index f3d7253224..c480606591 100644 --- a/integration_tests/test_unary_op_05.py +++ b/integration_tests/test_unary_op_05.py @@ -1,27 +1,30 @@ -from lpython import u16, u32, u64 +from lpython import u8, u16, u32, u64 def f(): + h: u8 + h = u8(67) + print(+h) + assert +h == u8(67) + i: u16 i = u16(67) - print(-i, +i, -(-i)) - assert -i == u16(65469) + print(+i) assert +i == u16(67) - assert -(-i) == u16(67) j: u32 j = u32(25) - print(-j, +j, -(-j)) - assert -j == u32(4294967271) + print(+j) assert +j == u32(25) - assert -(-j) == u32(25) k: u64 k = u64(100000000000123) - print(-k, +k, -(-k)) - # TODO: We are unable to store the following u64 in AST/R - # assert -k == u64(18446644073709551493) + print(+k) assert +k == u64(100000000000123) - assert -(-k) == u64(100000000000123) + + assert -u8(0) == u8(0) + assert -u16(0) == u16(0) + assert -u32(0) == u32(0) + assert -u64(0) == u64(0) f() diff --git a/integration_tests/test_unsigned_01.py b/integration_tests/test_unsigned_01.py index 0f599f1806..44068cceb3 100644 --- a/integration_tests/test_unsigned_01.py +++ b/integration_tests/test_unsigned_01.py @@ -4,9 +4,9 @@ def f(): h: u8 h = u8(5) - print(h << u8(4), h << u8(7), h >> u8(4), h >> u8(7)) + print(h << u8(4), h << u8(2), h >> u8(4), h >> u8(7)) assert h << u8(4) == u8(80) - assert h << u8(7) == u8(128) + assert h << u8(2) == u8(20) assert h >> u8(4) == u8(0) assert h >> u8(7) == u8(0) diff --git a/src/libasr/asr_utils.cpp b/src/libasr/asr_utils.cpp index b4b188883c..160d799cc7 100644 --- a/src/libasr/asr_utils.cpp +++ b/src/libasr/asr_utils.cpp @@ -1265,6 +1265,14 @@ ASR::asr_t* make_Cast_t_value(Allocator &al, const Location &a_loc, int64_t int_value = ASR::down_cast( ASRUtils::expr_value(a_arg))->m_n; value = ASR::down_cast(ASR::make_UnsignedIntegerConstant_t(al, a_loc, int_value, a_type)); + } else if (a_kind == ASR::cast_kindType::UnsignedIntegerToInteger) { + int64_t int_value = ASR::down_cast( + ASRUtils::expr_value(a_arg))->m_n; + value = ASR::down_cast(ASR::make_IntegerConstant_t(al, a_loc, int_value, a_type)); + } else if (a_kind == ASR::cast_kindType::UnsignedIntegerToUnsignedInteger) { + int64_t int_value = ASR::down_cast( + ASRUtils::expr_value(a_arg))->m_n; + value = ASR::down_cast(ASR::make_UnsignedIntegerConstant_t(al, a_loc, int_value, a_type)); } else if (a_kind == ASR::cast_kindType::IntegerToLogical) { // TODO: implement } else if (a_kind == ASR::cast_kindType::ComplexToComplex) { diff --git a/src/lpython/semantics/python_ast_to_asr.cpp b/src/lpython/semantics/python_ast_to_asr.cpp index 6c66d8ba04..7836aa7ca2 100644 --- a/src/lpython/semantics/python_ast_to_asr.cpp +++ b/src/lpython/semantics/python_ast_to_asr.cpp @@ -3646,8 +3646,20 @@ class CommonVisitor : public AST::BaseVisitor { if (ASRUtils::expr_value(operand) != nullptr) { int64_t op_value = ASR::down_cast( ASRUtils::expr_value(operand))->m_n; + if (op_value != 0) { + int kind = ASRUtils::extract_kind_from_ttype_t(operand_type); + int signed_promote_kind = (kind < 8) ? kind * 2 : kind; + diag.add(diag::Diagnostic( + "The result of the unary minus `-` operation is negative, thus out of range for u" + std::to_string(kind * 8), + diag::Level::Error, diag::Stage::Semantic, { + diag::Label("use -i" + std::to_string(signed_promote_kind * 8) + + "(u) for signed result", {x.base.base.loc}) + }) + ); + throw SemanticAbort(); + } value = ASR::down_cast(ASR::make_UnsignedIntegerConstant_t( - al, x.base.base.loc, -op_value, operand_type)); + al, x.base.base.loc, 0, operand_type)); } tmp = ASR::make_UnsignedIntegerUnaryMinus_t(al, x.base.base.loc, operand, operand_type, value); @@ -7615,6 +7627,15 @@ class BodyVisitor : public CommonVisitor { target_type = ASRUtils::TYPE(ASR::make_SymbolicExpression_t(al, x.base.base.loc)); } ASR::expr_t* arg = args[0].m_value; + if (ASR::is_a(*target_type)) { + int64_t value_int; + if( ASRUtils::extract_value(ASRUtils::expr_value(arg), value_int) ) { + if (value_int < 0) { + throw SemanticError("Cannot cast negative value to unsigned integer ", + x.base.base.loc); + } + } + } cast_helper(target_type, arg, x.base.base.loc, true); tmp = (ASR::asr_t*) arg; return ; diff --git a/tests/errors/unsigned_01.py b/tests/errors/unsigned_01.py new file mode 100644 index 0000000000..b8635cfdb2 --- /dev/null +++ b/tests/errors/unsigned_01.py @@ -0,0 +1,3 @@ +from lpython import u16 + +i: u16 = u16(-5) diff --git a/tests/errors/unsigned_02.py b/tests/errors/unsigned_02.py new file mode 100644 index 0000000000..6ecce57234 --- /dev/null +++ b/tests/errors/unsigned_02.py @@ -0,0 +1,3 @@ +from lpython import u16 + +i: u16 = -u16(5) diff --git a/tests/errors/unsigned_03.py b/tests/errors/unsigned_03.py new file mode 100644 index 0000000000..64b6e43cf3 --- /dev/null +++ b/tests/errors/unsigned_03.py @@ -0,0 +1,3 @@ +from lpython import u32, u64 + +print(-u64(u32(10))) diff --git a/tests/errors/unsigned_04.py b/tests/errors/unsigned_04.py new file mode 100644 index 0000000000..3009f0cd1f --- /dev/null +++ b/tests/errors/unsigned_04.py @@ -0,0 +1,4 @@ +from lpython import u16 + +i: u16 = u16(5) +i = ~i diff --git a/tests/reference/asr-unsigned_01-892b178.json b/tests/reference/asr-unsigned_01-892b178.json new file mode 100644 index 0000000000..0a797a2c11 --- /dev/null +++ b/tests/reference/asr-unsigned_01-892b178.json @@ -0,0 +1,13 @@ +{ + "basename": "asr-unsigned_01-892b178", + "cmd": "lpython --show-asr --no-color {infile} -o {outfile}", + "infile": "tests/errors/unsigned_01.py", + "infile_hash": "c176f4ec65c0b5ca7c2c95b2e35d65f22b4cef342baad15c2dd5ef24", + "outfile": null, + "outfile_hash": null, + "stdout": null, + "stdout_hash": null, + "stderr": "asr-unsigned_01-892b178.stderr", + "stderr_hash": "54c7cfbd16c73cbe802a3492cd9c9e8d2fb25035192d229232c377b2", + "returncode": 2 +} \ No newline at end of file diff --git a/tests/reference/asr-unsigned_01-892b178.stderr b/tests/reference/asr-unsigned_01-892b178.stderr new file mode 100644 index 0000000000..c3526cf36e --- /dev/null +++ b/tests/reference/asr-unsigned_01-892b178.stderr @@ -0,0 +1,5 @@ +semantic error: Cannot cast negative value to unsigned integer + --> tests/errors/unsigned_01.py:3:10 + | +3 | i: u16 = u16(-5) + | ^^^^^^^ diff --git a/tests/reference/asr-unsigned_02-6563e58.json b/tests/reference/asr-unsigned_02-6563e58.json new file mode 100644 index 0000000000..40e7b2a0f0 --- /dev/null +++ b/tests/reference/asr-unsigned_02-6563e58.json @@ -0,0 +1,13 @@ +{ + "basename": "asr-unsigned_02-6563e58", + "cmd": "lpython --show-asr --no-color {infile} -o {outfile}", + "infile": "tests/errors/unsigned_02.py", + "infile_hash": "7892abcbe7cecdbddc7362fd0940986bf64458880ccd198c16dd2a6e", + "outfile": null, + "outfile_hash": null, + "stdout": null, + "stdout_hash": null, + "stderr": "asr-unsigned_02-6563e58.stderr", + "stderr_hash": "93f2cf309dfa7f13d56df9184615fde6a832b79510d8541f95ad5a70", + "returncode": 2 +} \ No newline at end of file diff --git a/tests/reference/asr-unsigned_02-6563e58.stderr b/tests/reference/asr-unsigned_02-6563e58.stderr new file mode 100644 index 0000000000..d00c5647fd --- /dev/null +++ b/tests/reference/asr-unsigned_02-6563e58.stderr @@ -0,0 +1,5 @@ +semantic error: The result of the unary minus `-` operation is negative, thus out of range for u16 + --> tests/errors/unsigned_02.py:3:10 + | +3 | i: u16 = -u16(5) + | ^^^^^^^ use -i32(u) for signed result diff --git a/tests/reference/asr-unsigned_03-f785652.json b/tests/reference/asr-unsigned_03-f785652.json new file mode 100644 index 0000000000..6929c59a27 --- /dev/null +++ b/tests/reference/asr-unsigned_03-f785652.json @@ -0,0 +1,13 @@ +{ + "basename": "asr-unsigned_03-f785652", + "cmd": "lpython --show-asr --no-color {infile} -o {outfile}", + "infile": "tests/errors/unsigned_03.py", + "infile_hash": "72dd2c6e17b137b6b9f66deeaab0af4ebe3044cb16009e0fab25f05a", + "outfile": null, + "outfile_hash": null, + "stdout": null, + "stdout_hash": null, + "stderr": "asr-unsigned_03-f785652.stderr", + "stderr_hash": "d90013a25d9aaa91fbbf2b1193cd06be94a4e716f0fe99771cde5601", + "returncode": 2 +} \ No newline at end of file diff --git a/tests/reference/asr-unsigned_03-f785652.stderr b/tests/reference/asr-unsigned_03-f785652.stderr new file mode 100644 index 0000000000..a68ae96697 --- /dev/null +++ b/tests/reference/asr-unsigned_03-f785652.stderr @@ -0,0 +1,5 @@ +semantic error: The result of the unary minus `-` operation is negative, thus out of range for u64 + --> tests/errors/unsigned_03.py:3:7 + | +3 | print(-u64(u32(10))) + | ^^^^^^^^^^^^^ use -i64(u) for signed result diff --git a/tests/reference/asr-unsigned_04-c856d3a.json b/tests/reference/asr-unsigned_04-c856d3a.json new file mode 100644 index 0000000000..3753e9ae30 --- /dev/null +++ b/tests/reference/asr-unsigned_04-c856d3a.json @@ -0,0 +1,13 @@ +{ + "basename": "asr-unsigned_04-c856d3a", + "cmd": "lpython --show-asr --no-color {infile} -o {outfile}", + "infile": "tests/errors/unsigned_04.py", + "infile_hash": "d1c2f82e9578ce3f2364f4bbd3177bc0ae72357c644953418eaffe4c", + "outfile": null, + "outfile_hash": null, + "stdout": null, + "stdout_hash": null, + "stderr": "asr-unsigned_04-c856d3a.stderr", + "stderr_hash": "3dcdf2e97f8c5f2816bed266587c7c3743f666cf2a4602f65e8ec9b8", + "returncode": 2 +} \ No newline at end of file diff --git a/tests/reference/asr-unsigned_04-c856d3a.stderr b/tests/reference/asr-unsigned_04-c856d3a.stderr new file mode 100644 index 0000000000..8547a0848c --- /dev/null +++ b/tests/reference/asr-unsigned_04-c856d3a.stderr @@ -0,0 +1,5 @@ +semantic error: The result of the bitnot ~ operation is negative, thus out of range for u16 + --> tests/errors/unsigned_04.py:4:5 + | +4 | i = ~i + | ^^ use ~i32(u) for signed result or bitnot_u16(u) for unsigned result diff --git a/tests/tests.toml b/tests/tests.toml index d18869f873..7180531b45 100644 --- a/tests/tests.toml +++ b/tests/tests.toml @@ -1194,6 +1194,22 @@ asr = true filename = "errors/func_08.py" asr = true +[[test]] +filename = "errors/unsigned_01.py" +asr = true + +[[test]] +filename = "errors/unsigned_02.py" +asr = true + +[[test]] +filename = "errors/unsigned_03.py" +asr = true + +[[test]] +filename = "errors/unsigned_04.py" +asr = true + # tests/runtime_errors [[test]] filename = "runtime_errors/test_list_01.py"