2.9 KiB
0118 — cast(<compound type>) expr dies with "unresolved 'unknown_expr'"
Symptom
cast(T) expr with a COMPOUND static type argument (*s64, []u8, ?s32,
[*]s64, [4]s64, …) 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 : *s64 = cast(*s64) p;
| ^^^^
Expected: the cast resolves the type argument statically and routes through
coerceExplicit (for cast(*s64) p where p : *s64, a no-op), exactly as it
does for bare names (cast(s32) 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 *s64 reproduces it.
Reproduction
#import "modules/std.sx";
main :: () {
x := 42;
p : *s64 = @x;
q : *s64 = cast(*s64) 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(*s64) folds
fine while cast(*s64) 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(?s32),cast([*]s64)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.