44// collections, resulting in having to optimize down excess IR multiple times.
55// Your performance intuition is useless. Run perf.
66
7- use safety:: requires;
7+ use safety:: { ensures , requires} ;
88use crate :: error:: Error ;
99use crate :: ptr:: { Alignment , NonNull } ;
1010use crate :: { assert_unsafe_precondition, cmp, fmt, mem} ;
@@ -69,6 +69,10 @@ impl Layout {
6969 #[ rustc_const_stable( feature = "const_alloc_layout_size_align" , since = "1.50.0" ) ]
7070 #[ inline]
7171 #[ 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) ]
7276 pub const fn from_size_align ( size : usize , align : usize ) -> Result < Self , LayoutError > {
7377 if Layout :: is_size_align_valid ( size, align) {
7478 // SAFETY: Layout::is_size_align_valid checks the preconditions for this call.
@@ -128,6 +132,8 @@ impl Layout {
128132 #[ inline]
129133 #[ rustc_allow_const_fn_unstable( ptr_alignment_type) ]
130134 #[ requires( Layout :: from_size_align( size, align) . is_ok( ) ) ]
135+ #[ ensures( |result| result. size( ) == size) ]
136+ #[ ensures( |result| result. align( ) == align) ]
131137 pub const unsafe fn from_size_align_unchecked ( size : usize , align : usize ) -> Self {
132138 assert_unsafe_precondition ! (
133139 check_library_ub,
@@ -169,6 +175,8 @@ impl Layout {
169175 #[ rustc_const_stable( feature = "alloc_layout_const_new" , since = "1.42.0" ) ]
170176 #[ must_use]
171177 #[ inline]
178+ #[ ensures( |result| result. size( ) == mem:: size_of:: <T >( ) ) ]
179+ #[ ensures( |result| result. align( ) == mem:: align_of:: <T >( ) ) ]
172180 pub const fn new < T > ( ) -> Self {
173181 let ( size, align) = size_align :: < T > ( ) ;
174182 // SAFETY: if the type is instantiated, rustc already ensures that its
@@ -184,6 +192,8 @@ impl Layout {
184192 #[ rustc_const_unstable( feature = "const_alloc_layout" , issue = "67521" ) ]
185193 #[ must_use]
186194 #[ inline]
195+ #[ requires( mem:: align_of_val( t) . is_power_of_two( ) ) ]
196+ #[ ensures( |result| result. align( ) == mem:: align_of_val( t) ) ]
187197 pub const fn for_value < T : ?Sized > ( t : & T ) -> Self {
188198 let ( size, align) = ( mem:: size_of_val ( t) , mem:: align_of_val ( t) ) ;
189199 // SAFETY: see rationale in `new` for why this is using the unsafe variant
@@ -220,6 +230,10 @@ impl Layout {
220230 #[ unstable( feature = "layout_for_ptr" , issue = "69835" ) ]
221231 #[ rustc_const_unstable( feature = "const_alloc_layout" , issue = "67521" ) ]
222232 #[ 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( ) ) ]
223237 pub const unsafe fn for_value_raw < T : ?Sized > ( t : * const T ) -> Self {
224238 // SAFETY: we pass along the prerequisites of these functions to the caller
225239 let ( size, align) = unsafe { ( mem:: size_of_val_raw ( t) , mem:: align_of_val_raw ( t) ) } ;
@@ -237,6 +251,7 @@ impl Layout {
237251 #[ rustc_const_unstable( feature = "alloc_layout_extra" , issue = "55724" ) ]
238252 #[ must_use]
239253 #[ inline]
254+ #[ ensures( |result| result. is_aligned( ) ) ]
240255 pub const fn dangling ( & self ) -> NonNull < u8 > {
241256 // SAFETY: align is guaranteed to be non-zero
242257 unsafe { NonNull :: new_unchecked ( crate :: ptr:: without_provenance_mut :: < u8 > ( self . align ( ) ) ) }
@@ -258,6 +273,8 @@ impl Layout {
258273 /// `align` violates the conditions listed in [`Layout::from_size_align`].
259274 #[ stable( feature = "alloc_layout_manipulation" , since = "1.44.0" ) ]
260275 #[ 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( ) ) ]
261278 pub fn align_to ( & self , align : usize ) -> Result < Self , LayoutError > {
262279 Layout :: from_size_align ( self . size ( ) , cmp:: max ( self . align ( ) , align) )
263280 }
@@ -283,6 +300,7 @@ impl Layout {
283300 #[ must_use = "this returns the padding needed, \
284301 without modifying the `Layout`"]
285302 #[ inline]
303+ #[ ensures( |result| * result <= align) ]
286304 pub const fn padding_needed_for ( & self , align : usize ) -> usize {
287305 let len = self . size ( ) ;
288306
@@ -319,6 +337,10 @@ impl Layout {
319337 #[ must_use = "this returns a new `Layout`, \
320338 without modifying the original"]
321339 #[ 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( ) ) ]
322344 pub const fn pad_to_align ( & self ) -> Layout {
323345 let pad = self . padding_needed_for ( self . align ( ) ) ;
324346 // This cannot overflow. Quoting from the invariant of Layout:
@@ -341,6 +363,20 @@ impl Layout {
341363 /// On arithmetic overflow, returns `LayoutError`.
342364 #[ unstable( feature = "alloc_layout_extra" , issue = "55724" ) ]
343365 #[ 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 ) ) ]
344380 pub fn repeat ( & self , n : usize ) -> Result < ( Self , usize ) , LayoutError > {
345381 // This cannot overflow. Quoting from the invariant of Layout:
346382 // > `size`, when rounded up to the nearest multiple of `align`,
@@ -401,6 +437,10 @@ impl Layout {
401437 /// ```
402438 #[ stable( feature = "alloc_layout_manipulation" , since = "1.44.0" ) ]
403439 #[ 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( ) ) ]
404444 pub fn extend ( & self , next : Self ) -> Result < ( Self , usize ) , LayoutError > {
405445 let new_align = cmp:: max ( self . align , next. align ) ;
406446 let pad = self . padding_needed_for ( next. align ( ) ) ;
@@ -427,6 +467,13 @@ impl Layout {
427467 /// On arithmetic overflow, returns `LayoutError`.
428468 #[ unstable( feature = "alloc_layout_extra" , issue = "55724" ) ]
429469 #[ 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( ) ) ]
430477 pub fn repeat_packed ( & self , n : usize ) -> Result < Self , LayoutError > {
431478 let size = self . size ( ) . checked_mul ( n) . ok_or ( LayoutError ) ?;
432479 // The safe constructor is called here to enforce the isize size limit.
@@ -441,6 +488,8 @@ impl Layout {
441488 /// On arithmetic overflow, returns `LayoutError`.
442489 #[ unstable( feature = "alloc_layout_extra" , issue = "55724" ) ]
443490 #[ 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( ) ) ]
444493 pub fn extend_packed ( & self , next : Self ) -> Result < Self , LayoutError > {
445494 let new_size = self . size ( ) . checked_add ( next. size ( ) ) . ok_or ( LayoutError ) ?;
446495 // The safe constructor is called here to enforce the isize size limit.
@@ -454,6 +503,8 @@ impl Layout {
454503 #[ stable( feature = "alloc_layout_manipulation" , since = "1.44.0" ) ]
455504 #[ rustc_const_unstable( feature = "const_alloc_layout" , issue = "67521" ) ]
456505 #[ 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 >( ) ) ]
457508 pub const fn array < T > ( n : usize ) -> Result < Self , LayoutError > {
458509 // Reduce the amount of code we need to monomorphize per `T`.
459510 return inner ( mem:: size_of :: < T > ( ) , Alignment :: of :: < T > ( ) , n) ;
@@ -520,15 +571,153 @@ impl fmt::Display for LayoutError {
520571mod verify {
521572 use super :: * ;
522573
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
523591 #[ kani:: proof_for_contract( Layout :: from_size_align_unchecked) ]
524592 pub fn check_from_size_align_unchecked ( ) {
525593 let s = kani:: any :: < usize > ( ) ;
526594 let a = kani:: any :: < usize > ( ) ;
595+ unsafe {
596+ let _ = Layout :: from_size_align_unchecked ( s, a) ;
597+ }
598+ }
527599
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 ( ) ) ;
528606 unsafe {
529607 let layout = Layout :: from_size_align_unchecked ( s, a) ;
530608 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( ) ) ;
532721 }
533722 }
534723}
0 commit comments