Skip to content

Commit c8291b1

Browse files
committed
WIP(will be squashed)
1 parent cee4294 commit c8291b1

File tree

1 file changed

+144
-17
lines changed

1 file changed

+144
-17
lines changed

library/core/src/slice/sort/shared/smallsort.rs

+144-17
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ impl<T: FreezeMarker> StableSmallSortTypeImpl for T {
7373
// - kani contract attribute macros doesnt't work with `default fn`
7474
// - we cannot specify the trait member function with `proof_for_contract`
7575
#[cfg(kani)]
76-
#[kani::modifies(v)]
76+
#[kani::modifies(v, scratch)]
7777
#[ensures(|_| {
7878
let mut is_less = is_less.clone();
7979
v.is_sorted_by(|a, b| !is_less(b, a))
@@ -605,7 +605,7 @@ where
605605
///
606606
/// # Safety
607607
/// begin < tail and p must be valid and initialized for all begin <= p <= tail.
608-
#[cfg_attr(kani, kani::modifies(begin, tail))]
608+
#[cfg_attr(kani, kani::modifies(begin, tail))] // FIXME: This should contain all ptrs [begin, tail]
609609
#[requires(begin.addr() < tail.addr() && {
610610
let len = tail.addr() - begin.addr();
611611
let is_less: &mut F = unsafe { mem::transmute(&is_less) };
@@ -638,13 +638,15 @@ unsafe fn insert_tail<T, F: FnMut(&T, &T) -> bool>(begin: *mut T, tail: *mut T,
638638
let tmp = ManuallyDrop::new(tail.read());
639639
let mut gap_guard = CopyOnDrop { src: &*tmp, dst: tail, len: 1 };
640640

641-
#[safety::loop_invariant(
642-
sift.addr() >= begin.addr() && sift.addr() < tail.addr()
643-
)]
644-
// FIXME: This was `loop` but kani's loop contract doesn't support `loop`.
645-
// Once it is supported, replace `while true` with the original `loop`
646-
#[allow(while_true)]
647-
while true {
641+
loop {
642+
// FIXME: This should be loop contract but sadly, making this into
643+
// loop invariant takes too much time in verification and causes OOM
644+
#[cfg(kani)]
645+
kani::assert(
646+
sift.addr() >= begin.addr() && sift.addr() < tail.addr(),
647+
"loop invariants",
648+
);
649+
648650
// SAFETY: we move sift into the gap (which is valid), and point the
649651
// gap guard destination at sift, ensuring that if a panic occurs the
650652
// gap is once again filled.
@@ -665,7 +667,8 @@ unsafe fn insert_tail<T, F: FnMut(&T, &T) -> bool>(begin: *mut T, tail: *mut T,
665667
}
666668

667669
/// Sort `v` assuming `v[..offset]` is already sorted.
668-
#[cfg_attr(kani, kani::modifies(v))]
670+
// FIXME: Disabled this due to [model-checking/kani#3682]
671+
// #[cfg_attr(kani, kani::modifies(v))]
669672
#[requires(offset != 0 && offset <= v.len() && {
670673
let is_less: &mut F = unsafe { mem::transmute(&is_less) };
671674
v[..offset].is_sorted_by(|a, b| !is_less(b, a))
@@ -695,7 +698,7 @@ pub fn insertion_sort_shift_left<T, F: FnMut(&T, &T) -> bool>(
695698
let mut tail = v_base.add(offset);
696699
while tail != v_end {
697700
// FIXME: This should be loop contract but sadly, making this into
698-
// loop invariant causes OOM
701+
// loop invariant takes too much time in verification and causes OOM
699702
#[cfg(kani)]
700703
kani::assert(
701704
tail.addr() > v_base.addr() && tail.addr() <= v_end.addr(),
@@ -1034,17 +1037,26 @@ mod verify {
10341037
);
10351038
}
10361039

1037-
#[kani::proof_for_contract(insert_tail)]
1040+
// FIXME: Ideally, this should be `proof_for_contract(insertion_sort_shift_left)`,
1041+
// but there is no way to set `kani::modifies` for arbitrary number of pointers
1042+
// in [begin, tail]
1043+
#[kani::proof]
10381044
#[kani::unwind(17)]
10391045
pub fn check_insert_tail() {
1040-
let mut array: [u8; INSERTION_SORT_MAX_LEN] = kani::any();
1041-
let tail = kani::any_where(|x: &usize| *x < INSERTION_SORT_MAX_LEN);
10421046
let mut is_less = |x: &u8, y: &u8| x < y;
1047+
let tail = kani::any_where(|x: &usize| *x > 0 && *x < INSERTION_SORT_MAX_LEN);
1048+
let mut array = kani::any_where(|x: &[u8; INSERTION_SORT_MAX_LEN]| {
1049+
x[..tail].is_sorted_by(|a, b| !is_less(b, a))
1050+
});
10431051
unsafe {
10441052
let begin = array.as_mut_ptr();
10451053
let tail = begin.add(tail);
10461054
insert_tail(begin, tail, &mut is_less);
10471055
}
1056+
kani::assert(
1057+
array[..=tail].is_sorted_by(|a, b| !is_less(b, a)),
1058+
"slice is not sorted",
1059+
);
10481060
}
10491061

10501062
// FIXME: Ideally, this should be `proof_for_contract(insertion_sort_shift_left)`,
@@ -1120,13 +1132,128 @@ mod verify {
11201132
}
11211133

11221134
#[kani::proof_for_contract(_stable_small_sort_type_impl_small_sort)]
1123-
#[kani::stub_verified(insertion_sort_shift_left)]
1135+
#[kani::stub_verified(insert_tail)]
1136+
#[kani::unwind(32)]
11241137
pub fn check_stable_small_sort_type_impl_small_sort() {
1125-
let mut array: [u8; SMALL_SORT_FALLBACK_THRESHOLD] = kani::any();
1126-
let len = kani::any_where(|x: &usize| *x <= SMALL_SORT_FALLBACK_THRESHOLD);
1138+
assert_eq!(
1139+
<u8 as StableSmallSortTypeImpl>::small_sort_threshold(),
1140+
SMALL_SORT_GENERAL_THRESHOLD,
1141+
);
1142+
1143+
let mut array: [u8; SMALL_SORT_GENERAL_THRESHOLD] = kani::any();
1144+
let len = kani::any_where(|x: &usize| *x <= SMALL_SORT_GENERAL_THRESHOLD);
11271145
let mut scratch: [MaybeUninit<u8>; SMALL_SORT_GENERAL_SCRATCH_LEN]
11281146
= [const { MaybeUninit::uninit() }; SMALL_SORT_GENERAL_SCRATCH_LEN];
11291147
let mut is_less = |x: &u8, y: &u8| x < y;
11301148
_stable_small_sort_type_impl_small_sort(&mut array[..len], &mut scratch, &mut is_less);
11311149
}
1150+
1151+
struct NonFreeze(u8);
1152+
1153+
impl !crate::marker::Freeze for NonFreeze {}
1154+
1155+
impl kani::Arbitrary for NonFreeze {
1156+
fn any() -> NonFreeze {
1157+
NonFreeze(kani::any())
1158+
}
1159+
}
1160+
1161+
#[kani::proof_for_contract(_stable_small_sort_type_impl_small_sort)]
1162+
#[kani::stub_verified(insertion_sort_shift_left)]
1163+
#[kani::unwind(17)]
1164+
pub fn check_stable_small_sort_type_impl_small_sort_nonfreeze() {
1165+
assert_eq!(
1166+
<NonFreeze as StableSmallSortTypeImpl>::small_sort_threshold(),
1167+
SMALL_SORT_FALLBACK_THRESHOLD,
1168+
);
1169+
1170+
let mut array: [NonFreeze; SMALL_SORT_FALLBACK_THRESHOLD] = kani::any();
1171+
let len = kani::any_where(|x: &usize| *x <= SMALL_SORT_FALLBACK_THRESHOLD);
1172+
let mut scratch: [MaybeUninit<NonFreeze>; SMALL_SORT_GENERAL_SCRATCH_LEN]
1173+
= [const { MaybeUninit::uninit() }; SMALL_SORT_GENERAL_SCRATCH_LEN];
1174+
let mut is_less = |x: &NonFreeze, y: &NonFreeze| x.0 < y.0;
1175+
_stable_small_sort_type_impl_small_sort(&mut array[..len], &mut scratch, &mut is_less);
1176+
}
1177+
1178+
// Freeze version is same with `_unstable_small_sort_freeze_type_impl_small_sort`
1179+
#[kani::proof_for_contract(_unstable_small_sort_type_impl_small_sort)]
1180+
#[kani::stub_verified(insertion_sort_shift_left)]
1181+
#[kani::unwind(17)]
1182+
pub fn check_unstable_small_sort_type_impl_small_sort_nonfreeze() {
1183+
assert_eq!(
1184+
<NonFreeze as UnstableSmallSortTypeImpl>::small_sort_threshold(),
1185+
SMALL_SORT_FALLBACK_THRESHOLD,
1186+
);
1187+
1188+
let mut array: [NonFreeze; SMALL_SORT_FALLBACK_THRESHOLD] = kani::any();
1189+
let len = kani::any_where(|x: &usize| *x <= SMALL_SORT_FALLBACK_THRESHOLD);
1190+
let mut is_less = |x: &NonFreeze, y: &NonFreeze| x.0 < y.0;
1191+
_unstable_small_sort_type_impl_small_sort(&mut array[..len], &mut is_less);
1192+
}
1193+
1194+
// This calls `small_sort_network` internally
1195+
#[kani::proof_for_contract(_unstable_small_sort_freeze_type_impl_small_sort)]
1196+
#[kani::stub_verified(insertion_sort_shift_left)]
1197+
#[kani::unwind(17)]
1198+
pub fn check_unstable_small_sort_freeze_type_impl_small_sort_network() {
1199+
assert_eq!(
1200+
<u8 as UnstableSmallSortFreezeTypeImpl>::small_sort_threshold(),
1201+
SMALL_SORT_NETWORK_THRESHOLD,
1202+
);
1203+
1204+
let mut array: [u8; SMALL_SORT_NETWORK_THRESHOLD] = kani::any();
1205+
let len = kani::any_where(|x: &usize| *x <= SMALL_SORT_NETWORK_THRESHOLD);
1206+
let mut is_less = |x: &u8, y: &u8| x < y;
1207+
_unstable_small_sort_freeze_type_impl_small_sort(&mut array[..len], &mut is_less);
1208+
}
1209+
1210+
// This calls `small_sort_general` internally
1211+
#[kani::proof_for_contract(_unstable_small_sort_freeze_type_impl_small_sort)]
1212+
#[kani::stub_verified(insert_tail)]
1213+
#[kani::unwind(32)]
1214+
pub fn check_unstable_small_sort_freeze_type_impl_small_sort_general() {
1215+
#[derive(Clone, Copy)]
1216+
struct Bigger(u8, MaybeUninit<[u8; 8]>);
1217+
1218+
impl kani::Arbitrary for Bigger {
1219+
fn any() -> Bigger {
1220+
Bigger(kani::any(), MaybeUninit::uninit())
1221+
}
1222+
}
1223+
1224+
assert_eq!(
1225+
<Bigger as UnstableSmallSortFreezeTypeImpl>::small_sort_threshold(),
1226+
SMALL_SORT_GENERAL_THRESHOLD,
1227+
);
1228+
1229+
let mut array: [Bigger; SMALL_SORT_GENERAL_THRESHOLD] = kani::any();
1230+
let len = kani::any_where(|x: &usize| *x <= SMALL_SORT_GENERAL_THRESHOLD);
1231+
let mut is_less = |x: &Bigger, y: &Bigger| x.0 < y.0;
1232+
_unstable_small_sort_freeze_type_impl_small_sort(&mut array[..len], &mut is_less);
1233+
}
1234+
1235+
// This calls `small_sort_fallback` internally
1236+
#[kani::proof_for_contract(_unstable_small_sort_freeze_type_impl_small_sort)]
1237+
#[kani::stub_verified(insertion_sort_shift_left)]
1238+
#[kani::unwind(17)]
1239+
pub fn check_unstable_small_sort_freeze_type_impl_small_sort_fallback() {
1240+
#[derive(Clone, Copy)]
1241+
struct Biggest(u8, MaybeUninit<[u8; 85]>);
1242+
1243+
impl kani::Arbitrary for Biggest {
1244+
fn any() -> Biggest {
1245+
Biggest(kani::any(), MaybeUninit::uninit())
1246+
}
1247+
}
1248+
1249+
assert_eq!(
1250+
<Biggest as UnstableSmallSortFreezeTypeImpl>::small_sort_threshold(),
1251+
SMALL_SORT_FALLBACK_THRESHOLD,
1252+
);
1253+
1254+
let mut array: [Biggest; SMALL_SORT_FALLBACK_THRESHOLD] = kani::any();
1255+
let len = kani::any_where(|x: &usize| *x <= SMALL_SORT_FALLBACK_THRESHOLD);
1256+
let mut is_less = |x: &Biggest, y: &Biggest| x.0 < y.0;
1257+
_unstable_small_sort_freeze_type_impl_small_sort(&mut array[..len], &mut is_less);
1258+
}
11321259
}

0 commit comments

Comments
 (0)