unsafeBitCast() and alignment

The documentation for unsafeBitCast() says that the two types must have compatible memory layout. Does that rule include memory alignment?

Here is an example:

let src: (UInt32, UInt32) = (1, 1)
let dest = unsafeBitCast(src, to: UInt64.self)

My guess is that this is undefined (but the reverse operation is defined). unsafeBitCast() calls Builtin.reinterpretCast(), and my research on C++ reinterpret_cast() suggests that alignment rules must be respected.

That's right, you have to respect alignment. As with reinterpret cast in C++, it may happen to work when not suitably aligned, but you are formally outside the defined behavior of the Swift model, and there's no guarantee that it will keep working.

Note that explicit unaligned loads and stores were added in Swift 5.7, if you need them: swift-evolution/0349-unaligned-loads-and-stores.md at main · apple/swift-evolution · GitHub

unsafeBitCast is a value operation in Swift, so there's no fundamental reason it needs to require the alignment to match, and I would call it a bug if we do. We can copy the value to a correctly-aligned memory buffer (if it even needs to hit memory at all). reinterpret_cast in C++ casts a pointer to one type into one to another, and that's where the alignment requirement comes from. The closer analogy in C++ would be C++23's std::bit_cast function, which only requires the sizes to match, not alignment since it is defined in terms of memcpy.

7 Likes

Thanks, that's great to hear.