Skip to content
Open
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
29 changes: 29 additions & 0 deletions vlib/v/checker/for.v
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,17 @@ fn (mut c Checker) for_in_stmt(mut node ast.ForInStmt) {

if high_type in [ast.int_type, ast.int_literal_type] {
node.val_type = typ

if narrow_max := narrow_int_type_max(typ_idx) {
if high_const := c.eval_comptime_const_expr(node.high, 0) {
if high_val := high_const.u64() {
if high_val > u64(narrow_max) {
c.error('`high` value `${high_val}` does not fit in the range value type `${c.table.type_to_str(typ)}` (max `${narrow_max}`); the loop variable would overflow and the loop would never terminate',
cond_pos.extend(high_pos))
}
}
}
}
} else {
node.val_type = high_type
}
Expand Down Expand Up @@ -369,3 +380,21 @@ fn (mut c Checker) for_stmt(mut node ast.ForStmt) {
c.smartcast_mut_pos = token.Pos{}
}
}

// narrow_int_type_max returns the maximum value representable by a fixed-width
// integer type that is narrower than `int`/`i64`/`u64`, or none if `typ_idx`
// is not one of those narrow types (or is not an integer type at all).
// This is used to detect `for x in low .. high` loops where `high` is a
// compile-time constant that does not fit in the (narrower) type of `low`,
// which would make the loop variable wrap around and the loop never terminate.
fn narrow_int_type_max(typ_idx int) ?i64 {
return match typ_idx {
ast.i8_type_idx { i64(max_i8) }
ast.i16_type_idx { i64(max_i16) }
ast.i32_type_idx { i64(max_i32) }
ast.u8_type_idx { i64(max_u8) }
ast.u16_type_idx { i64(max_u16) }
ast.u32_type_idx { i64(max_u32) }
else { none }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
vlib/v/checker/tests/for_in_range_narrow_type_overflow_256_err.vv:2:11: error: `high` value `256` does not fit in the range value type `u8` (max `255`); the loop variable would overflow and the loop would never terminate
1 | fn main() {
2 | for b in u8(0) .. 256 {
| ~~~~~~~~~~~~
3 | println(b)
4 | }
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
fn main() {
for b in u8(0) .. 256 {
println(b)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
vlib/v/checker/tests/for_in_range_narrow_type_overflow_cast_err.vv:2:11: error: `high` value `256` does not fit in the range value type `u8` (max `255`); the loop variable would overflow and the loop would never terminate
1 | fn main() {
2 | for b in u8(0) .. int(256) {
| ~~~~~~~~~~~~~~~~~~~~~~~~
3 | println(b)
4 | }
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
fn main() {
for b in u8(0) .. int(256) {

Check failure on line 2 in vlib/v/checker/tests/for_in_range_narrow_type_overflow_cast_err.vv

View workflow job for this annotation

GitHub Actions / msvc-windows

`high` value `256` does not fit in the range value type `u8` (max `255`); the loop variable would overflow and the loop would never terminate

Check failure on line 2 in vlib/v/checker/tests/for_in_range_narrow_type_overflow_cast_err.vv

View workflow job for this annotation

GitHub Actions / msvc-windows

`high` value `256` does not fit in the range value type `u8` (max `255`); the loop variable would overflow and the loop would never terminate

Check failure on line 2 in vlib/v/checker/tests/for_in_range_narrow_type_overflow_cast_err.vv

View workflow job for this annotation

GitHub Actions / clang-macos (macos-14)

`high` value `256` does not fit in the range value type `u8` (max `255`); the loop variable would overflow and the loop would never terminate

Check failure on line 2 in vlib/v/checker/tests/for_in_range_narrow_type_overflow_cast_err.vv

View workflow job for this annotation

GitHub Actions / clang-macos (macos-14)

`high` value `256` does not fit in the range value type `u8` (max `255`); the loop variable would overflow and the loop would never terminate

Check failure on line 2 in vlib/v/checker/tests/for_in_range_narrow_type_overflow_cast_err.vv

View workflow job for this annotation

GitHub Actions / tcc-linux

`high` value `256` does not fit in the range value type `u8` (max `255`); the loop variable would overflow and the loop would never terminate

Check failure on line 2 in vlib/v/checker/tests/for_in_range_narrow_type_overflow_cast_err.vv

View workflow job for this annotation

GitHub Actions / tcc-linux

`high` value `256` does not fit in the range value type `u8` (max `255`); the loop variable would overflow and the loop would never terminate

Check failure on line 2 in vlib/v/checker/tests/for_in_range_narrow_type_overflow_cast_err.vv

View workflow job for this annotation

GitHub Actions / tcc-windows

`high` value `256` does not fit in the range value type `u8` (max `255`); the loop variable would overflow and the loop would never terminate

Check failure on line 2 in vlib/v/checker/tests/for_in_range_narrow_type_overflow_cast_err.vv

View workflow job for this annotation

GitHub Actions / tcc-windows

`high` value `256` does not fit in the range value type `u8` (max `255`); the loop variable would overflow and the loop would never terminate

Check failure on line 2 in vlib/v/checker/tests/for_in_range_narrow_type_overflow_cast_err.vv

View workflow job for this annotation

GitHub Actions / gcc-windows

`high` value `256` does not fit in the range value type `u8` (max `255`); the loop variable would overflow and the loop would never terminate

Check failure on line 2 in vlib/v/checker/tests/for_in_range_narrow_type_overflow_cast_err.vv

View workflow job for this annotation

GitHub Actions / gcc-windows

`high` value `256` does not fit in the range value type `u8` (max `255`); the loop variable would overflow and the loop would never terminate

Check failure on line 2 in vlib/v/checker/tests/for_in_range_narrow_type_overflow_cast_err.vv

View workflow job for this annotation

GitHub Actions / clang-openbsd

`high` value `256` does not fit in the range value type `u8` (max `255`); the loop variable would overflow and the loop would never terminate

Check failure on line 2 in vlib/v/checker/tests/for_in_range_narrow_type_overflow_cast_err.vv

View workflow job for this annotation

GitHub Actions / clang-openbsd

`high` value `256` does not fit in the range value type `u8` (max `255`); the loop variable would overflow and the loop would never terminate

Check failure on line 2 in vlib/v/checker/tests/for_in_range_narrow_type_overflow_cast_err.vv

View workflow job for this annotation

GitHub Actions / gcc-linux

`high` value `256` does not fit in the range value type `u8` (max `255`); the loop variable would overflow and the loop would never terminate

Check failure on line 2 in vlib/v/checker/tests/for_in_range_narrow_type_overflow_cast_err.vv

View workflow job for this annotation

GitHub Actions / gcc-linux

`high` value `256` does not fit in the range value type `u8` (max `255`); the loop variable would overflow and the loop would never terminate

Check failure on line 2 in vlib/v/checker/tests/for_in_range_narrow_type_overflow_cast_err.vv

View workflow job for this annotation

GitHub Actions / docker-alpine-musl-gcc

`high` value `256` does not fit in the range value type `u8` (max `255`); the loop variable would overflow and the loop would never terminate

Check failure on line 2 in vlib/v/checker/tests/for_in_range_narrow_type_overflow_cast_err.vv

View workflow job for this annotation

GitHub Actions / docker-alpine-musl-gcc

`high` value `256` does not fit in the range value type `u8` (max `255`); the loop variable would overflow and the loop would never terminate

Check failure on line 2 in vlib/v/checker/tests/for_in_range_narrow_type_overflow_cast_err.vv

View workflow job for this annotation

GitHub Actions / tcc-openbsd

`high` value `256` does not fit in the range value type `u8` (max `255`); the loop variable would overflow and the loop would never terminate

Check failure on line 2 in vlib/v/checker/tests/for_in_range_narrow_type_overflow_cast_err.vv

View workflow job for this annotation

GitHub Actions / tcc-openbsd

`high` value `256` does not fit in the range value type `u8` (max `255`); the loop variable would overflow and the loop would never terminate

Check failure on line 2 in vlib/v/checker/tests/for_in_range_narrow_type_overflow_cast_err.vv

View workflow job for this annotation

GitHub Actions / docker-ubuntu-musl

`high` value `256` does not fit in the range value type `u8` (max `255`); the loop variable would overflow and the loop would never terminate

Check failure on line 2 in vlib/v/checker/tests/for_in_range_narrow_type_overflow_cast_err.vv

View workflow job for this annotation

GitHub Actions / docker-ubuntu-musl

`high` value `256` does not fit in the range value type `u8` (max `255`); the loop variable would overflow and the loop would never terminate

Check failure on line 2 in vlib/v/checker/tests/for_in_range_narrow_type_overflow_cast_err.vv

View workflow job for this annotation

GitHub Actions / gcc-freebsd

`high` value `256` does not fit in the range value type `u8` (max `255`); the loop variable would overflow and the loop would never terminate

Check failure on line 2 in vlib/v/checker/tests/for_in_range_narrow_type_overflow_cast_err.vv

View workflow job for this annotation

GitHub Actions / gcc-freebsd

`high` value `256` does not fit in the range value type `u8` (max `255`); the loop variable would overflow and the loop would never terminate

Check failure on line 2 in vlib/v/checker/tests/for_in_range_narrow_type_overflow_cast_err.vv

View workflow job for this annotation

GitHub Actions / tcc-freebsd

`high` value `256` does not fit in the range value type `u8` (max `255`); the loop variable would overflow and the loop would never terminate

Check failure on line 2 in vlib/v/checker/tests/for_in_range_narrow_type_overflow_cast_err.vv

View workflow job for this annotation

GitHub Actions / tcc-freebsd

`high` value `256` does not fit in the range value type `u8` (max `255`); the loop variable would overflow and the loop would never terminate

Check failure on line 2 in vlib/v/checker/tests/for_in_range_narrow_type_overflow_cast_err.vv

View workflow job for this annotation

GitHub Actions / clang-freebsd

`high` value `256` does not fit in the range value type `u8` (max `255`); the loop variable would overflow and the loop would never terminate

Check failure on line 2 in vlib/v/checker/tests/for_in_range_narrow_type_overflow_cast_err.vv

View workflow job for this annotation

GitHub Actions / clang-freebsd

`high` value `256` does not fit in the range value type `u8` (max `255`); the loop variable would overflow and the loop would never terminate
println(b)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
vlib/v/checker/tests/for_in_range_narrow_type_overflow_err.vv:2:11: error: `high` value `256` does not fit in the range value type `u8` (max `255`); the loop variable would overflow and the loop would never terminate
1 | fn main() {
2 | for b in min_u8 .. int(max_u8) + 1 {
| ~~~~~~~~~~~~~~~~~~~~~~~~~
3 | println(b)
4 | }
5 changes: 5 additions & 0 deletions vlib/v/checker/tests/for_in_range_narrow_type_overflow_err.vv
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
fn main() {
for b in min_u8 .. int(max_u8) + 1 {
println(b)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
vlib/v/checker/tests/for_in_range_narrow_type_overflow_max_int_err.vv:2:11: error: `high` value `2147483647` does not fit in the range value type `u8` (max `255`); the loop variable would overflow and the loop would never terminate
1 | fn main() {
2 | for b in u8(0) .. max_int {
| ~~~~~~~~~~~~~~~~
3 | println(b)
4 | }
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
fn main() {
for b in u8(0) .. max_int {
println(b)
}
}
66 changes: 66 additions & 0 deletions vlib/v/checker/tests/for_in_range_narrow_type_overflow_test.v
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// Regression tests
// `for x in low .. high` where `low` has a narrow integer type (e.g. `u8`)
// and `high` is a compile-time constant that overflows that type (e.g. `min_u8 .. int(max_u8) + 1`)
// is now a compile error instead of silently generating an infinite loop.

fn test_full_u8_range_works_when_low_is_widened_to_int() {
// Using `int(min_u8)` (or a plain `0`) keeps the loop variable's type as
// `int`, which can represent the full 0..256 range without overflowing.
mut count := 0
for _ in int(min_u8) .. int(max_u8) + 1 {
count++
}
assert count == 256
}

fn test_plain_zero_low_also_works_for_full_u8_range() {
mut count := 0
for _ in 0 .. int(max_u8) + 1 {
count++
}
assert count == 256
}

fn test_partial_u8_range_still_works() {
mut count := 0
for _ in min_u8 .. u8(200) {
count++
}
assert count == 200
}

fn test_dynamic_bounds_range_is_unaffected() {
low := 0
high := 5
mut count := 0
for _ in low .. high {
count++
}
assert count == 5
}

fn test_explicit_wider_high_type_is_not_flagged() {
mut count := 0
for _ in u8(0) .. u16(256) {
count++
}
assert count == 256
}

fn test_typed_low_with_concrete_int_high_keeps_low_type() {
arr := [10, 20, 30]
mut count := 0
for _ in u8(0) .. arr.len {
count++
}
assert count == 3
}

fn test_dynamic_bounds_that_silently_wrap_at_runtime_are_unaffected() {
n := max_int
mut count := 0
for _ in u8(0) .. n + 1 {
count++
}
assert count == 0
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
vlib/v/checker/tests/for_in_range_narrow_type_overflow_u64_literal_err.vv:2:11: error: `high` value `9223372036854775808` does not fit in the range value type `u8` (max `255`); the loop variable would overflow and the loop would never terminate
1 | fn main() {
2 | for b in u8(0) .. 9223372036854775808 {
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3 | println(b)
4 | }
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
fn main() {
for b in u8(0) .. 9223372036854775808 {
println(b)
}
}
Loading