fix: resolve module-alias-qualified type in reflection arg slot (issue 0147)

size_of(sel.Selection) and the other reflection builtins rejected a
module-alias-qualified type: in argument position it parses as a .field_access
expression (not the dotted .type_expr a declaration produces), and neither
isStaticTypeArg nor resolveTypeArg had a .field_access arm. Add both: a pure
namespace-decl scan in isStaticTypeArg, and resolution via namespaceAliasTarget
+ resolveNominalLeaf in the target module context in resolveTypeArg (mirroring
the value-position lowerFieldAccess path). No fabricated-stub fallback.

Regression: examples/0192-types-size-of-qualified-alias.sx
This commit is contained in:
agra
2026-06-21 09:33:46 +03:00
parent c21b683b08
commit 21d91e6718
7 changed files with 152 additions and 0 deletions

View File

@@ -0,0 +1,68 @@
# 0147 — `size_of(alias.Type)` on a module-aliased type fails: "expects a type, got 'unresolved'"
> **RESOLVED.** In a reflection-builtin argument slot, a module-alias-qualified
> type (`sel.Selection`) parses as a `.field_access` expression (not the dotted
> `.type_expr` a declaration annotation produces), and neither `isStaticTypeArg`
> nor `resolveTypeArg` (src/ir/lower/generic.zig) had a `.field_access` arm — so
> the reflection guard rejected it as a non-type. Both now handle the qualified
> form: `isStaticTypeArg` recognizes `alias.Type` when `alias` is a namespace
> alias whose target authors a type named `Type` (pure decl scan), and
> `resolveTypeArg` resolves it via `namespaceAliasTarget` + `resolveNominalLeaf`
> in the target module's context (the same mechanism `lowerFieldAccess` uses for
> `alias.Type` in value position). Regression test:
> `examples/0192-types-size-of-qualified-alias.sx`.
## Summary
`size_of(T)` does not resolve a type referenced through a module ALIAS
(`alias :: #import "..."`), even though that same alias resolves fine everywhere
else (declarations, casts, struct-literal construction). The compiler reports the
qualified type as `unresolved` only inside `size_of`.
## Repro
```sx
sel :: #import "doc/selection.sx"; // selection.sx exports `Selection`
box :: () -> *sel.Selection {
// both of these fail:
p : *sel.Selection = xx context.allocator.alloc_bytes(size_of(sel.Selection));
memset(xx p, 0, size_of(sel.Selection));
p
}
```
Error:
```
error: size_of expects a type, got 'unresolved'
|
| ... alloc_bytes(size_of(sel.Selection));
| ^^^^^^^^^^^^^
```
Note the SAME `sel.Selection` resolves correctly in the variable declaration
`p : *sel.Selection` and in calls like `sel.selection_create(...)` — only the
`size_of(...)` argument position treats the qualified name as unresolved.
## Expected
`size_of(sel.Selection)` resolves the aliased type and yields its size, exactly
as `size_of(Selection)` does for an unqualified/flat-imported type.
## Workaround (clean)
Introduce an unqualified local type alias and feed THAT to `size_of`:
```sx
sel :: #import "doc/selection.sx";
Selection :: sel.Selection; // unqualified alias
box :: () -> *Selection {
p : *Selection = xx context.allocator.alloc_bytes(size_of(Selection));
memset(xx p, 0, size_of(Selection));
p
}
```
`size_of(Selection)` (the unqualified alias) resolves fine. Used in
photo `tests/toolbar.sx`'s `box_sel` (the selection model is imported qualified
as `sel` there to avoid a `Point` collision with `modules/ui/types.sx`).
## Impact
Minor. Only bites when a type must be reached through a module alias AND its size
is needed (heap-boxing a zeroed value of that type). The unqualified-alias
workaround is a one-liner and reads clearly.