# 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>])
}
}
```