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:
@@ -1234,15 +1234,28 @@ pub const Interpreter = struct {
|
||||
},
|
||||
.subslice => |sub| {
|
||||
const base = frame.getRef(sub.base);
|
||||
const lo_val = frame.getRef(sub.lo);
|
||||
const hi_val = frame.getRef(sub.hi);
|
||||
const lo: usize = @intCast(lo_val.asInt() orelse return error.TypeError);
|
||||
const hi: usize = @intCast(hi_val.asInt() orelse return error.TypeError);
|
||||
const lo: usize = @intCast(frame.getRef(sub.lo).asInt() orelse return bailDetail("comptime subslice: lo index is not an integer"));
|
||||
const hi: usize = @intCast(frame.getRef(sub.hi).asInt() orelse return bailDetail("comptime subslice: hi index is not an integer"));
|
||||
if (hi < lo) return error.OutOfBounds;
|
||||
if (base.asString(self)) |s| {
|
||||
if (hi > s.len) return error.OutOfBounds;
|
||||
return .{ .value = .{ .string = s[lo..hi] } };
|
||||
}
|
||||
return bailDetail("comptime subslice: base is not a string-backed value (slice over non-string aggregates not yet supported)");
|
||||
// Non-string aggregate (array or `{data,len}` slice). The
|
||||
// underlying element list comes from the aggregate directly (an
|
||||
// array) or its data field (a slice) — `sub.base_ty` picks which,
|
||||
// since a 2-element array and a `{ptr,len}` pair are
|
||||
// indistinguishable by Value shape alone.
|
||||
const elems = self.subsliceElements(frame, base, sub.base_ty) orelse
|
||||
return bailDetail("comptime subslice: base is not a sliceable array/slice value");
|
||||
if (hi > elems.len) return error.OutOfBounds;
|
||||
const sub_elems = elems[lo..hi];
|
||||
// Return a proper slice VALUE `{data, len}`: data is the element
|
||||
// aggregate, len the (int) count. The int len is what lets
|
||||
// downstream `.length` / `index_get` / `decodeVariantElements`
|
||||
// read this as a slice and not a bare array.
|
||||
const pair = self.alloc.dupe(Value, &.{ .{ .aggregate = sub_elems }, .{ .int = @intCast(sub_elems.len) } }) catch return error.CannotEvalComptime;
|
||||
return .{ .value = .{ .aggregate = pair } };
|
||||
},
|
||||
|
||||
// ── Addr/deref ─────────────────────────────────────
|
||||
@@ -1753,6 +1766,33 @@ pub const Interpreter = struct {
|
||||
return current;
|
||||
}
|
||||
|
||||
/// The element list backing a comptime array/slice VALUE, for `subslice`.
|
||||
/// `base_ty` (threaded onto the op at lower time) disambiguates the two
|
||||
/// shapes that look identical as Values: an ARRAY's aggregate holds its
|
||||
/// elements directly, while a SLICE is a `{data, len}` fat pointer whose
|
||||
/// `data` field holds them. Returns null for any other shape (caller bails).
|
||||
fn subsliceElements(self: *Interpreter, frame: *Frame, base: Value, base_ty: TypeId) ?[]const Value {
|
||||
var b = base;
|
||||
if (b == .slot_ptr) b = self.resolveSlotChain(frame, b);
|
||||
const fields = switch (b) {
|
||||
.aggregate => |f| f,
|
||||
else => return null,
|
||||
};
|
||||
const is_slice = !base_ty.isBuiltin() and self.module.types.get(base_ty) == .slice;
|
||||
if (is_slice) {
|
||||
if (fields.len != 2) return null;
|
||||
const len: usize = @intCast(fields[1].asInt() orelse return null);
|
||||
var data = fields[0];
|
||||
if (data == .slot_ptr) data = self.resolveSlotChain(frame, data);
|
||||
return switch (data) {
|
||||
.aggregate => |arr| if (len <= arr.len) arr[0..len] else null,
|
||||
else => null,
|
||||
};
|
||||
}
|
||||
// Array (or unknown base_ty fallback): the fields ARE the elements.
|
||||
return fields;
|
||||
}
|
||||
|
||||
// ── Constant → Value conversion ─────────────────────────────
|
||||
|
||||
fn constToValue(self: *Interpreter, cv: inst_mod.ConstantValue) Value {
|
||||
|
||||
Reference in New Issue
Block a user