feat(borrow): CORE-01 Slice D (#177 pt3) — reject @linear capture by closure at borrow check by hyperpolymath · Pull Request #397 · hyperpolymath/affinescript · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 90 additions & 4 deletions lib/borrow.ml
16 changes: 16 additions & 0 deletions test/e2e/fixtures/slice_d_captured_linear_let_rejected.affine
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright (c) 2026 Jonathan D.A. Jewell
//
// CORE-01 pt3 Slice D / #177: a closure that captures a @linear
// let-binding must be rejected by the borrow checker — a closure
// may be called 0..N times, so capturing a @linear binding lifts
// its consumption count out of finite-once provability.
// Expected: Error LinearCapturedByClosure.

module SliceDCapturedLinearLetRejected;

fn capture_linear_let() -> Int {
@linear let x = 42;
let f = fn() => x + 1;
f()
}
17 changes: 17 additions & 0 deletions test/e2e/fixtures/slice_d_captured_linear_param_rejected.affine
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright (c) 2026 Jonathan D.A. Jewell
//
// CORE-01 pt3 Slice D / #177: a closure that captures a @linear
// function parameter is rejected at the borrow checker for the
// same reason as the let-binding case — a closure may be called
// 0..N times. Pre-Slice-D this would have surfaced only at the
// quantity checker (as "used multiple times via QOmega scaling");
// now it surfaces at borrow check with a span pointing at the
// closure. Expected: Error LinearCapturedByClosure.

module SliceDCapturedLinearParamRejected;

fn capture_linear_param(@linear y: Int) -> Int {
let f = fn() => y + 1;
f()
}
15 changes: 15 additions & 0 deletions test/e2e/fixtures/slice_d_captured_nonlinear_ok.affine
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright (c) 2026 Jonathan D.A. Jewell
//
// CORE-01 pt3 Slice D / #177: anti-regression — capturing a
// non-linear (unannotated → defaults to QOmega) binding by a
// closure must still pass. This pins that the new rejection is
// scoped to @linear captures, not any free-var capture. Expected: Ok.

module SliceDCapturedNonlinearOk;

fn capture_nonlinear() -> Int {
let x = 7;
let f = fn() => x + 1;
f()
}
46 changes: 46 additions & 0 deletions test/test_e2e.ml
Loading