Repository: bevyengine/bevy
File: crates/bevy_ptr/src/lib.rs (Rust)

Code before:
///
/// assert_eq!(target_a.0, 11);
/// assert_eq!(target_b.0, 22);
/// assert_eq!(target_c.0, 33);
/// ```
///
/// [`assign_to`]: MovingPtr::assign_to
#[macro_export]
macro_rules! deconstruct_moving_ptr {
    ($ptr:ident => {$($field_name:ident),* $(,)?}) => {
        $crate::deconstruct_moving_ptr!($ptr => ($($field_name => $field_name,)*))
    };
    ($ptr:ident => ($($field_index:tt => $field_alias:ident),* $(,)?)) => {
        // Specify the type to make sure we don't `mem::forget` a mere `&mut MovingPtr`
        let mut ptr: $crate::MovingPtr<_, _> = $ptr;
        let _ = || {
            let value = &mut *ptr;
            // If a field is mentioned twice, this will cause mutable aliasing and fail to compile.
            let _ = ($(&mut value.$field_index),*);
        };
        // SAFETY:
        // - `f` does a raw pointer offset, which always returns a non-null pointer to a field inside `T`
        // - The struct is not `repr(packed)`, since otherwise the block of code above would fail compilation
        // - We call `mem::forget` on `self` immediately after these calls
        // - Each field is distinct, since otherwise the block of code above would fail compilation
        $(let $field_alias = unsafe { ptr.move_field(|f| &raw mut (*f).$field_index) };)*
        core::mem::forget(ptr);
    };
    ($ptr:ident: MaybeUninit => {$($field_name:tt),* $(,)?}) => {
        $crate::deconstruct_moving_ptr!($ptr: MaybeUninit => ($($field_name => $field_name,)*))
    };
    ($ptr:ident: MaybeUninit => ($($field_index:tt => $field_alias:ident),* $(,)?)) => {
        // Specify the type to make sure we don't `mem::forget` a mere `&mut MovingPtr`
        let mut ptr: $crate::MovingPtr<_, _> = $ptr;
        let _ = || {
            // SAFETY: This closure is never called
            let value = unsafe { ptr.assume_init_mut() };
            // If a field is mentioned twice, this will cause mutable aliasing and fail to compile.
            let _ = ($(&mut value.$field_index),*);
        };
        // SAFETY:
        // - `f` does a raw pointer offset, which always returns a non-null pointer to a field inside `T`
        // - The struct is not `repr(packed)`, since otherwise the block of code above would fail compilation
        // - We call `mem::forget` on `self` immediately after these calls
        // - Each field is distinct, since otherwise the block of code above would fail compilation
        $(let $field_alias = unsafe { ptr.move_maybe_uninit_field(|f| &raw mut (*f).$field_index) };)*
        core::mem::forget(ptr);
    };
}


Review comment (suggestion):
I believe this is most likely unsound. Doing `&raw mut (*f).$field_index` does not guarantee you'are actually accessing a field on the type pointed by `f`, but instead you might trigger an an autoderef, which will call `DerefMut` with a `&mut` reference to your type. That in turn allows to invalidate all the previous pointers you obtained. This will most likely trigger the (deny-by-default) `dangerous_implicit_autorefs` lint, but lints cannot guarantee soundness.

Destructuring as mentioned in the other comment should solve this issue, even if never executed (e.g. in the above closure), as that still statically forces the idents to be valid fields of your type.

Code after:
///
/// // Converts `parent` into a `MovingPtr`
/// move_as_ptr!(parent);
///
/// // The field names must match the name used in the type definition.
/// // Each one will be a `MovingPtr` of the field's type.
/// bevy_ptr::deconstruct_moving_ptr!({
///   let tuple { 0: field_a, 1: field_b, 2: field_c } = parent;
/// });
///
/// field_a.assign_to(&mut target_a);
/// field_b.assign_to(&mut target_b);
/// field_c.assign_to(&mut target_c);
///
/// assert_eq!(target_a.0, 11);
/// assert_eq!(target_b.0, 22);
/// assert_eq!(target_c.0, 33);
/// ```
///
/// ## `MaybeUninit`
///
/// ```
/// use core::mem::{offset_of, MaybeUninit};
/// use bevy_ptr::{MovingPtr, move_as_ptr};
/// # use bevy_ptr::Unaligned;
/// # struct FieldAType(usize);
/// # struct FieldBType(usize);
/// # struct FieldCType(usize);
///
/// # pub struct Parent {
/// #  pub field_a: FieldAType,
/// #  pub field_b: FieldBType,
/// #  pub field_c: FieldCType,
/// # }
///
/// let parent = MaybeUninit::new(Parent {
///   field_a: FieldAType(11),
///   field_b: FieldBType(22),
///   field_c: FieldCType(33),
/// });
///
/// let mut target_a = MaybeUninit::new(FieldAType(101));
/// let mut target_b = MaybeUninit::new(FieldBType(102));
/// let mut target_c = MaybeUninit::new(FieldCType(103));
///
/// // Converts `parent` into a `MovingPtr`
/// move_as_ptr!(parent);
///
/// // The field names must match the name used in the type definition.
/// // Each one will be a `MovingPtr` of the field's type.
/// bevy_ptr::deconstruct_moving_ptr!({