Autoharness: assume safety invariants of generated values - #4677
Open
tautschnig wants to merge 2 commits into
Open
Autoharness: assume safety invariants of generated values#4677tautschnig wants to merge 2 commits into
tautschnig wants to merge 2 commits into
Conversation
Contributor
There was a problem hiding this comment.
Pull request overview
This PR updates Kani’s automatic harness generation (kani autoharness) to assume type safety invariants (kani::Invariant::is_safe()) for generated nondeterministic values, including for nested ADT fields, reducing spurious counterexamples caused by invariant-violating inputs.
Changes:
- Move/generate the
Invarianttrait fromkaniintokani_core(viagenerate_invariant!) and add anassume_safe<T: Invariant>(T) -> Tmodel function (KaniModel::AssumeSafe). - Update the autoharness and compiler-synthesized
Arbitrarypaths to wrap generated ADT values withAssumeSafewhen the type implementsInvariant. - Add a new script-based regression test and update autoharness reference docs to describe invariant assumptions.
Reviewed changes
Copilot reviewed 13 out of 13 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/script-based-pre/cargo_autoharness_assume_invariant/src/lib.rs | New regression crate covering invariant assumptions (including nested and negative controls). |
| tests/script-based-pre/cargo_autoharness_assume_invariant/config.yml | Script-based test configuration for the new regression test. |
| tests/script-based-pre/cargo_autoharness_assume_invariant/Cargo.toml | New test crate manifest for the regression. |
| tests/script-based-pre/cargo_autoharness_assume_invariant/assume-invariant.sh | Runs cargo kani autoharness -Z autoharness for the new test. |
| tests/script-based-pre/cargo_autoharness_assume_invariant/assume-invariant.expected | Expected summary output: 6 successes / 2 failures (exit code 1). |
| library/kani/src/lib.rs | Removes old invariant module exports (potential compatibility impact). |
| library/kani/src/invariant.rs | Removes the prior Invariant trait/module implementation (now generated from kani_core). |
| library/kani_core/src/lib.rs | Adds mod invariant; and invokes generate_invariant!() in kani_lib!. |
| library/kani_core/src/invariant.rs | Introduces generate_invariant! macro, the Invariant trait, and assume_safe model. |
| kani-compiler/src/kani_middle/transform/automatic.rs | Wraps generated ADT nondet values with AssumeSafe when Invariant is implemented. |
| kani-compiler/src/kani_middle/mod.rs | Adds implements_invariant resolution helper mirroring implements_arbitrary. |
| kani-compiler/src/kani_middle/kani_functions.rs | Adds KaniModel::AssumeSafe marker mapping. |
| docs/src/reference/experimental/autoharness.md | Documents invariant assumptions (needs wording alignment with implementation scope). |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+109
to
+113
| If a type implements the [`Invariant`](https://model-checking.github.io/kani/crates/doc/kani/trait.Invariant.html) trait, | ||
| Kani assumes that the nondeterministic values it generates for automatic harnesses respect the type's safety invariant, | ||
| i.e., each generated value `v` satisfies `v.is_safe()`. | ||
| This assumption applies to nested values as well: if a field of a generated value has a type that implements `Invariant`, | ||
| the field's safety invariant is assumed to hold, even if the enclosing type does not implement `Invariant` itself. |
Comment on lines
33
to
34
| pub mod futures; | ||
| pub mod invariant; | ||
| pub mod iter; |
Move the definition of the `kani::Invariant` trait and its implementations for primitive types from the `kani` library into a new `generate_invariant!` macro in `kani_core`, so that the trait is also available in `no_core` mode (e.g. for `kani verify-std`). The trait is unchanged and remains available as `kani::Invariant`. Additionally, introduce an `assume_safe` model function (registered as `KaniModel::AssumeSafe`), which assumes that a value respects its type's safety invariant. The compiler will use this model to constrain the nondeterministic values generated for automatic harnesses to values that respect the type's safety invariant. Co-authored-by: Kiro <kiro-agent@users.noreply.github.com>
Previously, automatic harnesses generated nondeterministic values purely via `Arbitrary`, so a type's safety invariant (`kani::Invariant`) was respected only if it happened to be baked into the type's `Arbitrary` implementation (e.g. via `#[safety_constraint(...)]`). Types with an `Invariant` impl but an unconstrained (or compiler-synthesized) `Arbitrary` impl would receive invariant-violating inputs, producing spurious counterexamples. Now, whenever an automatic harness or a compiler-synthesized `Arbitrary` implementation generates a nondeterministic value whose type implements `Invariant`, we insert a call to the `AssumeSafe` model, which assumes that the value satisfies `is_safe()`. This applies to nested values as well: fields of synthesized `Arbitrary` implementations are constrained the same way. This addresses the 'Types with invariants that the harnesses need to respect' item of the automatic harness generation tracking issue. Towards model-checking#3832 Co-authored-by: Kiro <kiro-agent@users.noreply.github.com>
tautschnig
force-pushed
the
autoharness-assume-invariants
branch
from
July 29, 2026 00:38
1493e41 to
92d3b3b
Compare
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.
Description
Automatic harnesses (
kani autoharness) now assume that the nondeterministic values they generate respect the type's safety invariant (kani::Invariant), including for nested types.Previously, automatic harnesses generated values purely via
Arbitrary, so a safety invariant was respected only if it happened to be baked into the type'sArbitraryimplementation (e.g. via#[safety_constraint(...)]or a manualimpl Arbitrarythat assumesis_safe()). Types with anInvariantimplementation but an unconstrained (or compiler-synthesized)Arbitraryimplementation received invariant-violating inputs, producing spurious counterexamples. Per the Unsafe Code Guidelines' definition of a safety invariant, safe code may assume its inputs uphold their types' safety invariants, so these counterexamples are noise.This PR is split into two commits:
Invarianttrait intokani_core(newgenerate_invariant!macro) so it is also available inno_coremode, and add anassume_safe<T: Invariant>model function (KaniModel::AssumeSafe) that assumesvalue.is_safe(). The trait remains available askani::Invariant; no user-facing change.Invariantin a call to theAssumeSafemodel. Since this happens incall_kani_any_for_ty, which is shared byAutomaticHarnessPass(harness arguments, including behind references) andAutomaticArbitraryPass(fields of compiler-synthesizedArbitraryimplementations), nested invariants are respected as well. A newimplements_invariantcheck mirrors the existingimplements_arbitraryresolution approach.Note that automatic harnesses still do not assert type invariants (e.g. on return values); that remains the job of function contracts such as
#[kani::ensures(|result| result.is_safe())]. The autoharness reference documentation now spells out both halves of this behavior.Testing
New script-based test
cargo_autoharness_assume_invariantwith 8 automatic harnesses: manualInvariantwith compiler-synthesizedArbitrary, manualInvariantwith unconstrained derivedArbitrary,#[safety_constraint], two nested-invariant variants, an argument behind a reference, and two negative controls (verifying that the harness neither assumes more than the invariant nor invents invariants for types without anInvariantimplementation). Before this change, three of these harnesses failed spuriously.Manually verified no regressions in: all existing
autoharnessandautoderivescript-based tests, theInvariantandderive-invariantsuites,verify_std_cmd/std_codegen(exercising theno_corepath),kani-compilerunit tests, and thekanilibrary doctests.Towards #3832 (ticks the "Types with invariants that the harnesses need to respect" box).
By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses.