mem: thread val_ty through inst.Store; per-width comptime regression test

The interp's `storeAtRawPtr` used to write 8 bytes from a `.int` /
`.float` Value regardless of the destination's declared width. The
Value tag flattens s8..s64/u*/pointer all to `.int`, so it can't
disambiguate widths on its own — every store risked clobbering up to
7 neighbor bytes if the actual IR type was sub-8.

Fix:

- `inst.Store` gains `val_ty: TypeId` (defaults to `.void` for
  backward compat with the LLVM emitter, which doesn't read it).
- `builder.store` captures `getRefType(val)` at emit time.
- `storeAtRawPtr` now takes `val_ty`, looks up
  `types.typeSizeBytes(val_ty)`, and writes exactly that many bytes:
  `.int` → width bytes of the i64 representation (1..8),
  `.float` → 4 (f32 round-trip via @floatCast) or 8,
  `.boolean` → 1 (zeros higher width bytes when destination is wider),
  `.null_val` → width bytes of zero. Width outside the expected band
  bails with a clear diagnostic.

Regression test: `examples/132-comptime-typed-store-widths.sx`. For
every primitive type (u8/u16/u32/u64, s8/s16/s32/s64, bool, f32, f64),
the test:

1. Allocates a 32-byte libc buffer through `context.allocator`.
2. Fills with sentinel byte 0xAA.
3. Writes ONE typed value at offset 8.
4. Sums every byte back.
5. Compares the runtime checksum (LLVM-emitted store, already
   correct) against a comptime checksum baked via `#run`.

Mismatch = neighbor clobber. The test exits non-zero with a per-width
"FAIL u8: comptime=X runtime=Y" line so future regressions surface
the offending width.

Also wired:

- Interp's `index_get` gains `.int` / `.byte_ptr` base arms — `buf[i]`
  through a raw libc-malloc'd pointer reads one byte at offset i.
  Used by the new test's `sum_bytes` loop; previously bailed at
  `op=index_get`.
- `emit_llvm`'s comptime-init catch block prints a real diagnostic
  instead of swallowing the error and filling the const with zero.
  Stale bail state from a previous init is cleared before each call.

154/154 example tests pass (the new test + the existing 153). Chess
still green on macOS / iOS sim / Android.
This commit is contained in:
agra
2026-05-25 11:41:59 +03:00
parent 26d96ac15e
commit f2b3868579
7 changed files with 255 additions and 25 deletions

View File

@@ -659,7 +659,20 @@ pub const LLVMEmitter = struct {
if (global.comptime_func) |func_id| {
var interp_inst = Interpreter.init(self.ir_mod, self.alloc);
interp_inst.build_config = &self.build_config;
const result = interp_inst.call(func_id, &.{}) catch .void_val;
Interpreter.last_bail_op = null;
Interpreter.last_bail_builtin = null;
Interpreter.last_bail_detail = null;
const result = interp_inst.call(func_id, &.{}) catch |err| blk: {
// Surface the bail loudly instead of silently filling
// the const with zero. Stale state from a previous
// comptime function would otherwise hide the error.
const op = Interpreter.last_bail_op orelse "<unknown>";
const detail = Interpreter.last_bail_detail orelse "";
const sep: []const u8 = if (detail.len > 0) ": " else "";
const gname = self.ir_mod.types.getString(global.name);
std.debug.print("error: comptime init of '{s}' failed: {s} (op={s}{s}{s})\n", .{ gname, @errorName(err), op, sep, detail });
break :blk .void_val;
};
const init_val = self.valueToLLVMConst(result, llvm_ty);
c.LLVMSetInitializer(llvm_global, init_val);
} else if (global.init_val) |iv| {