lowerVarDecl (unannotated) and lowerDestructureDecl now clear target_type around the initializer lowering: a declaration without annotation provides no target, so int/float literals take their spec defaults (s64/f64) instead of the enclosing function's implicit-return type (x := 0 in a -> s8 fn was s8; big := 3000000000 in -> s32 silently wrapped to -1294967296). Regression: examples/0173-types-int-literal-default-s64.sx. The remaining explicit-annotation wrap (x : s8 = 300) is filed as issue 0112.
67 lines
3.0 KiB
Markdown
67 lines
3.0 KiB
Markdown
# 0112 — out-of-range int literal silently wraps into a narrower annotated target
|
|
|
|
**Symptom.** An integer literal that does not fit its explicitly-annotated
|
|
integer target truncates with no diagnostic: `x : s8 = 300;` binds 44,
|
|
`y : u8 = 256;` binds 0. Expected: a compile-time error (the value is known
|
|
exactly at compile time; this is the integer analogue of the float→int
|
|
narrowing rule, which errors on non-exact `y : s64 = 1.5`).
|
|
|
|
Split from issue 0111 (whose fix removed the *implicit* narrowing — an
|
|
unannotated `x := 0` no longer adopts the fn return type — but the explicit
|
|
annotation path keeps wrapping).
|
|
|
|
## Reproduction
|
|
|
|
```sx
|
|
#import "modules/std.sx";
|
|
|
|
main :: () {
|
|
x : s8 = 300;
|
|
print("x: {}\n", x);
|
|
y : u8 = 256;
|
|
print("y: {}\n", y);
|
|
}
|
|
```
|
|
|
|
- **Observed** (current master): prints `x: 44` / `y: 0`, exit 0, no
|
|
diagnostic.
|
|
- **Expected**: compile error per literal, e.g.
|
|
`integer literal 300 does not fit in s8 (range -128..127)`, and the analog
|
|
for `256` / `u8 (range 0..255)`.
|
|
|
|
Repro co-located: `issues/0112-int-literal-out-of-range-silent-wrap.sx`
|
|
(unpinned until fixed).
|
|
|
|
## Root cause (suspected area)
|
|
|
|
`src/ir/lower/expr.zig` `.int_literal` arm (~1499): when `target_type` is an
|
|
integer type, it emits `constInt(lit.value, tt)` with no fits-check — the
|
|
value truncates at LLVM emission width. The annotated-decl path
|
|
(`lowerVarDecl` with `type_annotation`, `src/ir/lower/stmt.zig` ~255) sets
|
|
`target_type` to the annotation before lowering the initializer, so every
|
|
annotated narrow decl funnels through this arm. Assignments to narrow
|
|
lvalues (`b = 300` where `b: s8`) reach the same arm via `lowerAssignment`'s
|
|
LHS-derived target and likely need the same check.
|
|
|
|
## Investigation prompt (paste into a fresh session)
|
|
|
|
> Fix issue 0112: an int literal that does not fit its integer target type
|
|
> silently wraps. In the `.int_literal` arm of `lowerExpr`
|
|
> (`src/ir/lower/expr.zig` ~1499), before adopting an integer `target_type`,
|
|
> range-check `lit.value` against the target's signedness/width (the type
|
|
> table knows both; mirror the bounds logic used by
|
|
> `TypeResolver.integerLimitFor`). On overflow emit a diagnostic via
|
|
> `self.diagnostics.addFmt(.err, node.span, ...)` naming the literal, the
|
|
> type, and its range — do NOT silently fall back to s64 (REJECTED PATTERNS:
|
|
> no silent fallback defaults); still return a `constInt` of the target type
|
|
> so lowering continues to surface further errors. Audit sibling literal
|
|
> sinks that bypass this arm (comptime folds, `lowerStructConstant`, global
|
|
> initializers) for the same check.
|
|
>
|
|
> Verify: `issues/0112-int-literal-out-of-range-silent-wrap.sx` errors with
|
|
> two diagnostics (s8/300, u8/256); boundary values still compile
|
|
> (`x : s8 = -128` / `127`, `y : u8 = 0` / `255`, `m : u64` large literals).
|
|
> `zig build && zig build test && bash tests/run_examples.sh` — any example
|
|
> that relied on silent wrapping must be reviewed individually. Promote the
|
|
> repro per the resolution flow (likely `examples/11xx-diagnostics-...`).
|