Surface rename of the signed integer family: s1..s64 become i1..i64
(u1..u64, usize, isize unchanged). 'string' keeps the s-prefix arm in
name classification; width parsing moves to the i-prefix arm next to
isize.
Internal TypeId tags follow the surface (.s8/.s16/.s32/.s64 ->
.i8/.i16/.i32/.i64), as do mono-key mangle fragments (ptr_i64,
tu_i64_bool) and all display/diagnostic formatting (i{d}).
Migrated in the same sweep: stdlib + examples + issue repros + FFI C
companions (shared symbol names like ffi_id_i64), expected
stdout/stderr/ir snapshots, specs.md, readme.md, CLAUDE.md/AGENTS.md,
implementation_plan.md, docs/, issue writeups. Vendored stb_image and
historical flow state left untouched.
zig build test: 426/426; examples suite: 595/595.
3.9 KiB
0118 — cast(<compound type>) expr dies with "unresolved 'unknown_expr'"
RESOLVED (2026-06-11, same session, Agra-authorized in-session fix). Two-part root cause: (1)
lowerCalllowers args BEFORE the cast handler, and compound type literals had NO expression-position lowering — they hitlowerExpr's catch-allunknown_exprerror. Fixed by giving the six compound type-expr shapes (*T,[*]T,[]T,?T,[N]T, fn types) a first-classconst_typelowering arm insrc/ir/lower/expr.zig, mirroring named types (t : Type = *i64;now works liket : Type = f64;). (2) The cast handler's private static-type gate only accepted bare names — replaced with the canonicalisStaticTypeArg(src/ir/lower/call.zig), so static compound casts route throughcoerceExplicitwhile the runtime-Type-variable form (cast(type) valin category arms) still falls through to runtime dispatch. Regression test: examples/0182-types-cast-compound-types.sx (all compound cast forms + the first-class Type value). Suite 579/579.
Symptom
cast(T) expr with a COMPOUND static type argument (*i64, []u8, ?i32,
[*]i64, [4]i64, …) fails to compile with a junk diagnostic pointing at the
type argument. Observed:
error: unresolved 'unknown_expr' (in probe.sx fn main)
--> probe.sx:5:21
|
5 | q : *i64 = cast(*i64) p;
| ^^^^
Expected: the cast resolves the type argument statically and routes through
coerceExplicit (for cast(*i64) p where p : *i64, a no-op), exactly as it
does for bare names (cast(i32) 3.14 works). The spec places no scalar-only
restriction on cast(Type) (specs.md "cast(Type) expr — prefix operator that
converts expr to Type").
Pre-existing on master (verified on a clean build of 679653f) — independent of
the in-flight const-pointer work; plain *i64 reproduces it.
Reproduction
#import "modules/std.sx";
main :: () {
x := 42;
p : *i64 = @x;
q : *i64 = cast(*i64) p; // error: unresolved 'unknown_expr'
print("{}\n", q.*); // expected: 42
}
Investigation prompt
The cast handler in src/ir/lower/call.zig (~line 391, the
Handle cast(TargetType, val) block) gates static resolution with a PRIVATE
is_static_type check that only accepts type_expr and identifier AST
shapes. Every compound type-expression shape (pointer_type_expr,
slice_type_expr, many_pointer_type_expr, optional_type_expr,
array_type_expr, function_type_expr) falls through to the "runtime cast"
builtin path, which cannot resolve it and surfaces the catch-all
"unresolved 'unknown_expr'".
The codebase already has the canonical gate: Lowering.isStaticTypeArg
(src/ir/lower/generic.zig:206), which type_name / type_eq use — it
accepts the full compound-shape set (this is why type_name(*i64) folds
fine while cast(*i64) dies). The fix likely: replace the private
is_static_type block with self.isStaticTypeArg(type_arg) (keeping the
scope-shadow semantics: an identifier bound to a runtime Type variable must
still route to the runtime-dispatch path used by case-arm cast(type) —
that path is load-bearing for any_to_string, see specs.md "runtime generic
dispatch"). Then resolveTypeArg already handles the compound shapes.
Verification:
- Run the repro above — expect
42, exit 0. - Sanity:
cast([]u8),cast(?i32),cast([*]i64)forms resolve. bash tests/run_examples.sh— thecase-arm runtime-dispatch examples (any_to_string formatting suite) must stay green, proving the runtime-Type-variable path still falls through to the builtin.- Pin the repro as a regression example per CLAUDE.md ("Resolving an open issue").
Context note: surfaced while probing PLAN-CONST-AGG step 5 (*const T), whose
const-discard diagnostic suggests "use an explicit cast (xx/cast)" — xx
works for pointer-shaped targets; cast currently does not.