(feat): EscapedPoolArray guard — block-form tail escapes trap on use, escape_lint defaults to warn#55
Merged
Merged
Conversation
… 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 Report❌ Patch coverage is
❌ 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.
Additional details and impacted files@@ 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
🚀 New features to boost your workflow:
|
`_ = 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).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
v,(x, v),dest[...] = v,v .= rhs, bareacquire!) no longer error at expansion. They@warn, and the escaping value is replaced by an inert, metadata-onlyEscapedPoolArrayguard: discarded → silent; first use →EscapedPoolUseErrorwith variable name, shape, and scope location. Tail side effects (the copy indest[...] = v, broadcasts) still run; tuple literals are poisoned element-wise so non-pool elements stay usable.return(abeginblock is not a function boundary) still always throwPoolEscapeErrorat expansion.escape_lintnow 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_yas a scope tail insideThreads.@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
ext/changes.