lang: for-loop over List(T); deref a *T method receiver
The collection for-loop now iterates a List(T)-like struct ({ items: [*]T, len, … }) — and a *List — by viewing it as items[0..len]. So 'for legal: (m)' / 'for pieces: (*p)' work like iterating a slice, with by-ref captures writing back into the backing.
fixupMethodReceiver also derefs a *T receiver when the method takes T by value, so a 'for xs: (*x)' capture can call value-self methods (x.method()). Regression: examples/for-list.sx.
This commit is contained in:
@@ -3346,6 +3346,34 @@ pub const Lowering = struct {
|
||||
return self.builder.constInt(0, .void);
|
||||
}
|
||||
|
||||
/// View a `List(T)`-like struct (`{ items: [*]T, len, … }`) as its backing
|
||||
/// `items` pointer + element type + `len`, so `for list: (x)` iterates the
|
||||
/// elements. Null for anything that isn't such a struct.
|
||||
fn listView(self: *Lowering, value: Ref, ty: TypeId) ?struct { data: Ref, data_ty: TypeId, len: Ref } {
|
||||
if (ty.isBuiltin()) return null;
|
||||
const info = self.module.types.get(ty);
|
||||
if (info != .@"struct") return null;
|
||||
const items_id = self.module.types.internString("items");
|
||||
const len_id = self.module.types.internString("len");
|
||||
var items_idx: ?u32 = null;
|
||||
var items_ty: TypeId = .unresolved;
|
||||
var len_idx: ?u32 = null;
|
||||
for (info.@"struct".fields, 0..) |f, i| {
|
||||
if (f.name == items_id and !f.ty.isBuiltin() and self.module.types.get(f.ty) == .many_pointer) {
|
||||
items_idx = @intCast(i);
|
||||
items_ty = f.ty;
|
||||
} else if (f.name == len_id) {
|
||||
len_idx = @intCast(i);
|
||||
}
|
||||
}
|
||||
if (items_idx == null or len_idx == null) return null;
|
||||
return .{
|
||||
.data = self.builder.emit(.{ .struct_get = .{ .base = value, .field_index = items_idx.? } }, items_ty),
|
||||
.data_ty = items_ty,
|
||||
.len = self.builder.emit(.{ .struct_get = .{ .base = value, .field_index = len_idx.? } }, .s64),
|
||||
};
|
||||
}
|
||||
|
||||
fn lowerFor(self: *Lowering, fe: *const ast.ForExpr) Ref {
|
||||
if (fe.range_end) |end_node| {
|
||||
if (fe.is_inline) return self.lowerInlineRangeFor(fe, end_node);
|
||||
@@ -3357,11 +3385,27 @@ pub const Lowering = struct {
|
||||
return self.diagPackAsValue(fe.iterable.data.identifier.name, fe.iterable.span, .runtime_iter);
|
||||
}
|
||||
|
||||
// Lower iterable
|
||||
const iterable = self.lowerExpr(fe.iterable);
|
||||
// Lower iterable + resolve its static type.
|
||||
var iterable = self.lowerExpr(fe.iterable);
|
||||
var iterable_ty = self.inferExprType(fe.iterable);
|
||||
|
||||
// Get length
|
||||
const len = self.builder.emit(.{ .length = .{ .operand = iterable } }, .s64);
|
||||
// `*List` / `*[]T` etc. — deref to the collection value.
|
||||
const ptr_info = if (iterable_ty.isBuiltin()) null else self.module.types.get(iterable_ty);
|
||||
if (ptr_info != null and ptr_info.? == .pointer) {
|
||||
iterable = self.builder.load(iterable, ptr_info.?.pointer.pointee);
|
||||
iterable_ty = ptr_info.?.pointer.pointee;
|
||||
}
|
||||
|
||||
// A `List(T)`-like struct iterates its `items[0..len]`; arrays/slices
|
||||
// use their intrinsic length.
|
||||
var len: Ref = undefined;
|
||||
if (self.listView(iterable, iterable_ty)) |lv| {
|
||||
iterable = lv.data;
|
||||
iterable_ty = lv.data_ty;
|
||||
len = lv.len;
|
||||
} else {
|
||||
len = self.builder.emit(.{ .length = .{ .operand = iterable } }, .s64);
|
||||
}
|
||||
|
||||
// Create index variable
|
||||
const idx_slot = self.builder.alloca(.s64);
|
||||
@@ -3387,7 +3431,6 @@ pub const Lowering = struct {
|
||||
// Bind element — resolve element type from iterable. `for xs: (*x)`
|
||||
// binds a pointer into the collection (no per-element copy); `(x)`
|
||||
// binds a value copy.
|
||||
const iterable_ty = self.inferExprType(fe.iterable);
|
||||
const elem_ty = self.getElementType(iterable_ty);
|
||||
const bind_ty = if (fe.capture_by_ref) self.module.types.ptrTo(elem_ty) else elem_ty;
|
||||
const elem = if (fe.capture_by_ref) blk: {
|
||||
@@ -4188,6 +4231,15 @@ pub const Lowering = struct {
|
||||
self.builder.store(slot, method_args.items[0]);
|
||||
method_args.items[0] = slot;
|
||||
}
|
||||
} else {
|
||||
// Method expects a value `T` but the receiver is a `*T` (e.g. a
|
||||
// `for xs: (*x)` by-ref capture) — deref to pass the value.
|
||||
if (!obj_ty.isBuiltin()) {
|
||||
const oi = self.module.types.get(obj_ty);
|
||||
if (oi == .pointer and oi.pointer.pointee == first_param_ty) {
|
||||
method_args.items[0] = self.builder.load(method_args.items[0], first_param_ty);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user