issues: file 0118 — cast(<compound type>) unresolved
This commit is contained in:
71
issues/0118-cast-compound-type-arg-unresolved.md
Normal file
71
issues/0118-cast-compound-type-arg-unresolved.md
Normal file
@@ -0,0 +1,71 @@
|
||||
# 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
|
||||
|
||||
```sx
|
||||
#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:
|
||||
1. Run the repro above — expect `42`, exit 0.
|
||||
2. Sanity: `cast([]u8)`, `cast(?s32)`, `cast([*]s64)` forms resolve.
|
||||
3. `bash tests/run_examples.sh` — the `case`-arm runtime-dispatch examples
|
||||
(any_to_string formatting suite) must stay green, proving the
|
||||
runtime-`Type`-variable path still falls through to the builtin.
|
||||
4. 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.
|
||||
Reference in New Issue
Block a user