# Transmutation (2020-12) We provide two APIs: 1. An API that connotes a SemVer-stable transmutation conversion. 2. A foundational intrinsic that is purely a compiler analysis of well-definedness and safety. These APIs are distinct features, on distinct stabilization tracks. ## SemVer `Muckable` API We introduce an initial API that *does* connote SemVer stability: ```rust pub unsafe trait TransmuteFrom<Src, const NEGLECT: Neglect> where Src: ?Sized {} unsafe impl<Src, Dst, const Neglect: NEGLECT> TransmuteFrom<Src, NEGLECT> for Dst where Src: MuckableInto + ?Sized, Dst: MuckableFrom + ?Sized, Dst: /* see foundational API section for details */ {} /* Implemented by user to denote transmutation stability: */ /// Implement this to denote that your type can be stably /// transmuted *from* any compatible type. pub unsafe trait MuckableFrom {} /// Implement this to denote that your type can be stably /// transmuted *into* any compatible type. pub unsafe trait MuckableInto {} ``` ### Where does this intrinsic live? We recommend that it lives in `core::convert`. ### What is the role of this API? This API provides a mechanism for transmuting from `Src` to `Dst` in cases where *all* observable layout qualities of `Src` and `Dst` are declared SemVer stable. ### What *isn't* the role of this API? This API *doesn't* provide an arbitrarily fine-grained reflection of layout stability into the trait system. This API is optimized for the everything-is-stable use-case. The foundational API may be used to construct separate stable abstractions that are specialized for other use-cases. ### What does implementing `MuckableFrom` connote? Implement `MuckableFrom` to denote that your type can be stably transmuted from any layout compatible type. By implementing this type, you vow that you will **not**: - reduce the bit validity of its fields - apply any library validity invariants to its fields - increase its size - increase its minimum alignment - reorder its fields ### What does implementing `MuckableInto` connote? Implement `MuckableInto` to denote that your type can be stably transmuted from any layout compatible type. By implementing this type, you vow that you will **not**: - expand the bit validity of its fields - decrease its size - decrease its minimum alignment - reorder its fields ### What design work remains? We need to: - [ ] bikeshed the names - [ ] exhaustively document the full set of layout changes that the stability markers prohibit - [ ] determine if the stability markers must be `unsafe` - [ ] determine if this is the right reflection of layout stability; potential alternatives include: - a single `Muckable` trait, slightly less flexible but more explainable - an archetype-based system (that might be more flexible) ## Foundational API The foundational API is a *compiler-intrinsic* trait (end-users *cannot* implement it) in the form: ```rust pub unsafe trait UnstableTransmuteFrom<Src, Scope, const NEGLECT: Neglect> where Src: ?Sized {} ``` This API is capable of telling you whether a particular transmutation is well-defined and safe (notwithstanding whatever static checks you decide to `Neglect`). ### Where does this intrinsic live? We recommend that it exists in `core::mem`. ### Why is it called `UnstableTransmuteFrom`? This intrinsic does *not* reflect the SemVer stability of the layouts of `Src` and `Dst`. In this respect, it mirrors most other intrinsics in `core::mem`, which provide layout observability *without* connotations of stability. Nonetheless, this is a potential footgun, so we take the additional step of sign-posting its instability in its name. ### What is the role of `UnstableTransmuteFrom`? This intrinsic is capable of detecting whether *any* particular transmutation-like operation is safe (or, specifically, as safe `Neglect` permits). It is a *general* mechanism for both: * auditing existing unsafe code * constructing abstractions that *do* connote some degree of SemVer stability For instance, the `Muckable` API may be defined as: ```rust unsafe impl<Src, Dst, const Neglect: NEGLECT> TransmuteFrom<Src, NEGLECT> for Dst where Src: MuckableInto + ?Sized, Dst: MuckableFrom + ?Sized, Dst: mem::UnstableTransmuteFrom<Src, !, {Neglect { visibility: true, ..NEGLECT}}> {} ``` ...and a `FromZeros` trait is definable as: ```rust pub unsafe trait FromZeros<Scope, const NEGLECT: Neglect> {} #[repr(u8)] enum Zero { Zero = 0u8 } unsafe impl<Dst, Scope, const NEGLECT: Neglect> FromZeros<Scope, NEGLECT> for Dst where Dst: UnstableTransmuteFrom<[Zero; usize::MAX], Scope, NEGLECT>, {} ``` ### What is `Neglect`? The `Neglect` parameter encodes the set of static checks that the compiler should ignore when determining transmutability. These checks include: - alignment - lifetimes - validity - visibility Neglecting *any* static checks disqualifies a transmutation from being safe. The `Neglect` type is represented like this: ```rust #[derive(PartialEq, Eq)] #[non_exhaustive] pub struct Neglect { pub alignment : bool, pub lifetimes : bool, pub validity : bool, pub visibility : bool, } impl Neglect { pub const NOTHING: Self = Neglect { alignment : false, lifetimes : false, validity : false, visibility : false, }; } ``` ### How does this know whether a transmutation is well-defined? A transmutation is well-defined if *any* possible values of type `Src` are a valid instance of `Dst`. The compiler determines this by inspecting the layouts of `Src` and `Dst`. ### How does this know whether a transmutation is safe? In order to be safe, a transmutation must not allow you to: - construct instances of a hidden `Dst` type - mutate hidden fields of the `Src` type - construct hidden fields of the `Dst` type Whether a type or field is visibile or hidden depends is particular to the scope the transmutation occurs it. [Type privacy](https://rust-lang.github.io/rfcs/2145-type-privacy.html) ensures that a hidden `Dst` cannot be constructed. The `Scope` parameter is used to detect whether fields have adequate visibility: * When visibility is enforced, `Scope` must be instantiated with a private type. The compiler pretends its at the defining scope of that type, and checks that the necessary fields of `Src` and `Dst` are visible. * When visibility is neglected, the `Scope` parameter is totally ignored. ## Extended FAQ ### Why do we need `Neglect`? The ability to neglect *particular* static checks permits the safer construction of *failable* abstractions, like those defined by bytemuck. For instance: ```rust /// Try to convert a `&T` into `&U`. /// /// This produces `None` if the referent isn't appropriately /// aligned, as required by the destination type. pub fn try_cast_ref<'t, 'u, T, U>(src: &'t T) -> Option<&'u U> where &'t T: TransmuteFrom<&'u U, {Neglect {alignment: true, ..Neglect::NOTHING}}>, { if (src as *const T as usize) % align_of::<U>() != 0 { None } else { // Safe because we dynamically enforce the alignment // requirement, whose static check we chose to neglect. Some(unsafe { TransmuteFrom::unsafe_transmute_from(src) }) } } ``` Above, we communicate to the compiler that we are taking the burden of alignment enforcement onto ourselves — but the compiler is still enforcing all *other* checks that might impact transmutation safety ### Why is `Neglect` represented in the way it is? [We've considered a few different ways in which `Neglect` might be represented.](https://hackmd.io/@jswrenn/S192QCR9D) An ideal representation has three properties: - [ ] is defaultable with "neglect nothing", so doing the totally safe thing is truly the easiest thing - [ ] every subset of neglected options has exactly *one* encoding (i.e., neglecting validity and alignment is the same as neglecting alignment and validity) - [ ] is generically adjustable (i.e., I can add or remove a neglect from an existing set of options) The type parameter approaches we've considered tick both the first box, and *either* the second xor third. The type system extensions that would permit ticking all three of these boxes are far off. In contrast: 1. A const generic `Neglect` parameter is *not* (yet) defaultable, but this doesn't seem like a permenant limitation of const generics. 2. The const generic `Neglect` admits exactly one encoding of each subset. The value produced by `Neglect {alignment: true, validity: true}` is identical to the value produced by `Neglect {validity: true, alignment: true}`. 3. The const generic `Neglect` is generically extendable. Given an existing, arbitrary `NEGLECT`, we additionally can disable the alignment check like so: ```rust where Dst: TransmuteFrom<Src, {Neglect { alignment: true, ..NEGLECT }}> ``` ### Why can't the compiler always infer `Scope`? This explicit `Scope` parameter of `UnstableTransmuteFrom` makes possible the creation of generic abstractions over it. For instance, consider a hypothetical `FromZeros` trait that indicates whether `Self` is safely initializable from a sufficiently large buffer of zero-initialized bytes: ```rust pub mod zerocopy { pub unsafe trait FromZeros<const NEGLECT: Neglect> { /// Safely initialize `Self` from zeroed bytes. fn zeroed() -> Self; } #[repr(u8)] enum Zero { Zero = 0u8 } unsafe impl<Dst, const NEGLECT: Neglect> FromZeros<NEGLECT> for Dst where Dst: UnstableTransmuteFrom<[Zero; mem::MAX_OBJ_SIZE], ???, NEGLECT>, { fn zeroed() -> Self { Dst::transmute_from([Zero; size_of::<Self>]) } } } ``` The above definition leaves ambiguous (`???`) the scope in which the constructability of `Dst` is checked: is it from the perspective of where this trait is defined, or where this trait is *used*? You probably do *not* intend for this trait to *only* be usable with `Dst` types that are defined in the same scope as the `FromZeros` trait! Adding an explicit `Scope` parameter to `FromZeros` makes this unambiguous; the transmutability of `Dst` should be assessed from where the trait is used, *not* where it is defined: ```rust pub unsafe trait FromZeros<Scope, const NEGLECT: Neglect> { /// Safely initialize `Self` from zeroed bytes. fn zeroed() -> Self; } unsafe impl<Dst, Scope, const NEGLECT: Neglect> FromZeros<Scope, NEGLECT> for Dst where Dst: UnstableTransmuteFrom<[Zero; usize::MAX], Scope, NEGLECT> { fn zeroed() -> Self { Dst::transmute_from([Zero; size_of::<Self>]) } } ```