Skip to content

Add a #[derive(Invariant)] macro - #3250

Merged
adpaco merged 7 commits into
model-checking:mainfrom
adpaco:derive-invariant
Jun 13, 2024
Merged

Add a #[derive(Invariant)] macro#3250
adpaco merged 7 commits into
model-checking:mainfrom
adpaco:derive-invariant

Conversation

@adpaco

Copy link
Copy Markdown
Contributor

This PR adds a #[derive(Invariant)] macro for structs which allows users to automatically derive the Invariant implementations for any struct. The derived implementation determines the invariant for the struct as the conjunction of invariants of its fields. In other words, the invariant is derived as true && self.field1.is_safe() && self.field2.is_safe() && ...

For example, for the struct

#[derive(kani::Invariant)]structPoint<X,Y>{x:X,y:Y,}

we derive the Invariant implementation as

impl<X: kani::Invariant,Y: kani::Invariant> kani::InvariantforPoint<X,Y>{fnis_safe(&self) -> bool{true && self.x.is_safe() && self.y.is_safe()}}

Related #3095

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

@adpaco
adpaco requested a review from a team as a code ownerJune 10, 2024 19:49
@github-actionsgithub-actionsBot added the Z-EndToEndBenchCI Tag a PR to run benchmark CI label Jun 10, 2024
@adpacoadpaco removed their assignment Jun 10, 2024

@celinvalcelinval 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.

Can you add a little test where an invariant created fails? Something like you have a type A with a custom safety invariant and a struct B with a derived Invariant that has a member of type A. Now ensure that breaking the A invariant in the B field also breaks B invariant. Thanks!

@adpaco

Copy link
Copy Markdown
ContributorAuthor

Added a test where the invariant check fails but let me know if you have suggestions for the setup step (right now it's just an overly generic Arbitrary implementation).

@celinvalcelinval 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.

We need to figure out how to ensure Arbitrary respects Invariant.

@adpaco
adpaco enabled auto-merge (squash) June 13, 2024 23:14
@adpaco
adpaco merged commit 5c7cd63 into model-checking:mainJun 13, 2024
adpaco added a commit that referenced this pull request Jul 22, 2024
…rary` and `Invariant` macros (#3283)
This PR enables an `#[safety_constraint(...)]` attribute helper for the
`#[derive(Arbitrary)]` and `#[derive(Invariant)]` macro.
For the `Invariant` derive macro, this allows users to derive more
sophisticated invariants for their data types by annotating individual
named fields with the `#[safety_constraint(<cond>)]` attribute, where
`<cond>` represents the predicate to be evaluated for the corresponding
field. In addition, the implementation always checks `#field.is_safe()`
for each field.
For example, let's say we are working with the `Point` type from #3250
```rs
#[derive(kani::Invariant)]
struct Point<X, Y> {
x: X,
y: Y,
}
```
and we need to extend it to only allow positive values for both `x` and
`y`.
With the `[safety_constraint(...)]` attribute, we can achieve this
without explicitly writing the `impl Invariant for ...` as follows:
```rs
#[derive(kani::Invariant)]
struct PositivePoint {
#[safety_constraint(*x >= 0)]
x: i32,
#[safety_constraint(*y >= 0)]
y: i32,
}
```
For the `Arbitrary` derive macro, this allows users to derive more
sophisticated `kani::any()` generators that respect the specified
invariants. In other words, the `kani::any()` will assume any invariants
specified through the `#[safety_constraint(...)]` attribute helper.
Going back to the `PositivePoint` example, we'd expect this harness to
be successful:
```rs
#[kani::proof]
fn check_invariant_helper_ok() {
let pos_point: PositivePoint = kani::any();
assert!(pos_point.x >= 0);
assert!(pos_point.y >= 0);
}
```
The PR includes tests to ensure it's working as expected, in addition to
UI tests checking for cases where the arguments provided to the macro
are incorrect. Happy to add any other cases that you feel are missing.
Related #3095
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Z-EndToEndBenchCITag a PR to run benchmark CI

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@adpaco@celinval