ABI and MaybeUninit

So memory itself is untyped, even on the stack. When you write 0x02_u8 to a byte, that byte contains 0x02_u8, and reading it will produce that value.

Where the potential pitfall is, is when you make a typed copy of that value — that is, you read the memory as a MaybeUninit<bool>.

In RalfJ's MiniRust experimental partial formalization, a typed copy consists of essentially decoding the AM-bytes into the abstract value then encoding that abstract value back to AM-bytes at the new location.

So it's a question of defining the decode and encode steps for bool and for MaybeUninit<bool>.

bool is simple enough (minirust/values.md at master · RalfJung/minirust · GitHub pseudo-Rust):

impl Type {
    fn decode(Type::Bool: Self, bytes: List<AbstractByte>) -> Option<Value> {
        match *bytes {
            [AbstractByte::Init(0, _)] => Value::Bool(false),
            [AbstractByte::Init(1, _)] => Value::Bool(true),
            _ => throw_ub!(),
        }
    }
    fn encode(Type::Bool: Self, val: Value) -> List<AbstractByte> {
        let Value::Bool(b) = val else { unreachable!() };
        [AbstractByte::Init(if b { 1 } else { 0 }, None)]
    }
}

The question then is how is MaybeUninit's encode/decode defined, and how does that match with how rustc lowers it to the concrete machine's ABI?

The simple and desirable definition for #[repr(Rust)] union is that encode/decode just copy the AM-bytes directly.

But unfortunately MaybeUninit<T> is more complicated, because we want to lower it with the ABI of T. The simple answer for decode is then to decode { .init: T }, but if that fails, decode { .uninit: () }. This would then only preserve through a typed copy AM-bytes which are valid for the wrapped type's decode.

And some amount of this may be necessary — at a minimum, internal padding can be nonpreserved at the ABI level, so passing MaybeUninit<T> of such a value cannot preserve the padding bytes if it's passed with the ABI of T.

The linked discussion is more about #[repr(Rust)] union but includes some discussion about #[repr(transparent)] union (but more specifically MaybeUninit) as well.

3 Likes