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:
@@ -466,6 +466,7 @@ pub const TypeTable = struct {
|
||||
if (ty == .s64 or ty == .u64 or ty == .f64) return 8;
|
||||
if (ty == .usize or ty == .isize) return ptr_size;
|
||||
if (ty == .string) return 16; // {ptr, i64} — always 16 (i64 alignment pads on wasm32)
|
||||
if (ty == .any) return 16; // {i64 tag, i64 value} — Any boxed layout
|
||||
if (ty.isBuiltin()) return ptr_size; // default for unknown builtins
|
||||
const info = self.get(ty);
|
||||
return switch (info) {
|
||||
@@ -564,6 +565,7 @@ pub const TypeTable = struct {
|
||||
if (ty == .s64 or ty == .u64 or ty == .f64) return 8;
|
||||
if (ty == .usize or ty == .isize) return ptr_align;
|
||||
if (ty == .string) return 8; // i64 drives alignment
|
||||
if (ty == .any) return 8; // {i64, i64} aligns to 8
|
||||
if (ty.isBuiltin()) return ptr_align;
|
||||
const info = self.get(ty);
|
||||
return switch (info) {
|
||||
@@ -650,6 +652,91 @@ pub const TypeTable = struct {
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/// Like `typeName` but produces structural names for compound
|
||||
/// types (`*T`, `[]T`, `[N]T`, `?T`, `Vector(N,T)`, function and
|
||||
/// tuple types) instead of returning `"?"`. Compound names are
|
||||
/// freshly allocated via `alloc`; builtin and named user types
|
||||
/// return borrowed slices.
|
||||
pub fn formatTypeName(self: *const TypeTable, alloc: std.mem.Allocator, id: TypeId) []const u8 {
|
||||
if (id.isBuiltin()) return self.typeName(id);
|
||||
const info = self.get(id);
|
||||
return switch (info) {
|
||||
.@"struct" => |s| self.getString(s.name),
|
||||
.@"enum" => |e| self.getString(e.name),
|
||||
.@"union" => |u| self.getString(u.name),
|
||||
.tagged_union => |u| self.getString(u.name),
|
||||
.protocol => |p| self.getString(p.name),
|
||||
.pointer => |p| blk: {
|
||||
const inner = self.formatTypeName(alloc, p.pointee);
|
||||
break :blk std.fmt.allocPrint(alloc, "*{s}", .{inner}) catch "*?";
|
||||
},
|
||||
.many_pointer => |p| blk: {
|
||||
const inner = self.formatTypeName(alloc, p.element);
|
||||
break :blk std.fmt.allocPrint(alloc, "[*]{s}", .{inner}) catch "[*]?";
|
||||
},
|
||||
.slice => |s| blk: {
|
||||
const inner = self.formatTypeName(alloc, s.element);
|
||||
break :blk std.fmt.allocPrint(alloc, "[]{s}", .{inner}) catch "[]?";
|
||||
},
|
||||
.array => |a| blk: {
|
||||
const inner = self.formatTypeName(alloc, a.element);
|
||||
break :blk std.fmt.allocPrint(alloc, "[{d}]{s}", .{ a.length, inner }) catch "[N]?";
|
||||
},
|
||||
.vector => |v| blk: {
|
||||
const inner = self.formatTypeName(alloc, v.element);
|
||||
break :blk std.fmt.allocPrint(alloc, "Vector({d},{s})", .{ v.length, inner }) catch "Vector(?)";
|
||||
},
|
||||
.optional => |o| blk: {
|
||||
const inner = self.formatTypeName(alloc, o.child);
|
||||
break :blk std.fmt.allocPrint(alloc, "?{s}", .{inner}) catch "?_";
|
||||
},
|
||||
.function => |f| blk: {
|
||||
var buf = std.ArrayList(u8).empty;
|
||||
defer buf.deinit(alloc);
|
||||
buf.append(alloc, '(') catch break :blk "(?)";
|
||||
for (f.params, 0..) |p, i| {
|
||||
if (i > 0) buf.appendSlice(alloc, ", ") catch break :blk "(?)";
|
||||
buf.appendSlice(alloc, self.formatTypeName(alloc, p)) catch break :blk "(?)";
|
||||
}
|
||||
buf.append(alloc, ')') catch break :blk "(?)";
|
||||
if (f.ret != .void) {
|
||||
buf.appendSlice(alloc, " -> ") catch break :blk "(?)";
|
||||
buf.appendSlice(alloc, self.formatTypeName(alloc, f.ret)) catch break :blk "(?)";
|
||||
}
|
||||
break :blk buf.toOwnedSlice(alloc) catch "(?)";
|
||||
},
|
||||
.closure => |co| blk: {
|
||||
var buf = std.ArrayList(u8).empty;
|
||||
defer buf.deinit(alloc);
|
||||
buf.appendSlice(alloc, "Closure(") catch break :blk "Closure(?)";
|
||||
for (co.params, 0..) |p, i| {
|
||||
if (i > 0) buf.appendSlice(alloc, ", ") catch break :blk "Closure(?)";
|
||||
buf.appendSlice(alloc, self.formatTypeName(alloc, p)) catch break :blk "Closure(?)";
|
||||
}
|
||||
buf.append(alloc, ')') catch break :blk "Closure(?)";
|
||||
if (co.ret != .void) {
|
||||
buf.appendSlice(alloc, " -> ") catch break :blk "Closure(?)";
|
||||
buf.appendSlice(alloc, self.formatTypeName(alloc, co.ret)) catch break :blk "Closure(?)";
|
||||
}
|
||||
break :blk buf.toOwnedSlice(alloc) catch "Closure(?)";
|
||||
},
|
||||
.tuple => |tu| blk: {
|
||||
var buf = std.ArrayList(u8).empty;
|
||||
defer buf.deinit(alloc);
|
||||
buf.append(alloc, '(') catch break :blk "(?)";
|
||||
for (tu.fields, 0..) |f, i| {
|
||||
if (i > 0) buf.appendSlice(alloc, ", ") catch break :blk "(?)";
|
||||
buf.appendSlice(alloc, self.formatTypeName(alloc, f)) catch break :blk "(?)";
|
||||
}
|
||||
buf.append(alloc, ')') catch break :blk "(?)";
|
||||
break :blk buf.toOwnedSlice(alloc) catch "(?)";
|
||||
},
|
||||
.signed => |w| std.fmt.allocPrint(alloc, "s{d}", .{w}) catch "s?",
|
||||
.unsigned => |w| std.fmt.allocPrint(alloc, "u{d}", .{w}) catch "u?",
|
||||
else => self.typeName(id),
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
// ── Intern map support ──────────────────────────────────────────────────
|
||||
|
||||
Reference in New Issue
Block a user