You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
fix(quantity): scaled Let rule + L-to-R eval_list — closes BUG-001/002/003
Two soundness fixes that share a single test infrastructure pass.
BUG-001 (high severity, soundness — ω-let smuggles linear values):
The quantity-checking Let case now implements the QTT-orthodox
scaled Let rule per ADR-002:
Γ₁ ⊢ e₁ : A (Γ₂, x:^q A) ⊢ e₂ : B
─────────────────────────────────────
q·Γ₁ + Γ₂ ⊢ let x :^q = e₁ in e₂ : B
Implementation: snapshot the env, walk e1 (which records usages
into the live env), compute the per-variable delta, scale each
delta by q via the new scale_usage helper, restore the snapshot,
and re-apply the scaled deltas as additions. Then walk e2 normally.
The interesting case is q = ω: scale_usage QOmega UOne = UMany,
which causes a linear variable consumed in e1 to be reported as
"used multiple times" once viewed through the @unrestricted binder.
This is what closes BUG-001.
BUG-002 (medium, semantics — :0 lets do not erase their RHS):
Closed transitively by the same scaling code path. scale_usage
QZero _ = UZero, so RHS contributions to outer linear variables
are correctly dropped under @Erased binders. The optional
interpreter-side erasure (skipping eval entirely) is a separate
optimisation, deferred.
BUG-003 (medium, semantics — eval_list right-to-left):
lib/interp.ml eval_list rewritten from List.fold_right (which is
right-to-left under OCaml's strict monadic bind) to an explicit
L-to-R recursive loop with terminal List.rev. Per ADR-003. Brings
ExprApp/ExprTuple/ExprArray/ExprRecord into agreement with
ExprBinary, which was already left-to-right.
Error vocabulary updated: format_quantity_error now speaks
@linear/@erased/@unrestricted regardless of which surface form
the user wrote, via canonical_quantity_name and usage_in_words
helpers. Source vocabulary and diagnostic vocabulary stay aligned
per ADR-007.
Four behavioural regression fixtures and test cases:
- bug_001_omega_let_smuggles_linear.affine (Option C, must reject)
- bug_001_sugar_form.affine (Option B, must reject)
- affine_let_valid.affine (Option C, must accept)
- affine_let_valid_sugar.affine (Option B, must accept)
All four pass. Test count 64 → 68. The 22 pre-existing baseline
failures are unchanged (zero new regressions, zero accidentally
resolved).
BUG-003 lacks a behavioural fixture in this commit because
observable order verification requires the let-mut interpreter
path, which is broken in baseline (root cause of several pre-
existing failing interp tests). The fix is verified by code
review against ADR-003.
Refs ADR-002, ADR-003, ADR-007.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
0 commit comments