mem: remove resolveType(null) → .s64 silent fallback
CLAUDE.md REJECTED PATTERNS forbids silent default returns where the
"reasonable-looking" value happens to match one common case (s64 = 8
bytes = pointer-sized on the host) and is silently wrong everywhere
else. `resolveType(null) → .s64` was exactly this shape: a top-level
`g_pi := 3.14;` was silently typed as `s64`, producing a wrong-typed
slot and the wrong runtime value.
`resolveType` now takes a non-optional `*const Node`. Twelve callers
were classified:
- Six were already guarded by `if (x.type_annotation != null)` blocks
— the null branch was unreachable. Cleaned up to optional-payload
syntax (`if (cd.type_annotation) |ta|`) so the always-non-null path
is obvious from the type.
- Two (`#objc_call` / `#jni_call` return types) pass `FfiIntrinsicCall.
return_type`, which is `*Node` (not optional) in the AST — the
silent fallback couldn't be reached there either.
- One (top-level `var_decl` at lower.zig:630) DID legitimately receive
null when the user omitted both annotation and initializer typing.
Now mirrors `lowerVarDecl`'s local-scope behavior: explicit
annotation → resolveType; no annotation → `inferExprType` from the
initializer; neither → diagnose with a real error message.
- One (`lowerComptimeGlobal`, fixed in commit 82e7b04 alongside
Phase 1.4) already infers from the comptime expression.
- Two (JNI super-call / JNI method return type) were already
hand-rolled with `if (rt) |t| resolveType(t) else .void`.
Regression at `examples/137-toplevel-var-type-inference.sx`: `g_count
:= 42;` / `g_pi := 3.14;` / `g_flag := true;` at module scope. Pre-fix
`g_pi` got silently typed as `s64` and printed `0` or garbage; now it
prints `3.140000`. 159/159 example tests + chess clean.
This commit is contained in:
21
examples/137-toplevel-var-type-inference.sx
Normal file
21
examples/137-toplevel-var-type-inference.sx
Normal file
@@ -0,0 +1,21 @@
|
||||
// Pre-fix: `resolveType(null)` silently returned `.s64`, so a top-level
|
||||
// var without a type annotation got typed as `s64` regardless of what
|
||||
// the initializer was. For `g_pi := 3.14;` this meant the float literal
|
||||
// was assigned to an s64 slot, producing a wrong value at runtime or
|
||||
// the wrong codegen shape.
|
||||
//
|
||||
// After the fix `lowerVarDecl` at the top level mirrors the local-scope
|
||||
// path: explicit annotation → resolveType; no annotation → infer from
|
||||
// the initializer's type. Mirrors how `:=` already worked for locals.
|
||||
#import "modules/std.sx";
|
||||
|
||||
g_count := 42; // inferred s64
|
||||
g_pi := 3.14; // inferred f64 — used to silently become s64
|
||||
g_flag := true; // inferred bool
|
||||
|
||||
main :: () -> s32 {
|
||||
print("count = {}\n", g_count);
|
||||
print("pi = {}\n", g_pi);
|
||||
print("flag = {}\n", g_flag);
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user