Skip to content

Autoharness: instantiate Fn-bounded type parameters with nondet closures - #4726

Merged
feliperodri merged 5 commits into
model-checking:mainfrom
tautschnig:c5p2-closures-pr
Aug 27, 2026
Merged

Autoharness: instantiate Fn-bounded type parameters with nondet closures#4726
feliperodri merged 5 commits into
model-checking:mainfrom
tautschnig:c5p2-closures-pr

Conversation

@tautschnig

Copy link
Copy Markdown
Member

Stacked on #4706 (per-parameter generic instantiation); only the last commit is new. Marked draft until #4706 merges. Part of #3832.

Fn-bound generic functions outnumber Iterator-bound ones 4:1 in the top-500 crates.io corpus (2,373 vs 612 signatures), and no primitive candidate can satisfy an Fn bound — these functions were all skipped.

Approach

Fn/FnMut/FnOnce-bounded type parameters are instantiated with the function item type of a matching nondet_fn* model: fn items implement all three Fn traits and are zero-sized (materialized as a constant in the harness), and each call returns a fresh nondeterministic value — over-approximating every real closure with that signature, including stateful FnMut ones. Harness names are self-documenting: apply::<fn(u8) -> u8 {kani::arbitrary::nondet_fn1::<u8, u8>}>.

Design points (each validated on the crate that motivated it)

  • Shape-based model selection: by-value models bind input regions early and cannot satisfy HRTB bounds like for<'a> Fn(&'a T); the four dominant by-ref shapes (96% of ref-involving corpus bounds) get region-polymorphic models with late-bound regions.
  • Binder handling: candidate derivation erases late-bound regions instead of skipping binders (escaping bound vars panic the trait solver — tap).
  • Param-referencing signatures (fn apply<T, F: Fn(T) -> T>): deferred specs substituted per candidate choice, normalized (tap), and admission-checked against the model's own R: Arbitrary bound (syn).
  • Vacant vtable slots: concrete instantiations may leave an HRTB method slot vacant where the vtable struct type declares a pointer; pad with typed null, mirroring rustc's layout (reqwest).
  • Arity ceiling 3, by data: 98.5% of corpus Fn-bound signatures.

Iterator/IntoIterator-bounded parameters are a follow-up PR (the iterator model rides on the unbounded-Vec generation of #4721).

In the crates.io sweep this converted 3,867 generic functions from skipped to attempted, with closure-instantiated harnesses in 76 crates.

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses.

@github-actions github-actions Bot added Z-EndToEndBenchCI Tag a PR to run benchmark CI Z-CompilerBenchCI Tag a PR to run benchmark CI labels Aug 7, 2026
@feliperodri feliperodri added the Z-Autoharness Issue related to autoharness subcommand label Aug 7, 2026
@feliperodri feliperodri added this to the Autoharness milestone Aug 18, 2026
@feliperodri
feliperodri marked this pull request as ready for review August 25, 2026 21:07
@feliperodri
feliperodri requested review from a team as code owners August 25, 2026 21:07
Corpus data (top-500 crates.io): Fn-bound generic functions outnumber
Iterator-bound ones 4:1 (2,373 vs 612 signatures); no primitive candidate
can ever satisfy an Fn bound, so these functions were all skipped.

Fn/FnMut/FnOnce-bounded type parameters are instantiated with the
function item type of a matching nondet model (kani::arbitrary::
nondet_fn*): function items implement all three Fn traits and are
zero-sized (the harness materializes the value as a zero-sized constant),
and each call returns a fresh nondeterministic value, over-approximating
every real closure with that signature -- including stateful FnMut ones.

Design points, each validated on the crate that motivated it:
- Models are selected by input SHAPE, not just arity: by-value models
  bind their input regions early-bound and cannot satisfy HRTB bounds
  like for<'a> Fn(&'a T), so the four dominant by-ref shapes (96% of the
  3,768 ref-involving Fn bounds in the corpus) get region-polymorphic
  models (nondet_fn1_ref etc.) whose fn items carry late-bound regions.
- Candidate derivation reads the Fn trait predicates (tupled inputs) and
  the FnOnce::Output projection, erasing late-bound regions rather than
  skipping binders (escaping bound vars panic the trait solver; tap).
- Signatures referencing other generic parameters (fn apply<T, F: Fn(T)
  -> T>) are collected as deferred specs: their slots carry a placeholder
  through the candidate search and are substituted per candidate choice
  (EarlyBinder::instantiate), normalized (unnormalizable projections such
  as <i32 as Tap>::Val abort the choice; tap), and admission-checked
  against the model's own R: Arbitrary bound (Instance::resolve does not
  check bounds; syn).
- Vtables built for a concrete type may mark a method slot Vacant where
  the trait's vtable struct type declares a method pointer (an HRTB
  predicate the concrete fn item does not satisfy): pad the slot with a
  typed null, mirroring rustc's vtable layout (reqwest). Dispatchable
  methods use real slots via the region-polymorphic models.
- Arity ceiling of 3 justified by data: 98.5% of corpus Fn-bound
  signatures have arity <= 3.

The regression test pins bug-finding through nondet closures (overflow on
unconstrained results at arities 1 and 2), cover-based reachability of
closure-dependent branches, HRTB closures plain and dyn-coerced through a
wrapper struct, param-referencing signatures, and tuple arguments.

Co-authored-by: Kiro <kiro-agent@users.noreply.github.com>
Copilot AI lite review requested due to automatic review settings August 26, 2026 14:25

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@feliperodri
feliperodri enabled auto-merge August 26, 2026 14:30
@feliperodri
feliperodri added this pull request to the merge queue Aug 26, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Aug 26, 2026
@feliperodri
feliperodri enabled auto-merge August 26, 2026 15:31
@feliperodri
feliperodri disabled auto-merge August 26, 2026 21:29
@feliperodri
feliperodri added this pull request to the merge queue Aug 26, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Aug 26, 2026
@feliperodri
feliperodri enabled auto-merge August 26, 2026 23:57
@feliperodri
feliperodri added this pull request to the merge queue Aug 27, 2026
Merged via the queue into model-checking:main with commit 3c30080 Aug 27, 2026
34 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Z-Autoharness Issue related to autoharness subcommand Z-CompilerBenchCI Tag a PR to run benchmark CI Z-EndToEndBenchCI Tag a PR to run benchmark CI

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants