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:
@@ -1397,19 +1397,29 @@ pub fn lowerIndexExpr(self: *Lowering, ie: *const ast.IndexExpr) Ref {
|
||||
|
||||
pub fn lowerSliceExpr(self: *Lowering, se: *const ast.SliceExpr) Ref {
|
||||
const obj = self.lowerExpr(se.object);
|
||||
const obj_ty = self.inferExprType(se.object);
|
||||
var lo = if (se.start) |s| self.lowerExpr(s) else self.builder.constInt(0, .i64);
|
||||
if (se.start_exclusive) lo = self.builder.add(lo, self.builder.constInt(1, .i64), .i64);
|
||||
var hi = if (se.end) |e| self.lowerExpr(e) else self.builder.emit(.{ .length = .{ .operand = obj } }, .i64);
|
||||
// Open-ended `hi`: for a fixed-size array the length is a compile-time
|
||||
// constant — emit it directly rather than a runtime `.length` op. Runtime
|
||||
// codegen folds the identical constant for an array (`emitLength`), so the
|
||||
// result is unchanged; the win is the comptime interp, which can't
|
||||
// disambiguate a 2-element array from a `{ptr,len}` fat pointer by Value
|
||||
// shape and so would misread a `.length` op on an array.
|
||||
var hi = if (se.end) |e|
|
||||
self.lowerExpr(e)
|
||||
else if (!obj_ty.isBuiltin() and self.module.types.get(obj_ty) == .array)
|
||||
self.builder.constInt(@intCast(self.module.types.get(obj_ty).array.length), .i64)
|
||||
else
|
||||
self.builder.emit(.{ .length = .{ .operand = obj } }, .i64);
|
||||
if (se.end_inclusive) hi = self.builder.add(hi, self.builder.constInt(1, .i64), .i64);
|
||||
// Infer result slice type from the object
|
||||
const obj_ty = self.inferExprType(se.object);
|
||||
// Subslice of string stays string (same {ptr, i64} layout, correct type category)
|
||||
if (obj_ty == .string) {
|
||||
return self.builder.emit(.{ .subslice = .{ .base = obj, .lo = lo, .hi = hi } }, .string);
|
||||
return self.builder.emit(.{ .subslice = .{ .base = obj, .lo = lo, .hi = hi, .base_ty = obj_ty } }, .string);
|
||||
}
|
||||
const elem_ty = self.getElementType(obj_ty);
|
||||
const slice_ty = if (elem_ty != .void) self.module.types.sliceOf(elem_ty) else self.module.types.sliceOf(.u8);
|
||||
return self.builder.emit(.{ .subslice = .{ .base = obj, .lo = lo, .hi = hi } }, slice_ty);
|
||||
return self.builder.emit(.{ .subslice = .{ .base = obj, .lo = lo, .hi = hi, .base_ty = obj_ty } }, slice_ty);
|
||||
}
|
||||
|
||||
pub fn lowerTupleLiteral(self: *Lowering, tl: *const ast.TupleLiteral) Ref {
|
||||
|
||||
Reference in New Issue
Block a user