fix(std): render integer formatter extremes — i64::MIN and unsigned all-ones [F0.8]
Resolves issue 0090. The `{}` integer formatter mis-rendered both ends of
the 64-bit range:
- `int_to_string` computed the magnitude as `0 - n`, which overflows for
`s64::MIN` (its magnitude is unrepresentable as a positive s64) — the
value stayed negative, the digit loop ran zero times, so only `-`
printed. It now extracts digits straight from `n` (per-digit
`|n % 10|`, `n` truncating toward zero), never negating MIN.
- `any_to_string`'s `case int:` formatted every integer as s64, so a u64
all-ones value printed as `-1`. There was no `uint` type-category to
distinguish signedness. Added an additive `type_is_unsigned(T)`
reflection builtin (static fold + dynamic interp/LLVM paths, mirroring
`type_name`), backed by the new `TypeTable.isUnsignedInt` predicate, and
a `uint_to_string` formatter (unsigned decimal via long-division over
four 16-bit limbs). `case int:` routes through `type_is_unsigned(type)`.
The 16-bit-limb split is factored into a shared `decompose_u16x4`, now
reused by `int_to_hex_string` (no second unsigned-math routine).
Regression: examples/0046-basic-int-formatter-extremes pins both extremes
plus a width spread; unit tests cover `isUnsignedInt`. Docs (specs.md
representation note, readme std API) updated for unsigned/extreme `{}`
behavior. IR snapshots refreshed for the two new std functions.
This commit is contained in:
@@ -1900,6 +1900,27 @@ pub const Interpreter = struct {
|
||||
const b = frame.getRef(bi.args[1]).asTypeId() orelse return bailDetail("comptime type_eq: second argument is not a Type value");
|
||||
return .{ .value = .{ .boolean = a == b } };
|
||||
},
|
||||
.type_is_unsigned => {
|
||||
if (bi.args.len < 1) return bailDetail("comptime type_is_unsigned: missing argument");
|
||||
const arg = frame.getRef(bi.args[0]);
|
||||
// Accept a bare `.type_tag`, an Any-boxed Type (`{tag,
|
||||
// .type_tag}`), or the `type_of(x)` shape (`{.int(any),
|
||||
// .int(typeid)}`) — the last is what `any_to_string`'s
|
||||
// `case int:` passes, where the inner TypeId is carried
|
||||
// as a plain integer rather than a `.type_tag`.
|
||||
const tid = blk: {
|
||||
if (arg.asTypeId()) |t| break :blk t;
|
||||
if (arg == .aggregate) {
|
||||
const fields = arg.aggregate;
|
||||
if (fields.len >= 2) {
|
||||
if (fields[1].asTypeId()) |t| break :blk t;
|
||||
if (fields[1].asInt()) |iv| break :blk TypeId.fromIndex(@intCast(iv));
|
||||
}
|
||||
}
|
||||
return bailDetail("comptime type_is_unsigned: argument is not a Type value (expected `.type_tag`, Any-boxed Type, or `type_of(x)`)");
|
||||
};
|
||||
return .{ .value = .{ .boolean = self.module.types.isUnsignedInt(tid) } };
|
||||
},
|
||||
.has_impl => {
|
||||
// has_impl at interp time needs access to the host's
|
||||
// protocol-registration maps (protocol_thunk_map +
|
||||
|
||||
Reference in New Issue
Block a user