When is it sound to transmute to a reference to an union?

Let's assume I have a few types A,B and C, as well as the following union:

#[repr(C)]
union U {
    a: A
    b: B
    c: C
}

I'm trying to know if it is correct to do the following:

let value: B = todo!();
let by_ref: &U = unsafe { transmute(&value) };
// Do something with by_ref that only reads the `b` field
let by_mut: &mut U = unsafe { transmute(&mut value) };
// Do something with by_mut that only reads and writes the `b` field

The opposite is always possible by just accessing the fields of the union (with unsafe) but I'm not convinced this is not UB.

I think it is sound because as long as you only access the b field of the union, you'll never read or write data that was not part of the initial value.

From the C spec:

A pointer to a union object, suitably converted, points to each of its members (or if a member is a bit- field, then to the unit in which it resides), and vice versa.

My understanding is that this would be sound with pointers. Does it also make it sound for Rust references?

If so, what is the point of the proposed #[repr(transparent)] for unions?

It's UB because the alignment is wrong (for appropriate choices of B and A/C.

For example, https://rust.godbolt.org/z/qd6GY66zb

#[repr(C)]
pub union Demo {
    a: [u8; 4],
    b: u64,
}

pub unsafe fn demo(u: &Demo) -> [u8; 4] {
    u.a
}

compiles to

define i32 @_ZN7example4demo17h2a69cf62bc406b28E(ptr noalias nocapture noundef readonly align 8 dereferenceable(8) %u) unnamed_addr #0 !dbg !6 {
  %.sroa.0.0.copyload = load i32, ptr %u, align 8, !dbg !11
                                          ^^^^^^^
  ret i32 %.sroa.0.0.copyload, !dbg !12
}

But if you cast a &[u8; 4] to a &Demo, it won't necessarily be that aligned, and thus that load can be UB.

1 Like

Thanks!

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.