@@ -1463,6 +1463,96 @@ macro_rules! transmute {
1463
1463
} }
1464
1464
}
1465
1465
1466
+ /// A type whose size is equal to `mem::align_of::<T>()`.
1467
+ ///
1468
+ /// This type is used internally in `transmute_ref!`, and so needs to be
1469
+ /// exported. It is not intended for direct use by users of zerocopy, and so is
1470
+ /// `#[doc(hidden)]`.
1471
+ #[ doc( hidden) ]
1472
+ #[ allow( missing_debug_implementations) ]
1473
+ #[ repr( C ) ]
1474
+ pub struct SizeIsAlign < T > {
1475
+ // This field ensures that:
1476
+ // - The size is always at least 1 (the minimum possible alignment).
1477
+ // - If the alignment is greater than 1, Rust has to round up to the next
1478
+ // multiple of it in order to make sure that `SizeIsAlign`'s size is a
1479
+ // multiple of that alignment. Without this field, its size could be 0,
1480
+ // which is a valid multiple of any alignment.
1481
+ _u : u8 ,
1482
+ _a : [ T ; 0 ] ,
1483
+ }
1484
+
1485
+ impl < T > SizeIsAlign < T > {
1486
+ #[ doc( hidden) ]
1487
+ pub fn new ( _t : T ) -> SizeIsAlign < T > {
1488
+ SizeIsAlign { _u : 0 , _a : [ ] }
1489
+ }
1490
+
1491
+ #[ doc( hidden) ]
1492
+ pub fn into_t ( self ) -> T {
1493
+ unreachable ! ( )
1494
+ }
1495
+ }
1496
+
1497
+ /// Safely transmutes an immutable reference of one type to an immutable
1498
+ /// reference of another type of the same size and alignment.
1499
+ ///
1500
+ /// The expression `$e` must have a concrete type, `&T`, where `T: Sized +
1501
+ /// AsBytes`. The `transmute_ref!` expression must also have a concrete type,
1502
+ /// `&U` (`U` is inferred from the calling context), where `U: Sized +
1503
+ /// FromBytes`.
1504
+ ///
1505
+ /// The lifetime of the input type, `&T`, must be the same as or outlive the
1506
+ /// lifetime of the output type, `&U`.
1507
+ #[ macro_export]
1508
+ macro_rules! transmute_ref {
1509
+ ( $e: expr) => { {
1510
+ // NOTE: This must be a macro (rather than a function with trait bounds)
1511
+ // because there's no way, in a generic context, to enforce that two
1512
+ // types have the same size or alignment. `core::mem::transmute` uses
1513
+ // compiler magic to enforce size equality so long as the types are
1514
+ // concrete. We use `SizeIsAlign` to create a type whose size is equal
1515
+ // to the alignment of another type so that we can use `transmute` to
1516
+ // check alignment as well.
1517
+
1518
+ let e = $e;
1519
+ #[ allow( unused, clippy:: diverging_sub_expression) ]
1520
+ if false {
1521
+ // This branch, though never taken, ensures that the type of `e` is
1522
+ // `&T` where `T: 't + Sized + AsBytes`, that the type of this macro
1523
+ // expression is `&U` where `U: 'u + Sized + FromBytes`, and that
1524
+ // `'t` outlives `'u`.
1525
+ const fn transmute<' u, ' t: ' u, T : ' t + Sized + $crate:: AsBytes , U : ' u + Sized + $crate:: FromBytes >( _t: & ' t T ) -> & ' u U {
1526
+ unreachable!( )
1527
+ }
1528
+ transmute( e)
1529
+ } else if false {
1530
+ // This branch, though never taken, ensures that the alignment of
1531
+ // `T` is equal to the alignment of `U`.
1532
+ let target = unreachable!( ) ;
1533
+ e = & target;
1534
+
1535
+ // SAFETY: This code is never executed.
1536
+ let ret: $crate:: SizeIsAlign <_> = unsafe { $crate:: __real_transmute( $crate:: SizeIsAlign :: new( target) ) } ;
1537
+ & ret. into_t( )
1538
+ } else {
1539
+ // SAFETY: `core::mem::transmute` ensures that the type of `e` and
1540
+ // the type of this macro invocation expression have the same size.
1541
+ // We know this transmute is safe thanks to the `AsBytes` and
1542
+ // `FromBytes` bounds enforced by the `false` branch.
1543
+ //
1544
+ // We use `$crate::__real_transmute` because we know it will always
1545
+ // be available for crates which are using the 2015 edition of Rust.
1546
+ // By contrast, if we were to use `std::mem::transmute`, this macro
1547
+ // would not work for such crates in `no_std` contexts, and if we
1548
+ // were to use `core::mem::transmute`, this macro would not work in
1549
+ // `std` contexts in which `core` was not manually imported. This is
1550
+ // not a problem for 2018 edition crates.
1551
+ unsafe { $crate:: __real_transmute( e) }
1552
+ }
1553
+ } }
1554
+ }
1555
+
1466
1556
/// A length- and alignment-checked reference to a byte slice which can safely
1467
1557
/// be reinterpreted as another type.
1468
1558
///
@@ -3195,6 +3285,43 @@ mod tests {
3195
3285
assert_eq ! ( X , ARRAY_OF_ARRAYS ) ;
3196
3286
}
3197
3287
3288
+ #[ test]
3289
+ fn test_size_is_align ( ) {
3290
+ macro_rules! test {
3291
+ ( $ty: ty) => {
3292
+ assert_eq!( mem:: size_of:: <SizeIsAlign <$ty>>( ) , mem:: align_of:: <$ty>( ) ) ;
3293
+ } ;
3294
+ }
3295
+
3296
+ test ! ( ( ) ) ;
3297
+ test ! ( u8 ) ;
3298
+ test ! ( AU64 ) ;
3299
+ test ! ( [ AU64 ; 2 ] ) ;
3300
+ }
3301
+
3302
+ #[ test]
3303
+ fn test_transmute_ref ( ) {
3304
+ // Test that memory is transmuted as expected.
3305
+ let array_of_u8s = [ 0u8 , 1 , 2 , 3 , 4 , 5 , 6 , 7 ] ;
3306
+ let array_of_arrays = [ [ 0 , 1 ] , [ 2 , 3 ] , [ 4 , 5 ] , [ 6 , 7 ] ] ;
3307
+ let x: & [ [ u8 ; 2 ] ; 4 ] = transmute_ref ! ( & array_of_u8s) ;
3308
+ assert_eq ! ( * x, array_of_arrays) ;
3309
+ let x: & [ u8 ; 8 ] = transmute_ref ! ( & array_of_arrays) ;
3310
+ assert_eq ! ( * x, array_of_u8s) ;
3311
+
3312
+ // Test that `transmute_ref!` is legal in a const context.
3313
+ const ARRAY_OF_U8S : [ u8 ; 8 ] = [ 0u8 , 1 , 2 , 3 , 4 , 5 , 6 , 7 ] ;
3314
+ const ARRAY_OF_ARRAYS : [ [ u8 ; 2 ] ; 4 ] = [ [ 0 , 1 ] , [ 2 , 3 ] , [ 4 , 5 ] , [ 6 , 7 ] ] ;
3315
+ #[ allow( clippy:: redundant_static_lifetimes) ]
3316
+ const X : & ' static [ [ u8 ; 2 ] ; 4 ] = transmute_ref ! ( & ARRAY_OF_U8S ) ;
3317
+ assert_eq ! ( * X , ARRAY_OF_ARRAYS ) ;
3318
+
3319
+ // Test that it's legal to transmute a reference while shrinking the
3320
+ // lifetime (note that `X` has the lifetime `'static`).
3321
+ let x: & [ u8 ; 8 ] = transmute_ref ! ( X ) ;
3322
+ assert_eq ! ( * x, ARRAY_OF_U8S ) ;
3323
+ }
3324
+
3198
3325
#[ test]
3199
3326
fn test_address ( ) {
3200
3327
// Test that the `Deref` and `DerefMut` implementations return a
0 commit comments