comptime VM: flip Type to .type_value; migrate the .any refs that mean a Type value
type_resolver "Type" -> .type_value; const_type result + emitConstType now a bare 8-byte i64 handle (not a 16-byte Any box). Migrated every .any ref meaning "a Type value", leaving real boxed-Any refs: - "Any holds a Type" meta-marker tag .any -> .type_value at all 4 consumers (reflectArgTypeId, reflectTypeId, the comptime type_tag-as-struct path, resolveTypeCategoryTags "type"). - reflection-builtin return types (type_of/declare/define) -> .type_value; runtime type_of(any) reads the tag as a .type_value (no re-box). - expr_typer: a bare type-name expr is .type_value (backtick is_raw exempt). - reflectionArgIsType accepts .type_value OR .any (a reflection arg can be a bare Type or a boxed Any). - comptime switch_br accepts a .type_tag discriminant (type-category match). - a bare function name in a Type slot -> const_type(its function type), not a func-ref (fixes a JIT crash); old string-box kept only for genuine Any params. - field-not-found diagnostic + formatTypeName render .type_value as "Type". Fixed 3 unit tests asserting the old .any behavior. 697/0 both gates (gate ON bails cleanly to legacy since the VM doesn't model Type values yet) + 494 unit tests. 24 snapshots regenerated (22 .ir const_type shape; 2 .stderr Any->Type).
This commit is contained in:
@@ -130,23 +130,15 @@ pub const Ops = struct {
|
||||
}
|
||||
|
||||
pub fn emitConstType(self: Ops, tid: TypeId) void {
|
||||
// Type values are Any-shaped pairs:
|
||||
// { tag = .any.index() (the meta-marker),
|
||||
// value = tid.index() }
|
||||
// Lets storage in Any slots, struct fields,
|
||||
// `Type`-typed vars, and slice elements all round-
|
||||
// trip through the standard Any infrastructure.
|
||||
// `case type:` in `any_to_string` matches on
|
||||
// tag == `.any.index()`. Runtime `type_name(t)`
|
||||
// extracts the value field and indexes into the
|
||||
// type-name lookup table.
|
||||
const any_ty = self.e.getAnyStructType();
|
||||
const tag = c.LLVMConstInt(self.e.cached_i64, TypeId.any.index(), 0);
|
||||
// A Type value is an 8-byte handle: a bare i64 carrying `tid.index()`
|
||||
// (the `.type_value` builtin TypeId), distinct from the 16-byte boxed
|
||||
// `.any`. Flowing a Type into an `Any` slot boxes it via the standard
|
||||
// box-any coercion (`{ tag = .any.index(), value = tid }`); `case type:`
|
||||
// in `any_to_string` then matches tag == `.any.index()`, and runtime
|
||||
// `type_name(t)` reads the TypeId through `reflectArgTypeId` (`.bare`
|
||||
// when the arg is `.type_value`, `.boxed` when it is an Any).
|
||||
const val = c.LLVMConstInt(self.e.cached_i64, tid.index(), 0);
|
||||
var result = c.LLVMGetUndef(any_ty);
|
||||
result = c.LLVMBuildInsertValue(self.e.builder, result, tag, 0, "ct.tag");
|
||||
result = c.LLVMBuildInsertValue(self.e.builder, result, val, 1, "ct.val");
|
||||
self.e.mapRef(result);
|
||||
self.e.mapRef(val);
|
||||
}
|
||||
|
||||
// ── Arithmetic ─────────────────────────────────────────
|
||||
@@ -1341,11 +1333,11 @@ pub const Ops = struct {
|
||||
/// Resolve the `TypeId` (as a runtime `i64`) that a dynamic
|
||||
/// `type_name` / `type_is_unsigned` must operate on. A reflection
|
||||
/// builtin reads an `Any`'s runtime TYPE-TAG, never its raw payload:
|
||||
/// - `.bare`: a `Type` value already lowered to a bare i64 `TypeId`
|
||||
/// index (an unboxed direct call site) → the value itself.
|
||||
/// - `.bare`: a `Type` value (a `.type_value` arg) — a bare i64 `TypeId`
|
||||
/// index (e.g. `type_of(x)` directly) → the value itself.
|
||||
/// - `.boxed`: an `Any` aggregate `{ tag, value }`. When the tag is
|
||||
/// `.any`, the box carries a *Type value* (the `{ .any, tid }` shape
|
||||
/// `const_type` / `type_of` produce) → the TypeId is the payload.
|
||||
/// `.type_value`, the box carries a *Type value* (a `Type` boxed into an
|
||||
/// `Any`, `{ .type_value, tid }`) → the TypeId is the payload.
|
||||
/// Otherwise the box carries a *runtime value* whose type IS the tag
|
||||
/// → use the tag as the TypeId. This is what makes `type_name(av)`
|
||||
/// for `av : Any = 6` report `i64` (the held value's type), while
|
||||
@@ -1360,8 +1352,8 @@ pub const Ops = struct {
|
||||
.boxed => blk: {
|
||||
const tag = c.LLVMBuildExtractValue(self.e.builder, arg_val, 0, "refl.tag");
|
||||
const payload = c.LLVMBuildExtractValue(self.e.builder, arg_val, 1, "refl.val");
|
||||
const any_tag = c.LLVMConstInt(self.e.cached_i64, @intCast(TypeId.any.index()), 0);
|
||||
const holds_type = c.LLVMBuildICmp(self.e.builder, c.LLVMIntEQ, tag, any_tag, "refl.istype");
|
||||
const type_tag = c.LLVMConstInt(self.e.cached_i64, @intCast(TypeId.type_value.index()), 0);
|
||||
const holds_type = c.LLVMBuildICmp(self.e.builder, c.LLVMIntEQ, tag, type_tag, "refl.istype");
|
||||
break :blk c.LLVMBuildSelect(self.e.builder, holds_type, payload, tag, "refl.tid");
|
||||
},
|
||||
};
|
||||
|
||||
@@ -86,7 +86,7 @@ test "calls: builtin and reflection result types, unknown fallthrough" {
|
||||
.{ .name = "error_tag_name", .want = .string },
|
||||
.{ .name = "is_comptime", .want = .bool },
|
||||
.{ .name = "is_flags", .want = .bool },
|
||||
.{ .name = "type_of", .want = .any },
|
||||
.{ .name = "type_of", .want = .type_value },
|
||||
.{ .name = "field_value", .want = .any },
|
||||
.{ .name = "__interp_print_frames", .want = .void },
|
||||
// A math builtin with a non-`f32` argument widens to `f64` (the int
|
||||
|
||||
@@ -154,7 +154,7 @@ pub const CallResolver = struct {
|
||||
return refl(bare_name, self.l.module.types.findByName(self.l.module.types.internString("TraceFrame")) orelse .unresolved);
|
||||
if (std.mem.eql(u8, bare_name, "is_flags")) return refl(bare_name, .bool);
|
||||
if (std.mem.eql(u8, bare_name, "type_is_unsigned")) return refl(bare_name, .bool);
|
||||
if (std.mem.eql(u8, bare_name, "type_of")) return refl(bare_name, .any);
|
||||
if (std.mem.eql(u8, bare_name, "type_of")) return refl(bare_name, .type_value);
|
||||
if (std.mem.eql(u8, bare_name, "field_value")) return refl(bare_name, .any);
|
||||
// Plain bare same-name flat collision (R5 §C): route through the ONE
|
||||
// author producer `selectedFreeAuthor` so `plan` types the call as the
|
||||
|
||||
@@ -306,8 +306,11 @@ pub const ExprTyper = struct {
|
||||
}
|
||||
// A bare type name (alias like `Vec4`, struct name, or
|
||||
// builtin primitive) referenced in expression position
|
||||
// is a Type value — IR type `.any`.
|
||||
if (self.l.isKnownTypeName(id.name)) return .any;
|
||||
// is a Type value — IR type `.type_value` (8-byte handle). A
|
||||
// BACKTICK raw identifier (`` `string ``) is explicitly a value
|
||||
// binding, never the reserved type — so it never resolves here as a
|
||||
// Type (it was found in scope above, or is genuinely unresolved).
|
||||
if (!id.is_raw and self.l.isKnownTypeName(id.name)) return .type_value;
|
||||
return .unresolved;
|
||||
},
|
||||
.type_expr => |te| {
|
||||
@@ -318,8 +321,8 @@ pub const ExprTyper = struct {
|
||||
}
|
||||
}
|
||||
// A bare type name in expression position (e.g. `i64`,
|
||||
// `Point`, `*u8`) is a Type value — IR type `.any`.
|
||||
if (self.l.isKnownTypeName(te.name)) return .any;
|
||||
// `Point`, `*u8`) is a Type value — IR type `.type_value`.
|
||||
if (self.l.isKnownTypeName(te.name)) return .type_value;
|
||||
return .unresolved;
|
||||
},
|
||||
.enum_literal => {
|
||||
|
||||
@@ -812,7 +812,10 @@ test "comptime: type_eq builtin on type_tag values" {
|
||||
// `av : Any = 6` (`{ tag = i64, value = 6 }`) must resolve to `i64`, NOT
|
||||
// `types[6]` (`u8`).
|
||||
test "reflect: reflectTypeId branches on the Any tag" {
|
||||
const any_idx: i64 = @intCast(TypeId.any.index());
|
||||
// The "Any holds a Type" meta-marker tag is `.type_value` (an Any boxing a
|
||||
// Type value carries `{ tag = .type_value, value = tid }`), distinct from a
|
||||
// boxed runtime value whose tag is the held value's own type.
|
||||
const type_marker: i64 = @intCast(TypeId.type_value.index());
|
||||
|
||||
// Native first-class Type value → the held TypeId directly.
|
||||
try std.testing.expectEqual(@as(?TypeId, .u64), (Value{ .type_tag = .u64 }).reflectTypeId());
|
||||
@@ -827,13 +830,13 @@ test "reflect: reflectTypeId branches on the Any tag" {
|
||||
try std.testing.expectEqual(@as(?TypeId, .u32), (Value{ .aggregate = &held_u32 }).reflectTypeId());
|
||||
|
||||
// Any holding a TYPE value (the `type_of(x)` / `const_type` shape):
|
||||
// `{ tag = .any, value = u64 }` → u64 (the payload). Payload as a plain
|
||||
// `{ tag = .type_value, value = u64 }` → u64 (the payload). Payload as a plain
|
||||
// int (the runtime box shape) ...
|
||||
var held_type_int = [_]Value{ .{ .int = any_idx }, .{ .int = @intCast(TypeId.u64.index()) } };
|
||||
var held_type_int = [_]Value{ .{ .int = type_marker }, .{ .int = @intCast(TypeId.u64.index()) } };
|
||||
try std.testing.expectEqual(@as(?TypeId, .u64), (Value{ .aggregate = &held_type_int }).reflectTypeId());
|
||||
|
||||
// ... and payload as a `.type_tag` (the comptime box shape) → same result.
|
||||
var held_type_tag = [_]Value{ .{ .int = any_idx }, .{ .type_tag = .u64 } };
|
||||
var held_type_tag = [_]Value{ .{ .int = type_marker }, .{ .type_tag = .u64 } };
|
||||
try std.testing.expectEqual(@as(?TypeId, .u64), (Value{ .aggregate = &held_type_tag }).reflectTypeId());
|
||||
|
||||
// Neither shape → null (the caller bails loudly, never guesses a TypeId).
|
||||
|
||||
@@ -107,7 +107,7 @@ pub const Value = union(enum) {
|
||||
const fields = self.aggregate;
|
||||
if (fields.len >= 2) {
|
||||
const tag = fields[0].asInt() orelse return null;
|
||||
if (tag == @as(i64, @intCast(TypeId.any.index()))) {
|
||||
if (tag == @as(i64, @intCast(TypeId.type_value.index()))) {
|
||||
if (fields[1].asTypeId()) |t| return t;
|
||||
if (fields[1].asInt()) |iv| return TypeId.fromIndex(@intCast(iv));
|
||||
return null;
|
||||
@@ -927,7 +927,16 @@ pub const Interpreter = struct {
|
||||
switch (base) {
|
||||
.aggregate => |fields| {
|
||||
if (fa.field_index >= fields.len) return error.OutOfBounds;
|
||||
return .{ .value = fields[fa.field_index] };
|
||||
const field_val = fields[fa.field_index];
|
||||
// `type_of(an_any)` lowers to `struct_get(any, 0, .type_value)`:
|
||||
// the Any's field 0 is the held value's type id (a plain
|
||||
// `.int`), but the SSA result type is `.type_value`, so yield a
|
||||
// first-class `.type_tag` Value (a `.type_value`-typed value is
|
||||
// always a `.type_tag` in the interp — mirrors `const_type`).
|
||||
if (instruction.ty == .type_value) {
|
||||
if (field_val.asInt()) |iv| return .{ .value = .{ .type_tag = TypeId.fromIndex(@intCast(iv)) } };
|
||||
}
|
||||
return .{ .value = field_val };
|
||||
},
|
||||
.string => |s| {
|
||||
// String as fat pointer: field 0 = ptr (string), field 1 = len
|
||||
@@ -942,12 +951,12 @@ pub const Interpreter = struct {
|
||||
},
|
||||
.type_tag => |tid| {
|
||||
// A first-class Type value is the comptime form of the
|
||||
// runtime Any-Type aggregate `{ tag=.any, value=tid }`
|
||||
// runtime Any-Type aggregate `{ tag=.type_value, value=tid }`
|
||||
// (see `const_type` lowering in buildPackSliceValue).
|
||||
// `type_of(any_holding_a_Type)` lowers to struct_get
|
||||
// field 0, expecting that runtime layout — mirror it so
|
||||
// field 0 reads the `.any` tag and field 1 the type id.
|
||||
if (fa.field_index == 0) return .{ .value = .{ .int = @intCast(TypeId.any.index()) } };
|
||||
// field 0 reads the `.type_value` tag and field 1 the type id.
|
||||
if (fa.field_index == 0) return .{ .value = .{ .int = @intCast(TypeId.type_value.index()) } };
|
||||
if (fa.field_index == 1) return .{ .value = .{ .type_tag = tid } };
|
||||
return error.OutOfBounds;
|
||||
},
|
||||
@@ -1080,7 +1089,12 @@ pub const Interpreter = struct {
|
||||
return .branch;
|
||||
},
|
||||
.switch_br => |sb| {
|
||||
const operand = frame.getRef(sb.operand).asInt() orelse return error.TypeError;
|
||||
// A type-category match (`type_of(x) == { case int: … }`)
|
||||
// switches on a Type value — a `.type_tag` Value whose discriminant
|
||||
// is its TypeId index; an enum/error switch uses a plain int.
|
||||
const sb_val = frame.getRef(sb.operand);
|
||||
const operand = sb_val.asInt() orelse
|
||||
(if (sb_val.asTypeId()) |t| @as(i64, @intCast(t.index())) else return error.TypeError);
|
||||
for (sb.cases) |case| {
|
||||
if (operand == case.value) {
|
||||
const args = self.alloc.alloc(Value, case.args.len) catch return error.CannotEvalComptime;
|
||||
|
||||
@@ -1690,7 +1690,7 @@ pub fn tryLowerReflectionCall(self: *Lowering, name: []const u8, c: *const ast.C
|
||||
// self-reference resolves); the interp's `declare` returns that slot.
|
||||
const name_ref = self.lowerExpr(c.args[0]);
|
||||
const args_owned = self.alloc.dupe(Ref, &.{name_ref}) catch return Ref.none;
|
||||
return self.builder.callBuiltin(.declare, args_owned, .any);
|
||||
return self.builder.callBuiltin(.declare, args_owned, .type_value);
|
||||
}
|
||||
if (std.mem.eql(u8, name, "define")) {
|
||||
// Comptime type-construction primitive: complete a declare()'d slot
|
||||
@@ -1711,7 +1711,7 @@ pub fn tryLowerReflectionCall(self: *Lowering, name: []const u8, c: *const ast.C
|
||||
const args_owned = self.alloc.dupe(Ref, &.{ handle_ref, info_ref }) catch return Ref.none;
|
||||
// define returns the (now-completed) handle as a `Type` value, so the
|
||||
// one-shot constructor form chains: `T :: define(declare(), info)`.
|
||||
return self.builder.callBuiltin(.define, args_owned, .any);
|
||||
return self.builder.callBuiltin(.define, args_owned, .type_value);
|
||||
}
|
||||
if (std.mem.eql(u8, name, "type_info")) {
|
||||
// Comptime reflection-into-data: reflect a type INTO a `TypeInfo`
|
||||
@@ -1935,16 +1935,15 @@ pub fn tryLowerReflectionCall(self: *Lowering, name: []const u8, c: *const ast.C
|
||||
} }, .any);
|
||||
}
|
||||
if (std.mem.eql(u8, name, "type_of")) {
|
||||
// type_of(val) — produce a Type value (.any-typed aggregate).
|
||||
// type_of(val) — produce a Type value (`.type_value`, a bare i64 handle).
|
||||
if (c.args.len < 1) return self.builder.constType(.void);
|
||||
const arg_ty = self.inferExprType(c.args[0]);
|
||||
if (arg_ty == .any) {
|
||||
// Runtime: extract tag, rebuild Any with `{.any, tag}` so
|
||||
// the returned value carries Type semantics (tag field
|
||||
// says ".any" → the value field holds the type id).
|
||||
// Runtime: the held value's type is the Any's tag (field 0). Read it
|
||||
// out AS the 8-byte `.type_value` handle — type_of of a runtime Any
|
||||
// names the held value's type, so the tag IS the type id.
|
||||
const val = self.lowerExpr(c.args[0]);
|
||||
const tag_val = self.builder.structGet(val, 0, .i64);
|
||||
return self.builder.boxAny(tag_val, .any);
|
||||
return self.builder.structGet(val, 0, .type_value);
|
||||
} else {
|
||||
return self.builder.constType(arg_ty);
|
||||
}
|
||||
@@ -1988,13 +1987,17 @@ pub fn tryLowerReflectionCall(self: *Lowering, name: []const u8, c: *const ast.C
|
||||
/// Strict `$T: Type` classification shared by the 7 type-introspection
|
||||
/// builtins. An argument denotes a type iff it is a spelled /
|
||||
/// compile-time type or generic type parameter (the `isStaticTypeArg`
|
||||
/// shapes), or a runtime `Type` value — which is `.any`-typed at
|
||||
/// shapes), or a runtime `Type` value — which is `.type_value`-typed at
|
||||
/// runtime (`type_of(x)`, a `[]Type` element `list[i]`, a `Type`-typed
|
||||
/// local / field / param). Any other expression — a value of type
|
||||
/// i64 / f64 / bool / a struct — is NOT a type.
|
||||
pub fn reflectionArgIsType(self: *Lowering, arg: *const Node) bool {
|
||||
if (self.isStaticTypeArg(arg)) return true;
|
||||
return self.inferExprType(arg) == .any;
|
||||
// Either a bare `Type` value (`.type_value`) or an `Any` that may hold a Type
|
||||
// — the boxed reflection path (`case type: type_name(val)` where `val: Any`,
|
||||
// the runtime tag deciding). Both are valid reflection arguments.
|
||||
const ty = self.inferExprType(arg);
|
||||
return ty == .type_value or ty == .any;
|
||||
}
|
||||
|
||||
/// Guard for the type-introspection builtins (`size_of`, `align_of`,
|
||||
|
||||
@@ -1881,8 +1881,12 @@ pub fn lowerExpr(self: *Lowering, node: *const Node) Ref {
|
||||
d.addFmt(.err, node.span, "'{s}' is not visible; #import the module that declares it", .{eff_fn_name});
|
||||
break :blk self.emitError(eff_fn_name, node.span);
|
||||
}
|
||||
// Type-as-value: if target is Any (Type variable), produce a type name string
|
||||
if (self.target_type == .any) {
|
||||
// Type-as-value: a bare function name in a `Type` (`.type_value`)
|
||||
// slot is its FUNCTION TYPE — `const_type(() -> R)` — so it prints
|
||||
// / reflects as the real function type, not a func-ref. For a
|
||||
// genuine `Any` param the old behavior is kept (a formatted
|
||||
// type-name string boxed as Any).
|
||||
if (self.target_type == .any or self.target_type == .type_value) {
|
||||
const fd_any: ?*const ast.FnDecl = self.program_index.fn_ast_map.get(eff_fn_name) orelse fd_blk: {
|
||||
switch (self.selectPlainCallableAuthor(id.name, self.current_source_file.?)) {
|
||||
.func => |sf| break :fd_blk sf.decl,
|
||||
@@ -1890,6 +1894,13 @@ pub fn lowerExpr(self: *Lowering, node: *const Node) Ref {
|
||||
}
|
||||
};
|
||||
if (fd_any) |fd| {
|
||||
if (self.target_type == .type_value) {
|
||||
var param_ids = std.ArrayList(TypeId).empty;
|
||||
defer param_ids.deinit(self.alloc);
|
||||
for (fd.params) |p| param_ids.append(self.alloc, self.resolveParamType(&p)) catch {};
|
||||
const fn_tid = self.module.types.functionType(param_ids.items, self.resolveReturnType(fd));
|
||||
break :blk self.builder.constType(fn_tid);
|
||||
}
|
||||
const fn_type_str = self.formatFnTypeString(fd);
|
||||
const sid = self.module.types.internString(fn_type_str);
|
||||
const str = self.builder.constString(sid);
|
||||
|
||||
@@ -438,6 +438,7 @@ pub fn formatTypeName(self: *Lowering, ty: TypeId) []const u8 {
|
||||
if (ty == .void) return "void";
|
||||
if (ty == .string) return "string";
|
||||
if (ty == .any) return "Any";
|
||||
if (ty == .type_value) return "Type";
|
||||
if (ty == .usize) return "usize";
|
||||
if (ty == .isize) return "isize";
|
||||
|
||||
@@ -665,7 +666,9 @@ pub fn resolveTypeCategoryTags(self: *Lowering, name: []const u8) []const u64 {
|
||||
return tags.items;
|
||||
}
|
||||
if (std.mem.eql(u8, name, "type") or std.mem.eql(u8, name, "Type")) {
|
||||
tags.append(self.alloc, TypeId.any.index()) catch {};
|
||||
// A Type value's runtime tag is `.type_value` (was `.any` when Type and
|
||||
// Any shared a TypeId) — so `case type:` matches an Any holding a Type.
|
||||
tags.append(self.alloc, TypeId.type_value.index()) catch {};
|
||||
return tags.items;
|
||||
}
|
||||
|
||||
|
||||
@@ -454,11 +454,12 @@ pub const Builder = struct {
|
||||
/// fail loudly rather than silently materialise the TypeId as an
|
||||
/// int.
|
||||
pub fn constType(self: *Builder, tid: TypeId) Ref {
|
||||
// Type values are Any-shaped at runtime —
|
||||
// `{ tag = .any.index() (the meta-marker), value = tid }`.
|
||||
// Matches `Type → .any` in `type_bridge`. The interp keeps
|
||||
// the high-fidelity `.type_tag` Value for comptime ops.
|
||||
return self.emit(.{ .const_type = tid }, .any);
|
||||
// A Type value is its own 8-byte builtin handle (`.type_value`), a bare
|
||||
// i64 carrying `tid.index()` — distinct from the 16-byte boxed `.any`.
|
||||
// Flowing it into an `Any` slot boxes it (`{ tag = .any.index(), value =
|
||||
// tid }`) via the standard box-any coercion. The interp keeps the
|
||||
// high-fidelity `.type_tag` Value for comptime ops.
|
||||
return self.emit(.{ .const_type = tid }, .type_value);
|
||||
}
|
||||
|
||||
// ── Arithmetic ──────────────────────────────────────────────────
|
||||
|
||||
@@ -37,7 +37,7 @@ test "TypeResolver.resolvePrimitive maps builtin keywords, null otherwise" {
|
||||
try std.testing.expectEqual(@as(?TypeId, .f64), TypeResolver.resolvePrimitive("f64"));
|
||||
try std.testing.expectEqual(@as(?TypeId, .void), TypeResolver.resolvePrimitive("void"));
|
||||
try std.testing.expectEqual(@as(?TypeId, .any), TypeResolver.resolvePrimitive("Any"));
|
||||
try std.testing.expectEqual(@as(?TypeId, .any), TypeResolver.resolvePrimitive("Type"));
|
||||
try std.testing.expectEqual(@as(?TypeId, .type_value), TypeResolver.resolvePrimitive("Type"));
|
||||
try std.testing.expectEqual(@as(?TypeId, .usize), TypeResolver.resolvePrimitive("usize"));
|
||||
try std.testing.expectEqual(@as(?TypeId, .isize), TypeResolver.resolvePrimitive("isize"));
|
||||
try std.testing.expectEqual(@as(?TypeId, .noreturn), TypeResolver.resolvePrimitive("noreturn"));
|
||||
|
||||
@@ -58,10 +58,11 @@ pub const TypeResolver = struct {
|
||||
if (std.mem.eql(u8, name, "cstring")) return .cstring;
|
||||
if (std.mem.eql(u8, name, "void")) return .void;
|
||||
if (std.mem.eql(u8, name, "Any")) return .any;
|
||||
// `Type` values are runtime-representable as Any-shaped pairs
|
||||
// `{ tag = .any.index(), value = TypeId.index() }`, so `Type` maps to
|
||||
// `.any` and routes through the existing Any infrastructure.
|
||||
if (std.mem.eql(u8, name, "Type")) return .any;
|
||||
// A `Type` value is its own 8-byte builtin handle (`.type_value`), DISTINCT
|
||||
// from the 16-byte boxed `.any`. Flowing a `Type` into an `Any` slot boxes
|
||||
// it (`{ tag = .any.index(), value = TypeId.index() }`) via the standard
|
||||
// box-any coercion; reflection reads it back through `reflectArgRepr`.
|
||||
if (std.mem.eql(u8, name, "Type")) return .type_value;
|
||||
if (std.mem.eql(u8, name, "noreturn")) return .noreturn;
|
||||
if (std.mem.eql(u8, name, "usize")) return .usize;
|
||||
if (std.mem.eql(u8, name, "isize")) return .isize;
|
||||
|
||||
Reference in New Issue
Block a user