From 298f6b8193db0a83d6048209ecaf14a5e541a4ea Mon Sep 17 00:00:00 2001 From: Jack Wrenn Date: Fri, 15 Mar 2024 15:39:41 +0000 Subject: [PATCH] Use pseudocode to document `transmute_ref` Partially addresses #1046. --- src/lib.rs | 67 +++++++++++++++++++++++++++++++----------------------- 1 file changed, 39 insertions(+), 28 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 2e74ae2390b..61fa5a31b72 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4418,14 +4418,25 @@ macro_rules! transmute { /// Safely transmutes a mutable or immutable reference of one type to an /// immutable reference of another type of the same size. /// -/// The expression `$e` must have a concrete type, `&T` or `&mut T`, where `T: -/// Sized + IntoBytes`. The `transmute_ref!` expression must also have a -/// concrete type, `&U` (`U` is inferred from the calling context), where `U: -/// Sized + FromBytes`. It must be the case that `align_of::() >= -/// align_of::()`. +/// This macro behaves like an invocation of this function: +/// +/// ```ignore +/// const fn transmute_ref<'src, 'dst, Src, Dst>(src: &'src Src) -> &'dst Dst +/// where +/// 'src: 'dst, +/// Src: IntoBytes + NoCell, +/// Dst: FromBytes + NoCell, +/// size_of::() == size_of::(), +/// align_of::() >= align_of::(), +/// { +/// # /* +/// ... +/// # */ +/// } +/// ``` /// -/// The lifetime of the input type, `&T` or `&mut T`, must be the same as or -/// outlive the lifetime of the output type, `&U`. +/// However, unlike a function, this macro can only be invoked when the types of +/// `Src` and `Dst` are completely concrete. /// /// # Examples /// @@ -4550,9 +4561,10 @@ macro_rules! transmute_ref { /// another type of the same size. /// /// The expression `$e` must have a concrete type, `&mut T`, where `T: Sized + -/// IntoBytes`. The `transmute_mut!` expression must also have a concrete type, -/// `&mut U` (`U` is inferred from the calling context), where `U: Sized + -/// FromBytes`. It must be the case that `align_of::() >= align_of::()`. +/// FromBytes + IntoBytes`. The `transmute_mut!` expression must also have a +/// concrete type, `&mut U` (`U` is inferred from the calling context), where +/// `U: Sized + FromBytes + IntoBytes`. It must be the case that +/// `align_of::() >= align_of::()`. /// /// The lifetime of the input type, `&mut T`, must be the same as or outlive the /// lifetime of the output type, `&mut U`. @@ -4617,9 +4629,9 @@ macro_rules! transmute_mut { #[allow(unused, clippy::diverging_sub_expression)] if false { // This branch, though never taken, ensures that the type of `e` is - // `&mut T` where `T: 't + Sized + FromBytes + IntoBytes + NoCell` - // and that the type of this macro expression is `&mut U` where `U: - // 'u + Sized + FromBytes + IntoBytes + NoCell`. + // `&mut T` where `T: 't + Sized + FromBytes + IntoBytes` and that + // the type of this macro expression is `&mut U` where `U: 'u + + // Sized + FromBytes + IntoBytes`. // We use immutable references here rather than mutable so that, if // this macro is used in a const context (in which, as of this @@ -4629,20 +4641,16 @@ macro_rules! transmute_mut { struct AssertSrcIsSized<'a, T: ::core::marker::Sized>(&'a T); struct AssertSrcIsFromBytes<'a, T: ?::core::marker::Sized + $crate::FromBytes>(&'a T); struct AssertSrcIsIntoBytes<'a, T: ?::core::marker::Sized + $crate::IntoBytes>(&'a T); - struct AssertSrcIsNoCell<'a, T: ?::core::marker::Sized + $crate::NoCell>(&'a T); struct AssertDstIsSized<'a, T: ::core::marker::Sized>(&'a T); struct AssertDstIsFromBytes<'a, T: ?::core::marker::Sized + $crate::FromBytes>(&'a T); struct AssertDstIsIntoBytes<'a, T: ?::core::marker::Sized + $crate::IntoBytes>(&'a T); - struct AssertDstIsNoCell<'a, T: ?::core::marker::Sized + $crate::NoCell>(&'a T); if true { let _ = AssertSrcIsSized(&*e); } else if true { let _ = AssertSrcIsFromBytes(&*e); - } else if true { - let _ = AssertSrcIsIntoBytes(&*e); } else { - let _ = AssertSrcIsNoCell(&*e); + let _ = AssertSrcIsIntoBytes(&*e); } if true { @@ -4653,13 +4661,9 @@ macro_rules! transmute_mut { #[allow(unused, unreachable_code)] let u = AssertDstIsFromBytes(loop {}); &mut *u.0 - } else if true { - #[allow(unused, unreachable_code)] - let u = AssertDstIsIntoBytes(loop {}); - &mut *u.0 } else { #[allow(unused, unreachable_code)] - let u = AssertDstIsNoCell(loop {}); + let u = AssertDstIsIntoBytes(loop {}); &mut *u.0 } } else if false { @@ -4682,11 +4686,10 @@ macro_rules! transmute_mut { &mut u } else { // SAFETY: For source type `Src` and destination type `Dst`: - // - We know that `Src: FromBytes + IntoBytes + NoCell` and `Dst: - // FromBytes + IntoBytes + NoCell` thanks to the uses of - // `AssertSrcIsFromBytes`, `AssertSrcIsIntoBytes`, - // `AssertSrcIsNoCell`, `AssertDstIsFromBytes`, - // `AssertDstIsIntoBytes`, and `AssertDstIsNoCell` above. + // - We know that `Src: FromBytes + IntoBytes` and `Dst: FromBytes + + // IntoBytes` thanks to the uses of `AssertSrcIsFromBytes`, + // `AssertSrcIsIntoBytes`, `AssertDstIsFromBytes`, and + // `AssertDstIsIntoBytes` above. // - We know that `size_of::() == size_of::()` thanks to // the use of `assert_size_eq!` above. // - We know that `align_of::() >= align_of::()` thanks to @@ -7418,6 +7421,14 @@ mod tests { #[allow(clippy::useless_transmute)] let y: &u8 = transmute_mut!(&mut x); assert_eq!(*y, 0); + + // Test that the referents can contain `UnsafeCell`s. + let mut src = AtomicU8::new(42); + { + let dst: &mut AtomicI8 = transmute_mut!(&mut src); + *dst.get_mut() += 1; + } + assert_eq!(src.into_inner(), 43); } #[test]