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
{{ message }}
Repository navigation
fix(cargo-gamma): generate reference return value mutants - #243
Functions returning &T or &mut T receive no whole-body fn_value replacement for most referent types, even when Gamma already has useful replacement values for T. A generated Box::leak replacement can also accumulate allocations if the mutated function is called repeatedly.
Change
Reuse existing fn_value candidates for reference referents. Shared references to literals and name-independent tuple or array compositions borrow them directly. Empty mutable slices and zero-length arrays use &mut []. Other constructible referents use Box::leak.
Resolve local reference and referent aliases, including type arguments and defaults, while withholding guesses for const-parameter aliases and direct references to impl Trait.
Keep no-op detection conservative around leaked defaults and macro names, and serialize generated reference-leak mutants across workers.
Count every generated reference-leak replacement before construction. After 8192 calls in one test process, the runtime stops it with a distinct leaklimit outcome, separate from outofmem and a test kill. On macOS, the first completed construction establishes an RSS baseline; later growth of at least 64 MiB also produces leaklimit. This is a soft process-local check: it cannot stop a single huge construction or a hang inside it. The outcome remains eligible for cargo gamma suppress.
On other hosts, leave generated leaks pending when a reachable test binary has no enforced memory ceiling. A complete census can still report NoCoverage without running that mutant.
Update the affected design and user docs, and add collection, instrumentation, runtime, sweep, and compiler-acceptance coverage.
Validation
cargo test -p cargo-gamma-engine --lib -q: 827 passed.
chinayangxiaowei
changed the title
Generate fn_value mutants for reference returns
fix(cargo-gamma): generate reference return value mutants
Oct 10, 2026
The reason will be displayed to describe this comment to others. Learn more.
This is a deliberate coverage tradeoff. The macOS monitor is documented as a soft, process-local signal: it admits the first construction, samples only after it returns, and cannot prevent a one-call OOM or descendant allocation. A normal test can make one construction, so withholding every candidate without a hard subtree ceiling would discard useful reference mutants on macOS. We classify a one-call OOM separately and do not claim a hard memory bound. I am leaving this design concern visible for maintainer judgment.
The reason will be displayed to describe this comment to others. Learn more.
Correction to my wording: a one-call OOM is outside this guard and may be handled by the OS; Gamma does not guarantee an outofmem verdict for it on macOS without an enforced ceiling. The documented choice is to keep the candidate despite that residual risk.
Two concurrent calls can both load a zero baseline and choose EstablishBaseline; after one CAS wins, the loser ignores the established value and never checks whether its RSS sample is already 64 MiB above it. If no later call occurs, the documented growth guard is missed. Retry the decision with the value returned by a failed CAS.
This only compares the payload of the outer leak and does not recurse when that payload contains another generated leak. The new &Option<&T> case in ops/collect/tests.rs:4112-4135 therefore retains ...Default::default()... against an otherwise identical ...T::default()...; both use the same proven Default implementation, so the reported mutant is unchanged and cannot be killed. Recurse through matching leak structure (while checking surrounding tokens) before retaining it.
Dispatch generic values using the instantiated referent
The parameterized referent is instantiated into resolved_elem above, but the generic branch dispatches on the original elem and loses those substitutions again. With type Id<T> = T; fn f() -> &'static Id<bool>, values_for(Id<bool>) raw-resolves to T, so this misses the existing &true/&false candidates and falls back to an unknown/default value. Dispatch using the instantiated referent while preserving the intended alias-constructor safeguards.
The reason will be displayed to describe this comment to others. Learn more.
One new correctness finding: a successful leak-limit receipt can be hidden by a later normal-exit record from another nextest process, converting resource exhaustion into mutation-kill credit.
The new reference construction preserves explicit shared reborrows, instantiates supported alias arguments/defaults, and avoids allocations for empty mutable slices/literal zero-length arrays; compiler-acceptance fixtures cover the intended type matrix. The distinct leaklimit outcome is consistently undetected across scoring, schema reasons, diagnostics, and suppression. Existing discussions about the macOS soft bound, marker authentication, stated async expressions, and zero-length syntax are not duplicated.
All five passes covered collection, instrumentation, runtime/FFI, scheduler admission, verdict parsing, and outcome consumers. No builds, tests, or benchmarks were executed. macOS RSS sampling remains a documented soft bound rather than host-memory containment; native macOS assurance is not established by this static review.
The reason will be displayed to describe this comment to others. Learn more.
A later nextest exit can hide a valid leak-limit receipt and earn kill credit — Correctness · High · High confidence
One attempt exports the same reach-file path to the nextest runner and its test children (observe_with:1427–1430, configure:891–893). Consider two already-running tests of this binary: process A exhausts the allowance, appends and closes REFERENCE_LEAK_LIMIT_RECORD in runtime.rs:443–452; process B then exits normally and appends its bitmap plus SEAL in runtime.rs:1946–1974, before or after A's immediate exit. The receipt is valid but no longer last, so this predicate becomes false. Nextest reports a test failure and verdict_with_reference_leak_receipt leaves it as Failed, giving the suite detection credit for a resource guard rather than an assertion.
The new scheduler resource serializes mutants, not nextest test children: launcher:692–695 requests serial nextest only for configured binary/test resources. Ordinary tests have none; the process-local census lock also cannot order different children's append streams. Fail-fast cannot prevent B's exit from interleaving with A's receipt/termination.
The new test at line 2220 explicitly asserts that [REFERENCE_LEAK_LIMIT_RECORD, SEAL] is not a limit receipt, so it locks in this reachable misclassification.
Direction: Preserve successfully recorded limit evidence across other processes' census appends, e.g. with a separately owned receipt channel or a validated shared-record protocol rather than a last-record requirement.
Done when: An in-process fixture models a valid limit receipt followed by another child's reach/seal records and still produces ReferenceLeakLimit/undetected, while malformed or failed receipt writes remain unjudged and ordinary test output cannot forge the verdict.
The reason will be displayed to describe this comment to others. Learn more.
Including leaklimit in the default suppression set is the right choice: it records a resource limit reached by the generated replacement, not an assertion detecting the mutation. Users should be able to suppress these outcomes alongside timeout and outofmem, while retaining the existing preview/--apply approval step. Thanks for wiring this into the default and covering it with a regression test.
Closing this PR rather than reworking it. Thanks to everyone who reviewed it, Martin Taillefer (@geeknoid) and martinhavelka (@wukchung) in particular. The review rounds surfaced real problems, and they led to the conclusion below.
Why close. Nearly all of this PR's added coverage comes from leaked-allocation candidates (&*Box::leak(Box::new(v)), Vec::leak(vec![..])). #208 deliberately removed exactly that approach. A fabricated leaked allocation can change ownership and lifetime behaviour, and referents often cannot be constructed. I should have started from that decision instead of reintroducing the approach. Most of this PR's growth during review, including the leak-limit counter, the macOS RSS check, the receipt protocol, and the leaklimit outcome, existed only to contain the consequences of those leaks.
Usage data. We checked the draft build against a downstream project that ran it for several days (247 reports, no reruns):
17 unique leak-based mutants were generated. 14 failed to compile (E0277: the referent has no Default). The other 3 were killed, but they cover only two single-field accessors, and existing tests caught both.
No reference-return mutant ever survived: 210 were killed and 63 failed to compile. None of them exposed a test gap.
Of 266 reference-returning functions in that codebase, &str (138) and slices (16) are already covered on main. None returns a reference to a scalar or literal composition. The remaining 112 return references to user structs, Path, Arc, Vec, Option and similar types, which no allocation-free candidate can cover.
So an allocation-free subset compatible with #208, such as borrowing promoted literals like &0, &true or &(0, false), would add almost nothing in practice. Copy types are rarely returned by reference. For such sites, #[gamma::value(...)] remains the right tool, as #208 documents.
Memory. The underlying concern was that a mutant can allocate without bound on a host with no memory ceiling. That is not specific to leaks. A mutated loop condition does the same, much faster. #249 addresses it for every mutant on macOS. Linux and Windows already have a ceiling through cgroups and job objects. All open review threads here concern the leak machinery or its candidates, so they lapse with this PR.
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
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.
Problem
Functions returning
&Tor&mut Treceive no whole-bodyfn_valuereplacement for most referent types, even when Gamma already has useful replacement values forT. A generatedBox::leakreplacement can also accumulate allocations if the mutated function is called repeatedly.Change
fn_valuecandidates for reference referents. Shared references to literals and name-independent tuple or array compositions borrow them directly. Empty mutable slices and zero-length arrays use&mut []. Other constructible referents useBox::leak.impl Trait.leaklimitoutcome, separate fromoutofmemand a test kill. On macOS, the first completed construction establishes an RSS baseline; later growth of at least 64 MiB also producesleaklimit. This is a soft process-local check: it cannot stop a single huge construction or a hang inside it. The outcome remains eligible forcargo gamma suppress.NoCoveragewithout running that mutant.Validation
cargo test -p cargo-gamma-engine --lib -q: 827 passed.cargo test -p cargo-gamma-rt --lib -q: 72 passed.cargo test -p cargo-gamma-lib --features internals --test instrumented_compiles -q: 17 passed.cargo-gamma-libtests for reference-leak policy, resource verdicts, scoring, reporting, CI output, dashboard, and suppression passed.cargo check -p cargo-gamma-lib --target x86_64-unknown-linux-gnu -qpassed.cargo clippy -p cargo-gamma-engine -p cargo-gamma-rt --all-targets -- -D warningsandcargo +nightly fmt --all -- --checkpassed. Thecargo-gamma-libClippy check passed when unrelated existing macOS/test lints were allowed.This PR contains only reference return-value mutation. It does not include #240, #241, or #242.