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:
@@ -25,5 +25,5 @@ main :: () {
|
||||
// ** stdout **
|
||||
// f64
|
||||
// 3.200000
|
||||
// Vec4
|
||||
// Vector(4,f32)
|
||||
// () -> s32
|
||||
206
examples/189-type-all-interactions.sx
Normal file
206
examples/189-type-all-interactions.sx
Normal file
@@ -0,0 +1,206 @@
|
||||
// Type as a first-class value — comprehensive interaction smoke.
|
||||
//
|
||||
// Every way a user can plausibly touch a `Type` value, in one
|
||||
// place. Pre-fix: some sections fail (`<?>`, false, garbage, or
|
||||
// compile errors). Post-fix: every section's output matches the
|
||||
// header line.
|
||||
//
|
||||
// Sections labelled `--- N. <thing> ---` so the diff localises
|
||||
// which interaction regressed.
|
||||
|
||||
#import "modules/std.sx";
|
||||
|
||||
// ── Test fixtures ─────────────────────────────────────────────
|
||||
|
||||
Point :: struct { x: s32; y: s32; }
|
||||
|
||||
Color :: enum { red; green; blue; }
|
||||
|
||||
Wrap :: struct ($T: Type) { v: T; }
|
||||
|
||||
identity :: ($T: Type, val: T) -> T => val;
|
||||
|
||||
// Generic comptime fn that dispatches on type identity (compile-
|
||||
// time fold via type_eq → const_bool → inline-if folds the
|
||||
// branch away).
|
||||
describe :: ($T: Type) -> string {
|
||||
inline if type_eq(T, s64) { return "int64"; }
|
||||
inline if type_eq(T, string) { return "text"; }
|
||||
inline if type_eq(T, bool) { return "boolean"; }
|
||||
return "other";
|
||||
}
|
||||
|
||||
// Pack-fn collecting per-position types (step 4A.bare path).
|
||||
type_list :: (..$args) -> string {
|
||||
list := $args;
|
||||
s := "[";
|
||||
i : s64 = 0;
|
||||
while i < list.len {
|
||||
if i > 0 { s = concat(s, ", "); }
|
||||
s = concat(s, type_name(list[i]));
|
||||
i = i + 1;
|
||||
}
|
||||
return concat(s, "]");
|
||||
}
|
||||
|
||||
// Type stored in a struct field.
|
||||
TypeHolder :: struct { t: Type; }
|
||||
|
||||
main :: () -> s32 {
|
||||
// ── 1. Type literal equality ────────────────────────────
|
||||
print("=== 1. literal == ===\n");
|
||||
print("s64 == s64: {}\n", s64 == s64);
|
||||
print("s64 == string: {}\n", s64 == string);
|
||||
print("*u8 == *u8: {}\n", *u8 == *u8);
|
||||
print("?s64 == ?s64: {}\n", ?s64 == ?s64);
|
||||
print("?s64 == ?s32: {}\n", ?s64 == ?s32);
|
||||
|
||||
// ── 2. type_of(value) ───────────────────────────────────
|
||||
print("=== 2. type_of(value) == T ===\n");
|
||||
a : s64 = 42;
|
||||
b : f64 = 3.14;
|
||||
s : string = "hi";
|
||||
print("type_of(a) == s64: {}\n", type_of(a) == s64);
|
||||
print("type_of(b) == f64: {}\n", type_of(b) == f64);
|
||||
print("type_of(s) == string: {}\n", type_of(s) == string);
|
||||
print("type_of(a) == f64: {}\n", type_of(a) == f64);
|
||||
|
||||
// ── 3. Type variable storage + readback ─────────────────
|
||||
print("=== 3. Type variable storage ===\n");
|
||||
t : Type = f64;
|
||||
print("t == f64: {}\n", t == f64);
|
||||
print("t == string: {}\n", t == string);
|
||||
t = string;
|
||||
print("after reassign t == string: {}\n", t == string);
|
||||
t = bool;
|
||||
print("t == bool: {}\n", t == bool);
|
||||
|
||||
// ── 4. type_name on literals + variables ────────────────
|
||||
print("=== 4. type_name ===\n");
|
||||
print("type_name(s64): {}\n", type_name(s64));
|
||||
print("type_name(*u8): {}\n", type_name(*u8));
|
||||
print("type_name(Point): {}\n", type_name(Point));
|
||||
print("type_name(Color): {}\n", type_name(Color));
|
||||
t = f64;
|
||||
print("type_name(t): {}\n", type_name(t));
|
||||
|
||||
// ── 5. Print Type values directly ───────────────────────
|
||||
print("=== 5. print Type values ===\n");
|
||||
print("literal: {}\n", s64);
|
||||
t = string;
|
||||
print("var: {}\n", t);
|
||||
print("type_of(b): {}\n", type_of(b));
|
||||
|
||||
// ── 6. Generic dispatch via $T: Type ────────────────────
|
||||
print("=== 6. generic dispatch ===\n");
|
||||
print("describe(s64): {}\n", describe(s64));
|
||||
print("describe(string): {}\n", describe(string));
|
||||
print("describe(bool): {}\n", describe(bool));
|
||||
print("describe(f64): {}\n", describe(f64));
|
||||
|
||||
// ── 7. identity(T, val) ─────────────────────────────────
|
||||
print("=== 7. identity($T, val) ===\n");
|
||||
print("identity(s64, 7): {}\n", identity(s64, 7));
|
||||
print("identity(string, hi): {}\n", identity(string, "hi"));
|
||||
print("identity(bool, true): {}\n", identity(bool, true));
|
||||
|
||||
// ── 8. Comptime-generated struct (Wrap($T)) ─────────────
|
||||
print("=== 8. Wrap($T) ===\n");
|
||||
w_int := Wrap(s64).{ v = 42 };
|
||||
w_str := Wrap(string).{ v = "wrapped" };
|
||||
print("Wrap(s64).v: {}\n", w_int.v);
|
||||
print("Wrap(string).v: {}\n", w_str.v);
|
||||
|
||||
// ── 9. Reflection builtins on Types ─────────────────────
|
||||
print("=== 9. reflection on Type ===\n");
|
||||
print("size_of(s64): {}\n", size_of(s64));
|
||||
print("size_of(*u8): {}\n", size_of(*u8));
|
||||
print("align_of(f64): {}\n", align_of(f64));
|
||||
print("field_count(Point): {}\n", field_count(Point));
|
||||
print("type_eq(s64, s64): {}\n", type_eq(s64, s64));
|
||||
print("type_eq(s64, string): {}\n", type_eq(s64, string));
|
||||
|
||||
// ── 10. Type pack (..$args) walking ─────────────────────
|
||||
print("=== 10. ..$args walking ===\n");
|
||||
print("type_list(): {}\n", type_list());
|
||||
print("type_list(1): {}\n", type_list(42));
|
||||
print("type_list(1, \"x\"): {}\n", type_list(42, "x"));
|
||||
print("type_list(true, 3.14): {}\n", type_list(true, 3.14));
|
||||
|
||||
// ── 11. Type in struct field ────────────────────────────
|
||||
print("=== 11. Type in struct field ===\n");
|
||||
h := TypeHolder.{ t = s64 };
|
||||
print("h.t == s64: {}\n", h.t == s64);
|
||||
print("h.t == string: {}\n", h.t == string);
|
||||
print("type_name(h.t): {}\n", type_name(h.t));
|
||||
|
||||
// ── 12. Compound type literals ──────────────────────────
|
||||
print("=== 12. compound literals ===\n");
|
||||
print("type_name(*Point): {}\n", type_name(*Point));
|
||||
print("type_name([4]s32): {}\n", type_name([4]s32));
|
||||
print("type_name([]bool): {}\n", type_name([]bool));
|
||||
print("type_name(?f64): {}\n", type_name(?f64));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ** stdout **
|
||||
// === 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
|
||||
Reference in New Issue
Block a user