Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Define a struct-level #[safety_constraint(...)] attribute #3270

Merged
merged 49 commits into from
Jul 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
51b7b2f
Add `expected` & `ui` tests
adpaco-aws Jun 21, 2024
441c26e
Another newline
adpaco-aws Jun 21, 2024
f043724
Use references for expressions (`Invariant` needs fix)
adpaco-aws Jun 28, 2024
8f0ca5b
Apply fixes for format
adpaco-aws Jun 28, 2024
accec8e
Some refactoring and documentation
adpaco-aws Jun 28, 2024
f89ae0f
Fix bug and all tests
adpaco-aws Jun 28, 2024
7d7f294
Add new test for `Arbitrary`-only invariant behavior
adpaco-aws Jun 28, 2024
09e3ee5
Add test with functions
adpaco-aws Jul 8, 2024
472e659
Add test where invariant is violated thru mut
adpaco-aws Jul 8, 2024
9c92e7b
Add support for `kani::invariant` attribute
adpaco-aws Jun 17, 2024
bb70242
Add positive test for `kani::invariant` impl
adpaco-aws Jun 17, 2024
b7cd512
Declare `proc_macro_error`
adpaco-aws Jun 17, 2024
b3cc69a
Add two UI tests
adpaco-aws Jun 17, 2024
35b41fc
Fix comments in all test cases
adpaco-aws Jun 17, 2024
7bc5c3a
Fix format
adpaco-aws Jun 17, 2024
ba59f8f
Move macro to `lib.rs`, fix tests
adpaco-aws Jul 9, 2024
ad55213
Fix invalid-pred-error test.
adpaco-aws Jul 9, 2024
aa935fe
Fix another test
adpaco-aws Jul 9, 2024
631b103
Add double-attr-invariant test
adpaco-aws Jul 9, 2024
f2ee78c
Derive `Arbitrary` as well, fix tests
adpaco-aws Jul 9, 2024
e3ebe57
Add another test
adpaco-aws Jul 9, 2024
d0ae3bd
Restore import
adpaco-aws Jul 9, 2024
92fcede
Remove renamed test cases
adpaco-aws Jul 23, 2024
1fc797e
Add `is_safe` calls to generated return value
adpaco-aws Jul 23, 2024
df7f002
Merge branch 'main' into attr-invariant
adpaco-aws Jul 23, 2024
64a5a8b
Fix fmt
adpaco-aws Jul 23, 2024
812a2b0
Tweak generation of `Invariant` to work with both attr types
adpaco-aws Jul 26, 2024
5078b07
Remove code related to outdated attribute
adpaco-aws Jul 26, 2024
b05de12
Refactor safe body generation
adpaco-aws Jul 26, 2024
7cc466f
Fix default safe body for unnamed fields
adpaco-aws Jul 26, 2024
dddc556
Fix attribute tests (still need to be moved)
adpaco-aws Jul 26, 2024
64010a9
Fix generation for `Arbitrary` and refactor
adpaco-aws Jul 26, 2024
74c338e
Pass down trait name for better errors
adpaco-aws Jul 26, 2024
3d19c34
Add some documentation
adpaco-aws Jul 26, 2024
a2ed3db
Fix UI tests mostly
adpaco-aws Jul 26, 2024
24a7a9c
Fix double-attr test
adpaco-aws Jul 26, 2024
0ed1f23
Merge branch 'main' into attr-invariant
adpaco-aws Jul 26, 2024
180105c
More error handling, improve UI testing
adpaco-aws Jul 29, 2024
d51fed0
Add more tests and do some renames
adpaco-aws Jul 29, 2024
c909522
Fixes for tests
adpaco-aws Jul 29, 2024
728af85
Undo some `pub` on derive functions
adpaco-aws Jul 29, 2024
c1c3cf5
Merge branch 'main' into attr-invariant
adpaco-aws Jul 29, 2024
ffd858c
Ignore check numbers in test
adpaco-aws Jul 29, 2024
206a35c
Update test comment.
adpaco-aws Jul 29, 2024
88f7443
Add comment missing in previous commit
adpaco-aws Jul 30, 2024
8d0cd96
Address minor comments
adpaco-aws Jul 30, 2024
5847597
Add test for mixed attribute use
adpaco-aws Jul 30, 2024
ae3f70b
Merge branch 'main' into attr-invariant
adpaco-aws Jul 30, 2024
8e37e6d
Merge branch 'main' into attr-invariant
adpaco-aws Jul 31, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
321 changes: 223 additions & 98 deletions library/kani_macros/src/derive.rs

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Copyright Kani Contributors
// SPDX-License-Identifier: Apache-2.0 OR MIT

//! Check that the `#[safety_constraint(...)]` attribute works as expected when
//! deriving `Arbitrary` and `Invariant` implementations.

//! In this case, we test the attribute on a struct with a generic type `T`
//! which requires the bound `From<i32>` because of the comparisons in the
//! `#[safety_constraint(...)]` predicate. The struct represents an abstract
//! value for which we only track its sign. The actual value is kept private.

extern crate kani;
use kani::Invariant;

#[derive(kani::Arbitrary)]
#[derive(kani::Invariant)]
#[safety_constraint((*positive && *conc_value >= 0.into()) || (!*positive && *conc_value < 0.into()))]
struct AbstractValue<T>
where
T: PartialOrd + From<i32>,
{
pub positive: bool,
conc_value: T,
}

#[kani::proof]
fn check_abstract_value() {
let value: AbstractValue<i32> = kani::any();
assert!(value.is_safe());
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Check 1: check_abstract_value.assertion.1\
- Status: SUCCESS\
- Description: "assertion failed: value.is_safe()"

VERIFICATION:- SUCCESSFUL

Complete - 1 successfully verified harnesses, 0 failures, 1 total.
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Copyright Kani Contributors
// SPDX-License-Identifier: Apache-2.0 OR MIT

//! Check that the `#[safety_constraint(...)]` attribute works as expected when
//! deriving `Arbitrary` and `Invariant` implementations.

extern crate kani;
use kani::Invariant;

#[derive(kani::Arbitrary)]
#[derive(kani::Invariant)]
#[safety_constraint(*x >= 0 && *y >= 0)]
struct Point {
x: i32,
y: i32,
}

#[kani::proof]
fn check_arbitrary() {
let point: Point = kani::any();
assert!(point.x >= 0);
assert!(point.y >= 0);
assert!(point.is_safe());
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
Check 1: check_arbitrary.assertion.1\
- Status: SUCCESS\
- Description: "assertion failed: point.x >= 0"

Check 2: check_arbitrary.assertion.2\
- Status: SUCCESS\
- Description: "assertion failed: point.y >= 0"

Check 3: check_arbitrary.assertion.3\
- Status: SUCCESS\
- Description: "assertion failed: point.is_safe()"
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Copyright Kani Contributors
// SPDX-License-Identifier: Apache-2.0 OR MIT

//! Check that the `#[safety_constraint(...)]` attribute works as expected when
//! deriving `Arbitrary` and `Invariant` implementations.

extern crate kani;
use kani::Invariant;

#[derive(kani::Arbitrary)]
#[derive(kani::Invariant)]
#[safety_constraint(*x == *y)]
struct SameCoordsPoint {
x: i32,
y: i32,
}

#[kani::proof]
fn check_invariant() {
let point: SameCoordsPoint = kani::any();
assert!(point.is_safe());

// Assuming `point.x != point.y` here should be like assuming `false`.
// The assertion should be unreachable because we're blocking the path.
kani::assume(point.x != point.y);
assert!(false, "this assertion should be unreachable");
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Check 1: check_invariant.assertion.1\
- Status: SUCCESS\
- Description: "assertion failed: point.is_safe()"

Check 2: check_invariant.assertion.2\
- Status: UNREACHABLE\
- Description: ""this assertion should be unreachable""
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
Grade::check_percentage_safety.assertion.1\
- Status: SUCCESS\
- Description: "assertion failed: self.percentage <= 100"

check_grade_safe.assertion.1\
- Status: SUCCESS\
- Description: "assertion failed: grade.is_safe()"

VERIFICATION:- SUCCESSFUL

Complete - 1 successfully verified harnesses, 0 failures, 1 total.
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// Copyright Kani Contributors
// SPDX-License-Identifier: Apache-2.0 OR MIT

//! Check that the `#[safety_constraint(...)]` attribute works as expected when
//! deriving `Arbitrary` and `Invariant` implementations.

//! In this case, we test the attribute on a struct that represents a hybrid
//! grade (letter-numerical) which should keep the following equivalences:
//! - A for 90-100%
//! - B for 80-89%
//! - C for 70-79%
//! - D for 60-69%
//! - F for 0-59%
//!
//! In addition, we explicitly test that `percentage` is 0-100%

extern crate kani;
use kani::Invariant;

#[derive(kani::Arbitrary)]
#[derive(kani::Invariant)]
#[safety_constraint((*letter == 'A' && *percentage >= 90 && *percentage <= 100) ||
(*letter == 'B' && *percentage >= 80 && *percentage < 90) ||
(*letter == 'C' && *percentage >= 70 && *percentage < 80) ||
(*letter == 'D' && *percentage >= 60 && *percentage < 70) ||
(*letter == 'F' && *percentage < 60))]
struct Grade {
letter: char,
percentage: u32,
}

impl Grade {
pub fn check_percentage_safety(&self) {
assert!(self.percentage <= 100);
}
}

#[kani::proof]
fn check_grade_safe() {
let grade: Grade = kani::any();
assert!(grade.is_safe());
grade.check_percentage_safety();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Copyright Kani Contributors
// SPDX-License-Identifier: Apache-2.0 OR MIT

//! Check that there is a compilation error when the `#[safety_constraint(...)]`
//! attribute is used more than once on the same struct.

extern crate kani;
use kani::Invariant;

#[derive(Invariant)]
#[safety_constraint(*x >= 0)]
#[safety_constraint(*y >= 0)]
struct Point {
x: i32,
y: i32,
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
error: Cannot derive `Invariant` for `Point`
|
| #[derive(Invariant)]
| ^^^^^^^^^
|
note: `#[safety_constraint(...)]` cannot be used more than once.
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
error[E0308]: mismatched types
|
| #[safety_constraint(x >= 0 && y >= 0)]
| ^ expected `&i32`, found integer
|
help: consider dereferencing the borrow
|
| #[safety_constraint(*x >= 0 && y >= 0)]
| +

error[E0308]: mismatched types
|
| #[safety_constraint(x >= 0 && y >= 0)]
| ^ expected `&i32`, found integer
|
help: consider dereferencing the borrow
|
| #[safety_constraint(x >= 0 && *y >= 0)]
|
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// Copyright Kani Contributors
// SPDX-License-Identifier: Apache-2.0 OR MIT

//! Check that there is a compilation error when the predicate passed to the
//! `#[safety_constraint(...)]` attribute would result in a compiler error.
//!
//! Note: the `#[derive(kani::Invariant)]` macro is required for the compiler error,
//! otherwise the `#[safety_constraint(...)]` attribute is ignored.

extern crate kani;

// Note: The struct fields `x` and `y` are references in this context, we should
// refer to `*x` and `*y` instead.
#[derive(kani::Invariant)]
#[safety_constraint(x >= 0 && y >= 0)]
struct Point {
x: i32,
y: i32,
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
error: Cannot derive `Invariant` for `Point`
|
| #[derive(Invariant)]
| ^^^^^^^^^
|
note: `#[safety_constraint(...)]` cannot be used in struct and its fields simultaneously
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// Copyright Kani Contributors
// SPDX-License-Identifier: Apache-2.0 OR MIT

//! Check that there is a compilation error when the `#[safety_constraint(...)]`
//! attribute for struct and the `#[safety_constraint(...)]` attribute for
//! fields is used at the same time.

extern crate kani;
use kani::Invariant;

#[derive(Invariant)]
#[safety_constraint(*x >= 0)]
struct Point {
x: i32,
#[safety_constraint(*y >= 0)]
y: i32,
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
error: Cannot derive `Invariant` for `MyEnum`
|
| #[derive(kani::Invariant)]
| ^^^^^^^^^^^^^^^
|
note: `#[safety_constraint(...)]` can only be used in structs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Copyright Kani Contributors
// SPDX-License-Identifier: Apache-2.0 OR MIT

//! Check that Kani raises an error when a derive macro with the
//! `#[safety_constraint(...)]` attribute is is used in items which are not a
//! struct.
//!
//! Note: the `#[derive(kani::Invariant)]` macro is required for the compiler error,
//! otherwise the `#[safety_constraint(...)]` attribute is ignored.

extern crate kani;

#[derive(kani::Invariant)]
#[safety_constraint(true)]
enum MyEnum {
A,
B(i32),
}
Loading