ir: Type as first-class value (Any-shaped {tag, value})

Previously, `t : Type = f64` stored a boxed string carrying the literal
name "f64"; comparisons and `type_of`/`type_name` round-trips lost the
underlying TypeId. This switches `Type` to a runtime-representable Any
pair: `{ tag = .any.index() (meta-marker), value = TypeId.index() }`.

Mechanism:
- `const_type` emits a 16-byte Any aggregate via insertvalue.
- `TypeId.any` advertises 16 bytes / 8-byte alignment so structs that
  embed `t: Type` size correctly under verifySizes.
- `lowerBinaryOp` folds `==`/`!=` between static type-refs to a
  `const_bool`, and decomposes runtime Any-vs-Any compares via
  `unbox_any` so LLVM doesn't see icmp on aggregates.
- `lowerMatch`'s `is_type_match` path unboxes Any-typed subjects to
  the i64 type tag before the switch, so `case type:` etc. fire.
- `lowerRuntimeDispatchCall` (used by `case T: ... cast(t) val`) does
  the same unbox for the type-tag arg.
- `type_of(val: Any)` rebuilds an Any with `{.any, tag_of(val)}` so
  the result is itself a `Type` value, not a bare i64.
- `buildPackSliceValue` stops re-boxing const_type — the value is
  already canonical Any.
- `__sx_type_names` now indexes by TypeId across the whole table
  using the new `types.formatTypeName` (structural names for `*T`,
  `[]T`, `[N]T`, `?T`, `Vector(N,T)`, function/closure/tuple) so
  runtime `type_name(t)` works for compound types.
- `interp.zig`'s comptime `type_name` accepts either the bare
  `.type_tag` Value or the Any-boxed aggregate it now sees.
- `scanDecls` registers `Vec4 :: Vector(4, f32)` style aliases in
  `type_alias_map` (before the `fn_ast_map` check; `Vector` IS a
  `#builtin` fn). Lets `Vec4` in expression position lower as
  `const_type(<vector tid>)`.
- `isStaticTypeArg` becomes scope-aware: a name shadowed by a runtime
  local is not static. `isStaticTypeRef` is the symmetric helper for
  the eq fold.
- `inferExprType` returns `.any` for bare type names (identifier and
  type_expr) so pack arg types are correct.

Side effect: `print("{}", Vec4)` now prints the structural name
`Vector(4,f32)` rather than the alias literal `Vec4` — 12-meta's
expectation updated. Aliases stay pointer-equal to their target
(`Vec4 == Vector(4, f32)` is true).

Tests:
- examples/189-type-all-interactions.sx: 12-section comprehensive
  coverage — literal `==`, `type_of(value) == T`, `Type` var storage,
  `type_name` (static + runtime), printing Type values, generic
  dispatch via `$T: Type`, `identity($T, val)`, `Wrap($T)`, reflection
  builtins (`size_of`, `align_of`, `field_count`, `type_eq`),
  `..$args` pack walking, `Type` in struct field, compound type
  literals (`*Point`, `[4]s32`, `[]bool`, `?f64`).
- examples/12-meta.sx: expected output updated to reflect structural
  name for the Vec4 alias path.
- ffi-objc-call-06-sret-return.ir: regenerated to absorb the new
  type-name strings now emitted globally.

223/223 examples pass.
This commit is contained in:
agra
2026-05-28 14:02:10 +03:00
parent 9b7ffd70b2
commit 3ac13b7442
13 changed files with 1305 additions and 570 deletions

View File

@@ -1,4 +1,4 @@
f64
3.200000
Vec4
Vector(4,f32)
() -> s32

View File

@@ -0,0 +1 @@
0

View File

@@ -0,0 +1,59 @@
=== 1. literal == ===
s64 == s64: true
s64 == string: false
*u8 == *u8: true
?s64 == ?s64: true
?s64 == ?s32: false
=== 2. type_of(value) == T ===
type_of(a) == s64: true
type_of(b) == f64: true
type_of(s) == string: true
type_of(a) == f64: false
=== 3. Type variable storage ===
t == f64: true
t == string: false
after reassign t == string: true
t == bool: true
=== 4. type_name ===
type_name(s64): s64
type_name(*u8): *u8
type_name(Point): Point
type_name(Color): Color
type_name(t): f64
=== 5. print Type values ===
literal: s64
var: string
type_of(b): f64
=== 6. generic dispatch ===
describe(s64): int64
describe(string): text
describe(bool): boolean
describe(f64): other
=== 7. identity($T, val) ===
identity(s64, 7): 7
identity(string, hi): hi
identity(bool, true): true
=== 8. Wrap($T) ===
Wrap(s64).v: 42
Wrap(string).v: wrapped
=== 9. reflection on Type ===
size_of(s64): 8
size_of(*u8): 8
align_of(f64): 8
field_count(Point): 2
type_eq(s64, s64): true
type_eq(s64, string): false
=== 10. ..$args walking ===
type_list(): []
type_list(1): [s64]
type_list(1, "x"): [s64, string]
type_list(true, 3.14): [bool, f64]
=== 11. Type in struct field ===
h.t == s64: true
h.t == string: false
type_name(h.t): s64
=== 12. compound literals ===
type_name(*Point): *Point
type_name([4]s32): [4]s32
type_name([]bool): []bool
type_name(?f64): ?f64

File diff suppressed because it is too large Load Diff