checker: error if smaller signed == unsigned (#14078)

master
Nick Treleaven 2022-04-25 10:09:25 +01:00 committed by GitHub
parent 147e6e669f
commit d8a5df9044
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 116 additions and 21 deletions

View File

@ -931,7 +931,7 @@ fn test_u64_keys() {
m[i]++ m[i]++
assert m[i] == i + 1 assert m[i] == i + 1
} }
assert m.len == end assert u64(m.len) == end
keys := m.keys() keys := m.keys()
for i in u64(0) .. end { for i in u64(0) .. end {
assert keys[i] == i assert keys[i] == i

View File

@ -919,7 +919,7 @@ fn test_u64_keys() {
m[i]++ m[i]++
assert m[i] == i + 1 assert m[i] == i + 1
} }
assert m.len == end assert u64(m.len) == end
keys := m.keys() keys := m.keys()
for i in u64(0) .. end { for i in u64(0) .. end {
assert keys[i] == i assert keys[i] == i

View File

@ -223,7 +223,7 @@ pub fn (mut ws Client) parse_frame_header() ?Frame {
buffer[bytes_read] = rbuff[0] buffer[bytes_read] = rbuff[0]
bytes_read++ bytes_read++
// parses the first two header bytes to get basic frame information // parses the first two header bytes to get basic frame information
if bytes_read == u64(websocket.header_len_offset) { if bytes_read == websocket.header_len_offset {
frame.fin = (buffer[0] & 0x80) == 0x80 frame.fin = (buffer[0] & 0x80) == 0x80
frame.rsv1 = (buffer[0] & 0x40) == 0x40 frame.rsv1 = (buffer[0] & 0x40) == 0x40
frame.rsv2 = (buffer[0] & 0x20) == 0x20 frame.rsv2 = (buffer[0] & 0x20) == 0x20
@ -249,7 +249,7 @@ pub fn (mut ws Client) parse_frame_header() ?Frame {
break break
} }
} }
if frame.payload_len == 126 && bytes_read == u64(websocket.extended_payload16_end_byte) { if frame.payload_len == 126 && bytes_read == websocket.extended_payload16_end_byte {
frame.header_len += 2 frame.header_len += 2
frame.payload_len = 0 frame.payload_len = 0
frame.payload_len |= int(u32(buffer[2]) << 8) frame.payload_len |= int(u32(buffer[2]) << 8)
@ -259,7 +259,7 @@ pub fn (mut ws Client) parse_frame_header() ?Frame {
break break
} }
} }
if frame.payload_len == 127 && bytes_read == u64(websocket.extended_payload64_end_byte) { if frame.payload_len == 127 && bytes_read == websocket.extended_payload64_end_byte {
frame.header_len += 8 frame.header_len += 8
// these shift operators needs 64 bit on clang with -prod flag // these shift operators needs 64 bit on clang with -prod flag
mut payload_len := u64(0) mut payload_len := u64(0)

View File

@ -41,7 +41,7 @@ fn test_open_file() {
mut file := os.open_file(filename, 'w+', 0o666) or { panic(err) } mut file := os.open_file(filename, 'w+', 0o666) or { panic(err) }
file.write_string(hello) or { panic(err) } file.write_string(hello) or { panic(err) }
file.close() file.close()
assert hello.len == os.file_size(filename) assert u64(hello.len) == os.file_size(filename)
read_hello := os.read_file(filename) or { panic('error reading file $filename') } read_hello := os.read_file(filename) or { panic('error reading file $filename') }
assert hello == read_hello assert hello == read_hello
os.rm(filename) or { panic(err) } os.rm(filename) or { panic(err) }
@ -58,7 +58,7 @@ fn test_open_file_binary() {
bytes := hello.bytes() bytes := hello.bytes()
unsafe { file.write_ptr(bytes.data, bytes.len) } unsafe { file.write_ptr(bytes.data, bytes.len) }
file.close() file.close()
assert hello.len == os.file_size(filename) assert u64(hello.len) == os.file_size(filename)
read_hello := os.read_bytes(filename) or { panic('error reading file $filename') } read_hello := os.read_bytes(filename) or { panic('error reading file $filename') }
assert bytes == read_hello assert bytes == read_hello
os.rm(filename) or { panic(err) } os.rm(filename) or { panic(err) }
@ -100,7 +100,7 @@ fn test_create_file() ? {
filename := './test1.txt' filename := './test1.txt'
hello := 'hello world!' hello := 'hello world!'
create_and_write_to_file(filename, hello) ? create_and_write_to_file(filename, hello) ?
assert hello.len == os.file_size(filename) assert u64(hello.len) == os.file_size(filename)
os.rm(filename) or { panic(err) } os.rm(filename) or { panic(err) }
} }
@ -138,7 +138,7 @@ fn test_write_and_read_string_to_file() {
filename := './test1.txt' filename := './test1.txt'
hello := 'hello world!' hello := 'hello world!'
os.write_file(filename, hello) or { panic(err) } os.write_file(filename, hello) or { panic(err) }
assert hello.len == os.file_size(filename) assert u64(hello.len) == os.file_size(filename)
read_hello := os.read_file(filename) or { panic('error reading file $filename') } read_hello := os.read_file(filename) or { panic('error reading file $filename') }
assert hello == read_hello assert hello == read_hello
os.rm(filename) or { panic(err) } os.rm(filename) or { panic(err) }
@ -157,7 +157,7 @@ fn test_write_and_read_bytes() {
// compare the length of the array with the file size (have to match). // compare the length of the array with the file size (have to match).
unsafe { file_write.write_ptr(payload.data, 5) } unsafe { file_write.write_ptr(payload.data, 5) }
file_write.close() file_write.close()
assert payload.len == os.file_size(file_name) assert u64(payload.len) == os.file_size(file_name)
mut file_read := os.open(os.real_path(file_name)) or { mut file_read := os.open(os.real_path(file_name)) or {
eprintln('failed to open file $file_name') eprintln('failed to open file $file_name')
return return
@ -792,7 +792,7 @@ fn test_truncate() ? {
mut f := os.create(filename) ? mut f := os.create(filename) ?
f.write_string(hello) ? f.write_string(hello) ?
f.close() f.close()
assert hello.len == os.file_size(filename) assert u64(hello.len) == os.file_size(filename)
newlen := u64(40000) newlen := u64(40000)
os.truncate(filename, newlen) or { panic(err) } os.truncate(filename, newlen) or { panic(err) }
assert newlen == os.file_size(filename) assert newlen == os.file_size(filename)

View File

@ -18,7 +18,7 @@ fn test_count_10_times_1_cycle_should_result_10_cycles_with_sync() {
go count_one_cycle(mut counter, mut wg) go count_one_cycle(mut counter, mut wg)
} }
wg.wait() wg.wait()
assert counter.counter == desired_iterations assert counter.counter == u64(desired_iterations)
eprintln(' with synchronization the counter is: ${counter.counter:10} , expectedly == ${desired_iterations:10}') eprintln(' with synchronization the counter is: ${counter.counter:10} , expectedly == ${desired_iterations:10}')
} }

View File

@ -689,3 +689,38 @@ pub fn (mut c Checker) infer_fn_generic_types(func ast.Fn, mut node ast.CallExpr
c.need_recheck_generic_fns = true c.need_recheck_generic_fns = true
} }
} }
pub fn (c &Checker) sizeof_integer(a ast.Type) int {
t := if a in ast.unsigned_integer_type_idxs { a.flip_signedness() } else { a }
r := match t {
ast.char_type_idx, ast.i8_type_idx {
1
}
ast.i16_type_idx {
2
}
ast.int_type_idx {
4
}
ast.rune_type_idx {
4
}
ast.i64_type_idx {
8
}
ast.isize_type_idx {
if c.pref.m64 { 8 } else { 4 }
}
ast.int_literal_type {
s := c.table.type_to_str(a)
panic('`$s` has unknown size')
0
}
else {
s := c.table.type_to_str(a)
panic('`$s` is not an integer')
0
}
}
return r
}

View File

@ -640,6 +640,18 @@ pub fn (mut c Checker) infix_expr(mut node ast.InfixExpr) ast.Type {
rt := c.table.sym(right_type).name rt := c.table.sym(right_type).name
c.error('negative value cannot be compared with `$rt`', node.left.pos) c.error('negative value cannot be compared with `$rt`', node.left.pos)
} }
} else if is_left_type_signed != is_right_type_signed
&& left_type != ast.int_literal_type_idx
&& right_type != ast.int_literal_type_idx {
ls := c.sizeof_integer(left_type)
rs := c.sizeof_integer(right_type)
// prevent e.g. `u32 == i16` but not `u16 == i32` as max_u16 fits in i32
// TODO u32 == i32, change < to <=
if (is_left_type_signed && ls < rs) || (is_right_type_signed && rs < ls) {
lt := c.table.sym(left_type).name
rt := c.table.sym(right_type).name
c.error('`$lt` cannot be compared with `$rt`', node.pos)
}
} }
} }
} }

View File

@ -17,10 +17,45 @@ vlib/v/checker/tests/compare_unsigned_signed.vv:10:16: error: `u8` cannot be com
10 | _ = u8(-1) == -1 // false! 10 | _ = u8(-1) == -1 // false!
| ~~ | ~~
11 | _ = -1 == u16(-1) // false! 11 | _ = -1 == u16(-1) // false!
12 | } 12 |
vlib/v/checker/tests/compare_unsigned_signed.vv:11:6: error: negative value cannot be compared with `u16` vlib/v/checker/tests/compare_unsigned_signed.vv:11:6: error: negative value cannot be compared with `u16`
9 | // unsigned == literal 9 | // unsigned == literal
10 | _ = u8(-1) == -1 // false! 10 | _ = u8(-1) == -1 // false!
11 | _ = -1 == u16(-1) // false! 11 | _ = -1 == u16(-1) // false!
| ~~ | ~~
12 | } 12 |
13 | // smaller unsigned == signed, OK
vlib/v/checker/tests/compare_unsigned_signed.vv:18:12: error: `i8` cannot be compared with `u16`
16 |
17 | // smaller signed == unsigned, NG
18 | _ = i8(0) == u16(0)
| ~~
19 | _ = i16(0) != u32(0)
20 | _ = int(0) == u64(0)
vlib/v/checker/tests/compare_unsigned_signed.vv:19:13: error: `i16` cannot be compared with `u32`
17 | // smaller signed == unsigned, NG
18 | _ = i8(0) == u16(0)
19 | _ = i16(0) != u32(0)
| ~~
20 | _ = int(0) == u64(0)
21 | _ = i32(0) == u64(0) // FIXME
vlib/v/checker/tests/compare_unsigned_signed.vv:20:13: error: `int` cannot be compared with `u64`
18 | _ = i8(0) == u16(0)
19 | _ = i16(0) != u32(0)
20 | _ = int(0) == u64(0)
| ~~
21 | _ = i32(0) == u64(0) // FIXME
22 | // swap order
vlib/v/checker/tests/compare_unsigned_signed.vv:23:13: error: `u16` cannot be compared with `i8`
21 | _ = i32(0) == u64(0) // FIXME
22 | // swap order
23 | _ = u16(0) == i8(0)
| ~~
24 | _ = u64(0) == i16(0)
25 | }
vlib/v/checker/tests/compare_unsigned_signed.vv:24:13: error: `u64` cannot be compared with `i16`
22 | // swap order
23 | _ = u16(0) == i8(0)
24 | _ = u64(0) == i16(0)
| ~~
25 | }

View File

@ -9,4 +9,17 @@ fn main() {
// unsigned == literal // unsigned == literal
_ = u8(-1) == -1 // false! _ = u8(-1) == -1 // false!
_ = -1 == u16(-1) // false! _ = -1 == u16(-1) // false!
// smaller unsigned == signed, OK
_ = u16(-1) == int(-1)
_ = int(-1) != u8(-1)
// smaller signed == unsigned, NG
_ = i8(0) == u16(0)
_ = i16(0) != u32(0)
_ = int(0) == u64(0)
_ = i32(0) == u64(0) // FIXME
// swap order
_ = u16(0) == i8(0)
_ = u64(0) == i16(0)
} }

View File

@ -42,9 +42,9 @@ fn test_cmp_u32_and_signed() {
fn test_cmp_signed_and_u64() { fn test_cmp_signed_and_u64() {
// == // ==
assert int(1) == u64(1) // assert int(1) == u64(1)
// != // !=
assert int(1) != u64(2) // assert int(1) != u64(2)
// > // >
assert !(int(1) > u64(1)) assert !(int(1) > u64(1))
assert int(1) > u64(0) assert int(1) > u64(0)
@ -63,9 +63,9 @@ fn test_cmp_signed_and_u64() {
fn test_cmp_u64_and_signed() { fn test_cmp_u64_and_signed() {
// == // ==
assert u64(1) == int(1) // assert u64(1) == int(1)
// != // !=
assert u64(2) != int(1) // assert u64(2) != int(1)
// > // >
assert !(u64(1) > int(1)) assert !(u64(1) > int(1))
assert u64(1) > int(0) assert u64(1) > int(0)

View File

@ -58,13 +58,13 @@ fn test_shift_operators() {
assert e == a assert e == a
mut e3 := u64(1) mut e3 := u64(1)
e3 <<= u32(i) e3 <<= u32(i)
assert e3 == b assert e3 == u64(b)
e3 >>= u32(i) e3 >>= u32(i)
assert e == a assert e == a
e3 <<= u64(i) e3 <<= u64(i)
assert e3 == b assert e3 == u64(b)
e3 >>= u64(i) e3 >>= u64(i)
assert e3 == a assert e3 == u64(a)
// Test shifts with custom int types // Test shifts with custom int types
x := MyInt(2) x := MyInt(2)
assert x << 2 == 8 assert x << 2 == 8