Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unsigned integers no wrap II #2189

Merged
merged 8 commits into from
Jul 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 2 additions & 4 deletions integration_tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
42 changes: 0 additions & 42 deletions integration_tests/cast_02.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()
25 changes: 14 additions & 11 deletions integration_tests/test_unary_op_05.py
Original file line number Diff line number Diff line change
@@ -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()
4 changes: 2 additions & 2 deletions integration_tests/test_unsigned_01.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
8 changes: 8 additions & 0 deletions src/libasr/asr_utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1265,6 +1265,14 @@ ASR::asr_t* make_Cast_t_value(Allocator &al, const Location &a_loc,
int64_t int_value = ASR::down_cast<ASR::IntegerConstant_t>(
ASRUtils::expr_value(a_arg))->m_n;
value = ASR::down_cast<ASR::expr_t>(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<ASR::UnsignedIntegerConstant_t>(
ASRUtils::expr_value(a_arg))->m_n;
value = ASR::down_cast<ASR::expr_t>(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<ASR::UnsignedIntegerConstant_t>(
ASRUtils::expr_value(a_arg))->m_n;
value = ASR::down_cast<ASR::expr_t>(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) {
Expand Down
23 changes: 22 additions & 1 deletion src/lpython/semantics/python_ast_to_asr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3646,8 +3646,20 @@ class CommonVisitor : public AST::BaseVisitor<Struct> {
if (ASRUtils::expr_value(operand) != nullptr) {
int64_t op_value = ASR::down_cast<ASR::UnsignedIntegerConstant_t>(
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::expr_t>(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);
Expand Down Expand Up @@ -7615,6 +7627,15 @@ class BodyVisitor : public CommonVisitor<BodyVisitor> {
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<ASR::UnsignedInteger_t>(*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 ;
Expand Down
3 changes: 3 additions & 0 deletions tests/errors/unsigned_01.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from lpython import u16

i: u16 = u16(-5)
3 changes: 3 additions & 0 deletions tests/errors/unsigned_02.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from lpython import u16

i: u16 = -u16(5)
3 changes: 3 additions & 0 deletions tests/errors/unsigned_03.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from lpython import u32, u64

print(-u64(u32(10)))
4 changes: 4 additions & 0 deletions tests/errors/unsigned_04.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
from lpython import u16

i: u16 = u16(5)
i = ~i
13 changes: 13 additions & 0 deletions tests/reference/asr-unsigned_01-892b178.json
Original file line number Diff line number Diff line change
@@ -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
}
5 changes: 5 additions & 0 deletions tests/reference/asr-unsigned_01-892b178.stderr
Original file line number Diff line number Diff line change
@@ -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)
| ^^^^^^^
13 changes: 13 additions & 0 deletions tests/reference/asr-unsigned_02-6563e58.json
Original file line number Diff line number Diff line change
@@ -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
}
5 changes: 5 additions & 0 deletions tests/reference/asr-unsigned_02-6563e58.stderr
Original file line number Diff line number Diff line change
@@ -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
Copy link
Contributor

@certik certik Jul 19, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be great if it could copy the actual expression inside:

Suggested change
| ^^^^^^^ use -i32(u) for signed result
| ^^^^^^^ use -i32(5) for signed result

But I guess for that we would have to have an ASR -> Python printer, which we plan to, but not right now. So we can do that later.

13 changes: 13 additions & 0 deletions tests/reference/asr-unsigned_03-f785652.json
Original file line number Diff line number Diff line change
@@ -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
}
5 changes: 5 additions & 0 deletions tests/reference/asr-unsigned_03-f785652.stderr
Original file line number Diff line number Diff line change
@@ -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
13 changes: 13 additions & 0 deletions tests/reference/asr-unsigned_04-c856d3a.json
Original file line number Diff line number Diff line change
@@ -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
}
5 changes: 5 additions & 0 deletions tests/reference/asr-unsigned_04-c856d3a.stderr
Original file line number Diff line number Diff line change
@@ -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
16 changes: 16 additions & 0 deletions tests/tests.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down