@@ -73,7 +73,7 @@ impl<T: FreezeMarker> StableSmallSortTypeImpl for T {
73
73
// - kani contract attribute macros doesnt't work with `default fn`
74
74
// - we cannot specify the trait member function with `proof_for_contract`
75
75
#[ cfg( kani) ]
76
- #[ kani:: modifies( v) ]
76
+ #[ kani:: modifies( v, scratch ) ]
77
77
#[ ensures( |_| {
78
78
let mut is_less = is_less. clone( ) ;
79
79
v. is_sorted_by( |a, b| !is_less( b, a) )
@@ -605,7 +605,7 @@ where
605
605
///
606
606
/// # Safety
607
607
/// 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]
609
609
#[ requires( begin. addr( ) < tail. addr( ) && {
610
610
let len = tail. addr( ) - begin. addr( ) ;
611
611
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,
638
638
let tmp = ManuallyDrop :: new ( tail. read ( ) ) ;
639
639
let mut gap_guard = CopyOnDrop { src : & * tmp, dst : tail, len : 1 } ;
640
640
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
+
648
650
// SAFETY: we move sift into the gap (which is valid), and point the
649
651
// gap guard destination at sift, ensuring that if a panic occurs the
650
652
// 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,
665
667
}
666
668
667
669
/// 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))]
669
672
#[ requires( offset != 0 && offset <= v. len( ) && {
670
673
let is_less: & mut F = unsafe { mem:: transmute( & is_less) } ;
671
674
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>(
695
698
let mut tail = v_base. add ( offset) ;
696
699
while tail != v_end {
697
700
// 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
699
702
#[ cfg( kani) ]
700
703
kani:: assert (
701
704
tail. addr ( ) > v_base. addr ( ) && tail. addr ( ) <= v_end. addr ( ) ,
@@ -1034,17 +1037,26 @@ mod verify {
1034
1037
) ;
1035
1038
}
1036
1039
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]
1038
1044
#[ kani:: unwind( 17 ) ]
1039
1045
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 ) ;
1042
1046
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
+ } ) ;
1043
1051
unsafe {
1044
1052
let begin = array. as_mut_ptr ( ) ;
1045
1053
let tail = begin. add ( tail) ;
1046
1054
insert_tail ( begin, tail, & mut is_less) ;
1047
1055
}
1056
+ kani:: assert (
1057
+ array[ ..=tail] . is_sorted_by ( |a, b| !is_less ( b, a) ) ,
1058
+ "slice is not sorted" ,
1059
+ ) ;
1048
1060
}
1049
1061
1050
1062
// FIXME: Ideally, this should be `proof_for_contract(insertion_sort_shift_left)`,
@@ -1120,13 +1132,128 @@ mod verify {
1120
1132
}
1121
1133
1122
1134
#[ 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 ) ]
1124
1137
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 ) ;
1127
1145
let mut scratch: [ MaybeUninit < u8 > ; SMALL_SORT_GENERAL_SCRATCH_LEN ]
1128
1146
= [ const { MaybeUninit :: uninit ( ) } ; SMALL_SORT_GENERAL_SCRATCH_LEN ] ;
1129
1147
let mut is_less = |x : & u8 , y : & u8 | x < y;
1130
1148
_stable_small_sort_type_impl_small_sort ( & mut array[ ..len] , & mut scratch, & mut is_less) ;
1131
1149
}
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
+ }
1132
1259
}
0 commit comments