@@ -1429,3 +1429,244 @@ mod tests {
1429
1429
validate_against_rust ! ( 4 , 16 , 4 ) ;
1430
1430
}
1431
1431
}
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