4
4
// collections, resulting in having to optimize down excess IR multiple times.
5
5
// Your performance intuition is useless. Run perf.
6
6
7
- use safety:: requires;
7
+ use safety:: { ensures , requires} ;
8
8
use crate :: error:: Error ;
9
9
use crate :: ptr:: { Alignment , NonNull } ;
10
10
use crate :: { assert_unsafe_precondition, cmp, fmt, mem} ;
@@ -69,6 +69,10 @@ impl Layout {
69
69
#[ rustc_const_stable( feature = "const_alloc_layout_size_align" , since = "1.50.0" ) ]
70
70
#[ inline]
71
71
#[ rustc_allow_const_fn_unstable( ptr_alignment_type) ]
72
+ #[ ensures( |result| result. is_err( ) || align. is_power_of_two( ) ) ]
73
+ #[ ensures( |result| result. is_err( ) || size <= isize :: MAX as usize - ( align - 1 ) ) ]
74
+ #[ ensures( |result| result. is_err( ) || result. as_ref( ) . unwrap( ) . size( ) == size) ]
75
+ #[ ensures( |result| result. is_err( ) || result. as_ref( ) . unwrap( ) . align( ) == align) ]
72
76
pub const fn from_size_align ( size : usize , align : usize ) -> Result < Self , LayoutError > {
73
77
if Layout :: is_size_align_valid ( size, align) {
74
78
// SAFETY: Layout::is_size_align_valid checks the preconditions for this call.
@@ -128,6 +132,8 @@ impl Layout {
128
132
#[ inline]
129
133
#[ rustc_allow_const_fn_unstable( ptr_alignment_type) ]
130
134
#[ requires( Layout :: from_size_align( size, align) . is_ok( ) ) ]
135
+ #[ ensures( |result| result. size( ) == size) ]
136
+ #[ ensures( |result| result. align( ) == align) ]
131
137
pub const unsafe fn from_size_align_unchecked ( size : usize , align : usize ) -> Self {
132
138
assert_unsafe_precondition ! (
133
139
check_library_ub,
@@ -169,6 +175,8 @@ impl Layout {
169
175
#[ rustc_const_stable( feature = "alloc_layout_const_new" , since = "1.42.0" ) ]
170
176
#[ must_use]
171
177
#[ inline]
178
+ #[ ensures( |result| result. size( ) == mem:: size_of:: <T >( ) ) ]
179
+ #[ ensures( |result| result. align( ) == mem:: align_of:: <T >( ) ) ]
172
180
pub const fn new < T > ( ) -> Self {
173
181
let ( size, align) = size_align :: < T > ( ) ;
174
182
// SAFETY: if the type is instantiated, rustc already ensures that its
@@ -184,6 +192,8 @@ impl Layout {
184
192
#[ rustc_const_unstable( feature = "const_alloc_layout" , issue = "67521" ) ]
185
193
#[ must_use]
186
194
#[ inline]
195
+ #[ requires( mem:: align_of_val( t) . is_power_of_two( ) ) ]
196
+ #[ ensures( |result| result. align( ) == mem:: align_of_val( t) ) ]
187
197
pub const fn for_value < T : ?Sized > ( t : & T ) -> Self {
188
198
let ( size, align) = ( mem:: size_of_val ( t) , mem:: align_of_val ( t) ) ;
189
199
// SAFETY: see rationale in `new` for why this is using the unsafe variant
@@ -220,6 +230,10 @@ impl Layout {
220
230
#[ unstable( feature = "layout_for_ptr" , issue = "69835" ) ]
221
231
#[ rustc_const_unstable( feature = "const_alloc_layout" , issue = "67521" ) ]
222
232
#[ must_use]
233
+ // TODO: we should try to capture the above constraints on T in a `requires` clause, and the
234
+ // metadata helpers from https://github.com/model-checking/verify-rust-std/pull/37 may be able
235
+ // to accomplish this.
236
+ #[ ensures( |result| result. align( ) . is_power_of_two( ) ) ]
223
237
pub const unsafe fn for_value_raw < T : ?Sized > ( t : * const T ) -> Self {
224
238
// SAFETY: we pass along the prerequisites of these functions to the caller
225
239
let ( size, align) = unsafe { ( mem:: size_of_val_raw ( t) , mem:: align_of_val_raw ( t) ) } ;
@@ -237,6 +251,7 @@ impl Layout {
237
251
#[ rustc_const_unstable( feature = "alloc_layout_extra" , issue = "55724" ) ]
238
252
#[ must_use]
239
253
#[ inline]
254
+ #[ ensures( |result| result. is_aligned( ) ) ]
240
255
pub const fn dangling ( & self ) -> NonNull < u8 > {
241
256
// SAFETY: align is guaranteed to be non-zero
242
257
unsafe { NonNull :: new_unchecked ( crate :: ptr:: without_provenance_mut :: < u8 > ( self . align ( ) ) ) }
@@ -258,6 +273,8 @@ impl Layout {
258
273
/// `align` violates the conditions listed in [`Layout::from_size_align`].
259
274
#[ stable( feature = "alloc_layout_manipulation" , since = "1.44.0" ) ]
260
275
#[ inline]
276
+ #[ ensures( |result| result. is_err( ) || result. as_ref( ) . unwrap( ) . align( ) >= align) ]
277
+ #[ ensures( |result| result. is_err( ) || result. as_ref( ) . unwrap( ) . align( ) . is_power_of_two( ) ) ]
261
278
pub fn align_to ( & self , align : usize ) -> Result < Self , LayoutError > {
262
279
Layout :: from_size_align ( self . size ( ) , cmp:: max ( self . align ( ) , align) )
263
280
}
@@ -283,6 +300,7 @@ impl Layout {
283
300
#[ must_use = "this returns the padding needed, \
284
301
without modifying the `Layout`"]
285
302
#[ inline]
303
+ #[ ensures( |result| * result <= align) ]
286
304
pub const fn padding_needed_for ( & self , align : usize ) -> usize {
287
305
let len = self . size ( ) ;
288
306
@@ -319,6 +337,10 @@ impl Layout {
319
337
#[ must_use = "this returns a new `Layout`, \
320
338
without modifying the original"]
321
339
#[ inline]
340
+ #[ ensures( |result| result. size( ) >= self . size( ) ) ]
341
+ #[ ensures( |result| result. align( ) == self . align( ) ) ]
342
+ #[ ensures( |result| result. size( ) % result. align( ) == 0 ) ]
343
+ #[ ensures( |result| self . size( ) + self . padding_needed_for( self . align( ) ) == result. size( ) ) ]
322
344
pub const fn pad_to_align ( & self ) -> Layout {
323
345
let pad = self . padding_needed_for ( self . align ( ) ) ;
324
346
// This cannot overflow. Quoting from the invariant of Layout:
@@ -341,6 +363,20 @@ impl Layout {
341
363
/// On arithmetic overflow, returns `LayoutError`.
342
364
#[ unstable( feature = "alloc_layout_extra" , issue = "55724" ) ]
343
365
#[ inline]
366
+ // for Kani (v0.54.0), the below modulo operation is too costly to prove (running into the
367
+ // 6-hours timeout on GitHub); we use a weaker postcondition instead
368
+ #[ cfg_attr( not( kani) ,
369
+ ensures( |result| result. is_err( ) || n == 0 || result. as_ref( ) . unwrap( ) . 0 . size( ) % n == 0 ) ) ]
370
+ #[ cfg_attr( kani,
371
+ ensures( |result| result. is_err( ) || n == 0 || result. as_ref( ) . unwrap( ) . 0 . size( ) >= self . size( ) ) ) ]
372
+ // for Kani (v0.54.0), the below multiplication is too costly to prove (running into the
373
+ // 6-hours timeout on GitHub); we use a weaker postcondition instead
374
+ #[ cfg_attr( not( kani) ,
375
+ ensures( |result| result. is_err( ) ||
376
+ result. as_ref( ) . unwrap( ) . 0 . size( ) == n * result. as_ref( ) . unwrap( ) . 1 ) ) ]
377
+ #[ cfg_attr( kani,
378
+ ensures( |result| result. is_err( ) || n == 0 ||
379
+ result. as_ref( ) . unwrap( ) . 0 . size( ) >= result. as_ref( ) . unwrap( ) . 1 ) ) ]
344
380
pub fn repeat ( & self , n : usize ) -> Result < ( Self , usize ) , LayoutError > {
345
381
// This cannot overflow. Quoting from the invariant of Layout:
346
382
// > `size`, when rounded up to the nearest multiple of `align`,
@@ -401,6 +437,10 @@ impl Layout {
401
437
/// ```
402
438
#[ stable( feature = "alloc_layout_manipulation" , since = "1.44.0" ) ]
403
439
#[ inline]
440
+ #[ ensures( |result| result. is_err( ) || result. as_ref( ) . unwrap( ) . 0 . align( ) == cmp:: max( self . align( ) , next. align( ) ) ) ]
441
+ #[ ensures( |result| result. is_err( ) || result. as_ref( ) . unwrap( ) . 0 . size( ) >= self . size( ) + next. size( ) ) ]
442
+ #[ ensures( |result| result. is_err( ) || result. as_ref( ) . unwrap( ) . 1 >= self . size( ) ) ]
443
+ #[ ensures( |result| result. is_err( ) || result. as_ref( ) . unwrap( ) . 1 <= result. as_ref( ) . unwrap( ) . 0 . size( ) ) ]
404
444
pub fn extend ( & self , next : Self ) -> Result < ( Self , usize ) , LayoutError > {
405
445
let new_align = cmp:: max ( self . align , next. align ) ;
406
446
let pad = self . padding_needed_for ( next. align ( ) ) ;
@@ -427,6 +467,13 @@ impl Layout {
427
467
/// On arithmetic overflow, returns `LayoutError`.
428
468
#[ unstable( feature = "alloc_layout_extra" , issue = "55724" ) ]
429
469
#[ inline]
470
+ // for Kani (v0.54.0), the below multiplication is too costly to prove (running into the
471
+ // 6-hours timeout on GitHub); we use a weaker postcondition instead
472
+ #[ cfg_attr( not( kani) ,
473
+ ensures( |result| result. is_err( ) || result. as_ref( ) . unwrap( ) . size( ) == n * self . size( ) ) ) ]
474
+ #[ cfg_attr( kani,
475
+ ensures( |result| result. is_err( ) || n == 0 || result. as_ref( ) . unwrap( ) . size( ) >= self . size( ) ) ) ]
476
+ #[ ensures( |result| result. is_err( ) || result. as_ref( ) . unwrap( ) . align( ) == self . align( ) ) ]
430
477
pub fn repeat_packed ( & self , n : usize ) -> Result < Self , LayoutError > {
431
478
let size = self . size ( ) . checked_mul ( n) . ok_or ( LayoutError ) ?;
432
479
// The safe constructor is called here to enforce the isize size limit.
@@ -441,6 +488,8 @@ impl Layout {
441
488
/// On arithmetic overflow, returns `LayoutError`.
442
489
#[ unstable( feature = "alloc_layout_extra" , issue = "55724" ) ]
443
490
#[ inline]
491
+ #[ ensures( |result| result. is_err( ) || result. as_ref( ) . unwrap( ) . size( ) == self . size( ) + next. size( ) ) ]
492
+ #[ ensures( |result| result. is_err( ) || result. as_ref( ) . unwrap( ) . align( ) == self . align( ) ) ]
444
493
pub fn extend_packed ( & self , next : Self ) -> Result < Self , LayoutError > {
445
494
let new_size = self . size ( ) . checked_add ( next. size ( ) ) . ok_or ( LayoutError ) ?;
446
495
// The safe constructor is called here to enforce the isize size limit.
@@ -454,6 +503,8 @@ impl Layout {
454
503
#[ stable( feature = "alloc_layout_manipulation" , since = "1.44.0" ) ]
455
504
#[ rustc_const_unstable( feature = "const_alloc_layout" , issue = "67521" ) ]
456
505
#[ inline]
506
+ #[ ensures( |result| result. is_err( ) || result. as_ref( ) . unwrap( ) . size( ) == n * mem:: size_of:: <T >( ) ) ]
507
+ #[ ensures( |result| result. is_err( ) || result. as_ref( ) . unwrap( ) . align( ) == mem:: align_of:: <T >( ) ) ]
457
508
pub const fn array < T > ( n : usize ) -> Result < Self , LayoutError > {
458
509
// Reduce the amount of code we need to monomorphize per `T`.
459
510
return inner ( mem:: size_of :: < T > ( ) , Alignment :: of :: < T > ( ) , n) ;
@@ -520,15 +571,153 @@ impl fmt::Display for LayoutError {
520
571
mod verify {
521
572
use super :: * ;
522
573
574
+ impl kani:: Arbitrary for Layout {
575
+ fn any ( ) -> Self {
576
+ let align = kani:: any :: < Alignment > ( ) ;
577
+ let size = kani:: any_where ( |s : & usize | * s <= isize:: MAX as usize - ( align. as_usize ( ) - 1 ) ) ;
578
+ unsafe { Layout { size, align } }
579
+ }
580
+ }
581
+
582
+ // pub const fn from_size_align(size: usize, align: usize) -> Result<Self, LayoutError>
583
+ #[ kani:: proof_for_contract( Layout :: from_size_align) ]
584
+ pub fn check_from_size_align ( ) {
585
+ let s = kani:: any :: < usize > ( ) ;
586
+ let a = kani:: any :: < usize > ( ) ;
587
+ let _ = Layout :: from_size_align ( s, a) ;
588
+ }
589
+
590
+ // pub const unsafe fn from_size_align_unchecked(size: usize, align: usize) -> Self
523
591
#[ kani:: proof_for_contract( Layout :: from_size_align_unchecked) ]
524
592
pub fn check_from_size_align_unchecked ( ) {
525
593
let s = kani:: any :: < usize > ( ) ;
526
594
let a = kani:: any :: < usize > ( ) ;
595
+ unsafe {
596
+ let _ = Layout :: from_size_align_unchecked ( s, a) ;
597
+ }
598
+ }
527
599
600
+ // pub const fn size(&self) -> usize
601
+ #[ kani:: proof]
602
+ pub fn check_size ( ) {
603
+ let s = kani:: any :: < usize > ( ) ;
604
+ let a = kani:: any :: < usize > ( ) ;
605
+ kani:: assume ( Layout :: from_size_align ( s, a) . is_ok ( ) ) ;
528
606
unsafe {
529
607
let layout = Layout :: from_size_align_unchecked ( s, a) ;
530
608
assert_eq ! ( layout. size( ) , s) ;
531
- assert_eq ! ( layout. align( ) , a) ;
609
+ }
610
+ }
611
+
612
+ // pub const fn align(&self) -> usize
613
+ #[ kani:: proof]
614
+ pub fn check_align ( ) {
615
+ let layout = kani:: any :: < Layout > ( ) ;
616
+ assert ! ( layout. align( ) . is_power_of_two( ) ) ;
617
+ }
618
+
619
+ // pub const fn new<T>() -> Self
620
+ #[ kani:: proof_for_contract( Layout :: new) ]
621
+ pub fn check_new_i32 ( ) {
622
+ let _ = Layout :: new :: < i32 > ( ) ;
623
+ }
624
+
625
+ // pub const fn for_value<T: ?Sized>(t: &T) -> Self
626
+ #[ kani:: proof_for_contract( Layout :: for_value) ]
627
+ pub fn check_for_value_i32 ( ) {
628
+ let x = kani:: any :: < i32 > ( ) ;
629
+ let _ = Layout :: for_value :: < i32 > ( & x) ;
630
+ let array : [ i32 ; 2 ] = [ 1 , 2 ] ;
631
+ let _ = Layout :: for_value :: < [ i32 ] > ( & array[ 1 .. 1 ] ) ;
632
+ let trait_ref: & dyn core:: fmt:: Debug = & x;
633
+ let _ = Layout :: for_value :: < dyn core:: fmt:: Debug > ( trait_ref) ;
634
+ // TODO: the case of an extern type as unsized tail is not yet covered
635
+ }
636
+
637
+ // pub const unsafe fn for_value_raw<T: ?Sized>(t: *const T) -> Self
638
+ #[ kani:: proof_for_contract( Layout :: for_value_raw) ]
639
+ pub fn check_for_value_raw_i32 ( ) {
640
+ unsafe {
641
+ let x = kani:: any :: < i32 > ( ) ;
642
+ let _ = Layout :: for_value_raw :: < i32 > ( & x as * const i32 ) ;
643
+ let _ = Layout :: for_value_raw :: < [ i32 ] > ( & [ ] as * const [ i32 ] ) ;
644
+ let trait_ref: & dyn core:: fmt:: Debug = & x;
645
+ let _ = Layout :: for_value_raw :: < dyn core:: fmt:: Debug > ( trait_ref) ;
646
+ // TODO: the case of an extern type as unsized tail is not yet covered
647
+ }
648
+ }
649
+
650
+ // pub const fn dangling(&self) -> NonNull<u8>
651
+ #[ kani:: proof_for_contract( Layout :: dangling) ]
652
+ pub fn check_dangling ( ) {
653
+ let layout = kani:: any :: < Layout > ( ) ;
654
+ let _ = layout. dangling ( ) ;
655
+ }
656
+
657
+ // pub fn align_to(&self, align: usize) -> Result<Self, LayoutError>
658
+ #[ kani:: proof_for_contract( Layout :: align_to) ]
659
+ pub fn check_align_to ( ) {
660
+ let layout = kani:: any :: < Layout > ( ) ;
661
+ let a2 = kani:: any :: < usize > ( ) ;
662
+ let _ = layout. align_to ( a2) ;
663
+ }
664
+
665
+ // pub const fn padding_needed_for(&self, align: usize) -> usize
666
+ #[ kani:: proof_for_contract( Layout :: padding_needed_for) ]
667
+ pub fn check_padding_needed_for ( ) {
668
+ let layout = kani:: any :: < Layout > ( ) ;
669
+ let a2 = kani:: any :: < usize > ( ) ;
670
+ if ( a2. is_power_of_two ( ) && a2 <= layout. align ( ) ) {
671
+ let _ = layout. padding_needed_for ( a2) ;
672
+ }
673
+ }
674
+
675
+ // pub const fn pad_to_align(&self) -> Layout
676
+ #[ kani:: proof_for_contract( Layout :: pad_to_align) ]
677
+ pub fn check_pad_to_align ( ) {
678
+ let layout = kani:: any :: < Layout > ( ) ;
679
+ let _ = layout. pad_to_align ( ) ;
680
+ }
681
+
682
+ // pub fn repeat(&self, n: usize) -> Result<(Self, usize), LayoutError>
683
+ #[ kani:: proof_for_contract( Layout :: repeat) ]
684
+ pub fn check_repeat ( ) {
685
+ let layout = kani:: any :: < Layout > ( ) ;
686
+ let n = kani:: any :: < usize > ( ) ;
687
+ let _ = layout. repeat ( n) ;
688
+ }
689
+
690
+ // pub fn extend(&self, next: Self) -> Result<(Self, usize), LayoutError>
691
+ #[ kani:: proof_for_contract( Layout :: extend) ]
692
+ pub fn check_extend ( ) {
693
+ let layout = kani:: any :: < Layout > ( ) ;
694
+ let layout2 = kani:: any :: < Layout > ( ) ;
695
+ let _ = layout. extend ( layout2) ;
696
+ }
697
+
698
+ // pub fn repeat_packed(&self, n: usize) -> Result<Self, LayoutError>
699
+ #[ kani:: proof_for_contract( Layout :: repeat_packed) ]
700
+ pub fn check_repeat_packed ( ) {
701
+ let layout = kani:: any :: < Layout > ( ) ;
702
+ let n = kani:: any :: < usize > ( ) ;
703
+ let _ = layout. repeat_packed ( n) ;
704
+ }
705
+
706
+ // pub fn extend_packed(&self, next: Self) -> Result<Self, LayoutError>
707
+ #[ kani:: proof_for_contract( Layout :: extend_packed) ]
708
+ pub fn check_extend_packed ( ) {
709
+ let layout = kani:: any :: < Layout > ( ) ;
710
+ let layout2 = kani:: any :: < Layout > ( ) ;
711
+ let _ = layout. extend_packed ( layout2) ;
712
+ }
713
+
714
+ // pub const fn array<T>(n: usize) -> Result<Self, LayoutError>
715
+ #[ kani:: proof_for_contract( Layout :: array) ]
716
+ pub fn check_array_i32 ( ) {
717
+ let n = kani:: any :: < usize > ( ) ;
718
+ if let Ok ( layout) = Layout :: array :: < i32 > ( n) {
719
+ assert ! ( layout. size( ) >= n * 4 ) ;
720
+ assert ! ( layout. align( ) . is_power_of_two( ) ) ;
532
721
}
533
722
}
534
723
}
0 commit comments