Skip to content

Commit df8da5a

Browse files
celinvaltautschnig
andauthored
Add a few more contract and harness examples (#18)
Add contracts to `core::mem::swap` and `core::intrinsics::typed_swap`. Co-authored-by: Michael Tautschnig <[email protected]>
1 parent 5b70960 commit df8da5a

File tree

2 files changed

+84
-0
lines changed

2 files changed

+84
-0
lines changed

library/core/src/intrinsics.rs

+44
Original file line numberDiff line numberDiff line change
@@ -63,12 +63,16 @@
6363
)]
6464
#![allow(missing_docs)]
6565

66+
use safety::requires;
6667
use crate::marker::DiscriminantKind;
6768
use crate::marker::Tuple;
6869
use crate::mem::align_of;
6970
use crate::ptr;
7071
use crate::ub_checks;
7172

73+
#[cfg(kani)]
74+
use crate::kani;
75+
7276
pub mod mir;
7377
pub mod simd;
7478

@@ -2709,6 +2713,12 @@ pub const fn is_val_statically_known<T: Copy>(_arg: T) -> bool {
27092713
#[rustc_intrinsic]
27102714
// This has fallback `const fn` MIR, so shouldn't need stability, see #122652
27112715
#[rustc_const_unstable(feature = "const_typed_swap", issue = "none")]
2716+
#[cfg_attr(kani, kani::modifies(x))]
2717+
#[cfg_attr(kani, kani::modifies(y))]
2718+
#[requires(ub_checks::can_dereference(x) && ub_checks::can_write(x))]
2719+
#[requires(ub_checks::can_dereference(y) && ub_checks::can_write(y))]
2720+
#[requires(x.addr() != y.addr() || core::mem::size_of::<T>() == 0)]
2721+
#[requires((x.addr() >= y.addr() + core::mem::size_of::<T>()) || (y.addr() >= x.addr() + core::mem::size_of::<T>()))]
27122722
pub const unsafe fn typed_swap<T>(x: *mut T, y: *mut T) {
27132723
// SAFETY: The caller provided single non-overlapping items behind
27142724
// pointers, so swapping them with `count: 1` is fine.
@@ -3142,3 +3152,37 @@ pub(crate) const fn miri_promise_symbolic_alignment(ptr: *const (), align: usize
31423152

31433153
const_eval_select((ptr, align), compiletime, runtime);
31443154
}
3155+
3156+
#[cfg(kani)]
3157+
#[unstable(feature="kani", issue="none")]
3158+
mod verify {
3159+
use core::{cmp, fmt};
3160+
use super::*;
3161+
use crate::kani;
3162+
3163+
#[kani::proof_for_contract(typed_swap)]
3164+
pub fn check_typed_swap_u8() {
3165+
check_swap::<u8>()
3166+
}
3167+
3168+
#[kani::proof_for_contract(typed_swap)]
3169+
pub fn check_typed_swap_char() {
3170+
check_swap::<char>()
3171+
}
3172+
3173+
#[kani::proof_for_contract(typed_swap)]
3174+
pub fn check_typed_swap_non_zero() {
3175+
check_swap::<core::num::NonZeroI32>()
3176+
}
3177+
3178+
pub fn check_swap<T: kani::Arbitrary + Copy + cmp::PartialEq + fmt::Debug>() {
3179+
let mut x = kani::any::<T>();
3180+
let old_x = x;
3181+
let mut y = kani::any::<T>();
3182+
let old_y = y;
3183+
3184+
unsafe { typed_swap(&mut x, &mut y) };
3185+
assert_eq!(y, old_x);
3186+
assert_eq!(x, old_y);
3187+
}
3188+
}

library/core/src/mem/mod.rs

+40
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ use crate::intrinsics;
1313
use crate::marker::DiscriminantKind;
1414
use crate::ptr;
1515

16+
#[cfg(kani)]
17+
use crate::kani;
18+
1619
mod manually_drop;
1720
#[stable(feature = "manually_drop", since = "1.20.0")]
1821
pub use manually_drop::ManuallyDrop;
@@ -725,6 +728,8 @@ pub unsafe fn uninitialized<T>() -> T {
725728
#[stable(feature = "rust1", since = "1.0.0")]
726729
#[rustc_const_unstable(feature = "const_swap", issue = "83163")]
727730
#[rustc_diagnostic_item = "mem_swap"]
731+
#[cfg_attr(kani, crate::kani::modifies(x))]
732+
#[cfg_attr(kani, crate::kani::modifies(y))]
728733
pub const fn swap<T>(x: &mut T, y: &mut T) {
729734
// SAFETY: `&mut` guarantees these are typed readable and writable
730735
// as well as non-overlapping.
@@ -1345,3 +1350,38 @@ pub macro offset_of($Container:ty, $($fields:expr)+ $(,)?) {
13451350
// The `{}` is for better error messages
13461351
{builtin # offset_of($Container, $($fields)+)}
13471352
}
1353+
1354+
#[cfg(kani)]
1355+
#[unstable(feature="kani", issue="none")]
1356+
mod verify {
1357+
use super::*;
1358+
use crate::kani;
1359+
1360+
/// Use this type to ensure that mem swap does not drop the value.
1361+
#[derive(kani::Arbitrary)]
1362+
struct CannotDrop<T: kani::Arbitrary> {
1363+
inner: T,
1364+
}
1365+
1366+
impl<T: kani::Arbitrary> Drop for CannotDrop<T> {
1367+
fn drop(&mut self) {
1368+
unreachable!("Cannot drop")
1369+
}
1370+
}
1371+
1372+
#[kani::proof_for_contract(swap)]
1373+
pub fn check_swap_primitive() {
1374+
let mut x: u8 = kani::any();
1375+
let mut y: u8 = kani::any();
1376+
swap(&mut x, &mut y)
1377+
}
1378+
1379+
#[kani::proof_for_contract(swap)]
1380+
pub fn check_swap_adt_no_drop() {
1381+
let mut x: CannotDrop<char> = kani::any();
1382+
let mut y: CannotDrop<char> = kani::any();
1383+
swap(&mut x, &mut y);
1384+
forget(x);
1385+
forget(y);
1386+
}
1387+
}

0 commit comments

Comments
 (0)