|
|
|
|
@@ -277,48 +277,110 @@ pub fn listView(self: *Lowering, value: Ref, ty: TypeId) ?struct { data: Ref, da
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Lowered prep for one position of a multi-iterable `for` header. Every
|
|
|
|
|
/// position gets its own s64 cursor slot (ranges start at their `start`,
|
|
|
|
|
/// collections at 0); all cursors advance by 1 per iteration, and ONLY the
|
|
|
|
|
/// first position's bound terminates the loop (first-iterable-wins).
|
|
|
|
|
const IterPrep = struct {
|
|
|
|
|
is_range: bool,
|
|
|
|
|
slot: Ref,
|
|
|
|
|
// Collection-only fields:
|
|
|
|
|
data: Ref = Ref.none,
|
|
|
|
|
data_ty: TypeId = .unresolved,
|
|
|
|
|
elem_ty: TypeId = .unresolved,
|
|
|
|
|
is_array: bool = false,
|
|
|
|
|
storage: ?Ref = null, // array's own alloca when addressable (not deref'd)
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/// `for it1, it2, ... (c1, c2, ...) { }` — parallel iteration. The first
|
|
|
|
|
/// iterable's length/bound drives the loop; the others follow by position.
|
|
|
|
|
/// Consequences of first-iterable-wins: a non-first range's end is never
|
|
|
|
|
/// lowered (its side effects do not run), and a shorter non-first collection
|
|
|
|
|
/// is read past its length on mismatch — the first iterable is the
|
|
|
|
|
/// authoritative one.
|
|
|
|
|
pub 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);
|
|
|
|
|
return self.lowerRuntimeRangeFor(fe, end_node);
|
|
|
|
|
}
|
|
|
|
|
// Collection-form `for xs : (x)` over a pack: a pack has no runtime
|
|
|
|
|
// value to iterate (Decision 1) — point the user at `inline for`.
|
|
|
|
|
if (fe.iterable.data == .identifier and self.isPackName(fe.iterable.data.identifier.name)) {
|
|
|
|
|
return self.diagPackAsValue(fe.iterable.data.identifier.name, fe.iterable.span, .runtime_iter);
|
|
|
|
|
if (fe.is_inline) return self.lowerInlineRangeFor(fe);
|
|
|
|
|
|
|
|
|
|
// A pack has no runtime value to iterate (Decision 1) — point the user
|
|
|
|
|
// at `inline for`.
|
|
|
|
|
for (fe.iterables) |it| {
|
|
|
|
|
if (!it.is_range and it.expr.data == .identifier and self.isPackName(it.expr.data.identifier.name)) {
|
|
|
|
|
return self.diagPackAsValue(it.expr.data.identifier.name, it.expr.span, .runtime_iter);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Lower iterable + resolve its static type.
|
|
|
|
|
var iterable = self.lowerExpr(fe.iterable);
|
|
|
|
|
var iterable_ty = self.inferExprType(fe.iterable);
|
|
|
|
|
var preps = std.ArrayList(IterPrep).empty;
|
|
|
|
|
defer preps.deinit(self.alloc);
|
|
|
|
|
var limit: Ref = Ref.none; // exclusive bound of position 0
|
|
|
|
|
|
|
|
|
|
// `*List` / `*[]T` etc. — deref to the collection value. Tracked because
|
|
|
|
|
// a deref'd iterable's identifier binding holds the POINTER, so its
|
|
|
|
|
// alloca is not the collection's storage.
|
|
|
|
|
var was_deref = false;
|
|
|
|
|
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;
|
|
|
|
|
was_deref = true;
|
|
|
|
|
for (fe.iterables, 0..) |it, i| {
|
|
|
|
|
if (it.is_range) {
|
|
|
|
|
const start_ref = self.lowerExpr(it.expr);
|
|
|
|
|
const slot = self.builder.alloca(.s64);
|
|
|
|
|
self.builder.store(slot, start_ref);
|
|
|
|
|
if (i == 0) {
|
|
|
|
|
// Parser guarantees the first iterable is bounded.
|
|
|
|
|
var end_ref = self.lowerExpr(it.range_end.?);
|
|
|
|
|
if (it.inclusive) end_ref = self.builder.add(end_ref, self.builder.constInt(1, .s64), .s64);
|
|
|
|
|
limit = end_ref;
|
|
|
|
|
}
|
|
|
|
|
preps.append(self.alloc, .{ .is_range = true, .slot = slot }) catch unreachable;
|
|
|
|
|
} else {
|
|
|
|
|
var data = self.lowerExpr(it.expr);
|
|
|
|
|
var data_ty = self.inferExprType(it.expr);
|
|
|
|
|
|
|
|
|
|
// `*List` / `*[]T` etc. — deref to the collection value. Tracked
|
|
|
|
|
// because a deref'd iterable's identifier binding holds the
|
|
|
|
|
// POINTER, so its alloca is not the collection's storage.
|
|
|
|
|
var was_deref = false;
|
|
|
|
|
const ptr_info = if (data_ty.isBuiltin()) null else self.module.types.get(data_ty);
|
|
|
|
|
if (ptr_info != null and ptr_info.? == .pointer) {
|
|
|
|
|
data = self.builder.load(data, ptr_info.?.pointer.pointee);
|
|
|
|
|
data_ty = ptr_info.?.pointer.pointee;
|
|
|
|
|
was_deref = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// A `List(T)`-like struct iterates its `items[0..len]`;
|
|
|
|
|
// arrays/slices use their intrinsic length.
|
|
|
|
|
var len: Ref = Ref.none;
|
|
|
|
|
if (self.listView(data, data_ty)) |lv| {
|
|
|
|
|
data = lv.data;
|
|
|
|
|
data_ty = lv.data_ty;
|
|
|
|
|
len = lv.len;
|
|
|
|
|
} else if (i == 0) {
|
|
|
|
|
len = self.builder.emit(.{ .length = .{ .operand = data } }, .s64);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const elem_ty = self.getElementType(data_ty);
|
|
|
|
|
if (elem_ty == .unresolved) {
|
|
|
|
|
// Not a collection. The common trip: `for f(n) { }` — the
|
|
|
|
|
// trailing parens are the CAPTURE, so the iterable is `f`.
|
|
|
|
|
if (self.diagnostics) |d| {
|
|
|
|
|
if (data_ty == .unresolved) {
|
|
|
|
|
d.addFmt(.err, it.expr.span, "cannot iterate this expression — if the parens were call arguments, a call iterable also needs a capture (`for f(n) (x) {{ }}`) or parentheses (`for (f(n)) {{ }}`)", .{});
|
|
|
|
|
} else {
|
|
|
|
|
d.addFmt(.err, it.expr.span, "cannot iterate a value of type '{s}' — if the parens were call arguments, a call iterable also needs a capture (`for f(n) (x) {{ }}`) or parentheses (`for (f(n)) {{ }}`)", .{self.module.types.typeName(data_ty)});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return self.builder.constInt(0, .void);
|
|
|
|
|
}
|
|
|
|
|
const is_array = !data_ty.isBuiltin() and self.module.types.get(data_ty) == .array;
|
|
|
|
|
const storage = if (is_array and !was_deref) self.getExprAlloca(it.expr) else null;
|
|
|
|
|
const slot = self.builder.alloca(.s64);
|
|
|
|
|
self.builder.store(slot, self.builder.constInt(0, .s64));
|
|
|
|
|
if (i == 0) limit = len;
|
|
|
|
|
preps.append(self.alloc, .{
|
|
|
|
|
.is_range = false,
|
|
|
|
|
.slot = slot,
|
|
|
|
|
.data = data,
|
|
|
|
|
.data_ty = data_ty,
|
|
|
|
|
.elem_ty = elem_ty,
|
|
|
|
|
.is_array = is_array,
|
|
|
|
|
.storage = storage,
|
|
|
|
|
}) catch unreachable;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 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);
|
|
|
|
|
const zero = self.builder.constInt(0, .s64);
|
|
|
|
|
self.builder.store(idx_slot, zero);
|
|
|
|
|
|
|
|
|
|
const header_bb = self.freshBlock("for.hdr");
|
|
|
|
|
const body_bb = self.freshBlock("for.body");
|
|
|
|
|
const inc_bb = self.freshBlock("for.inc");
|
|
|
|
|
@@ -326,49 +388,44 @@ pub fn lowerFor(self: *Lowering, fe: *const ast.ForExpr) Ref {
|
|
|
|
|
|
|
|
|
|
self.builder.br(header_bb, &.{});
|
|
|
|
|
|
|
|
|
|
// Header: compare index < length
|
|
|
|
|
// Header: first cursor against the first bound.
|
|
|
|
|
self.builder.switchToBlock(header_bb);
|
|
|
|
|
const idx_val = self.builder.load(idx_slot, .s64);
|
|
|
|
|
const cmp = self.builder.cmpLt(idx_val, len);
|
|
|
|
|
const cur0 = self.builder.load(preps.items[0].slot, .s64);
|
|
|
|
|
const cmp = self.builder.cmpLt(cur0, limit);
|
|
|
|
|
self.builder.condBr(cmp, body_bb, &.{}, exit_bb, &.{});
|
|
|
|
|
|
|
|
|
|
// Body
|
|
|
|
|
// Body: bind one capture per position (when captures are present).
|
|
|
|
|
self.builder.switchToBlock(body_bb);
|
|
|
|
|
|
|
|
|
|
// 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 elem_ty = self.getElementType(iterable_ty);
|
|
|
|
|
const bind_ty = if (fe.capture_by_ref) self.module.types.ptrTo(elem_ty) else elem_ty;
|
|
|
|
|
const is_array = !iterable_ty.isBuiltin() and self.module.types.get(iterable_ty) == .array;
|
|
|
|
|
const elem = if (fe.capture_by_ref) blk: {
|
|
|
|
|
// A slice value carries its backing pointer, so GEP on it writes
|
|
|
|
|
// through. An array is a value — GEP needs its storage (alloca) or
|
|
|
|
|
// mutations would hit a copy.
|
|
|
|
|
const base = if (is_array) (self.getExprAlloca(fe.iterable) orelse iterable) else iterable;
|
|
|
|
|
break :blk self.builder.emit(.{ .index_gep = .{ .lhs = base, .rhs = idx_val } }, bind_ty);
|
|
|
|
|
} else blk: {
|
|
|
|
|
// By-value over an array with addressable storage: GEP + load ONE
|
|
|
|
|
// element. `index_get` on the array VALUE spills the whole array to
|
|
|
|
|
// a temp on every iteration — O(N²) bytes copied per loop.
|
|
|
|
|
if (is_array and !was_deref) {
|
|
|
|
|
if (self.getExprAlloca(fe.iterable)) |storage| {
|
|
|
|
|
const elem_ptr = self.builder.emit(.{ .index_gep = .{ .lhs = storage, .rhs = idx_val } }, self.module.types.ptrTo(elem_ty));
|
|
|
|
|
break :blk self.builder.load(elem_ptr, elem_ty);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
break :blk self.builder.emit(.{ .index_get = .{ .lhs = iterable, .rhs = idx_val } }, bind_ty);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
var body_scope = Scope.init(self.alloc, self.scope);
|
|
|
|
|
const old_scope = self.scope;
|
|
|
|
|
self.scope = &body_scope;
|
|
|
|
|
|
|
|
|
|
body_scope.put(fe.capture_name, .{ .ref = elem, .ty = bind_ty, .is_alloca = false, .is_ref_capture = fe.capture_by_ref });
|
|
|
|
|
|
|
|
|
|
// Bind index if requested
|
|
|
|
|
if (fe.index_name) |iname| {
|
|
|
|
|
body_scope.put(iname, .{ .ref = idx_val, .ty = .s64, .is_alloca = false });
|
|
|
|
|
for (fe.captures, 0..) |cap, i| {
|
|
|
|
|
const prep = preps.items[i];
|
|
|
|
|
const cur = if (i == 0) cur0 else self.builder.load(prep.slot, .s64);
|
|
|
|
|
if (prep.is_range) {
|
|
|
|
|
body_scope.put(cap.name, .{ .ref = cur, .ty = .s64, .is_alloca = false });
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
const bind_ty = if (cap.by_ref) self.module.types.ptrTo(prep.elem_ty) else prep.elem_ty;
|
|
|
|
|
const elem = if (cap.by_ref) blk: {
|
|
|
|
|
// A slice value carries its backing pointer, so GEP on it writes
|
|
|
|
|
// through. An array is a value — GEP needs its storage (alloca)
|
|
|
|
|
// or mutations would hit a copy.
|
|
|
|
|
const base = if (prep.is_array) (prep.storage orelse prep.data) else prep.data;
|
|
|
|
|
break :blk self.builder.emit(.{ .index_gep = .{ .lhs = base, .rhs = cur } }, bind_ty);
|
|
|
|
|
} else blk: {
|
|
|
|
|
// By-value over an array with addressable storage: GEP + load ONE
|
|
|
|
|
// element. `index_get` on the array VALUE spills the whole array
|
|
|
|
|
// to a temp on every iteration — O(N²) bytes copied per loop.
|
|
|
|
|
if (prep.storage) |storage| {
|
|
|
|
|
const elem_ptr = self.builder.emit(.{ .index_gep = .{ .lhs = storage, .rhs = cur } }, self.module.types.ptrTo(prep.elem_ty));
|
|
|
|
|
break :blk self.builder.load(elem_ptr, prep.elem_ty);
|
|
|
|
|
}
|
|
|
|
|
break :blk self.builder.emit(.{ .index_get = .{ .lhs = prep.data, .rhs = cur } }, bind_ty);
|
|
|
|
|
};
|
|
|
|
|
body_scope.put(cap.name, .{ .ref = elem, .ty = bind_ty, .is_alloca = false, .is_ref_capture = cap.by_ref });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Save and set loop targets
|
|
|
|
|
@@ -392,13 +449,15 @@ pub fn lowerFor(self: *Lowering, fe: *const ast.ForExpr) Ref {
|
|
|
|
|
self.builder.br(inc_bb, &.{});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Increment block: increment index and jump back to header
|
|
|
|
|
// Increment block: advance every cursor and jump back to header.
|
|
|
|
|
self.builder.switchToBlock(inc_bb);
|
|
|
|
|
{
|
|
|
|
|
const cur_idx = self.builder.load(idx_slot, .s64);
|
|
|
|
|
const one = self.builder.constInt(1, .s64);
|
|
|
|
|
const next_idx = self.builder.add(cur_idx, one, .s64);
|
|
|
|
|
self.builder.store(idx_slot, next_idx);
|
|
|
|
|
for (preps.items) |prep| {
|
|
|
|
|
const cur = self.builder.load(prep.slot, .s64);
|
|
|
|
|
const next = self.builder.add(cur, one, .s64);
|
|
|
|
|
self.builder.store(prep.slot, next);
|
|
|
|
|
}
|
|
|
|
|
self.builder.br(header_bb, &.{});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -407,81 +466,26 @@ pub fn lowerFor(self: *Lowering, fe: *const ast.ForExpr) Ref {
|
|
|
|
|
return self.builder.constInt(0, .void);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Runtime counting loop `for start..end (i) { }` — `i` (optional) is the
|
|
|
|
|
/// cursor, `end` is exclusive. Lowers to the same header/inc/exit shape as
|
|
|
|
|
/// the collection form, minus the element fetch.
|
|
|
|
|
pub fn lowerRuntimeRangeFor(self: *Lowering, fe: *const ast.ForExpr, end_node: *Node) Ref {
|
|
|
|
|
const start = self.lowerExpr(fe.iterable);
|
|
|
|
|
const end = self.lowerExpr(end_node);
|
|
|
|
|
|
|
|
|
|
const idx_slot = self.builder.alloca(.s64);
|
|
|
|
|
self.builder.store(idx_slot, start);
|
|
|
|
|
|
|
|
|
|
const header_bb = self.freshBlock("for.hdr");
|
|
|
|
|
const body_bb = self.freshBlock("for.body");
|
|
|
|
|
const inc_bb = self.freshBlock("for.inc");
|
|
|
|
|
const exit_bb = self.freshBlock("for.exit");
|
|
|
|
|
|
|
|
|
|
self.builder.br(header_bb, &.{});
|
|
|
|
|
|
|
|
|
|
self.builder.switchToBlock(header_bb);
|
|
|
|
|
const idx_val = self.builder.load(idx_slot, .s64);
|
|
|
|
|
const cmp = self.builder.cmpLt(idx_val, end);
|
|
|
|
|
self.builder.condBr(cmp, body_bb, &.{}, exit_bb, &.{});
|
|
|
|
|
|
|
|
|
|
self.builder.switchToBlock(body_bb);
|
|
|
|
|
var body_scope = Scope.init(self.alloc, self.scope);
|
|
|
|
|
const old_scope = self.scope;
|
|
|
|
|
self.scope = &body_scope;
|
|
|
|
|
if (fe.capture_name.len > 0) {
|
|
|
|
|
body_scope.put(fe.capture_name, .{ .ref = idx_val, .ty = .s64, .is_alloca = false });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const old_break = self.break_target;
|
|
|
|
|
const old_continue = self.continue_target;
|
|
|
|
|
const old_defer_base = self.loop_defer_base;
|
|
|
|
|
self.break_target = exit_bb;
|
|
|
|
|
self.continue_target = inc_bb;
|
|
|
|
|
self.loop_defer_base = self.defer_stack.items.len;
|
|
|
|
|
|
|
|
|
|
self.lowerBlock(fe.body);
|
|
|
|
|
|
|
|
|
|
self.break_target = old_break;
|
|
|
|
|
self.continue_target = old_continue;
|
|
|
|
|
self.loop_defer_base = old_defer_base;
|
|
|
|
|
self.scope = old_scope;
|
|
|
|
|
body_scope.deinit();
|
|
|
|
|
|
|
|
|
|
if (!self.currentBlockHasTerminator()) {
|
|
|
|
|
self.builder.br(inc_bb, &.{});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
self.builder.switchToBlock(inc_bb);
|
|
|
|
|
{
|
|
|
|
|
const cur_idx = self.builder.load(idx_slot, .s64);
|
|
|
|
|
const one = self.builder.constInt(1, .s64);
|
|
|
|
|
const next_idx = self.builder.add(cur_idx, one, .s64);
|
|
|
|
|
self.builder.store(idx_slot, next_idx);
|
|
|
|
|
self.builder.br(header_bb, &.{});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
self.builder.switchToBlock(exit_bb);
|
|
|
|
|
return self.builder.constInt(0, .void);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Comptime-unrolled `inline for start..end (i) { }`. `start`/`end` must be
|
|
|
|
|
/// comptime-known. The body is lowered `end - start` times with the cursor
|
|
|
|
|
/// bound as an `int_val` comptime constant, so `xs[i]` over a pack
|
|
|
|
|
/// Comptime-unrolled `inline for start..end (i) { }`. A single bounded range
|
|
|
|
|
/// with comptime-known bounds. The body is lowered once per value with the
|
|
|
|
|
/// cursor bound as an `int_val` comptime constant, so `xs[i]` over a pack
|
|
|
|
|
/// substitutes the concrete per-position argument each iteration.
|
|
|
|
|
pub fn lowerInlineRangeFor(self: *Lowering, fe: *const ast.ForExpr, end_node: *Node) Ref {
|
|
|
|
|
const start = self.evalComptimeInt(fe.iterable) orelse {
|
|
|
|
|
if (self.diagnostics) |d| d.addFmt(.err, fe.iterable.span, "inline for: range start is not a compile-time integer", .{});
|
|
|
|
|
pub fn lowerInlineRangeFor(self: *Lowering, fe: *const ast.ForExpr) Ref {
|
|
|
|
|
const it = fe.iterables[0];
|
|
|
|
|
if (fe.iterables.len != 1 or !it.is_range or it.range_end == null) {
|
|
|
|
|
if (self.diagnostics) |d| d.addFmt(.err, it.expr.span, "inline for: a single bounded range is required — `inline for 0..N (i) {{ }}`", .{});
|
|
|
|
|
return self.builder.constInt(0, .void);
|
|
|
|
|
}
|
|
|
|
|
const start = self.evalComptimeInt(it.expr) orelse {
|
|
|
|
|
if (self.diagnostics) |d| d.addFmt(.err, it.expr.span, "inline for: range start is not a compile-time integer", .{});
|
|
|
|
|
return self.builder.constInt(0, .void);
|
|
|
|
|
};
|
|
|
|
|
const end = self.evalComptimeInt(end_node) orelse {
|
|
|
|
|
if (self.diagnostics) |d| d.addFmt(.err, end_node.span, "inline for: range end is not a compile-time integer", .{});
|
|
|
|
|
var end = self.evalComptimeInt(it.range_end.?) orelse {
|
|
|
|
|
if (self.diagnostics) |d| d.addFmt(.err, it.range_end.?.span, "inline for: range end is not a compile-time integer", .{});
|
|
|
|
|
return self.builder.constInt(0, .void);
|
|
|
|
|
};
|
|
|
|
|
if (it.inclusive) end += 1;
|
|
|
|
|
const capture_name = if (fe.captures.len > 0) fe.captures[0].name else "";
|
|
|
|
|
|
|
|
|
|
var i: i64 = start;
|
|
|
|
|
while (i < end) : (i += 1) {
|
|
|
|
|
@@ -493,22 +497,22 @@ pub fn lowerInlineRangeFor(self: *Lowering, fe: *const ast.ForExpr, end_node: *N
|
|
|
|
|
// `print(i)`) and as a comptime constant (for `xs[i]` substitution).
|
|
|
|
|
var had_prev = false;
|
|
|
|
|
var prev: ComptimeValue = undefined;
|
|
|
|
|
if (fe.capture_name.len > 0) {
|
|
|
|
|
body_scope.put(fe.capture_name, .{ .ref = self.builder.constInt(i, .s64), .ty = .s64, .is_alloca = false });
|
|
|
|
|
if (self.comptime_constants.get(fe.capture_name)) |p| {
|
|
|
|
|
if (capture_name.len > 0) {
|
|
|
|
|
body_scope.put(capture_name, .{ .ref = self.builder.constInt(i, .s64), .ty = .s64, .is_alloca = false });
|
|
|
|
|
if (self.comptime_constants.get(capture_name)) |p| {
|
|
|
|
|
had_prev = true;
|
|
|
|
|
prev = p;
|
|
|
|
|
}
|
|
|
|
|
self.comptime_constants.put(fe.capture_name, .{ .int_val = i }) catch {};
|
|
|
|
|
self.comptime_constants.put(capture_name, .{ .int_val = i }) catch {};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
self.lowerBlock(fe.body);
|
|
|
|
|
|
|
|
|
|
if (fe.capture_name.len > 0) {
|
|
|
|
|
if (capture_name.len > 0) {
|
|
|
|
|
if (had_prev) {
|
|
|
|
|
self.comptime_constants.put(fe.capture_name, prev) catch {};
|
|
|
|
|
self.comptime_constants.put(capture_name, prev) catch {};
|
|
|
|
|
} else {
|
|
|
|
|
_ = self.comptime_constants.remove(fe.capture_name);
|
|
|
|
|
_ = self.comptime_constants.remove(capture_name);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|