I've pushed an update to add an "alternatives considered" item that describes what may be a better design:
Separating the FullyInhabited protocol into separate ConvertibleToRawBytes and ConvertibleFromRawBytes protocols.
As described previously, FullyInhabited is a stronger constraint than is needed to prevent uninitialized bytes when initializing memory. The minimal constraint would simply be the absence of padding; this could be called ConvertibleToRawBytes. Similarly, FullyInhabited is a stronger constraint than is needed to exclude unsafety when loading bytes from a RawSpan instance. The minimal constraint would be similar to FullyInhabited, but would allow for padding bytes; this could be called ConvertibleFromRawBytes. Types that conform to both would meet the requirements of FullyInhabited as described here.
typealias FullyInhabited = ConvertibleToRawBytes & ConvertibleFromRawBytes
Separating FullyInhabited in this manner would make the system more flexible, at the cost of some simplicity. It would allow us to define a safe bitcast operation:
func bitCast<A,B>(_ original: consuming A, to: B.self) -> B
where A: ConvertibleToRawBytes, B: ConvertibleFromRawBytes
This design would get us closer to the ideal than the version of FullyInhabited we’ve been discussing.