(feat): compile-time and runtime structural mutation detection#35
Merged
(feat): compile-time and runtime structural mutation detection#35
Conversation
Add PoolMutationError that catches resize!, push!, pop!, append!, and other structural mutation calls on pool-backed variables at macro expansion time (zero runtime cost). Pool-backed arrays share memory with the pool's backing storage, so structural mutations corrupt pool invariants and cause undefined behavior on rewind!. Reuses existing _extract_acquired_vars infrastructure from escape detection. Wired into all 5 macro entry points (block + function forms, CPU + backend variants).
Detect resize!/push!/append! on pool-backed wrappers at rewind time by comparing wrapper state against backing vector (zero extra storage). CPU: MemoryRef identity + length divergence (1.11+ only, gated) CUDA: DataRef identity + length divergence Metal: DataRef identity + length divergence Warns (not throws) to avoid skipping cleanup of remaining TypedPools.
Runtime mutation detection (resize!/push! on pool-backed arrays) now emits a one-time @warn instead of alarming language. The pool self-heals on next acquire!, so mutation is not data corruption — only pooling benefits may be lost with temporary extra memory retention. - Update warning messages across CPU, CUDA, and Metal backends - Compile-time PoolMutationError: keep hard error, soften docstring tone - Add GPU runtime mutation test suites (CUDA + Metal) - Add mutation_safety_review.md for external review context
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #35 +/- ##
==========================================
+ Coverage 96.53% 96.55% +0.01%
==========================================
Files 14 14
Lines 2568 2726 +158
==========================================
+ Hits 2479 2632 +153
- Misses 89 94 +5
🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Pull request overview
Adds structural-mutation detection for pool-backed arrays, combining a compile-time macro check (hard error) with a rewind-time runtime check (one-time warning) across CPU, CUDA, and Metal backends.
Changes:
- Add compile-time AST-based detection of structural mutations inside
@with_pool/@maybe_with_poolblocks viaPoolMutationError. - Add runtime detection at
rewind!time that warns if cached wrappers’ underlying memory/data refs or lengths diverge from backing storage. - Add CPU/CUDA/Metal test coverage for both compile-time and runtime mutation detection, and wire new tests into each test runner.
Reviewed changes
Copilot reviewed 14 out of 14 changed files in this pull request and generated 9 comments.
Show a summary per file
| File | Description |
|---|---|
src/macros.jl |
Calls _check_structural_mutation during macro codegen and introduces PoolMutationError/MutationPoint plus AST-walking helpers. |
src/debug.jl |
Implements CPU runtime _check_wrapper_mutation! (Julia ≥ 1.11) and BitArray variant; adds fallback no-op for unsupported pools. |
src/state.jl |
Hooks runtime mutation checking into _invalidate_released_slots! before poisoning/invalidation. |
ext/AdaptiveArrayPoolsCUDAExt/debug.jl |
Adds CUDA runtime mutation detection (_check_wrapper_mutation!) and calls it during slot invalidation. |
ext/AdaptiveArrayPoolsMetalExt/debug.jl |
Adds Metal runtime mutation detection (_check_wrapper_mutation!) and calls it during slot invalidation. |
src/AdaptiveArrayPools.jl |
Exports PoolMutationError and MutationPoint. |
test/test_compile_mutation.jl |
New tests for compile-time mutation detection helpers and macro integration. |
test/test_runtime_mutation.jl |
New CPU runtime warning tests for wrapper mutation detection. |
test/runtests.jl |
Includes new CPU compile/runtime mutation test files. |
test/cuda/test_runtime_mutation.jl |
New CUDA runtime warning tests. |
test/cuda/runtests.jl |
Includes CUDA runtime mutation tests. |
test/metal/test_runtime_mutation.jl |
New Metal runtime warning tests. |
test/metal/runtests.jl |
Includes Metal runtime mutation tests. |
claudedocs/mutation_safety_review.md |
Adds design/safety analysis notes documenting the rationale and mechanisms. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
You can also share your feedback on Copilot code review. Take the survey.
Copilot review feedback: copy() with no args is invalid Julia, and the copy suggestion is unnecessary since mutation is not harmful. Simplify all warning/error messages to just recommend acquire!(pool, T, n).
Change "Request the exact size" → "Consider requesting the exact size if known in advance" and "Fix:" → "Tip:" for a gentler advisory tone.
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.
Summary
PoolMutationError) that catchesresize!/push!/pop!/deleteat!/empty!/splice!/sizehint!on pool-backed arrays inside@with_poolblocks — zero runtime cost@warn maxlog=1) that checks wrapper Memory/DataRef identity and length atrewind!time — catches mutations that escape compile-time analysisWhy warnings, not errors?
The pool is self-healing:
acquire!unconditionally resets the wrapper's memory reference to the backing vector. Structural mutation (e.g.resize!) does NOT cause data corruption or undefined behavior. The only consequences are:Runtime warning with
maxlog=1is appropriate — advisory, one-time per session, non-blocking.Compile-time remains a hard error as a zero-cost lint for definite anti-patterns.
Detection mechanisms
resize!,push!,pop!, etc. in@with_pool