Skip to content

Commit

Permalink
Use pseudocode to document transmute_ref
Browse files Browse the repository at this point in the history
Partially addresses #1046.
  • Loading branch information
jswrenn committed Mar 15, 2024
1 parent 730f400 commit 298f6b8
Showing 1 changed file with 39 additions and 28 deletions.
67 changes: 39 additions & 28 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::<T>() >=
/// align_of::<U>()`.
/// 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::<Src>() == size_of::<Dst>(),
/// align_of::<Src>() >= align_of::<Dst>(),
/// {
/// # /*
/// ...
/// # */
/// }
/// ```
///
/// 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
///
Expand Down Expand Up @@ -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::<T>() >= align_of::<U>()`.
/// 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::<T>() >= align_of::<U>()`.
///
/// The lifetime of the input type, `&mut T`, must be the same as or outlive the
/// lifetime of the output type, `&mut U`.
Expand Down Expand Up @@ -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
Expand All @@ -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 {
Expand All @@ -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 {
Expand All @@ -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::<Src>() == size_of::<Dst>()` thanks to
// the use of `assert_size_eq!` above.
// - We know that `align_of::<Src>() >= align_of::<Dst>()` thanks to
Expand Down Expand Up @@ -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]
Expand Down

0 comments on commit 298f6b8

Please sign in to comment.