comptime VM: dedicated Type builtin TypeId (8B), distinct from .any — foundation (dead)
Add TypeId.type_value (slot 19) + matching TypeInfo.type_value variant: an 8-byte type handle, distinct from the 16-byte boxed .any. All types.zig layout handlers wired (size/align 8, display "Type", hash/eql); toLLVMTypeInfo -> i64. Reserve builtin headroom: first_user 19 -> 100 (slots 20-99 padded with the unresolved tripwire) so future builtins don't renumber user TypeIds / churn sx ir snapshots. 22 IR snapshots regenerated (pure renumber to 100-base). type_resolver still returns .any for "Type" — nothing produces .type_value yet, so no behavior change. 697/0 both gates.
This commit is contained in:
@@ -97,6 +97,10 @@ pub const TypeLowering = struct {
|
||||
return c.LLVMVectorType(elem, vec.length);
|
||||
},
|
||||
.any => self.e.getAnyStructType(),
|
||||
// A comptime `Type` value is an 8-byte type handle (a `TypeId` in a
|
||||
// word), distinct from the 16-byte boxed Any. It is comptime-only, but
|
||||
// the type still lowers (dead comptime-body code / a global slot) as i64.
|
||||
.type_value => self.e.cached_i64,
|
||||
.noreturn => self.e.cached_void,
|
||||
.@"struct" => |s| {
|
||||
// Build LLVM struct type from fields
|
||||
|
||||
@@ -34,9 +34,23 @@ pub const TypeId = enum(u32) {
|
||||
usize = 16,
|
||||
void = 17,
|
||||
cstring = 18, // thin null-terminated char* (see TypeInfo.cstring)
|
||||
_, // user-defined types start at 19
|
||||
/// A comptime `Type` VALUE — an 8-byte handle (a `TypeId` stored in a word),
|
||||
/// DISTINCT from `.any`. A `Type` value is the reified type itself
|
||||
/// (`reflect`/`const_type`/the comptime compiler-API), not a boxed Any. It used
|
||||
/// to share `.any`'s slot, but `.any` is a 16-byte `{tag,value}` box (variadic
|
||||
/// any), so a `Type` stored in an aggregate was sized 16B while the value is 8B
|
||||
/// — which blocked the flat-memory comptime VM. Its own slot fixes the size and
|
||||
/// keeps every downstream `== .any`/`switch` check from conflating the two.
|
||||
type_value = 19,
|
||||
_, // user-defined types start at `first_user` (slots 20–99 reserved for future builtins)
|
||||
|
||||
pub const first_user: u32 = 19;
|
||||
/// User-defined types start here. Builtins occupy 0–18 (plus `type_value` at
|
||||
/// 19); slots 20–99 are RESERVED headroom so adding a new builtin doesn't
|
||||
/// renumber every user type (which would churn every `sx ir` snapshot in the
|
||||
/// corpus). The `TypeTable` pads its `infos` array out to this index with the
|
||||
/// `unresolved` tripwire so an accidental reference to a reserved slot panics
|
||||
/// rather than silently aliasing a real type.
|
||||
pub const first_user: u32 = 100;
|
||||
|
||||
pub fn index(self: TypeId) u32 {
|
||||
return @intFromEnum(self);
|
||||
@@ -86,6 +100,9 @@ pub const TypeInfo = union(enum) {
|
||||
noreturn,
|
||||
usize,
|
||||
isize,
|
||||
/// A comptime `Type` VALUE (see `TypeId.type_value`): an 8-byte type handle,
|
||||
/// distinct from the 16-byte boxed `any`.
|
||||
type_value,
|
||||
/// Resolution-failure sentinel (see `TypeId.unresolved`).
|
||||
unresolved,
|
||||
|
||||
@@ -385,10 +402,20 @@ pub const TypeTable = struct {
|
||||
.usize, // 16: usize (pointer-sized unsigned)
|
||||
.void, // 17
|
||||
.cstring, // 18: thin null-terminated char*
|
||||
.type_value, // 19: comptime `Type` value (8-byte handle, distinct from any)
|
||||
};
|
||||
for (&builtins) |info| {
|
||||
table.infos.append(alloc, info) catch unreachable;
|
||||
}
|
||||
// Pad the reserved builtin headroom (slots after the real builtins, up to
|
||||
// `first_user`) with the `unresolved` tripwire: these slots are never a
|
||||
// legitimate type, so any reference panics in `sizeOf`/`get` rather than
|
||||
// silently aliasing a user type. Reserving the range keeps user TypeIds
|
||||
// stable as new builtins are added (no snapshot churn).
|
||||
std.debug.assert(table.infos.items.len <= TypeId.first_user);
|
||||
while (table.infos.items.len < TypeId.first_user) {
|
||||
table.infos.append(alloc, .unresolved) catch unreachable;
|
||||
}
|
||||
|
||||
return table;
|
||||
}
|
||||
@@ -721,6 +748,7 @@ pub const TypeTable = struct {
|
||||
.array => |arr| arr.length * self.sizeOf(arr.element),
|
||||
.vector => |vec| vec.length * self.sizeOf(vec.element),
|
||||
.any => 16, // {type_tag, data_ptr}
|
||||
.type_value => 8, // an 8-byte type handle (a `TypeId` in a word), NOT the 16-byte any box
|
||||
.@"struct" => |s| {
|
||||
var total: u32 = 0;
|
||||
for (s.fields) |f| total += @max(self.sizeOf(f.ty), 8);
|
||||
@@ -803,6 +831,7 @@ pub const TypeTable = struct {
|
||||
if (ty == .cstring) 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 == .type_value) return 8; // 8-byte type handle (a `TypeId` in a word)
|
||||
if (ty.isBuiltin()) return ptr_size; // default for unknown builtins
|
||||
const info = self.get(ty);
|
||||
return switch (info) {
|
||||
@@ -904,6 +933,7 @@ pub const TypeTable = struct {
|
||||
if (ty == .string) return 8; // i64 drives alignment
|
||||
if (ty == .cstring) return ptr_align;
|
||||
if (ty == .any) return 8; // {i64, i64} aligns to 8
|
||||
if (ty == .type_value) return 8; // 8-byte type handle
|
||||
if (ty.isBuiltin()) return ptr_align;
|
||||
const info = self.get(ty);
|
||||
return switch (info) {
|
||||
@@ -975,6 +1005,7 @@ pub const TypeTable = struct {
|
||||
.string => "string",
|
||||
.cstring => "cstring",
|
||||
.any => "Any",
|
||||
.type_value => "Type",
|
||||
.noreturn => "noreturn",
|
||||
.isize => "isize",
|
||||
.usize => "usize",
|
||||
@@ -1120,7 +1151,7 @@ fn hashTypeInfo(h: *std.hash.Wyhash, info: TypeInfo) void {
|
||||
switch (info) {
|
||||
.signed => |w| h.update(&.{w}),
|
||||
.unsigned => |w| h.update(&.{w}),
|
||||
.f32, .f64, .void, .bool, .string, .cstring, .any, .noreturn, .usize, .isize, .unresolved => {},
|
||||
.f32, .f64, .void, .bool, .string, .cstring, .any, .type_value, .noreturn, .usize, .isize, .unresolved => {},
|
||||
.pointer => |p| h.update(std.mem.asBytes(&p.pointee)),
|
||||
.many_pointer => |p| h.update(std.mem.asBytes(&p.element)),
|
||||
.slice => |s| h.update(std.mem.asBytes(&s.element)),
|
||||
@@ -1191,7 +1222,7 @@ fn typeInfoEql(a: TypeInfo, b: TypeInfo) bool {
|
||||
return switch (a) {
|
||||
.signed => |w| w == b.signed,
|
||||
.unsigned => |w| w == b.unsigned,
|
||||
.f32, .f64, .void, .bool, .string, .cstring, .any, .noreturn, .usize, .isize, .unresolved => true,
|
||||
.f32, .f64, .void, .bool, .string, .cstring, .any, .type_value, .noreturn, .usize, .isize, .unresolved => true,
|
||||
.pointer => |p| p.pointee == b.pointer.pointee,
|
||||
.many_pointer => |p| p.element == b.many_pointer.element,
|
||||
.slice => |s| s.element == b.slice.element,
|
||||
|
||||
Reference in New Issue
Block a user