fix(ir): reflection builtins on an Any read its runtime tag, not payload [F0.8]
`type_name` / `type_is_unsigned` on an `Any` argument unconditionally read
the Any's payload as a TypeId index. That is correct only when the Any holds
a Type value (`{ .any, tid }`); for an Any holding a runtime *value*
(`av : Any = 6`, tag s64, payload 6) it returned `types[6]` — `type_name(av)`
gave "u8" and `type_is_unsigned(av)` gave true.
Both backends now branch on the Any's runtime type-tag: tag == `.any` → the
box is a Type value, use the payload as the TypeId; otherwise the tag IS the
held value's type. So `type_name(av)` → "s64", `type_is_unsigned(av)` → false,
while `type_name(type_of(x))` still names the held type. The `{}` formatter is
unchanged (it already passed `type_of(val)`, a proper Type value).
- src/ir/interp.zig: shared `Value.reflectTypeId` tag-branching resolver; the
`type_name` / `type_is_unsigned` interp arms route through it.
- src/backend/llvm/ops.zig: shared `Ops.reflectArgTypeId` emits
extractvalue-tag / icmp-eq-.any / select for the runtime path; both
reflection arms route through it. The two backends agree.
- examples/0164-types-reflection-any-tag.sx: regression pinning type_name /
type_is_unsigned / print on an Any holding a value vs a Type.
- src/ir/interp.test.zig: unit test for `reflectTypeId`.
- 22 .ir snapshots: the new select appears in every std-importing program's
IR (any_to_string embeds these builtins) — benign, verified structurally
identical apart from the three new instructions.
- issues/0090, specs.md: documented the Any-tag rule.
This commit is contained in:
@@ -804,3 +804,38 @@ test "comptime: type_eq builtin on type_tag values" {
|
||||
const result = try interp.call(FuncId.fromIndex(0), &.{});
|
||||
try std.testing.expectEqual(true, result.asBool().?);
|
||||
}
|
||||
|
||||
// ── Test: reflectTypeId reads an Any's runtime TYPE-TAG, not its payload ──
|
||||
// A reflection builtin on an Any must report the type OF a held value (the
|
||||
// tag) and only read the payload when the Any holds a Type value (tag ==
|
||||
// `.any`). Regression for issue 0090 (attempt 3): a boxed value like
|
||||
// `av : Any = 6` (`{ tag = s64, value = 6 }`) must resolve to `s64`, NOT
|
||||
// `types[6]` (`u8`).
|
||||
test "reflect: reflectTypeId branches on the Any tag" {
|
||||
const any_idx: i64 = @intCast(TypeId.any.index());
|
||||
|
||||
// Native first-class Type value → the held TypeId directly.
|
||||
try std.testing.expectEqual(@as(?TypeId, .u64), (Value{ .type_tag = .u64 }).reflectTypeId());
|
||||
|
||||
// Any holding a VALUE: `{ tag = s64, value = 6 }` → s64 (the tag),
|
||||
// never `types[6]` (u8). This is the bug the fix closes.
|
||||
var held_value = [_]Value{ .{ .int = @intCast(TypeId.s64.index()) }, .{ .int = 6 } };
|
||||
try std.testing.expectEqual(@as(?TypeId, .s64), (Value{ .aggregate = &held_value }).reflectTypeId());
|
||||
|
||||
// Any holding a VALUE of an unsigned type: `{ tag = u32, value = 7 }` → u32.
|
||||
var held_u32 = [_]Value{ .{ .int = @intCast(TypeId.u32.index()) }, .{ .int = 7 } };
|
||||
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
|
||||
// int (the runtime box shape) ...
|
||||
var held_type_int = [_]Value{ .{ .int = any_idx }, .{ .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 } };
|
||||
try std.testing.expectEqual(@as(?TypeId, .u64), (Value{ .aggregate = &held_type_tag }).reflectTypeId());
|
||||
|
||||
// Neither shape → null (the caller bails loudly, never guesses a TypeId).
|
||||
try std.testing.expectEqual(@as(?TypeId, null), (Value{ .int = 6 }).reflectTypeId());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user