fix(interp): comptime subslice over non-string aggregates

`arr[lo..hi]` at comptime bailed for any non-string base — the interp's
.subslice op only handled string-backed values. Worse, the open-ended
`hi` came from a .length op that misread a 2-element array as a {ptr,len}
fat pointer (returning the 2nd element, not the count), so even lo/hi
weren't valid ints.

Fix, interp-only (runtime already handles arrays via LLVMTypeOf):
- Thread the base operand's IR type onto the Subslice op (base_ty); the
  interp uses it to tell a bare array (elements = aggregate fields) from a
  {data,len} slice (elements in the data field) — indistinguishable by
  Value shape alone.
- Fold an open-ended slice's hi to the array's static length for fixed
  arrays at lower time (runtime emitLength folds the same constant, so the
  IR result is unchanged — no snapshot churn — but the comptime interp no
  longer hits the ambiguous .length op).
- subsliceElements() resolves the element list (array/slice, inline or
  slot_ptr-backed) and subslice returns a proper {data,len} slice value.

Suite green (678), no .ir changes.
This commit is contained in:
agra
2026-06-17 05:11:33 +03:00
parent 4e8075491d
commit d22037c4a7
3 changed files with 65 additions and 10 deletions

View File

@@ -323,6 +323,11 @@ pub const Subslice = struct {
base: Ref,
lo: Ref,
hi: Ref,
/// The base operand's IR type (array vs slice vs string). The runtime
/// backend reads array/slice-ness off `LLVMTypeOf`, but the comptime
/// interp can't tell a 2-element array from a `{ptr,len}` fat pointer by
/// Value shape alone, so it consults this. `.void` for old call sites.
base_ty: TypeId = .void,
};
pub const Call = struct {