Files
AILang/docs/journals/2026-05-18-iter-bugfix-over-strict-mode-ctor-rebuild-consume.md
T
Brummel 208d7095bc fix(check): GREEN — over-strict-mode recognises ctor-rebuild-from-primitive-fields as a consume
Drives the RED test from a11cb7c to GREEN. Bug-fix iter
bugfix-over-strict-mode-ctor-rebuild-consume (debug -> implement
mini). Check-only lint-precision fix; zero codegen / runtime / ABI /
schema / DESIGN.md change.

The [over-strict-mode] lint's consume-detection
(any_sub_binder_consumed_for / pattern_has_consumed_heap_binder,
crates/ailang-check/src/linearity.rs) only recognised a consume of an
(own (con T)) param when a heap-typed pattern-binder was moved out of
`match p`. When p was destructured into purely primitive fields fed
into a Term::Ctor rebuilding p's own ctor, that genuine
dismantle+rebuild consume was invisible, so the lint spuriously
advised "(borrow ...) would suffice". An LLM author "fixing" that by
flipping an export own->borrow would silently invert the ABI
ownership contract — why a low-severity advisory FP got a real
RED-first fix.

Fix: a second recognition path in any_sub_binder_consumed_for — a
`match p` arm destructuring binders out of p's ctor that references
any of them (primitive or not) inside a Term::Ctor's args in the arm
body genuinely consumes p. Two pure helpers: ctor_uses_any_binder
(finds a fresh-allocation Term::Ctor reachable in the arm body) +
term_mentions_any_binder (deep free-var scan, so binder flow through
an intervening expr like (+ acc px) is recognised, not only a bare
Var arg). Conservative toward NOT suppressing: a ctor ignoring p's
payload still warns (negative-control verified). Over-strict-only —
by-name-shadowing imprecision is extra-silence, never under-strict
(Known debt in the journal + the fn doc). The two stale doc comments
that mis-attributed this FP to a nested `match` corrected for
doc-honesty (debugger concern #2 — same code region, in-scope).

Boss-verified independently: RED test now GREEN; full
cargo test -p ailang-check 108/0 lib + every binary 0-failed with NO
existing test modified; ail check examples/embed_backtest_step_tick.ail
no longer warns over-strict on st/tick (exit 0);
embed_backtest_step_tick_borrow.ail + M3 embed_backtest_step_record.ail
still clean; embed_tick_e2e + bench posture untouched.

+166/-20 in crates/ailang-check/src/linearity.rs only. Journal +
stats + INDEX line in this commit.
2026-05-18 23:50:13 +02:00

3.2 KiB
Raw Blame History

iter bugfix-over-strict-mode-ctor-rebuild-consume — over-strict-mode FP on ctor-rebuild-from-primitive-fields

Date: 2026-05-18 Started from: a11cb7cc9f Status: DONE Tasks completed: 1 of 1

Summary

The over-strict-mode lint spuriously fired (borrow ...) would suffice on (own (con T)) params whose body destructures T into purely primitive fields and then feeds those fields into a Term::Ctor that builds a fresh allocation. The dismantle+rebuild is a genuine consume of the scrutinee's box (a borrowed value's payload cannot be taken apart and re-packaged), but the lint's consume-detection only recognised a heap-typed pattern-binder being moved out — primitive fields are filtered by pattern_has_consumed_heap_binder, so the rebuild was invisible. This is the false positive behind embed_backtest_step_tick.ail warning on both st and tick. The fix adds a second recognition path in any_sub_binder_consumed_for: for a match p arm that destructures binders out of p's ctor, if any of those binders (primitive or not) is mentioned anywhere inside a Term::Ctor's argument subtrees in the arm body, the param is genuinely consumed and the lint stays silent. Over-strict-only: a param merely read (no ctor build) still warns, proven by a negative control. No existing test changed; doc-honesty corrections applied to the two stale comments that mis-attributed this defect to nested match.

Per-task notes

  • iter bugfix-over-strict-mode-ctor-rebuild-consume.1: added collect_pattern_binders-based ctor-rebuild-consume recognition to any_sub_binder_consumed_for's matched-scrutinee arm loop in crates/ailang-check/src/linearity.rs, plus two pure private helpers (ctor_uses_any_binder — finds a fresh-allocation Term::Ctor reachable in the arm body; term_mentions_any_binder — deep free-var scan of that ctor's args, so the binder reaching the ctor through an expression like (+ acc px) is recognised, not only a bare Var arg). Corrected the stale "Known limit" doc on any_sub_binder_consumed_for and the matching "Known debt: deeply-nested-match-on-sub-binder" block in the module header — both previously mis-attributed the FP to a nested match; the real mechanism is the unrecognised ctor-rebuild consume, independent of match nesting.

Concerns

(none)

Known debt

  • term_mentions_any_binder's binder scan is by name and does not track shadowing. A destructured primitive binder shadowed and rebound before reaching the ctor would still be counted. This is not a corpus shape, and the resulting imprecision is strictly in the over-strict (extra-silence) direction the cause explicitly permits — never under-strict, so no soundness risk. Documented in the function's own doc comment; left as-is per the minimal-fix constraint.

Blocked detail

(not blocked)

Files touched

  • crates/ailang-check/src/linearity.rs (+166 / 20): lint-detection fix + two helpers + two doc-honesty corrections. Check-only; no codegen / runtime / ABI / schema / DESIGN.md change.

Stats

bench/orchestrator-stats/2026-05-18-iter-bugfix-over-strict-mode-ctor-rebuild-consume.json