Skip to content

(feat): EscapedPoolArray guard — block-form tail escapes trap on use, escape_lint defaults to warn#55

Merged
mgyoo86 merged 7 commits into
masterfrom
feat/escape-lint-warn-default
Jul 13, 2026
Merged

(feat): EscapedPoolArray guard — block-form tail escapes trap on use, escape_lint defaults to warn#55
mgyoo86 merged 7 commits into
masterfrom
feat/escape-lint-warn-default

Conversation

@mgyoo86

@mgyoo86 mgyoo86 commented Jul 13, 2026

Copy link
Copy Markdown
Member

What

  • Block-form implicit-tail escapes (bare v, (x, v), dest[...] = v, v .= rhs, bare acquire!) no longer error at expansion. They @warn, and the escaping value is replaced by an inert, metadata-only EscapedPoolArray guard: discarded → silent; first use → EscapedPoolUseError with variable name, shape, and scope location. Tail side effects (the copy in dest[...] = v, broadcasts) still run; tuple literals are poisoned element-wise so non-pool elements stay usable.
  • Function-form tails and explicit return (a begin block is not a function boundary) still always throw PoolEscapeError at expansion.
  • escape_lint now tunes only the block-form severity: "warn" (new default) = warn + guard, "error" = strict expansion error (previous behavior), "off" = untouched codegen.

Why

The macro cannot see its own call site, so whether a block's value is used is undecidable at expansion. Erroring was over-eager on real code — e.g. all_yy[:, :, k] = each_y as a scope tail inside Threads.@threads (value discarded, data safely copied) — while a warning alone is noise on safe code and ignorable on buggy code. The guard defers the decision to the only point where it is decidable: first actual use.

Notes

  • Zero hot-path cost: clean scopes expand identically; the guard holds metadata only (never the array) — no GC lifetime extension, no unwrap path.
  • Rides the shared block codegen: CUDA/Metal inherit it with zero ext/ changes.
  • Validation: full local suite green incl. on-device Metal; CUDA suite green on remote.

mgyoo86 added 3 commits July 13, 2026 13:29
… warn by default

Block-form implicit incidental tails (dest[...] = v, v .= rhs, bare
acquire!, v = acquire!) now report at the escape_lint severity, default
"warn": the macro cannot see its own call site, so whether the block's
value is actually used is undecidable at expansion time. Function-form
tails and explicit `return` tails (which deliver the value to the
enclosing function's caller — a begin block is not a function boundary)
remain unconditional PoolEscapeErrors regardless of the preference.

escape_lint default flips "error" -> "warn"; "error" restores the
strict behavior; "off" unchanged. Lint message now states the
undecidability, links the compile-time safety docs, and names the
always-error forms.
…me traps

A block-form @with_pool cannot know whether its value is used or
discarded (the macro cannot see its own call site), so a statically-
detected escaping implicit tail is now rewritten instead of erroring:
the escaping value is replaced by an inert, metadata-only
EscapedPoolArray guard (variable name, type, dims, scope location — it
deliberately does NOT hold the array). A discarded escape is completely
silent; the first actual use throws EscapedPoolUseError with full
provenance and fix guidance. Tail side effects (dest[...] = v copies,
broadcasts, the acquire itself) still execute; tuple/vect/NamedTuple
literals are poisoned element-wise so non-pool elements stay usable.

Form-based severity is unchanged from the previous commit and now also
applies to Stage-1 direct-exposure shapes: bare-var and container-
literal tails in block form demote to warn + guard, while function-form
tails and explicit returns (a begin block is not a function boundary)
always throw PoolEscapeError. escape_lint tunes only the block-form
severity: "warn" (default) = warn + guard rewrite, "error" = strict
expansion error, "off" = untouched codegen.

The guard rides the shared block codegen, so CUDA/Metal inherit it with
no extension changes; the maybe/disabled branches keep the original
body (pooling off => plain arrays, nothing escapes). Clean scopes are
byte-identical in expansion — zero hot-path cost.
@codecov

codecov Bot commented Jul 13, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 88.43537% with 17 lines in your changes missing coverage. Please review.
✅ Project coverage is 67.88%. Comparing base (307baf2) to head (3a937fc).

Files with missing lines Patch % Lines
src/macros.jl 83.96% 17 Missing ⚠️

❌ Your project check has failed because the head coverage (67.88%) is below the target coverage (95.00%). You can increase the head coverage or adjust the target coverage.

❗ There is a different number of reports uploaded between BASE (307baf2) and HEAD (3a937fc). Click for more details.

HEAD has 7 uploads less than BASE
Flag BASE (307baf2) HEAD (3a937fc)
9 2
Additional details and impacted files

Impacted file tree graph

@@             Coverage Diff             @@
##           master      #55       +/-   ##
===========================================
- Coverage   96.14%   67.88%   -28.26%     
===========================================
  Files          16       17        +1     
  Lines        3862     3983      +121     
===========================================
- Hits         3713     2704     -1009     
- Misses        149     1279     +1130     
Files with missing lines Coverage Δ
src/AdaptiveArrayPools.jl 100.00% <ø> (ø)
src/escape_guard.jl 100.00% <100.00%> (ø)
src/macros.jl 91.98% <83.96%> (-5.66%) ⬇️

... and 7 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

mgyoo86 added 4 commits July 13, 2026 14:41
`_ = acquire!(...)` as a block tail — the canonical discard spelling —
made the poison rewrite emit `EscapedPoolArray(_, ...)`, an rvalue read
of a write-only identifier, which fails lowering with a cryptic syntax
error instead of expanding. All-underscore LHS now takes the
wrap-the-whole-assignment path (the assignment evaluates to its RHS, so
the ctor records and discards the fresh array without touching `_`).

Found by Codex review; transform-level and runtime regression tests
added.
Cover the identity-unwrap leaf, nested-literal, identity-element, and
acquire-call-element paths of _poison_block_tails/_poison_literal_value
(the 8 patch lines codecov flagged once all uploads merged). Also
re-triggers CI so codecov re-posts its stale 2-of-9-uploads statuses.
Nine matrix legs all uploaded coverage, and codecov posted its PR
status/comment as soon as the first uploads merged — a partial report
missing one version tree's branches showed a phantom 28-point project
drop (PR #55: 67.88% from 2 of 9 uploads; 95.94% once all merged).

Only two uploads carry unique lines: ubuntu-lts (legacy-gated branches
of the shared sources) and ubuntu-1.x (modern tree). Upload from
exactly those two and set after_n_builds: 2 so codecov waits for both
before notifying. Tests still run on the full 3x3 matrix.
Also gives codecov a fresh commit for a full notification cycle — the
1b7f7d8 statuses never posted despite 3 merged uploads (notify appears
PR-context-stuck; master statuses post fine).
@mgyoo86
mgyoo86 merged commit 1263936 into master Jul 13, 2026
20 of 21 checks passed
@mgyoo86
mgyoo86 deleted the feat/escape-lint-warn-default branch July 13, 2026 23:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant