Skip to content

checker: add an error when high overflows low in a for-in range #27854

Open
rilaaax wants to merge 2 commits into
vlang:masterfrom
rilaaax:checker/fix-for-range-narrow-type-overflow
Open

checker: add an error when high overflows low in a for-in range #27854
rilaaax wants to merge 2 commits into
vlang:masterfrom
rilaaax:checker/fix-for-range-narrow-type-overflow

Conversation

@rilaaax

@rilaaax rilaaax commented Jul 18, 2026

Copy link
Copy Markdown
Contributor

Fixes #27728

Problem

A for i in low .. high loop where low has an explicit narrow integer type (e.g. u8) and high is a compile-time constant that exceeds that type's range (e.g. for b in min_u8 .. int(max_u8) + 1) compiles and runs, but results in an infinite loop: the loop variable's type silently defaults to the narrow type of low, so it wraps around (255 -> 0 for u8) instead of ever reaching high, and the loop never terminates.

Cause

In for_in_stmt (vlib/v/checker/for.v), whenever high's type is int/int_literal, the checker unconditionally sets the loop variable's type (node.val_type) to the type of low, without checking whether high's value actually fits in that (potentially narrower) type. The C code generator then emits a loop like for (u8 b = 0; b < 256; ++b), where b can never reach 256 because it overflows and wraps back to 0 first.

Fix

Added a check in for_in_stmt: when low's type is a fixed-width integer type narrower than int/i64/u64 (i8, i16, i32, u8, u16, u32), and high can be evaluated as a compile-time constant (via the existing eval_comptime_const_expr, which already resolves builtin constants like max_u8 and simple casts/arithmetic), the checker now verifies that this constant value fits within the maximum representable value of low's type. If it doesn't, a compile-time error is raised instead of silently generating an infinite loop. Ranges whose bounds cannot be evaluated statically (e.g. built from variables) are left unaffected, since this cannot be reliably checked at compile time.

Test

Regression test added.

@GGRei

GGRei commented Jul 18, 2026

Copy link
Copy Markdown
Contributor

While testing the PR, I found two remaining edge cases:

  • for value in u8(0) .. u16(300) {} works correctly on master because the loop variable is widened to u16, but the PR rejects it against the u8 maximum. This introduces a new false positive.
  • for value in u8(0) .. 9223372036854775808 {} still passes the checker. The generated C keeps a u8 loop variable, so it can still wrap around forever.

These appear to be one newly rejected valid case and one overflow case that remains undetected. Could you please take a look? Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

for-in loop causes an infinite loop when high overflows low via casting

2 participants