@@ -1429,3 +1429,244 @@ mod tests {
14291429 validate_against_rust ! ( 4 , 16 , 4 ) ;
14301430 }
14311431}
1432+
1433+ #[ cfg( kani) ]
1434+ mod proofs {
1435+ use core:: alloc:: Layout ;
1436+
1437+ use super :: * ;
1438+
1439+ impl kani:: Arbitrary for DstLayout {
1440+ fn any ( ) -> Self {
1441+ let align: NonZeroUsize = kani:: any ( ) ;
1442+ let size_info: SizeInfo = kani:: any ( ) ;
1443+
1444+ kani:: assume ( align. is_power_of_two ( ) ) ;
1445+ kani:: assume ( align < DstLayout :: THEORETICAL_MAX_ALIGN ) ;
1446+
1447+ // For testing purposes, we most care about instantiations of
1448+ // `DstLayout` that can correspond to actual Rust types. We use
1449+ // `Layout` to verify that our `DstLayout` satisfies the validity
1450+ // conditions of Rust layouts.
1451+ kani:: assume (
1452+ match size_info {
1453+ SizeInfo :: Sized { size } => Layout :: from_size_align ( size, align. get ( ) ) ,
1454+ SizeInfo :: SliceDst ( TrailingSliceLayout { offset, elem_size : _ } ) => {
1455+ // `SliceDst`` cannot encode an exact size, but we know
1456+ // it is at least `offset` bytes.
1457+ Layout :: from_size_align ( offset, align. get ( ) )
1458+ }
1459+ }
1460+ . is_ok ( ) ,
1461+ ) ;
1462+
1463+ Self { align : align, size_info : size_info }
1464+ }
1465+ }
1466+
1467+ impl kani:: Arbitrary for SizeInfo {
1468+ fn any ( ) -> Self {
1469+ let is_sized: bool = kani:: any ( ) ;
1470+
1471+ match is_sized {
1472+ true => {
1473+ let size: usize = kani:: any ( ) ;
1474+
1475+ kani:: assume ( size <= isize:: MAX as _ ) ;
1476+
1477+ SizeInfo :: Sized { size }
1478+ }
1479+ false => SizeInfo :: SliceDst ( kani:: any ( ) ) ,
1480+ }
1481+ }
1482+ }
1483+
1484+ impl kani:: Arbitrary for TrailingSliceLayout {
1485+ fn any ( ) -> Self {
1486+ let elem_size: usize = kani:: any ( ) ;
1487+ let offset: usize = kani:: any ( ) ;
1488+
1489+ kani:: assume ( elem_size < isize:: MAX as _ ) ;
1490+ kani:: assume ( offset < isize:: MAX as _ ) ;
1491+
1492+ TrailingSliceLayout { elem_size, offset }
1493+ }
1494+ }
1495+
1496+ #[ kani:: proof]
1497+ fn prove_dst_layout_extend ( ) {
1498+ use crate :: util:: { max, min, padding_needed_for} ;
1499+
1500+ let base: DstLayout = kani:: any ( ) ;
1501+ let field: DstLayout = kani:: any ( ) ;
1502+ let packed: Option < NonZeroUsize > = kani:: any ( ) ;
1503+
1504+ if let Some ( max_align) = packed {
1505+ kani:: assume ( max_align. is_power_of_two ( ) ) ;
1506+ kani:: assume ( base. align <= max_align) ;
1507+ }
1508+
1509+ // The base can only be extended if it's sized.
1510+ kani:: assume ( matches ! ( base. size_info, SizeInfo :: Sized { .. } ) ) ;
1511+ let base_size = if let SizeInfo :: Sized { size } = base. size_info {
1512+ size
1513+ } else {
1514+ unreachable ! ( ) ;
1515+ } ;
1516+
1517+ // Under the above conditions, `DstLayout::extend` will not panic.
1518+ let composite = base. extend ( field, packed) ;
1519+
1520+ // The field's alignment is clamped by `max_align` (i.e., the
1521+ // `packed` attribute, if any) [1].
1522+ //
1523+ // [1] Per https://doc.rust-lang.org/reference/type-layout.html#the-alignment-modifiers:
1524+ //
1525+ // The alignments of each field, for the purpose of positioning
1526+ // fields, is the smaller of the specified alignment and the
1527+ // alignment of the field's type.
1528+ let field_align = min ( field. align , packed. unwrap_or ( DstLayout :: THEORETICAL_MAX_ALIGN ) ) ;
1529+
1530+ // The struct's alignment is the maximum of its previous alignment and
1531+ // `field_align`.
1532+ assert_eq ! ( composite. align, max( base. align, field_align) ) ;
1533+
1534+ // Compute the minimum amount of inter-field padding needed to
1535+ // satisfy the field's alignment, and offset of the trailing field.
1536+ // [1]
1537+ //
1538+ // [1] Per https://doc.rust-lang.org/reference/type-layout.html#the-alignment-modifiers:
1539+ //
1540+ // Inter-field padding is guaranteed to be the minimum required in
1541+ // order to satisfy each field's (possibly altered) alignment.
1542+ let padding = padding_needed_for ( base_size, field_align) ;
1543+ let offset = base_size + padding;
1544+
1545+ // For testing purposes, we'll also construct `alloc::Layout`
1546+ // stand-ins for `DstLayout`, and show that `extend` behaves
1547+ // comparably on both types.
1548+ let base_analog = Layout :: from_size_align ( base_size, base. align . get ( ) ) . unwrap ( ) ;
1549+
1550+ match field. size_info {
1551+ SizeInfo :: Sized { size : field_size } => {
1552+ if let SizeInfo :: Sized { size : composite_size } = composite. size_info {
1553+ // If the trailing field is sized, the resulting layout will
1554+ // be sized. Its size will be the sum of the preceding
1555+ // layout, the size of the new field, and the size of
1556+ // inter-field padding between the two.
1557+ assert_eq ! ( composite_size, offset + field_size) ;
1558+
1559+ let field_analog =
1560+ Layout :: from_size_align ( field_size, field_align. get ( ) ) . unwrap ( ) ;
1561+
1562+ if let Ok ( ( actual_composite, actual_offset) ) = base_analog. extend ( field_analog)
1563+ {
1564+ assert_eq ! ( actual_offset, offset) ;
1565+ assert_eq ! ( actual_composite. size( ) , composite_size) ;
1566+ assert_eq ! ( actual_composite. align( ) , composite. align. get( ) ) ;
1567+ } else {
1568+ // An error here reflects that composite of `base`
1569+ // and `field` cannot correspond to a real Rust type
1570+ // fragment, because such a fragment would violate
1571+ // the basic invariants of a valid Rust layout. At
1572+ // the time of writing, `DstLayout` is a little more
1573+ // permissive than `Layout`, so we don't assert
1574+ // anything in this branch (e.g., unreachability).
1575+ }
1576+ } else {
1577+ panic ! ( "The composite of two sized layouts must be sized." )
1578+ }
1579+ }
1580+ SizeInfo :: SliceDst ( TrailingSliceLayout {
1581+ offset : field_offset,
1582+ elem_size : field_elem_size,
1583+ } ) => {
1584+ if let SizeInfo :: SliceDst ( TrailingSliceLayout {
1585+ offset : composite_offset,
1586+ elem_size : composite_elem_size,
1587+ } ) = composite. size_info
1588+ {
1589+ // The offset of the trailing slice component is the sum
1590+ // of the offset of the trailing field and the trailing
1591+ // slice offset within that field.
1592+ assert_eq ! ( composite_offset, offset + field_offset) ;
1593+ // The elem size is unchanged.
1594+ assert_eq ! ( composite_elem_size, field_elem_size) ;
1595+
1596+ let field_analog =
1597+ Layout :: from_size_align ( field_offset, field_align. get ( ) ) . unwrap ( ) ;
1598+
1599+ if let Ok ( ( actual_composite, actual_offset) ) = base_analog. extend ( field_analog)
1600+ {
1601+ assert_eq ! ( actual_offset, offset) ;
1602+ assert_eq ! ( actual_composite. size( ) , composite_offset) ;
1603+ assert_eq ! ( actual_composite. align( ) , composite. align. get( ) ) ;
1604+ } else {
1605+ // An error here reflects that composite of `base`
1606+ // and `field` cannot correspond to a real Rust type
1607+ // fragment, because such a fragment would violate
1608+ // the basic invariants of a valid Rust layout. At
1609+ // the time of writing, `DstLayout` is a little more
1610+ // permissive than `Layout`, so we don't assert
1611+ // anything in this branch (e.g., unreachability).
1612+ }
1613+ } else {
1614+ panic ! ( "The extension of a layout with a DST must result in a DST." )
1615+ }
1616+ }
1617+ }
1618+ }
1619+
1620+ #[ kani:: proof]
1621+ #[ kani:: should_panic]
1622+ fn prove_dst_layout_extend_dst_panics ( ) {
1623+ let base: DstLayout = kani:: any ( ) ;
1624+ let field: DstLayout = kani:: any ( ) ;
1625+ let packed: Option < NonZeroUsize > = kani:: any ( ) ;
1626+
1627+ if let Some ( max_align) = packed {
1628+ kani:: assume ( max_align. is_power_of_two ( ) ) ;
1629+ kani:: assume ( base. align <= max_align) ;
1630+ }
1631+
1632+ kani:: assume ( matches ! ( base. size_info, SizeInfo :: SliceDst ( ..) ) ) ;
1633+
1634+ let _ = base. extend ( field, packed) ;
1635+ }
1636+
1637+ #[ kani:: proof]
1638+ fn prove_dst_layout_pad_to_align ( ) {
1639+ use crate :: util:: padding_needed_for;
1640+
1641+ let layout: DstLayout = kani:: any ( ) ;
1642+
1643+ let padded: DstLayout = layout. pad_to_align ( ) ;
1644+
1645+ // Calling `pad_to_align` does not alter the `DstLayout`'s alignment.
1646+ assert_eq ! ( padded. align, layout. align) ;
1647+
1648+ if let SizeInfo :: Sized { size : unpadded_size } = layout. size_info {
1649+ if let SizeInfo :: Sized { size : padded_size } = padded. size_info {
1650+ // If the layout is sized, it will remain sized after padding is
1651+ // added. Its sum will be its unpadded size and the size of the
1652+ // trailing padding needed to satisfy its alignment
1653+ // requirements.
1654+ let padding = padding_needed_for ( unpadded_size, layout. align ) ;
1655+ assert_eq ! ( padded_size, unpadded_size + padding) ;
1656+
1657+ // Prove that calling `DstLayout::pad_to_align` behaves
1658+ // identically to `Layout::pad_to_align`.
1659+ let layout_analog =
1660+ Layout :: from_size_align ( unpadded_size, layout. align . get ( ) ) . unwrap ( ) ;
1661+ let padded_analog = layout_analog. pad_to_align ( ) ;
1662+ assert_eq ! ( padded_analog. align( ) , layout. align. get( ) ) ;
1663+ assert_eq ! ( padded_analog. size( ) , padded_size) ;
1664+ } else {
1665+ panic ! ( "The padding of a sized layout must result in a sized layout." )
1666+ }
1667+ } else {
1668+ // If the layout is a DST, padding cannot be statically added.
1669+ assert_eq ! ( padded. size_info, layout. size_info) ;
1670+ }
1671+ }
1672+ }
0 commit comments