From 8e0dc269e06fe247e5332003fa11a01f180c294c Mon Sep 17 00:00:00 2001 From: Jack Wrenn Date: Mon, 3 Aug 2020 11:37:00 -0400 Subject: [PATCH 01/61] Create 0000-safe-transmute.md --- rfcs/0000-safe-transmute.md | 2150 +++++++++++++++++++++++++++++++++++ 1 file changed, 2150 insertions(+) create mode 100644 rfcs/0000-safe-transmute.md diff --git a/rfcs/0000-safe-transmute.md b/rfcs/0000-safe-transmute.md new file mode 100644 index 0000000..f1d8ca8 --- /dev/null +++ b/rfcs/0000-safe-transmute.md @@ -0,0 +1,2150 @@ +# Safe Transmute RFC + +- Feature Name: `safe_transmute` +- Start Date: (fill me in with today's date, YYYY-MM-DD) +- RFC PR: [rust-lang/rfcs#0000](https://github.com/rust-lang/rfcs/pull/0000) +- Rust Issue: [rust-lang/rust#0000](https://github.com/rust-lang/rust/issues/0000) + + +# Summary +[summary]: #summary + +We propose traits, namely `TransmuteInto` and `TransmuteFrom`, that are implemented *automatically* for combinations of types that may be safely transmuted. In other words, this RFC makes safe transmutation *as easy as 1..., 2..., `repr(C)`!* +```rust +use core::transmute::{ + TransmuteInto, + stability::{PromiseTransmutableInto, PromiseTransmutableFrom}, +}; + +#[derive(PromiseTransmutableInto, PromiseTransmutableFrom)] +#[repr(C)] +pub struct Foo(pub u8, pub u16); +// ^ there's a padding byte here + +// Transmute fearlessly! +let _ : Foo = 64u32.transmute_into(); // Alchemy Achieved! + +let _ : u32 = Foo(16, 12).transmute_into(); // Compile Error! + +// error[E0277]: the trait bound `u32: TransmuteFrom` is not satisfied +// --> src/demo.rs:14:27 +// | +// 14 | let _ : u32 = Foo(16, 12).transmute_into(); // Compile Error! +// | ^^^^^^^^^^^^^^ the trait `TransmuteFrom` is not implemented for `u32` +// | +// = note: required because of the requirements on the impl of `TransmuteInto` for `foo::Foo` +``` + + +# Motivation +[motivation]: #motivation + +Beyond the titular goal of providing a mechanism of safe transmutation, this RFC seeks to provide mechanisms which: + - [authoritatively codify language layout guarantees](#codifying-language-layout-guarantees) + - [allow crate authors to codify their types' layout stability guarantees](#expressing-library-layout-guarantees) + - [allow crate authors to codify their abstractions' layout requirements](#expressing-layout-requirements) + +We foresee a range of practical consequences of fulfilling these goals, including [making unsafe Rust safer](#making-unsafe-rust-safer), providing [safer initialization primitives](#safer-initialization-primitives), and [generic atomics](#atomict). We explore these possibilities, and more, in this section. + +## Codifying Language Layout Guarantees +Documentation of Rust's layout guarantees for a type are often spread across countless issues, pull requests, RFCs and various official resources. It can be very difficult to get a straight answer. When transmutation is involved, users must reason about the *combined* layout properties of the source and destination types. + +This RFC proposes mechanisms that programmers will use to confidently answer such questions—by checking whether the `TransmuteFrom` and `TransmuteInto` traits are implemented, or (equivalently) by checking whether the `can_transmute` predicate (a `const fn`) is satisfied. + +## Expressing Library Layout Guarantees +There is no canonical way for crate authors to declare the SemVer layout guarantees of their types. Crate authors currently must state their layout guarantees using prose in their documentation. In contrast to structural stability (e.g., the declared visibility of fields), layout stability is expressed extra-linguistically. + +This isn't satisfactory: guarantees expressed in prose outside of the Rust programming language are guarantees that cannot be reasoned about *inside* the language. Whereas `rustc` can dutifully deny programmers access to private fields, it is unable to prevent programmers from making unfounded expectations of types' in-memory layouts. + +This RFC proposes simple-but-powerful [mechanisms][stability] for declaring layout stability guarantees. + +## Expressing Layout Requirements +Similarly, there is no canonical way for crate authors to declare the layout requirements of generic abstractions over types that have certain layout properties. + +For instance, a common bit-packing technique involves abusing the relationship between allocations and alignment. If a type is aligned to 2n, then the *n* least significant bits of pointers to that type will equal `0`. These known-zero bits can be packed with data. Since alignment cannot be currently reasoned about at the type-level, it's currently impossible to bound instantiations of a generic parameter based on minimum alignment. + +The mechanisms proposed by the RFC enable this. We return to this example near the end of the RFC, [here][case-study-alignment]. + +## Making Unsafe Rust Safer +[motivation-safer-unsafe]: #Making-Unsafe-Rust-Safer +In the blog post [*Unsafe Zig is Safer than Unsafe Rust*](https://andrewkelley.me/post/unsafe-zig-safer-than-unsafe-rust.html), Andrew Kelley asks readers to consider the following Rust code: + +```rust +struct Foo { + a: i32, + b: i32, +} + +fn main() { + unsafe { + let mut array: [u8; 1024] = [1; 1024]; + let foo = mem::transmute::<&mut u8, &mut Foo>(&mut array[0]); + foo.a += 1; + } +} +``` +This pattern, Kelly notes, is common when interacting with operating system APIs. + +This rust program compiles, but quietly introduces undefined behavior. `Foo` requires stricter in-memory alignment than `u8`. The equivalent code in Zig produces a compiler error for this reason and requires modification. + +And that's not all: Kelly misses that the very layout of `Foo` itself is undefined⁠—it must be annotated with `#[repr(C)]`! + +Kelley concludes: +> In Zig the problem of alignment is solved completely; the compiler catches all possible alignment issues. In the situation where you need to assert to the compiler that something is more aligned than Zig thinks it is, you can use `@alignCast`. This inserts a cheap safety check in debug mode to make sure the alignment assertion is correct. + +This situation is frustrating. Like `zig`, `rustc` *knows* the layout of `Foo`, it *knows* that the layout of `Foo` is unspecified because it lacks the `#[repr(C)]` attribute, and it *knows* the minimum alignment requirements of `Foo` are stricter than that of `u8`. Why doesn't it also help us avoid UB? + +This RFC proposes mechanisms that use this information to enable safer transmutation. We revisit Kelley's motivating example with these proposed mechanisms near the end of this RFC, [*here*][case-study-safer-unsafe]. + +## Efficient Network Parsing +***TODO: Josh?*** + +## Safer Initialization Primitives +The initialization primitives `mem::zeroed` and `mem::uninitialized` are `unsafe` because they may be used to initialized types for which zeroed or uninitialized memory are *not* valid bit-patterns. The `mem::zeroed` function recently gained a dynamically-enforced validity check, but this safety measure isn't wholly satisfactory: the validity properties of `T` are statically known, but the check is dynamic. + +This RFC proposes mechanisms that would allow these functions to explicitly bound their generic parameter `T` based on its validity properties. We revisit this motivating use-case with our proposed mechanisms near the end of this RFC, [*here*][future-possibility-safe-initialization]. + +## Including Structured Data +[motivation-including-data]: #Including-Structured-Data + +[`include_bytes`]: https://doc.rust-lang.org/core/macro.include_bytes.html + +The [`include_bytes`] macro statically includes the contents of another file as a byte array, producing a value of type `&'static [u8; N]`. Often, these bytes correspond to structured data and a `transmute` must be used to deserialize the bytes into useful form; e.g.: +```rust +pub fn recognize(input: &Matrix) -> usize +{ + static RAW_WEIGHT : &'static [u8; 62_720] = include_bytes!("/weight.bin"); + + static RAW_BIAS : &'static [u8; 80] = include_bytes!("/bias.bin"); + + let WEIGHT: &Matrix = unsafe{ mem::transmute(RAW_WEIGHT) }; + + let BIAS: &Matrix = unsafe{ mem::transmute(RAW_BIAS) }; + + network::recognize(input, WEIGHT, BIAS) +} +``` + +This RFC proposes mechanisms that would eliminate `unsafe` in this example. We revisit this motivating use-case with our proposed mechanisms near the end of this RFC, [*here*][future-possibility-include_data]. + + +## SIMD +***TODO:*** https://internals.rust-lang.org/t/pre-rfc-frombits-intobits/7071 + + +## `Atomic` +Rust defines a dozen `Atomic*` types (`AtomicBool`, `AtomicI8`, `AtomicI16`, `AtomicI32`, `AtomicI64`, `AtomicIsize`, `AtomicPtr`, `AtomicU8`, `AtomicU16`, `AtomicU32`, `AtomicU64`, and `AtomicUsize`). + +This set is large—a distinct `Atomic*` type is required for each primitive type—but incomplete. If one wants atomic operations on their own type, they must define a wrapper around an existing `Atomic*` type of appropriate size and validity, then transmute at API boundaries; e.g.: + +```rust +#[repr(u8)] +enum Trilean { + False, + True, + Unknown, +} + +#[repr(transparent)] +pub AtomicTrilean(AtomicU8); + +impl AtomicTrilean { + + pub const fn new(v: Trilean) -> Self { + AtomicTrilean( + AtomicU8::new( + unsafe { mem::transmute(v) } + )) + } + + ... +} +``` + +The mechanisms proposed by this RFC would eliminate this pattern, permitting truly generic `Atomic` types. We revisit this motivating use-case with these proposed mechanisms near the end of this RFC, [*here*][future-possibility-generic-atomics]. + +# Guide-level explanation +[guide-level-explanation]: #guide-level-explanation + +## Terminology & Concepts + +### 📖 Transmutation +**Transmutation** is the act of reinterpreting the bytes corresponding to a value of one type as if they corresponded to a different type. + +### 📖 Soundness +A transmutation is ***sound*** if the mere act of transmuting a value from one type to another is not compiler undefined behavior. + +### 📖 Safety +A sound transmutation is ***safe*** if *using* the transmuted value cannot violate memory safety. + +### 📖 Stability +A safe transmutation is ***stable*** if the authors of the source type and destination types have indicated that the layouts of those types is part of their libraries' stability guarantees. + +## Concepts in Depth + +***Disclaimer:** While the high-level definitions of transmutation soundness, safety and stability are a core component of this RFC, the detailed rules and examples in this section are **not**. We expect that the initial implementation of `TransmuteFrom` may initially be considerably less sophisticated than the examples in this section (and thus forbid valid transmutations). Nonetheless, this section explores nuanced cases of transmutation soundness and safety to demonstrate that the APIs we propose can grow to handle that nuance.* + + +### 📖 When is a transmutation sound? +[sound transmutation]: #When-is-a-transmutation-sound? +A transmutation is ***sound*** if the mere act of transmuting a value from one type to another is not undefined behavior. + +#### Well-Defined Representation +[`u8`]: core::u8 +[`f32`]: core::f32 + +Transmutation is *always unsound* if it occurs between types with unspecified representations. + +Most of Rust's primitive types have specified representations. That is, the precise layout characteristics of [`u8`], [`f32`] is a documented and guaranteed aspect of those types. + +In contrast, most `struct` and `enum` types defined without an explicit `#[repr(C)]` attribute do ***not*** have well-specified layout characteristics. + +To ensure that types you've define are soundly transmutable, you almost always (with very few exceptions) must mark them with the `#[repr(C)]` attribute. + +#### Requirements on Owned Values +[transmute-owned]: #transmuting-owned-values + +Transmutations involving owned values must adhere to two rules to be sound. They must: + * [preserve or broaden the bit validity][owned-validity], and + * [preserve or shrink the size][owned-size]. + +##### Preserve or Broaden Bit Validity +[owned-validity]: #Preserve-or-Broaden-Bit-Validity +[`NonZeroU8`]: https://doc.rust-lang.org/beta/core/num/struct.NonZeroU8.html + +The bits of any valid instance of the source type must be a bit-valid instance of the destination type. + +For example, we are permitted to transmute a `Bool` into a [`u8`]: +```rust +#[derive(Default, PromiseTransmutableFrom, PromiseTransmutableInto)] +#[repr(u8)] +enum Bool { + True = 1, + False = 0, +} + +let _ : u8 = Bool::True.transmute_into(); +let _ : u8 = Bool::False.transmute_into(); +``` + +(Note: #[derive(PromiseTransmutableFrom, PromiseTransmutableInto)] annotation connotes that all aspects of Bool's layout are part of its library stability guarantee.) + + +...because all possible instances of `Bool` are also valid instances of [`u8`]. However, transmuting a [`u8`] into a `Bool` is forbidden: +```rust +/* ⚠️ This example intentionally does not compile. */ +let _ : Bool = u8::default().transmute_into(); // Compile Error! +``` +...because not all instances of [`u8`] are valid instances of `Bool`. + +Another example: While laying out certain types, Rust may insert padding bytes between the layouts of fields. In the below example `Padded` has two padding bytes, while `Packed` has none: +```rust +#[repr(C)] +#[derive(Default, PromiseTransmutableFrom, PromiseTransmutableInto)] +struct Padded(pub u8, pub u16, pub u8); + +#[repr(C)] +#[derive(Default, PromiseTransmutableFrom, PromiseTransmutableInto)] +struct Packed(pub u16, pub u16, pub u16); + +assert_eq!(mem::size_of::(), mem::size_of::()); +``` + +We may safely transmute from `Packed` to `Padded`: +```rust +let _ : Padded = Packed::default().transmute_into(); +``` +...but not from `Padded` to `Packed`: +```rust +/* ⚠️ This example intentionally does not compile. */ +let _ : Packed = Padded::default().transmute_into(); // Compile Error! +``` +...because doing so would expose two uninitialized padding bytes in `Padded` as if they were initialized bytes in `Packed`. + +##### Preserve or Shrink Size +[owned-size]: #Preserve-or-Shrink-Size + +It's completely sound to transmute into a type with fewer bytes than the source type; e.g.: +```rust +let _ : [u8; 16] = [0u8; 32]::default().transmute_into(); +``` +This transmute truncates away the final sixteen bytes of the `[u8; 32]` value. + +A value may ***not*** be transmuted into a type of greater size: +```rust +/* ⚠️ This example intentionally does not compile. */ +let _ : [u8; 32] = [0u8; 16]::default().transmute_into(); // Compile Error! +``` + +#### Requirements on References +[transmute-references]: #transmuting-references + +The [restrictions above that apply to transmuting owned values][transmute-owned] also apply to transmuting references. However, references carry a few additional restrictions. + +A [sound transmutation] must: + - [preserve or relax alignment][reference-alignment], + - [preserve or shrink lifetimes][reference-lifetimes], + - [preserve or shrink uniqueness][reference-mutability], and + - and if the destination type is a mutate-able reference, [preserve validity][reference-validity]. + +##### Preserve or Relax Alignment +[reference-alignment]: #Preserve-or-Relax-Alignment + +Unaligned loads are undefined behavior. You may transmute a reference into reference of more relaxed alignment: +```rust +let _: &[u8; 0] = (&[0u16; 0]).transmute_into(); +``` + +However, you may **not** transmute a reference into a reference of more-restrictive alignment: +```rust +/* ⚠️ This example intentionally does not compile. */ +let _: &[u16; 0] = (&[0u8; 0]).transmute_into(); // Compile Error! +``` + +##### Preserve or Shrink Lifetimes +[reference-lifetimes]: #Preserve-or-Shrink-Lifetimes + +You may transmute a reference into a reference of lesser lifetime: +```rust +fn shrink<'a>() -> &'a u8 { + static long : &'static u8 = &16; + long.transmute_into() +} +``` + +However, you may **not** transmute a reference into a reference of greater lifetime: +```rust +/* ⚠️ This example intentionally does not compile. */ +fn extend<'a>(short: &'a u8) -> &'static u8 { + short.transmute_into() // Compile Error! +} +``` + +##### Preserve or Shrink Uniqueness +[reference-mutability]: #Preserve-or-Shrink-Uniqueness + +You may preserve or decrease the uniqueness of a reference through transmutation: +```rust +let _: &u8 = (&42u8).transmute_into(); +let _: &u8 = (&mut 42u8).transmute_into(); +``` + +However, you may **not** transmute a shared reference into a unique reference: +```rust +/* ⚠️ This example intentionally does not compile. */ +let _: &mut u8 = (&42u8).transmute_into(); // Compile Error! +``` + +##### Mutate-able References Must Preserve Validity +[reference-validity]: #Mutate-able-References-Must-Preserve-Validity + +A mutate-able reference is: +- all unique (i.e., `&mut T`) references +- all shared (i.e., `&T`) references whose referent type contain any bytes produced by the contents of `UnsafeCell`. + +Unlike transmutations of owned values, the transmutation of a mutate-able reference may also not expand the bit-validity of the referenced type. For instance: +```rust +/* ⚠️ This example intentionally does not compile. */ +let mut x = NonZeroU8::new(42).unwrap(); +{ + let y : &mut u8 = (&mut x).transmute_into(); // Compile Error! + *y = 0; +} + +let z : NonZeroU8 = x; +``` +If this example did not produce a compile error, the value of `z` would not be a bit-valid instance of its type, [`NonZeroU8`]. + + + +### 📖 When is a transmutation safe? +A sound transmutation is ***safe*** if *using* the transmuted value safely cannot violate memory safety. Whereas soundness solely concerns the act of transmutation, *safety* is concerned with what might happen with a value *after* transmutation occurs. + +#### Implicit Constructability +A struct or enum variant is *fully implicitly constructable* at a given location only if, at that location, that type can be instantiated via its *implicit constructor*, and its fields are also *implicitly constructable*. + +The *implicit constructor* of a struct or enum variant is the constructor Rust creates implicitly from its definition; e.g.: +```rust +struct Point { + x: T, + y: T, +} + +let p = Point { x: 4, y: 2 }; + // ^^^^^^^^^^^^^^^^^^^^ An instance of `Point` is created here, via its implicit constructor. +``` + +Limiting implicit constructability is the fundamental mechanism with which type authors build safe abstractions for `unsafe` code, whose soundness is dependent on preserving invariants on fields. Usually, this takes the form of restricting the visibility of fields. For instance, consider the type `Constrained`, which enforces a validity constraint on its fields via its constructor: + +```rust +pub mod crate_a { + + #[repr(C)] + pub struct Constrained { + wizz: i8, + bang: u8, + } + + impl Constrained { + /// the sum of `wizz` and `bang` must be greater than or equal to zero. + pub fn new(wizz: i8, bang: u8) -> Self { + assert!((wizz as i16) + (bang as i16) >= 0); + Constrained { wizz, bang } + } + + pub fn something_dangerous(&self) { + unsafe { + // do something that's only memory-safe if `wizz + bang >= 0` + } + } + } + +} +``` +The only reason it is sound for `something_dangerous` to be a *safe* method is because the fields `wizz` and `bang` are *not* marked `pub`: outside of `crate_a`, it is impossible to safely initialize these fields in a way that violates the invariant enforced by `Constrained::new`. In other words, `Constrained` is implicitly constructable within `crate_a`, but *not* outside of `crate_a`. Any field that is not marked `pub` of a type should be assumed to be subject to validity invariants that could impact the safety of using that type. + +Unfortunately, field visibility modifiers are not a surefire indicator of whether a type is *fully* implicitly constructable. A type author may restrict the implicit constructability of a type even in situations where all fields of that type *and all fields of those fields) are `pub`; consider: +```rust +pub mod crate_a { + + #[repr(C)] + pub struct Constrained(pub private::ConstrainedInner); + + impl Constrained { + /// the sum of `wizz` and `bang` must be greater than or equal to zero. + pub fn new(wizz: i8, bang: u8) -> Self { + assert!((wizz as i16) + (bang as i16) >= 0); + Constrained(private::ConstrainedInner { wizz, bang }) + } + + pub fn something_dangerous(&self) { + unsafe { + // do something that's only safe if `self.0.wizz + self.0.bang >= 0` + } + } + } + + // introduce a private module to avoid `private_in_public` error (E0446): + pub(crate) mod private { + /// the sum of `wizz` and `bang` MUST be greater than or equal to zero. + #[repr(C)] + pub struct ConstrainedInner { + pub wizz: i8, + pub bang: u8, + } + } + +} +``` +In the above example, the definitions of both `Constrained` and its field `ConstrainedInner` are marked `pub`. All fields of both types are marked `pub`. However, `Constrained` is *not* fully implicitly constructible outside of `crate_a`, because the module containing `ConstrainedInner` is not visibile outside of `crate_a`. + +#### Constructability and Transmutation +Transmutation supplies a mechanism for constructing instances of a type *without* invoking its implicit constructor, nor any constructors defined by the type's author. + +In the previous examples, it would be *unsafe* to transmute `0xFF00u16` into `Constrained` outside `crate_a`, because subsequent *safe* use of that value (namely, calling `something_dangerous`) could violate memory safety. (However, it's completely safe to transmute `Constrained` into `0xFF00u16`.) + + +For transmutations where the destination type involves mutate-able references, the constructability of the source type is also relevant. Consider: +```rust +/* ⚠️ This example intentionally does not compile. */ +let mut x = Constrained::new(0, 0); +{ + let y : &mut u16 = (&mut x).transmute_into(); // Compile Error! + *y = 0xFF00u16; +} + +let z : Constrained = x; +``` +If this example did not produce a compile error, the value of `z` would not be a safe instance of its type, `Constrained`. + +### 📖 When is a transmutation stable? +[stability]: #When-is-a-transmutation-stable + +Since the soundness and safety of a transmutation is affected by the layouts of the source and destination types, changes to those types' layouts may cause code which previously compiled to produce errors. In other words, transmutation causes a type's layout to become part of that type's API for the purposes of SemVer stability. + +The question is, then: *how can the author of a type reason about transmutations they did not write, from-or-to types they did not write?* We address this problem by introducing two traits which both allow an author to opt-in to stability guarantees for their types, and allow third-parties to reason at compile-time about what guarantees are provided for such types. + +#### `PromiseTransmutableFrom` and `PromiseTransmutableInto` + +You may declare the stability guarantees of your type by implementing one or both of two traits: +```rust +pub trait PromiseTransmutableFrom +{ + type Archetype + : TransmuteInto + + PromiseTransmutableFrom; +} + +pub trait PromiseTransmutableInto +{ + type Archetype + : TransmuteFrom + + PromiseTransmutableInto; +} +``` + +To implement each of these traits, you must specify an `Archetype`. An `Archetype` is a type whose layout exemplifies the extremities of your stability promise (i.e., the least/most constrained type for which it is valid to transmute your type into/from). + +By implementing `PromiseTransmutableFrom`, you promise that your type is guaranteed to be safely transmutable *from* `PromiseTransmutableFrom::Archetype`. Conversely, by implementing `PromiseTransmutableInto`, you promise that your type is guaranteed to be safely transmutable *into* `PromiseTransmutableInto::Archetype`. + +You are free to change the layout of your type however you like between minor crate versions so long as that change does not violates these promises. These two traits are capable of expressing simple and complex stability guarantees. + +#### Stability & Transmutation +A `Src` type is *stably* transmutable into a `Dst` type *only if* `::Archetype` is transmutable, stability notwithstanding, into `::Archetype`; formally: +```rust +unsafe impl TransmuteFrom for Dst +where + Src: PromiseTransmutableInto, + Dst: PromiseTransmutableFrom, + ::Archetype: + TransmuteFrom< + ::Archetype, + NeglectStability + > +{} +``` + +Why is this the case? + +The type `::Archetype` exemplifies the furthest extreme of non-breaking changes that could be made to the layout of `Src` that could affect its use as a source type in transmutations. Conversely, `::Archetype` exemplifies the furthest extreme of non-breaking changes that could be made to the layout of `Dst` that could affect its use as a destination type in transmutations. If a transmutation between these extremities is valid, then so is `Src: TransmuteInto`. + +#### Common Use-Case: Promising a Fixed Layout +To promise that type's layout is completely fixed, simply `derive` the `PromiseTransmutableFrom` and `PromiseTransmutableInto` traits: +```rust +#[derive(PromiseTransmutableFrom, PromiseTransmutableInto)] +#[repr(C)] +pub struct Foo(pub Bar, pub Baz); +``` +This expands to: +```rust +#[repr(C)] +pub struct Foo(pub Bar, pub Baz); + +// Generated stability declarations for `Foo`: +const _: () = { + + #[repr(C)] + struct TransmutableFromArchetype + ::Archetype, + ::Archetype, + ); + + #[repr(C)] + struct TransmutableIntoArchetype + ::Archetype, + ::Archetype, + ); + + impl PromiseTransmutableFrom for Foo { + type Archetype = TransmutableFromArchetype; + } + + impl PromiseTransmutableInto for FooTransmutableIntoArchetype { + type Archetype = TransmutableIntoArchetype; + } +}; +``` + + +#### Uncommon Use Case: Complex Stability Guarantees + +If you are *most people* and want to declare your types as layout-stable, you should follow the advice in the previous sections. In doing so, you declare that you will *not* modify the layout of your type in virtually any way, except as a breaking change. If your type's fields have simple stability guarantees, this effects the strongest possible declaration of stability: it declares that *all* transmutations that are safe are *also* stable. + +However, if you anticipate that you will need to make changes to your type's layouts in a non-breaking way, you can craft uninformative archetypes that effect *weaker* promises of stability. With uninformative archetypes, only a *subset* of transmutations that are safe are also considered stable. For instance, you may use `PromiseTransmutableInto` to specify an lower-bound for the size of a type, but *not* make promises about its bit-validity: + +```rust +#[repr(C)] pub struct Foo(u8, u8, u8, u8, u8, u8); + +#[repr(C)] +struct FooArchetype(pub [MaybeUninit; 5]); + +impl PromiseTransmutableInto for Foo { + type Type = [MaybeUninit; 5]; +} +``` + +##### Crafting Uninformative `PromiseTransmutableFrom` Archetypes + +###### Archetype Alignment +You may freely decrease the required alignment of your type to any value less-than-or-equal to the required alignment of your archetype. The maximum alignment requirement permitted by Rust is 229. + +###### Archetype Size +You may decrease the size of your type without violating stability to any size less-than-or-equal to the size of your archetype. Unfortunately, the maximum size of types in Rust is [architecture-dependent](https://github.com/rust-lang/rust/blob/63b441aafbf52d6ba789ecc478455800c1a48df9/src/librustc_target/abi/mod.rs#L179-L197). For the greatest portability, your archetype should be no larger than 215. An archetype of this size provides (un)reasonable future flexibility. + +###### Archetype Validity & Visibility +You may freely make your type *less* constructible (in terms of both theoretical validity and constructor visibility) than your archetype. + +A minimally-informative archetype may be constructed using this as a building-block: +```rust +struct None(u8); +``` +Since the `u8` field is private, its valid values are assumed to be constrained by library invariants. + +##### Crafting Uninformative `PromiseTransmutableInto` Archetypes + +###### Archetype Alignment +You may freely increase the required alignment of your type to any value greater-than-or-equal to the required alignment of your archetype. The minimum alignment requirement permitted by Rust is 1. + +###### Archetype Size +You may decrease the size of your type without violating stability to any size less-than-or-equal to the size of your archetype. The minimum size of types in Rust is 0. + +###### Archetype Validity & Visibility +You may freely make your type more constructible (in terms of both visibility and theoretical validity) than your archetype. A minimally-informative archetype may be constructed using `MaybeUninit` as a building-block, since *any* possible instantiation of a byte is a valid instantiation of `MaybeUninit`. + + +## Mechanisms of Transmutation + +Two traits provide mechanisms for transmutation between types: +```rust +// this trait is implemented automagically by the compiler +#[lang = "transmute_from"] +pub unsafe trait TransmuteFrom +where + Neglect: UnsafeTransmuteOptions, +{ + #[inline(always)] + fn transmute_from(src: Src) -> Self + where + Src: Sized, + Self: Sized, + Neglect: SafeTransmuteOptions, + { + unsafe { Self::unsafe_transmute_from(src) } + } + + #[inline(always)] + unsafe fn unsafe_transmute_from(src: Src) -> Self + where + Src: Sized, + Self: Sized, + Neglect: UnsafeTransmuteOptions, + { + use core::{mem, ptr}; + unsafe { + let dst = ptr::read_unaligned(&src as *const Src as *const Self); + mem::forget(src); + dst + } + } +} + +// implemented in terms of `TransmuteFrom` +pub unsafe trait TransmuteInto +where + Neglect: UnsafeTransmuteOptions, +{ + fn transmute_into(self) -> Dst + where + Self: Sized, + Dst: Sized, + Neglect: SafeTransmuteOptions; + + unsafe fn unsafe_transmute_into(self) -> Dst + where + Self: Sized, + Dst: Sized, + Neglect: UnsafeTransmuteOptions; +} + +unsafe impl TransmuteInto for Src +where + Src: ?Sized, + Dst: ?Sized + TransmuteFrom, + Neglect: UnsafeTransmuteOptions, +{ + ... +} +``` + +In the above definitions, `Src` represents the source type of the transmutation, `Dst` represents the destination type of the transmutation, and `Neglect` is a parameter that [encodes][options] which static checks the compiler ought to neglect when considering if a transmutation is valid. The default value of `Neglect` is `()`, which reflects that, by default, the compiler does not neglect *any* static checks. + +### Neglecting Static Checks +[options]: #Neglecting-Static-Checks + +The default value of the `Neglect` parameter, `()`, statically forbids transmutes that are unsafe, unsafe, or unstable. However, you may explicitly opt-out of some static checks: + +| Transmute Option | Compromises | Usable With | +|---------------------|-------------|---------------------------------------------------------| +| `NeglectStabilty` | Stability | `transmute_{from,into}`, `unsafe_transmute_{from,into}` | +| `NeglectAlignment` | Safety | `unsafe_transmute_{from,into}` | +| `NeglectValidity` | Soudness | `unsafe_transmute_{from,into}` | + +`NeglectStabilty` implements the `SafeTransmuteOptions` and `UnsafeTransmuteOptions` marker traits, as it can be used in both safe and unsafe code. The selection of multiple options is encoded by grouping them as a tuple; e.g., `(NeglectAlignment, NeglectValidity)` is a selection of both the `NeglectAlignment` and `NeglectValidity` options. + +We introduce two marker traits which serve to group together the options that may be used with safe transmutes, and those which may be used with `unsafe` transmutes: +```rust +pub trait SafeTransmuteOptions: private::Sealed +{} + +pub trait UnsafeTransmuteOptions: SafeTransmuteOptions +{} + +impl SafeTransmuteOptions for () {} +impl UnsafeTransmuteOptions for () {} +``` + +#### `NeglectStability` +[`NeglectStability`]: #neglectstability + +By default, `TransmuteFrom` and `TransmuteInto`'s methods require that the [layouts of the source and destination types are SemVer-stable][stability]. The `NeglectStability` option disables this requirement. +```rust +pub struct NeglectStability; + +impl SafeTransmuteOptions for NeglectStability {} +impl UnsafeTransmuteOptions for NeglectStability {} +``` + +Prior to the adoption of the [stability declaration traits][stability], crate authors documented the layout guarantees of their types with doc comments. The `TransmuteFrom` and `TransmuteInto` traits and methods may be used with these types by requesting that the stability check is neglected; for instance: + +```rust +fn serialize(val : LibraryType, dst: W) -> std::io::Result<()> +where + LibraryType: TransmuteInto<[u8; size_of::()], NeglectStability> +{ + ... +} +``` + +Neglecting stability over-eagerly cannot cause unsoundness or unsafety. For this reason, it is the only transmutation option available on the safe methods `transmute_from` and `transmute_into`. However, neglecting stability over-eagerly may cause your code to cease compiling if the authors of the source and destination types make changes that affect their layout. + +By using the `NeglectStability` option to transmute types you do not own, you are committing to ensure that your reliance on these types' layouts is consistent with their documented stability guarantees. + +#### `NeglectAlignment` +By default, `TransmuteFrom` and `TransmuteInto`'s methods require that, when transmuting references, the minimum alignment of the destination's referent type is no greater than the minimum alignment of the source's referent type. The `NeglectAlignment` option disables this requirement. +```rust +pub struct NeglectAlignment; + +impl UnsafeTransmuteOptions for NeglectAlignment {} +``` + +By using the `NeglectAlignment` option, you are committing to ensure that the transmuted reference satisfies the alignment requirements of the destination's referent type. 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: TransmuteInto<&'u U, neglect::Alignment>, +{ + if (src as *const T as usize) % align_of::() != 0 { + None + } else { + // Safe because we dynamically enforce the alignment + // requirement, whose static check we chose to neglect. + Some(unsafe { src.unsafe_transmute_into() }) + } +} +``` + +#### `NeglectValidity` +By default, `TransmuteFrom` and `TransmuteInto`'s methods require that all instantiations of the source type are guaranteed to be valid instantiations of the destination type. This precludes transmutations which *might* be valid depending on the source value: +```rust +#[derive(PromiseTransmutableFrom, PromiseTransmutableInto)] +#[repr(u8)] +enum Bool { + True = 1, + False = 0, +} + +/* ⚠️ This example intentionally does not compile. */ +let _ : Bool = some_u8_value.transmute_into(); // Compile Error! +``` +The `NeglectValidity` option disables this check. +```rust +pub struct NeglectValidity; + +impl UnsafeTransmuteOptions for NeglectValidity {} +``` + +By using the `NeglectValidity` option, you are committing to ensure dynamically source value is a valid instance of the destination type. For instance: +```rust +#[derive(PromiseTransmutableFrom, PromiseTransmutableInto)] +#[repr(u8)] +enum Bool { + True = 1, + False = 0, +} + +pub trait TryIntoBool +{ + fn try_into_bool(self) -> Option; +} + +impl TryIntoBool for T +where + T: TransmuteInto, + u8: TransmuteInto +{ + fn try_into_bool(self) -> Option { + let val: u8 = self.transmute_into(); + + if val > 1 { + None + } else { + // Safe, because we've first verified that + // `val` is a bit-valid instance of a boolean. + Some(unsafe {val.unsafe_transmute_into()}) + } + } +} +``` + +Even with `NeglectValidity`, the compiler will statically reject transmutations that cannot possibly be valid: +```rust +#[derive(PromiseTransmutableInto)] +#[repr(C)] enum Foo { A = 24 } + +#[derive(PromiseTransmutableFrom)] +#[repr(C)] enum Bar { Z = 42 } + +let _ = ::unsafe_transmute_from(Foo::N) // Compile error! +``` + +# Reference-level explanation +[reference-level-explanation]: #reference-level-explanation + + +## Implementation Guidance +The *only* item defined by this RFC requiring special compiler support is `TransmuteFrom`. To realize this RFC's proposal of safe transmutation between different types, this item will require compiler support. However, the *most* minimal acceptable implementation of `TransmuteFrom` can be achieved entirely in-language: + +```rust +/// A type is transmutable into itself. +unsafe impl TransmuteFrom for T +where + Neglect: UnsafeTransmuteOptions +{} + +/// A transmutation is *stable* if... +unsafe impl TransmuteFrom for Dst +where + Src: PromiseTransmutableInto, + Dst: PromiseTransmutableFrom, + ::Archetype: + TransmuteFrom< + ::Archetype, + NeglectStability + > +{} +``` +This [minimal implementation][minimal-impl] is sufficient for convincing the compiler to accept basic stability declarations, such as those of Rust's primitive types. It is *insufficient* for making the compiler accept transmutations between *different* types (and, consequently, complex stability declarations). Implementers should use this as a starting point. + +### Listing for Initial, Minimal Implementation +[minimal-impl]: #Listing-for-Initial-Minimal-Implementation +**[This module listing provides the minimal-viable implementation of this RFC (excepting the automatic derives).](https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=25a83d8d8e33e9f631a64b53e3c39844)** This listing is also the **canonical specification** of this RFC's public API surface. + +### Towards an Initial, Smart Implementation + +To support transmutations between different types, implementers of this RFC should begin by defining a `transmute_from` lang item to annotate libcore's definition of `TransmuteFrom`. Whether `TransmuteFrom` is implemented for a given type and parameters shall be determined within the implementation of the type system (à la [`Sized`](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TyS.html#method.is_sized) and [`Freeze`](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TyS.html#method.is_freeze)). + +This initial smart implementation may be made simpler by: + - *not* supporting enums + - *not* supporting unions + - *not* supporting `Neglect` parameters besides `()` and `NeglectStability` + - simplifying constructability + +#### Simplifying Constructability +The safety property of constructability defined in the guidance-level examplation of this RFC describes a platonic ideal of the property. + +However, we recognize that this definition poses implementation challenges: In our definition of constructability, answering the question of whether a struct or enum variant is constructible depends on *where* that question is being asked. Consequently, answering whether a given type `Src` is `TransmutableInto` a given type `Dst` will depend on *where* that question is posed. + +We recommend adopting a simplified definition of *constructability*: a type is *constructible* if its fields are marked `pub`, and those fields are constructible. With this definition, answering the question of whether a type is constructible does *not* depend on where the question is asked: we do not examine the visibility of the involved types. + +Unfortunately, with no other actions taken, this simplified definition comes... + +##### ...at the Cost of Safety +This definition is *usually* sufficient for ensuring safety: it is *generally* an error to expose a private type in a public type signature. However, these errors may be circumvented using the public-type-in-private-module trick: +```rust +pub mod crate_a { + + #[repr(C)] + pub struct Constrained(pub private::ConstrainedInner); + + impl Constrained { + /// the sum of `wizz` and `bang` must be greater than or equal to zero. + pub fn new(wizz: i8, bang: u8) -> Self { + assert!((wizz as i16) + (bang as i16) >= 0); + Constrained(private::ConstrainedInner { wizz, bang }) + } + + pub fn something_dangerous(&self) { + unsafe { + // do something that's only safe if `self.0.wizz + self.0.bang >= 0` + } + } + } + + // introduce a private module to avoid `private_in_public` error (E0446): + pub(crate) mod private { + /// the sum of `wizz` and `bang` MUST be greater than or equal to zero. + #[repr(C)] + pub struct ConstrainedInner { + pub wizz: i8, + pub bang: u8, + } + } + +} +``` +With this simplified definition of constructability, it is possible for a third-party to define a *safe* constructor of `Constrained` that produces a value is *unsafe* to use: +```rust +pub evil_constructor(src: T) -> Constrained +where + T: TransmuteInto, +{ + src.transmute_into() +} + +evil_constructor(0xFF_00u16).something_dangerous() // muahaha! +``` + +The above code is "safe" because our simplified definition of constructability fails to recognize this pattern of encapsulation, and because `NeglectStability` is a `SafeTransmutationOption`. + +The intent of `NeglectStability` is to permit the safe transmutation of types that predate the stabilization of the stability declaration traits. It also provides a convenient escape-hatch for type authors to neglect the stability of transmutations of their *own* types, without sacrificing safety. `NeglectStability` is a `SafeTransmutationOption` because, in principle, neglecting stability does not diminish safety. Our simplified definition of constructability violates this principle. + +By temporarily sacrificing these goals, we may preserve safety solely... + +##### ...at the Cost of `NeglectStability` +We may preserve safety by demoting `NeglectStability` to `UnsafeTransmutationOption`-status. + +In doing so, a third-party is forced to resort to an `unsafe` transmutation to construct `Constrained`; e.g.: + +```rust +pub evil_constructor(src: T) -> Constrained +where + T: TransmuteInto, +{ + // unsafe because we `NeglectStability` + unsafe { src.unsafe_transmute_into() } +} +``` + +Demoting `NeglectStability` to unsafe-status does not stop type authors from opting-in to stable (and thus safe) transmutations; e.g., with `derive(PromiseTransmutableFrom)`: +```rust +pub mod crate_a { + + #[derive(PromiseTransmutableFrom)] + #[repr(C)] + pub struct Constrained(pub private::ConstrainedInner); + + impl Constrained { + /// the sum of `wizz` and `bang` must be greater than or equal to zero. + pub fn new(wizz: i8, bang: u8) -> Self { + assert!((wizz as i16) + (bang as i16) >= 0); + Constrained(private::ConstrainedInner { wizz, bang }) + } + + pub fn something_dangerous(&self) { + unsafe { + // do something that's only safe if `self.0.wizz + self.0.bang >= 0` + } + } + } + + // introduce a private module to avoid `private_in_public` error (E0446): + pub(crate) mod private { + /// the sum of `wizz` and `bang` MUST be greater than or equal to zero. + #[derive(PromiseTransmutableFrom)] + #[repr(C)] + pub struct ConstrainedInner { + pub wizz: i8, + pub bang: u8, + } + } + +} +``` +In the above example, the type author declares `Constrained` and `ConstrainedInner` to be stably instantiatable via transmutation. Given this, a third-party no longer needs to resort to `unsafe` code to violate the the invariants on `wizz` and `bang`: +```rust +pub evil_constructor(src: T) -> Constrained +where + T: TransmuteInto, +{ + src.transmute_into() +} + +evil_constructor(0xFF_00u16).something_dangerous() // muahaha! +``` +This safety hazard is not materially different from the one that would be induced if the type author implemented `DerefMut` for `Constrained`, or made the `private` module `pub`, or otherwise provided mutable access to `wizz` and `bang`. + +##### Recommendation +We recommend that that implementers of this RFC initially simplify constructability by: + - adopting our simplified definition of constructability + - demoting `NeglectStability` to unsafe status (i.e., not implementing `SafeTransmuteOptions` for `NeglectStability`; *only* `UnsafeTransmuteOptions`) + +If and when the implementation of `TransmuteFrom` encodes our complete definition of constructability, `NeglectStability` shall become a safe transmute option. + + +## Dissecting Stability + +In this section, we iteratively review the design of the stability declaration traits, starting with a simplest-possible design: +```rust +pub unsafe trait PromiseTransmutableFrom +{ + type Archetype; +} + +pub unsafe trait PromiseTransmutableInto +{ + type Archetype; +} +``` + +### Part 1: Towards Safety + +#### Forbidding Archetypes with Nonsense Validity +The simplest definitions of the stability declaration traits *must* be `unsafe` because `Archetype` can be instantiated in ways that undermine soundness; e.g.: +```rust +unsafe impl PromiseTransmutableInto for u8 { + type Archetype = u16; // <- Nonsense! +} + +unsafe impl PromiseTransmutableFrom for u16 { + type Archetype = u16; +} +``` +These declarations subvert safety! Given these declarations, Rust will accept `u8: TransmuteInto`. This is problematic: `u8` is smaller than `u16`. + +--- + +
+Why would this transmutation be accepted? (Click to Expand) + +Let's solve this trait bound step-by-step: +```rust +/// 0. Initial equation: +u8: + TransmuteInto<&'a Aligned8> +``` + +First, recall that a `Src` type is *stably* transmutable into a `Dst` type *only if* `::Archetype` is transmutable into `::Archetype`, notwithstanding stability. The layouts of `Src` and `Dst` themselves are, in theory, immaterial — all that matters are the layouts of their stability archetypes. Formally: +```rust +𝙞𝙛 + ::Archetype: + TransmuteInto< + ::Archetype, + NeglectStability + > +𝙩𝙝𝙚𝙣 + Src: TransmuteInto +``` + +We apply this rule to our initial equation: +```rust +𝙞𝙛 + ::Archetype: + TransmuteInto< + ::Archetype, + NeglectStability + > +𝙩𝙝𝙚𝙣 + u8: TransmuteInto +``` + +Next, recall the definitions of `PromiseTransmutableInto` for `u8` and `PromiseTransmutableFrom` for `u16`: +```rust +unsafe impl PromiseTransmutableInto for u8 { + type Archetype = u16; // <- Nonsense! +} + +unsafe impl PromiseTransmutableFrom for u16 { + type Archetype = u16; +} +``` +We apply these definitions to evaluate `::Archetype` and `::Archetype`: +```rust +𝙞𝙛 + u16: + TransmuteInto< + u16, + NeglectStability + > +𝙩𝙝𝙚𝙣 + u8: TransmuteInto +``` +Since `u16` can be safely transmuted into `u16`, the compiler accepts the transmutation of `u8` into `u16`! + +
+ +--- + +We can prevent nonsense archetypes by demanding that `Archetype` is transmutable into `Self` (notwithstanding stability): +```rust +pub unsafe trait PromiseTransmutableFrom +where + Self::Archetype: TransmuteInto, +{ + type Archetype; +} + +pub unsafe trait PromiseTransmutableInto +where + Self::Archetype: TransmuteFrom, +{ + type Archetype; +} +``` + +Our previous attempt to skirt soundness now produces a compile error: +```rust +unsafe impl PromiseTransmutableInto for u8 { + type Archetype = u16; + // ^^^ the trait `TransmuteInto` is not implemented for `u16` +} +``` + +#### Forbidding Archetypes with Nonsense Alignment +Unfortunately, the above definition is still flawed. Consider these stability declarations: + +```rust +#[derive(PromiseTransmutableFrom)] +#[repr(C)] +pub struct Aligned1; + +#[derive(PromiseTransmutableInto)] +#[repr(C, align(8))] +pub struct Aligned8; + +unsafe impl PromiseTransmutableInto for Aligned1 { + type Archetype = Aligned8; +} + +unsafe impl PromiseTransmutableFrom for Aligned8 { + type Archetype = Aligned1; +} +``` + +These declarations subvert safety! Given these declarations, Rust would accept the bound `&Aligned1: TransmuteInto<&Aligned8>`. This is problematic: `Aligned8` has *stricter* alignment requirements than `Aligned1`. + +--- + +
+Why would this transmutation be accepted? (Click to Expand) + +Let's solve this trait bound step-by-step: +```rust +/// 0. Initial equation: +for<'a> &'a Aligned1: + TransmuteInto<&'a Aligned8> +``` + +As before, we first apply the stability rule to our equation: +```rust +𝙞𝙛 + <&'a Aligned1 as PromiseTransmutableInto>::Archetype: + TransmuteInto< + <&'a Aligned8 as PromiseTransmutableFrom>::Archetype, + NeglectStability + > +𝙩𝙝𝙚𝙣 + for<'a> &'a Aligned1: + TransmuteInto<&'a Aligned8> +``` + +Next, recall the definitions of `PromiseTransmutableInto` and `PromiseTransmutableFrom` for `&T`: +```rust +unsafe impl<'a, T> PromiseTransmutableFrom for &'a T +where + T: PromiseTransmutableFrom +{ + type Archetype = &'a T::Archetype; +} + +unsafe impl<'a, T> PromiseTransmutableInto for &'a T +where + T: PromiseTransmutableInto +{ + type Archetype = &'a T::Archetype; +} +``` +We apply these definitions to evaluate `<&_ as PromiseTransmutableInto>::Archetype` and `<&_ as PromiseTransmutableFrom>::Archetype`: +```rust +𝙞𝙛 + &'a ::Archetype: + TransmuteInto< + &'a ::Archetype, + NeglectStability + > +𝙩𝙝𝙚𝙣 + for<'a> &'a Aligned1: + TransmuteInto<&'a Aligned8> +``` + +Finally, recall the definitions of `PromiseTransmutableInto` for `Aligned1` and `PromiseTransmutableFrom` for `Aligned8`: +```rust +unsafe impl PromiseTransmutableInto for Aligned1 { + type Archetype = Aligned8; +} + +unsafe impl PromiseTransmutableFrom for Aligned8 { + type Archetype = Aligned1; +} +``` +We apply these definitions to evaluate `::Archetype` and `::Archetype`: +```rust +𝙞𝙛 + &'a Aligned8: + TransmuteInto< + &'a Aligned1, + NeglectStability + > +𝙩𝙝𝙚𝙣 + for<'a> &'a Aligned1: + TransmuteInto<&'a Aligned8> +``` +Since `&'a Aligned8` can be safely transmuted into `&'a Aligned1`, the compiler accepts the transmutation of `&'a Aligned1` into `&'a Aligned1`! + +
+ +--- + +This safety hazard can be prevented by rewriting our `where` bound like so: +```rust +pub unsafe trait PromiseTransmutableFrom +where + Self::Archetype: TransmuteInto, + for<'a> &'a Self::Archetype: TransmuteInto<&'a Self, NeglectStability> +{ + type Archetype; +} + +pub unsafe trait PromiseTransmutableInto +where + Self::Archetype: TransmuteFrom, + for<'a> &'a Self::Archetype: TransmuteFrom<&'a Self, NeglectStability> +{ + type Archetype; +} +``` + +Our previous attempt to skirt safety now produces a compiler error: +```rust +unsafe impl PromiseTransmutableInto for Aligned1 { + type Archetype = Aligned8; + // ^^^^^^^^ the trait `TransmuteFrom<&'_ Aligned1, NeglectStability>` is not implemented for `&'_ Aligned8` +} +``` + + +#### Forbidding Archetypes with Nonsense Validity Under Mutate-able Reference Transmutation +Unfortunately, the above definition is still flawed. Consider these stability declarations: +```rust +#[repr(u8)] +enum Boolean { + False = 0, + True = 1, +} + +unsafe impl PromiseTransmutableInto for Boolean { + type Type = u8; +} +``` + +Given these declarations, Rust would accept the bound `&mut Boolean: TransmuteInto<&mut u8>`. This is problematic: transmutations involving mutate-able references must not expand bit-validity. + +--- + +
+Why would this transmutation be accepted? (Click to Expand) + +Why would this transmutation be accepted? Let's solve this trait bound step-by-step: +```rust +/// 0. Initial equation: +for<'a> &'a mut Boolean: + TransmuteInto<&'a mut u8> +``` + +As before, we first apply the stability rule to our equation: +```rust +𝙞𝙛 + for<'a> <&'a mut Boolean as PromiseTransmutableInto>::Archetype: + TransmuteInto< + <&'a mut u8 as PromiseTransmutableFrom>::Archetype, + NeglectStability + > +𝙩𝙝𝙚𝙣 + for<'a> '&mut Boolean: + TransmuteInto<&'a mut u8> +``` + +Next, recall the definitions of `PromiseTransmutableInto` and `PromiseTransmutableFrom` for `&mut T`: +```rust +unsafe impl<'a, T> PromiseTransmutableFrom for &'a mut T +where + T: PromiseTransmutableFrom +{ + type Archetype = &'a T::Archetype; +} + +unsafe impl<'a, T> PromiseTransmutableInto for &'a mut T +where + T: PromiseTransmutableInto +{ + type Archetype = &'a T::Archetype; +} +``` +We apply these definitions to evaluate `<& mut _ as PromiseTransmutableInto>::Archetype` and `<& mut _ as PromiseTransmutableFrom>::Archetype`: +```rust +𝙞𝙛 + for<'a> &'a mut ::Archetype: + TransmuteInto< + &'a mut ::Archetype, + NeglectStability + > +𝙩𝙝𝙚𝙣 + for<'a> '&mut Boolean: + TransmuteInto<&'a mut u8> +``` + +Finally, recall the definitions of `PromiseTransmutableInto` for `Boolean` and `PromiseTransmutableFrom` for `u8`: +```rust +unsafe impl PromiseTransmutableInto for Boolean { + type Archetype = u8; +} + +unsafe impl PromiseTransmutableFrom for u8 { + type Archetype = Self; +} +``` +We apply these definitions to evaluate `::Archetype` and `::Archetype`: +```rust +𝙞𝙛 + for<'a> &'a mut u8: + TransmuteInto< + &'a mut u8, + NeglectStability + > +𝙩𝙝𝙚𝙣 + for<'a> '&mut Boolean: + TransmuteInto<&'a mut u8> +``` +Since `&'a mut u8` can be safely transmuted into `&'a mut u8`, the compiler accepts the transmutation of `&mut Boolean` into `&mut u8`. + +
+ +--- + +This safety hazard can be prevented by rewriting our `where` bounds like so: +```rust +pub trait PromiseTransmutableFrom +where + Self::Archetype: TransmuteInto, + for<'a> &'a Self::Archetype: TransmuteInto<&'a Self, NeglectStability>, + for<'a> &'a mut Self::Archetype: TransmuteInto<&'a mut Self, NeglectStability> +{ + type Archetype; +} + +pub trait PromiseTransmutableInto +where + Self::Archetype: TransmutFrom, + for<'a> &'a Self::Archetype: TransmuteFrom<&'a Self, NeglectStability>, + for<'a> &'a mut Self::Archetype: TransmuteFrom<&'a mut Self, NeglectStability> +{ + type Archetype; +} +``` + +Our previous attempt to skirt safety now produces a compiler error: +```rust +impl PromiseTransmutableInto for Boolean { + type Archetype = u8; + // ^^ the trait `TransmuteFrom<&'_ mut u8, NeglectStability>` is not implemented for `&'_ mut Boolean` +} +``` + +### Part 2: Towards Flexibility +In the previous two sections, we bypassed the safety effects of the `PromiseTransmutable{From,Into}` traits' `where` bounds by defining archetypes whose quirks were only apparent under a *more* restrictive transmutation than that enforced by the `where` bound. + +We cannot play the same trick to bypass safety with our latest `where` bound. Between transmutations involving values, shared references and unique references, the rules governing transmutations involving unique references are the most restrictive. + +However, this safety comes at the expense of flexibility. Recall our last 'dangerous' archetype: +```rust +impl PromiseTransmutableInto for Boolean { + type Archetype = u8; + // ^^ the trait `TransmuteFrom<&'_ mut u8, NeglectStability>` is not implemented for `&'_ mut Boolean` +} +``` +This archetype is sensible in the context of value transmutations. It's sensible in the context of immutable reference transmutations. The *only* context in which it is *not* sensible are mutate-able reference transmutations! Our changes achieved safety at the significant expense of flexibility. + +In this section, we begin at our previous value-safe definition of the stability declaration traits: +```rust +pub unsafe trait PromiseTransmutableFrom +where + Self::Archetype: TransmuteInto, +{ + type Archetype; +} + +pub unsafe trait PromiseTransmutableInto +where + Self::Archetype: TransmuteFrom, +{ + type Archetype; +} +``` +...and solve its reference-unsafety in a manner that preserves flexibility. + +#### Compartmentalizing Shared Reference Transmutation Stability +Reconsider these stability declarations: +```rust +#[derive(PromiseTransmutableFrom)] +#[repr(C)] +pub struct Aligned1; + +#[derive(PromiseTransmutableInto)] +#[repr(C, align(8))] +pub struct Aligned8; + +unsafe impl PromiseTransmutableInto for Aligned1 { + type Archetype = Aligned8; +} + +unsafe impl PromiseTransmutableFrom for Aligned8 { + type Archetype = Aligned1; +} +``` + +Given these declarations, Rust would accept the bound `&Aligned1: TransmuteInto<&Aligned8>`. This is problematic: `Aligned8` has *stricter* alignment requirements than `Aligned1`. + +In our derivation of why the compiler accepted this transmutation, we referred to the stability declarations of `&T`: +```rust +impl<'a, T> PromiseTransmutableFrom for &'a T +where + T: PromiseTransmutableFrom, +{ + type Archetype = &'a T::Archetype; +} + +impl<'a, T> PromiseTransmutableInto for &'a T +where + T: PromiseTransmutableInto +{ + type Archetype = &'a T::Archetype; +} +``` +Rather than change the `where` bound of the *definitions* of `PromiseTransmutableFrom` and `PromiseTransmutableInto`, we can simply change the `where` bound of those two *implementations* of the traits: +```rust +impl<'a, T> PromiseTransmutableFrom for &'a T +where + T: PromiseTransmutableFrom, + for<'a> &'a Self::Archetype: TransmuteInto<&'a Self, NeglectStability> +{ + type Archetype = &'a T::Archetype; +} + +impl<'a, T> PromiseTransmutableInto for &'a T +where + T: PromiseTransmutableInto, + for<'a> &'a Self::Archetype: TransmuteFrom<&'a Self, NeglectStability> +{ + type Archetype = &'a T::Archetype; +} +``` + +#### Compartmentalizing Unique Reference Transmutation Stability +Likewise, for unique references, we go from this: +```rust +impl<'a, T> PromiseTransmutableFrom for &'a mut T +where + T: PromiseTransmutableFrom +{ + type Archetype = &'a mut T::Archetype; +} + +impl<'a, T> PromiseTransmutableInto for &'a mut T +where + T: PromiseTransmutableInto +{ + type Archetype = &'a mut T::Archetype; +} +``` +...to this: +```rust +impl<'a, T> PromiseTransmutableFrom for &'a mut T +where + T: PromiseTransmutableFrom, + for<'a> &'a mut Self::Archetype: TransmuteInto<&'a mut Self, NeglectStability> +{ + type Archetype = &'a T::Archetype; +} + +impl<'a, T> PromiseTransmutableInto for &'a mut T +where + T: PromiseTransmutableInto, + for<'a> &'a mut Self::Archetype: TransmuteFrom<&'a mut Self, NeglectStability> +{ + type Archetype = &'a T::Archetype; +} +``` + +### Part 3: Towards Sensibility + +In the previous two parts, we developed safe-and-flexible stability declaration traits. While we cannot make unsafe archetype choices, we can still make *silly* archetype choices. For instance, we can declare the stability of our types in terms of types that are *not* stable: + +```rust +#[repr(u8)] +pub struct Foo { ... } + +#[repr(u8)] +pub struct Bar { ... } + +impl PromiseTransmutableInto for Foo { + type Archetype = Bar; +} +``` + +We can prevent these sorts of silly archetypes by requiring that `Archetype` itself is stable, and that its stability archetype is not transitively defined: + +```rust +pub trait PromiseTransmutableFrom +{ + type Archetype + : TransmuteInto + + PromiseTransmutableFrom; +} + +pub trait PromiseTransmutableInto +{ + type Archetype + : TransmuteFrom + + PromiseTransmutableInto; +} +``` + +# Drawbacks +[drawbacks]: #drawbacks + +## No Notion of Platform Stability +The stability declaration traits communicate library layout stability, but not *platform* layout stability. A transmutation is platform-stable if it compiling one one platform implies it will compile on all other platforms. Unfortunately, platform-unstable types are common; e.g.: + +- All pointer-related primitive types (`usize`, `isize`, `*const T`, `*mut T`, `&T`, `&mut T`) possess platform-dependent layouts; their sizes and alignments are well-defined, but vary between platforms. Concretely, whether `usize` is `TransmuteInto<[u8; 4]>` or `TransmuteInto<[u8; 8]>` will depend on the platform. +- The very existence of some types depends on platform, too; e.g., the contents of [`core::arch`](https://doc.rust-lang.org/stable/core/arch/), [`std::os`](https://doc.rust-lang.org/stable/std/os/), and [`core::sync::atomic`](https://doc.rust-lang.org/stable/std/sync/atomic/) all depend on platform. + +Expanding this RFC to address such types would introduce considerable additional complexity: +
    +
  1. + +**Cognitive Complexity:** For types whose layout varies between platforms, the [stability] declaration traits could, *perhaps*, be adapted to encode platform-related guarantees. We anticipate this would contribute substantial cognitive complexity. Type authors, even those with no interest in cross-platform stability, would nonetheless need to reason about the layout properties of their types on platforms that might not yet exist. +
  2. +
  3. + +**Ergonomic Complexity:** Platform instabilities are contagious: a type that *contains* a platform-unstable type is, itself, platform-unstable. Due to the sheer virulence of types with platform-dependent layouts, an explicit '`NeglectPlatformStability`' option would need to be used for *many* simple transmutations. The ergonomic cost of this would also be substantial. + +
  4. +
  5. + +**Implementation Complexity:** The mechanisms proposed by this RFC are, fundamentally, applications of and additions to Rust's type system (i.e., they're traits). Mechanisms that impact platform stability, namely `#[cfg(...)]` annotations, long precede type-resolution and layout computation in the compilation process. For instance, it's possible to define types with impossible layouts: +```rust +#[cfg(any())] +struct Recursive(Recursive); +``` +This program compiles successfully on all platforms because, from the perspective of later compilation stages, `Recursive` may as well not exist. + +
  6. +
+ +The issues of platform layout stability exposed by this RFC are not fundamentally different from the challenges of platform API stability. These challenges are already competently addressed by the mechanisms proposed in [RFC1868](https://github.com/rust-lang/rfcs/pull/1868). For this reason, and for the aforementioned concerns of additional complexity, we argue that communicating and enforcing platform layout stability must remain outside the scope of this RFC. + +## Stability of *Unsafe* Transmutations +[drawback-unsafe-stability]: #Stability-of-Unsafe-Transmutations + +The model of stability proposed by this RFC frames stability as a quality of *safe* transmutations. A type author cannot specify stability archetypes for *unsafe* transmutations, and it is reasonable to want to do so. + +To accomodate this, we may modify the definitions of `PromiseTransmutableFrom` and `PromiseTransmutableInto` to consume an optional `Neglect` parameter, to allow for stability declarations for unsafe transmutations: +```rust +pub trait PromiseTransmutableFrom +where + Neglect: UnsafeTransmuteOptions +{ + type Archetype + : TransmuteInto> + + PromiseTransmutableInto>; +} + +pub trait PromiseTransmutableInto +where + Neglect: UnsafeTransmuteOptions +{ + type Archetype + : TransmuteFrom> + + PromiseTransmutableInto>; +} +``` +Implementations of these traits for a given `Neglect` declares that a transmutation which is accepted while neglecting a particular set of checks (namely the set encoded by `Neglect`) will *continue* to be possible. + +We omit these definition from this RFC's recommendations because they are not completely satisfying. For instance, `Neglect` is a *logically* unordered set of options, but is encoded as a tuple (which *is* ordered). To declare a transmutation that requires neglecting validity and alignment checks as stable, only *one* of these impls ought to be necessary: + +```rust +impl PromiseTransmutableFrom<(NeglectAlignment, NeglectValidity)> for Foo +{ + ... +} + +impl PromiseTransmutableFrom<(NeglectValidity, NeglectAlignment)> for Foo +{ + ... +} +``` +Writing *both* impls (as we do above) is logically nonsense, but is nonetheless supported by Rust's coherence rules. + + +# Rationale and alternatives +[rationale-and-alternatives]: #rationale-and-alternatives + +## Case Studies + +### Case Study: Making Unsafe Rust Safer +[case-study-safer-unsafe]: #Case-Study-Making-Unsafe-Rust-Safer + +This RFC proposes mechanisms for safe transmutation and safer *unsafe* transmutation. How might [Kelly's motivating example][motivation-safer-unsafe] change in light of these mechanisms? + +#### Safer Unsafe +For the compiler to accept a use of `unsafe_transmute`, we must: + - use `unsafe_transmute` with the `NeglectAlignment` option + - annotate `Foo` with `#[repr(C)]` so its layout is defined + - annotate `Foo` with `#[derive(PromiseTransmutableFrom,PromiseTransmutableInto)]` to indicate its layout is library-stable (alternatively, we could also use the `NeglectStability` option) + +The sum of these changes: +```rust +use core::transmute::{ + unsafe_transmute, + options::NeglectAlignment, + stability::FixedLayout, +}; + + +#[derive(PromiseTransmutableFrom, PromiseTransmutableInto)] +#[repr(C)] +pub struct Foo { + a: i32, + b: i32, +} + +fn main() { + let mut array: [u8; 1024] = [1; 1024]; + + let mut foo = unsafe { + // check alignment ourselves + assert_eq!((src as *const u8 as usize) % align_of::(), 0); + + // transmute `&mut array` to `&mut Foo` + // permitted becasue we promise to check the alignment ourselves + let foo = unsafe_transmute::<&mut [u8; 1024], &mut Foo, NeglectAlignment>(&mut array); + }; + + foo.a += 1; +} +``` + +#### No Unsafe Needed! +Better yet, we may use a completely *safe* transmute if we statically align the byte array: + +```rust +use core::transmute::{ + TransmuteInto, + options::NeglectAlignment, + stability::FixedLayout, +}; + + +#[derive(PromiseTransmutableFrom, PromiseTransmutableInto)] +#[repr(C)] +struct Foo { + a: i32, + b: i32, +} + +#[derive(PromiseTransmutableFrom, PromiseTransmutableInto)] +#[repr(C, align(4)] +struct Aligned([u8; 1024]); + +fn main() { + let mut array: Aligned = Aligned([1; 1024]); + + let foo : &mut Foo = (&mut array[0]).transmute_into(); + + foo.a += 1; +} +``` + +### Case Study: Abstractions for Packet Parsing +***TODO: josh*** + +### Case Study: Abstractions for Pointer Bitpacking +[case-study-alignment]: #case-study-abstractions-for-pointer-bitpacking + +A [previous motivating example](#expressing-layout-requirements) involved a generic abstraction only usable with types meeting certain layout requirements: pointer bitpacking. Using this RFC's mechanisms, we can require that a reference to a generic type `T` has alignment of at least a given value (e.g., `8`) by first defining a ZST with that alignment: +```rust +#[derive(PromiseTransmutableFrom)] +#[repr(align(8)] +struct Aligned8; +``` +and then using this `where` bound: +```rust +where + &T: TransmuteInto<&Aligned8> +``` + +## Rationale: `TransmuteFrom`/`TransmuteInto` + +### Why support arbitrary transmutation? +Some [prior art][prior-art], especially in the crate ecosystem, provides an API that [only supports transmutations involving particular types](#Source-and-Destination-Types-Supported) (e.g., from/into bytes). As we discuss in the [prior art][prior-art] section, we believe that the inflexibility of such approaches make them a poor basis of a language proposal. In particular, these restrictive approaches don't leave room to grow: supporting additional transmutations requires additional traits. + +The API advocated by this proposal is unopinionated about what transmutations users might wish to do, and what transmutations the compiler is able to reason about. The implementation of this RFC may be initially very simple (and perhaps support no more than the restrictive approaches allow for), but then subsequently grow in sophistication—*without* necessitating public API changes. + +### Why *two* traits? +If `TransmuteInto` is implemented in terms of `TransmuteFrom`, why provide it at all? We do so for consistency with libcore's [`From`/`Into`](https://doc.rust-lang.org/stable/rust-by-example/conversion/from_into.html) traits, and because directionality conveys intent: `TransmuteInto` connotes *conversion*, whereas `TransmuteInto` connotes initialization. We believe that the supporting code examples of this RFC demonstrate the explanatory benefits of providing *both* traits. + +## Rationale: Transmutation Options + +### Granularity +Although the focus of our API is statically-correct, infalible transmutations, the ability to opt-out of particular static checks is essential for building safer *fallible* mechanisms, such as alignment-fallible [reference casting][ext-ref-casting], or validity-fallible transmutations (e.g., `bool` to `u8`). + +### Representation +Although transmutations options exist at a type-level, they're represented as type-level tuples, whose familiar syntax is identical to value-level tuples. An empty tuple seems like the natural choice for encoding *don't neglect anything*. + +We could not identify any advantages to representing options with const-generics. There is no clear syntactic advantage: tuples remain the most natural way to encode ad-hoc products of items. The comparative lack of default values for const-generic parameters poses an ergonomic *disadvantage*. + + +## Rationale: Stability +### Distinguishing Stability from Possibility +[Prior art][mechanism-manual] which requires an author's manual implementation of conversion traits conflates stability with possibility; i.e., there may be types for which a transmutation is known to be valid for which the authors do not implement the required traits for safe transmutation. For such types, transmuters must fall all the way back to the *wildly* unsafe `mem::transmute`; there is no middle-ground where transmuters can retain all static guarantees *besides* stability. By distinguishing possibility from stability, our proposal provides this middle ground. + +### Distinguishing Stability from `#[repr(C)]` +In our proposal, `#[repr(C)]` does not connote any promises of layout stability for SemVer purposes. It's [been suggested](https://rust-lang.zulipchat.com/#narrow/stream/216762-project-safe-transmute/topic/RFC.3A.20Stability.20Declaration.20Traits/near/204011238) that the presence of `#[repr(C)]` *already* connotes total layout stability; if so, explicit stability declaration traits are superfluous. However, we are unaware of any documentation indicating that `#[repr(C)]` carries this implication. (It's also [been suggested](https://rust-lang.zulipchat.com/#narrow/stream/216762-project-safe-transmute/topic/typic/near/201165897) that `#[repr(C)]` should *not* connote stability, because that would pose a stability hazard. We agree with this assessment.) + +### Permitting Granular Stability Declarations +The [stability declaration traits][stability] permit granular and incomplete promises of layout stability (e.g., guaranteeing the size and validity qualities of a type, but *not* its alignment. An alternative formulation of stability might be all-or-nothing (i.e., equivalent to only being able to write `Archetype = Self`). However, members of the safe-transmute working group have [expressed](https://rust-lang.zulipchat.com/#narrow/stream/216762-project-safe-transmute/topic/Transmutability.20Intrinsic/near/202712834) an interest in granular stability declarations. + +Our proposed API supports complex, granular stability declarations, all-the-while retaining simplicity in simple use-cases (namely, one can simply the stability declaration traits). + + +## Alternative: Implementing this RFC in a Crate + +This RFC builds on ample [prior art][prior-art] in the crate ecosystem, but these efforts strain against the fundamental limitations of crates. Fundamentally, safe transmutation efforts use traits to expose layout information to the type system. The burden of ensuring safety [is usually either placed entirely on the end-user, or assumed by complex, incomplete proc-macro `derives`][mechanism-manual]. + +An exception to this rule is the [typic][crate-typic] crate, which complex, type-level programming to emulate a compiler-supported, "smart" `TransmuteFrom` trait (like the one proposed in this RFC). Nonetheless, [typic][crate-typic] is fundamentally limited: since Rust does not provide a type-level mechanism for reflecting over the structure of arbitrary types, even [typic][crate-typic] cannot judge the safety of a transmutation without special user-added annotations on type definitions. Although [typic][crate-typic] succeeds as a proof-of-concept, its maintainability is questionable, and the error messages it produces are [lovecraftian](https://en.wikipedia.org/wiki/Lovecraftian_horror). + +The development approaches like [typic][crate-typic]'s could, perhaps, be eased by stabilizing [frunk](https://crates.io/crates/frunk)-like structural reflection, or (better yet) by stabilizing a compiler plugin API for registering "smart" traits like `TransmuteFrom`. However, we suspect that such features would be drastically harder to design and stabilize. + +Regardless of approach, almost all [prior art][prior-art] attempts to reproduce knowledge *already* possessed by rustc during the compilation process (i.e., the layout qualities of a concrete type). Emulating the process of layout computation to any degree is an error-prone duplication of effort between rustc and the crate, in a domain where correctness is crucial. + +Finally, community-led, crate-based approaches are, inescapably, unauthoritative. These approaches are incapable of fulfilling our motivating goal of providing a *standard* mechanism for programmers to statically ensure that a transmutation is safe, sound, or stable. + +# Prior art +[prior-art]: #prior-art + +[crate-plain]: https://crates.io/crates/plain +[crate-bytemuck]: https://crates.io/crates/bytemuck +[crate-dataview]: https://crates.io/crates/dataview +[crate-safe-transmute]: https://crates.io/crates/safe-transmute +[crate-pod]: https://crates.io/crates/pod +[crate-uncon]: https://crates.io/crates/uncon +[crate-typic]: https://crates.io/crates/typic +[crate-zerocopy]: https://crates.io/crates/zerocopy +[crate-convute]: https://crates.io/crates/convute +[crate-byterepr]: https://crates.io/crates/byterepr + +[2017-02]: https://internals.rust-lang.org/t/pre-rfc-safe-coercions/4823 +[2018-03]: https://internals.rust-lang.org/t/pre-rfc-frombits-intobits/7071 +[2018-03-18]: https://internals.rust-lang.org/t/pre-rfc-frombits-intobits/7071/23 +[2018-05-18]: https://internals.rust-lang.org/t/pre-rfc-trait-for-deserializing-untrusted-input/7519 +[2018-05-23]: https://github.com/joshlf/rfcs/blob/joshlf/from-bytes/text/0000-from-bytes.md +[2019-09]: https://internals.rust-lang.org/t/specifying-a-set-of-transmutes-from-struct-t-to-struct-u-which-are-not-ub/10917 +[2019-11]: https://internals.rust-lang.org/t/pre-rfc-safe-transmute/11347 +[2019-12-05-gnzlbg]: https://gist.github.com/gnzlbg/4ee5a49cc3053d8d20fddb04bc546000 +[2019-12-05-v2]: https://internals.rust-lang.org/t/pre-rfc-v2-safe-transmute/11431 +[2020-07]: https://internals.rust-lang.org/t/pre-rfc-explicit-opt-in-oibit-for-truly-pod-data-and-safe-transmutes/2361 + + +## Dimensions of Variation + +### Conversion Complexity +Prior work differs in whether it supports complex conversions, or only simple transmutation. [*Pre-RFC FromBits/IntoBits*][2018-03]'s proposed traits include conversion methods that are implemented by type authors. Because end-users provide their own definitions of these methods, they can be defined to do more than just transmutation (e.g., slice casting). (This approach is similar to the [uncon][crate-uncon] crate's [`FromUnchecked`](https://docs.rs/uncon/1.*/uncon/trait.FromUnchecked.html) and [`IntoUnchecked`](https://docs.rs/uncon/1.*/uncon/trait.IntoUnchecked.html) traits, which provide unsafe conversions between types. These traits are safe to implement, but their conversion methods are not.) + +In contrast, our RFC focuses only on transmutation. Our `TransmutableFrom` and `TransmutableInto` traits serve as both a marker *and* a mechanism: if `Dst: TransmuteFrom`, it is sound to transmute from `Dst` into `Src` using `mem::transmute`. However, these traits *also* provide transmutation methods that are guaranteed to compile into nothing more complex than a `memcpy`. These methods cannot be overridden by end-users to implement more complex behavior. + +The signal and transmutability and mechanism are, in principle, separable. The [convute][crate-convute] crate's [`Transmute`](https://docs.rs/convute/0.2.0/convute/marker/trait.Transmute.html) trait is an unsafe marker trait representing types that can be transmuted into `T`. This is *just* a marker trait; the actual conversion mechanisms are provided by a [separate suite](https://docs.rs/convute/0.2.0/convute/convert/index.html) of traits and functions. Our RFC combines marker with mechanism because we feel that separating these aspects introduces additional complexity with little added value. + +### Conversion Fallibility +Prior work differs in whether it supports only infallible conversions, or fallible conversions, too. The [convute][crate-convute] crate's [`TryTransmute`](https://docs.rs/convute/0.2.0/convute/marker/trait.TryTransmute.html) trait provides a method, `can_transmute`, that returns true a transmutation from `Self` to `T` is valid for a particular value of `&self`. An early version of [typic][crate-typic] abstracted a similar mechanism into an [`Invariants`](https://docs.rs/typic/0.1.0/typic/transmute/trait.Invariants.html) trait, with additional facilities for error reporting. [*Draft-RFC: `Compatible`/`TryCompatible`*][2019-12-05-gnzlbg] employs a similar mechanism to typic. + +Typic removed support for fallible transmutation after reckoning with several challenges: +- The causes of uncertain failure could be languaged-imposed (e.g., alignment or validity requirements), or library imposed (i.e., invariants placed on a structure's private fields). +- The points of uncertain failures could be arbitrarily 'deep' into the fields of a type. +- Error reporting incurs a runtime cost commensurate with the detail of the reporting, but the detail of reporting required by end-user depends on use-case, not just type. For instance: for some use-cases it may be necessary to know where and why a byte was not a valid `bool`; in others it may be sufficient to know simply *whether* an error occurred. + +Finally, we observed that the mechanisms of fallible transmutation were basically separable from the mechanisms of infallible transmutation, and thus these challenges could be addressed at a later date. For these reasons, our RFC *only* addresses infallible transmutation. + +While this RFC does not provide a grand, all-encompassing mechanism for fallible transmutation, the fundamental mechanisms of our RFC are useful for constructing safer, purpose-built fallible conversion mechanisms; e.g.: +```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: TransmuteInto<&'u U, NeglectAlignment>, +{ + if (src as *const T as usize) % align_of::() != 0 { + None + } else { + // Safe because we dynamically enforce the alignment + // requirement, whose static check we chose to neglect. + Some(unsafe { src.unsafe_transmute_into() }) + } +} +``` +In this approach, our RFC is joined by crates such as [plain](https://docs.rs/plain/0.2.3/plain/#functions), [bytemuck](https://docs.rs/bytemuck/1.*/bytemuck/#functions), [dataview](https://docs.rs/dataview/0.1.1/dataview/struct.DataView.html#methods), [safe-transmute](https://docs.rs/safe-transmute/0.11.0/safe_transmute/fn.transmute_one.html), [zerocopy](https://docs.rs/zerocopy/0.3.0/zerocopy/struct.LayoutVerified.html#methods), and [byterepr](https://docs.rs/byterepr/0.1.0/byterepr/trait.ByteRepr.html#provided-methods), and several pre-RFCs (such as [this][2018-05-18] and [this](https://github.com/joshlf/rfcs/blob/joshlf/from-bits/text/0000-from-bits.md#library-functions)). The ubiquity of these mechanisms makes a strong case for their inclusion in libcore. + +### Source and Destination Types Supported +Prior work differs in whether its API surface is flexible enough to support transmutation between arbitrary types, or something less. + +#### Arbitrary Types +Approaches supporting transmutations between arbitrary types invariably define traits akin to: +```rust +/// Indicates that `Self` may be transmuted into `Dst`. +pub unsafe trait TransmuteInto +{ ... } + +/// Indicates that `Self` may be transmuted from `Dst`. +pub unsafe trait TransmuteFrom +{ ... } +``` +This approach, taken by our RFC, is used by at least two crates: +- The [convute][crate-convute] crate's [`Transmute`](https://docs.rs/convute/0.2.0/convute/marker/trait.Transmute.html) trait is akin to the above definition of `TransmuteInto`. +- The [typic][crate-typic] crate's [`TransmuteInto`](https://docs.rs/typic/0.3.0/typic/transmute/trait.TransmuteInto.html) and [`TransmuteFrom`](https://docs.rs/typic/0.3.0/typic/transmute/trait.TransmuteFrom.html) traits almost exactly mirror the above definitions. + +...and several proposals: +- [*Pre-RFC: Safe coercions*][2017-02] proposes a `Coercible` trait that is implemented if `A` is safely transmutable into `B`. +- [*Pre-RFC: `FromBits`/`IntoBits`*][2018-03] proposes the traits `IntoBits` and `FromBits.` +- [*Draft-RFC: `FromBytes`*][2018-05-23] proposes the traits `IntoBytes` and `FromBytes.` +- [*Draft-RFC: `Compatible`/`TryCompatible`*][2019-12-05-gnzlbg] proposes the trait `Compatible`, akin to the above definition of `TransmuteInto`. + +##### From/Into Bytes Transmutations +Other approaches adopt an API that only supports transmutation of a type into initialized bytes, and from initialized bytes. These approaches invariably define traits akin to: +```rust +/// Indicates that a type may be transmuted into an appropriately-sized array of bytes. +pub unsafe trait IntoBytes +{} + +/// Indicates that a type may be transmuted from an appropriately-sized array of bytes. +pub unsafe trait FromBytes +{} +``` +This is the approach taken by the [zerocopy][crate-zerocopy] crate, and the [*Pre-RFC: Safe Transmute*][2019-11] and [*Pre-RFC: Safe Transmute v2*][2019-12-05-v2] proposals. + +This approach is strictly less flexible than an API supporting transmutation between arbitrary types. It is incapable of representing transmutations of bytes into types with validity constraints, and incapable of representing transmutations of types with padding bytes into bytes. + +Supporting additional transmutation source and destination types requires a commensurate addition of conversion traits. For instance, some of [zerocopy][crate-zerocopy]'s users [require](https://fuchsia-review.googlesource.com/c/fuchsia/+/306036/2#message-a1a0c9cf16e3dec24e7b0548e3c09382f63783f0) a trait that reflects types which can be transmuted from a buffer of zeroed bytes. This would require introducing an additional trait, `FromZeros`. + +An advantage of this API is that it gives descriptive names to perhaps the two most common transmutations. However, an API providing transmutation between arbitrary types can encode `FromBytes` and `IntoBytes`: +```rust +// `Dst` is `FromBytes` if it can be safely transmuted *from* an +// equivalently sized array of `u8`. +unsafe impl FromBytes for Dst +where + Dst: TransmuteFrom<[u8; size_of::()]>, +{} + +// `Src` is `IntoBytes` if it can be safely transmuted *into* an +// equivalently sized array of `u8`. +unsafe impl IntoBytes for Src +where + Src: TransmuteInto<[u8; size_of::()]>, +{} +``` +For these reasons, we argue that a `FromBytes`/`ToBytes` style API is a poor foundation for in-language safe transmutation. + +##### Bytes-to-Bytes Transmutations (aka "Plain Old Data") +Finally, many approaches (especially crates) supply a marker trait that represents "plain old data"; e.g.: +```rust +/// Implemented by types that are "plain old data": +pub unsafe trait PlainOldData +{} +``` +This sort of trait is present in crates such as [plain](https://docs.rs/plain/0.2.3/plain/trait.Plain.html), [bytemuck](https://docs.rs/bytemuck/1.*/bytemuck/trait.Pod.html), [dataview](https://docs.rs/dataview/0.1.1/dataview/trait.Pod.html), [safe-transmute](https://docs.rs/safe-transmute/0.11.0/safe_transmute/trivial/trait.TriviallyTransmutable.html), and [pod](https://docs.rs/pod/0.5.0/pod/trait.Pod.html), and at least two language proposals ([here][2018-05-18] and [here][2020-07]). + +The exact definition of what constitutes "plain old data" varies between crates. One simple definition is that a type `T` is "plain old data" if it can be transmuted both from and into initialized bytes; i.e.: +```rust +unsafe impl PlainOldData for T +where + T: FromBytes + IntoBytes, +{} +``` + +This definition precludes useful transmutations. For instance, `MaybeUninit` is transmutable from a `u8`, but not *into* a `u8`. + +Given this inflexibility, we argue that this approach is a poor foundation for in-language safe transmutation. + + +### Implementation Mechanism +Not only does prior work differ in which traits are used to encode valid transmutations, they differ in the level of user intervention required to take advantage of the traits. + +#### Manual +[mechanism-manual]: #Manual +Fully manual approaches require type authors to implement the transmutation traits manually. The involved traits are `unsafe`, so it is up to type authors to verify for themselves that their hand-written implementations are sound. This is the approach taken by crates such as [plain][crate-plain], [bytemuck][crate-bytemuck], [safe-transmute][crate-safe-transmute], and [pod][crate-pod], and at least one language proposal: [*Pre-RFC: Safe Transmute*][2019-12-05-v2] (which advocates for a "plain old data" API). + +In semi-manual approaches, type authors simply `derive` the applicable traits, using `derive` macros that produce a compile-error if the implementation is not sound. This approach is realized by crates such as ([zerocopy](https://docs.rs/zerocopy/0.3.0/zerocopy/#derives), [zeroable](https://docs.rs/zeroable/0.2.0/zeroable/) and [dataview](https://docs.rs/dataview/0.1.1/dataview/derive.Pod.html)) and advocated by at least two language proposals: [*Pre-RFC: Safe Transmute v2*][2019-12-05-v2] (which advocates for a `FromBytes`/`IntoBytes`-style API), and [*Pre-RFC FromBits/IntoBits*][2018-03] (which advocates for a general-transmutation API). + +We believe that the implementation burden these approaches place on end-users, and their inflexibility, make them a poor foundation for in-language safe transmutation: +- These approaches require authors to implement and, potentially, verify a large number of `unsafe` traits, ranging from *O(n)* implementations for plain-old-data trait approaches, to potentially [*many* more](https://internals.rust-lang.org/t/pre-rfc-frombits-intobits/7071/28). +- These approaches are generally impractical for APIs that permit truly general transmutation, as type authors can only construct implementations of the transmutation traits for types they have at their disposal. +- These approaches conflate transmutation stability with transmutation safety. An end-user wishing to transmute a type for which its author has *not* manually implemented the applicable traits must resort to the wildly unsafe `mem::transmute`. + + + +#### Automatic +Automatic approaches implement the transmutation traits without user intervention, whenever it is sound to do so. This is the approach taken by our RFC. Automatic mechanisms appear in at least four prior language proposals: +- [*Pre-RFC: Safe coercions*][2017-02] +- [*Draft-RFC: `from_bytes`*][2018-05-23] +- [*Pre-RFC: Trait for deserializing untrusted input*][2018-05-18] +- [*Draft-RFC: `compatible_trait`*][2019-12-05-gnzlbg] + +The [typic][crate-typic] crate mocks a fully-automatic approach: its `TransmuteFrom` trait is usable with any types that are `repr(C)`, or otherwise have a well-defined memory layout. (In practice, since Rust lacks reflection over type definitions, `repr(C)` annotations much be changed to `typic::repr(C)`.) + +### Stability Hazards +Fully automatic approaches introduce, at the very least, a stability hazard: they supply a safe constructor for types, without the consent of those types' authors. If a type author hid the internals of their type because they do not wish for its implementation details to become a part of the type's API for SemVer purposes, an automatic transmutation mechanism subverts that intent. + +No attempt to avoid this hazard is made by most of the proposals featuring automatic mechanisms; e.g.: +- [*Draft-RFC: `from_bytes`*][2018-05-23] +- [*Pre-RFC: Trait for deserializing untrusted input*][2018-05-18] +- [*Draft-RFC: `compatible_trait`*][2019-12-05-gnzlbg] + +#### Hazard-Avoidant +The automatic mechanism proposed by [*Pre-RFC: Safe coercions*][2017-02] exploits field visibility, requiring that all fields that have different types in `Src` and `Dst` are visible at the location where the coercion is made. This approach falls short in three respects: +1. Confining the visibility requirement only to fields of *different* types is insufficient; two different types with identical field types may subject those fields to different invariants. +2. The 'location' where the coercion is made is ill-defined; the presence of the proposed `Coercible` trait may be far-removed from the location of the actual conversion (if any conversion occurs at all). +3. Field visibility stabilizes the structure of a type, but *not* its layout (e.e., its size). + +Our RFC exploits the related concept of *constructability*, which is a property of a struct, or enum variant (rather than solely a property of fields). However, we recognize that it may be difficult to test for constructability within the trait resolution process. + +The simplified definition of *constructability* we propose is the same employed by [typic][crate-typic] (which uses the term "visibility"). [Typic][crate-typic] regards the pub-in-prive soundness hole of the simplified definition to be sufficiently niche that `NeglectStability` remains "safe". However, unlike [typic][crate-typic], we believe that this simplified definition imposes a safety hazard substantial enough to warrant making `NeglectStability` initially an unsafe transmute option. + +Our RFC separates *constructability*, which concerns what aspects of a type's structure are part of its public API, and *stability*, which concerns the aspects of a type's layout that are part of its public API for SemVer purposes. This distinction does not appear in prior work. + +# Unresolved questions +[unresolved-questions]: #unresolved-questions + +### Questions To Be Resolved Before RFC Acceptance +The following unresolved questions should be resolved during the RFC process: + +##### Unhandled Use-Cases? +We endeavored to design an API surface with ([nearly][drawbacks]) zero compromises. However, if you have a use-case that you believe is neither satisfied outright by our proposal, nor [aided][future-possibilities] by our proposal, we would *urgently* like to hear of it. + +##### Extensions for Inclusion? +In [*Future Possibilities*][future-possibilities], we propose a number of additional abstractions that are aided by this RFC. Some of these abstractions are commonplace in [prior art][prior-art] and should perhaps be included with this RFC. Some of our proposed extensions could begin their crates that work on stable Rust; others, such as [generic atomics][future-possibility-generic-atomics], require nightly-only intrinsics. + +### Questions To Be Resolved Before Feature Stabilization +The following unresolved questions should be resolved before feature stabilization: + +##### Layout-Stability for Unsafe Transmutations? +We [observe][drawback-unsafe-stability] that our proposed model for stability declaration, although very expressive, does not permit type authors to declare the stability of *unsafe* transmutations. Alongside that observation, we suggest a [SemVer-compatible](https://github.com/rust-lang/rfcs/blob/master/text/1105-api-evolution.md#minor-change-adding-a-defaulted-type-parameter) upgrade of the stability declaration traits that may resolve this shortcoming. + +While it is unclear if there is any demand for this degree of flexibility, this upgrade-path should be carefully considered *before* stabilizing (and thus committing) to this RFC's layout stability declaration traits. + + +### Questions Out of Scope +We consider the following unresolved questions to be out-of-scope of *this* RFC process: + +##### Design of `NeglectConstructability`? +`TransmuteFrom` and `TransmuteInto` require that the destination type has a matching constructor in which all fields are marked `pub`. Conspicuously *missing* from this RFC is a `NeglectConstructability` unsafe option to disable this check. + +The omission is intentional. The consequences of such an option are suprising in both their subtlety and their unsafety. Some of unsafe Rust's hairiest interactions lie at the intersections of `!Send`, `!Sync`, `UnsafeCell` and restricted field visibility. These building blocks are used to build safe, public abstractions that encapsulate unsafe, hidden internals. + +# Future possibilities +[future-possibilities]: #future-possibilities + + +## Extension: Byte Transmutation Traits +[marker-traits]: #Extension-Byte-Transmutation-Traits +[future-possibility-safe-initialization]: #Safe-Initialization + +The marker traits from [zerocopy][crate-zerocopy] are expressible using `TransmuteFrom` and `TransmuteInto`. In principle, these traits could remain in crates. However, we believe their clarity and broad usefulness may justify their inclusion in `libcore`. + +### `IntoBytes` +Indicates that a type may be transmuted into an appropriately-sized array of bytes. +```rust +pub unsafe trait IntoBytes +where + Neglect: UnsafeTransmuteOptions +{} + +// `Src` is `IntoBytes` if it can be safely transmuted *into* an +// equivalently sized array of `u8`. +unsafe impl IntoBytes for Src +where + Src: Sized + TransmuteInto<[u8; size_of::()], Neglect>, + Neglect: UnsafeTransmuteOptions +{} +``` + +### `FromBytes` +Indicates that a type may be transmuted from an appropriately-sized array of bytes. +```rust +pub unsafe trait FromBytes +where + Neglect: UnsafeTransmuteOptions +{} + +// `Dst` is `FromBytes` if it can be safely transmuted *from* an +// equivalently sized array of `u8`. +unsafe impl FromBytes for Dst +where + Dst: Sized + TransmuteFrom<[u8; size_of::()], Neglect>, + Neglect: UnsafeTransmuteOptions +{} +``` + +### `FromZeros` +[future-possibility-safe-initialization]: #FromZeros + +Indicates that a type may be transmuted from an appropriately-sized array of zeroed bytes. Equivalently, `FromZeros` indicates that a type may be safely initialized with `mem::zeroed`. + +```rust +pub unsafe trait FromZeros +where + Neglect: UnsafeTransmuteOptions +{ + /// Safely initialize `Self` from zeroed bytes. + fn zeroed() -> Self + where + Neglect: SafeTransmuteOptions; + + /// Unsafely initialize `Self` from zeroed bytes. + fn unsafe_zeroed() -> Self + where + Neglect: SafeTransmuteOptions; +} + +#[derive(Copy)] +#[repr(u8)] +enum Zero { + Zero = 0u8 +} + +// `T` is `FromZeros` if it can be safely transmuted *from* an +// equivalently sized array of zeroed bytes. +unsafe impl FromZeros for Dst +where + Dst: Sized + TransmuteFrom<[Zero; size_of::()], Neglect>, + Neglect: UnsafeTransmuteOptions +{ + fn zeroed() -> Self + where + Neglect: SafeTransmuteOptions + { + [Zero; size_of::].transmute_into() + } + + unsafe fn unsafe_zeroed() -> Self + where + Neglect: UnsafeTransmuteOptions + { + [Zero; size_of::].unsafe_transmute_into() + } +} +``` + +## Extension: Slice Casting +Transmuting the contained type of a slice is a [common operation](https://internals.rust-lang.org/t/safe-trasnsmute-for-slices-e-g-u64-u32-particularly-simd-types/2871) in cryptography. Although this RFC does not propose the addition of a concrete method for slice casting, the mechanisms proposed in this RFC make possible sound and complete slice casting abstractions; e.g.: +```rust +impl<'t, T> &'t [T] { + + /// Safely convert a `&[T]` into `&[U]`. + pub fn transmute_into<'u, U, Neglect>(&self) -> &'u [U] + where + Neglect: SafeTransmuteOptions, + &'t [T; size_of::()]: TransmuteInto<&'u [U; size_of::()], Neglect> + { + unsafe { self.unsafe_cast() } + } + + /// Unsafely convert a `&[T]` into `&[U]`. + pub unsafe fn unsafe_transmute_into<'u, U, Neglect>(&self) -> &'u [U] + where + Neglect: UnsafeTransmuteOptions, + &'t [T; size_of::()]: TransmuteInto<&'u [U; size_of::()], Neglect> + { + let len = size_of_val(self).checked_div(size_of::()).unwrap_or(0); + unsafe { + slice::from_raw_parts(self.as_ptr() as *const U, len) + } + } +} +``` +The `TransmuteInto` bound statically ensures that `T` and `U` have compatible alignments, even though `.transmute_into()` isn't called in the body of this function. + +## Extension: `include_data!` +[future-possibility-include_data]: #Extension-include_data + +With the mechanisms proposed in this RFC, we may define a structured-data alternative to `include_bytes`: +```rust +macro_rules! include_data { + ($file:expr) => {{ + use core::transmute::TransmuteInto; + include_bytes!($file).transmute_into() + }}; +} +``` +...and, with this, replace all instances of `unsafe` in our [earlier][motivation-including-data] motivating example: +```rust +pub fn recognize(input: &Matrix) -> usize +{ + static WEIGHT: &Matrix = include_data!("/weight.bin"); + + static BIAS: &Matrix = include_data!("/bias.bin") + + network::recognize(input, WEIGHT, BIAS) +} +``` + +## Extension: Reference Casting +[ext-ref-casting]: #Extension-Reference-Casting +```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: UnsafeTransmuteInto<&'u U, neglect::Alignment>, +{ + if (src as *const T as usize) % align_of::() != 0 { + None + } else { + // Safe because we dynamically enforce the alignment + // requirement, whose static check we chose to neglect. + Some(unsafe { src.unsafe_transmute_into() }) + } +} +``` + +## Extension: Generic Atomics +[future-possibility-generic-atomics]: #extension-generic-atomics + +```rust +type LargestPlatformAtomic = u64; + +pub struct Atomic +where + // ensures that size_of::() <= size_of::(): + MaybeUninit: TransmuteFrom +{ + v: UnsafeCell +} + +impl Atomic +{ + #[inline] + pub fn load(&self, order: Ordering) -> T { + unsafe { atomic_load(self.v.get(), order) } + } + + #[inline] + pub fn store(&self, val: T, order: Ordering) -> T { + unsafe { atomic_store(self.v.get(), val, order) } + } +} +``` From 651e3bfe2181f56709d2ccfc13346b73bfb89aac Mon Sep 17 00:00:00 2001 From: Jack Wrenn Date: Mon, 3 Aug 2020 15:19:37 -0400 Subject: [PATCH 02/61] Apply suggestions from code review typo fixes from @jonas-schievink Co-authored-by: Jonas Schievink --- rfcs/0000-safe-transmute.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/rfcs/0000-safe-transmute.md b/rfcs/0000-safe-transmute.md index f1d8ca8..0985ac3 100644 --- a/rfcs/0000-safe-transmute.md +++ b/rfcs/0000-safe-transmute.md @@ -524,13 +524,13 @@ pub struct Foo(pub Bar, pub Baz); const _: () = { #[repr(C)] - struct TransmutableFromArchetype + struct TransmutableFromArchetype( ::Archetype, ::Archetype, ); #[repr(C)] - struct TransmutableIntoArchetype + struct TransmutableIntoArchetype( ::Archetype, ::Archetype, ); @@ -661,13 +661,13 @@ In the above definitions, `Src` represents the source type of the transmutation, ### Neglecting Static Checks [options]: #Neglecting-Static-Checks -The default value of the `Neglect` parameter, `()`, statically forbids transmutes that are unsafe, unsafe, or unstable. However, you may explicitly opt-out of some static checks: +The default value of the `Neglect` parameter, `()`, statically forbids transmutes that are unsafe, unsound, or unstable. However, you may explicitly opt-out of some static checks: | Transmute Option | Compromises | Usable With | |---------------------|-------------|---------------------------------------------------------| | `NeglectStabilty` | Stability | `transmute_{from,into}`, `unsafe_transmute_{from,into}` | | `NeglectAlignment` | Safety | `unsafe_transmute_{from,into}` | -| `NeglectValidity` | Soudness | `unsafe_transmute_{from,into}` | +| `NeglectValidity` | Soundness | `unsafe_transmute_{from,into}` | `NeglectStabilty` implements the `SafeTransmuteOptions` and `UnsafeTransmuteOptions` marker traits, as it can be used in both safe and unsafe code. The selection of multiple options is encoded by grouping them as a tuple; e.g., `(NeglectAlignment, NeglectValidity)` is a selection of both the `NeglectAlignment` and `NeglectValidity` options. From 7a883702a5390f8419689dfc188f263a665ac871 Mon Sep 17 00:00:00 2001 From: Jack Wrenn Date: Mon, 3 Aug 2020 15:29:14 -0400 Subject: [PATCH 03/61] Fix `derive` expansion. --- rfcs/0000-safe-transmute.md | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/rfcs/0000-safe-transmute.md b/rfcs/0000-safe-transmute.md index 0985ac3..b2b5548 100644 --- a/rfcs/0000-safe-transmute.md +++ b/rfcs/0000-safe-transmute.md @@ -520,26 +520,32 @@ This expands to: #[repr(C)] pub struct Foo(pub Bar, pub Baz); -// Generated stability declarations for `Foo`: +/// Generated `PromiseTransmutableFrom` for `Foo` const _: () = { + use core::transmute::stability::PromiseTransmutableFrom; #[repr(C)] - struct TransmutableFromArchetype( - ::Archetype, - ::Archetype, - ); - - #[repr(C)] - struct TransmutableIntoArchetype( - ::Archetype, - ::Archetype, + pub struct TransmutableFromArchetype( + pub ::Archetype, + pub ::Archetype, ); impl PromiseTransmutableFrom for Foo { type Archetype = TransmutableFromArchetype; } +}; + +/// Generated `PromiseTransmutableInto` for `Foo` +const _: () = { + use core::transmute::stability::PromiseTransmutableInto; + + #[repr(C)] + pub struct TransmutableIntoArchetype( + pub ::Archetype, + pub ::Archetype, + ); - impl PromiseTransmutableInto for FooTransmutableIntoArchetype { + impl PromiseTransmutableInto for Foo { type Archetype = TransmutableIntoArchetype; } }; From 32bc4b901912864ad950a3f44912b3131ccd738b Mon Sep 17 00:00:00 2001 From: Jack Wrenn Date: Tue, 4 Aug 2020 11:46:02 -0400 Subject: [PATCH 04/61] reword motivation Resolves https://github.com/rust-lang/project-safe-transmute/pull/5/files#r464502312 --- rfcs/0000-safe-transmute.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/rfcs/0000-safe-transmute.md b/rfcs/0000-safe-transmute.md index b2b5548..a2cc831 100644 --- a/rfcs/0000-safe-transmute.md +++ b/rfcs/0000-safe-transmute.md @@ -39,12 +39,12 @@ let _ : u32 = Foo(16, 12).transmute_into(); // Compile Error! # Motivation [motivation]: #motivation -Beyond the titular goal of providing a mechanism of safe transmutation, this RFC seeks to provide mechanisms which: - - [authoritatively codify language layout guarantees](#codifying-language-layout-guarantees) - - [allow crate authors to codify their types' layout stability guarantees](#expressing-library-layout-guarantees) - - [allow crate authors to codify their abstractions' layout requirements](#expressing-layout-requirements) +We foresee a range of practical consequences of fulfilling these goals, including [making unsafe Rust safer](#making-unsafe-rust-safer), providing [safer initialization primitives](#safer-initialization-primitives), and [generic atomics](#atomict). We explore these use-cases, and more, in this section. -We foresee a range of practical consequences of fulfilling these goals, including [making unsafe Rust safer](#making-unsafe-rust-safer), providing [safer initialization primitives](#safer-initialization-primitives), and [generic atomics](#atomict). We explore these possibilities, and more, in this section. +A *comprehensive* approach to safe transmutation provides benefits beyond the mere act of transmutation; namely: + - [authoritatively codifies language layout guarantees](#codifying-language-layout-guarantees) + - [allows crate authors to codify their types' layout stability guarantees](#expressing-library-layout-guarantees) + - [allows crate authors to codify their abstractions' layout requirements](#expressing-layout-requirements) ## Codifying Language Layout Guarantees Documentation of Rust's layout guarantees for a type are often spread across countless issues, pull requests, RFCs and various official resources. It can be very difficult to get a straight answer. When transmutation is involved, users must reason about the *combined* layout properties of the source and destination types. From 6ea37052e67e2fc53ba94a4e883b18c7bd59d8c1 Mon Sep 17 00:00:00 2001 From: Jack Wrenn Date: Tue, 4 Aug 2020 13:02:31 -0400 Subject: [PATCH 05/61] `Constrained` -> `NonEmptySlice` https://github.com/rust-lang/project-safe-transmute/pull/5#issuecomment-668684758 --- rfcs/0000-safe-transmute.md | 164 ++++++++++++++++++------------------ 1 file changed, 84 insertions(+), 80 deletions(-) diff --git a/rfcs/0000-safe-transmute.md b/rfcs/0000-safe-transmute.md index a2cc831..4c05483 100644 --- a/rfcs/0000-safe-transmute.md +++ b/rfcs/0000-safe-transmute.md @@ -374,88 +374,90 @@ let p = Point { x: 4, y: 2 }; // ^^^^^^^^^^^^^^^^^^^^ An instance of `Point` is created here, via its implicit constructor. ``` -Limiting implicit constructability is the fundamental mechanism with which type authors build safe abstractions for `unsafe` code, whose soundness is dependent on preserving invariants on fields. Usually, this takes the form of restricting the visibility of fields. For instance, consider the type `Constrained`, which enforces a validity constraint on its fields via its constructor: +Limiting implicit constructability is the fundamental mechanism with which type authors build safe abstractions for `unsafe` code, whose soundness is dependent on preserving invariants on fields. Usually, this takes the form of restricting the visibility of fields. For instance, consider the type `NonEmptySlice`, which enforces a validity constraint on its fields via its constructor: ```rust pub mod crate_a { #[repr(C)] - pub struct Constrained { - wizz: i8, - bang: u8, + pub struct NonEmptySlice { + data: *const T, + len: usize, } - impl Constrained { - /// the sum of `wizz` and `bang` must be greater than or equal to zero. - pub fn new(wizz: i8, bang: u8) -> Self { - assert!((wizz as i16) + (bang as i16) >= 0); - Constrained { wizz, bang } + impl NonEmptySlice { + pub fn from_array(arr: &[T; N], len: usize) -> Self { + assert!(len <= N); + assert!(len > 0); + Self { + data: arr as *const T, + len, + } } - pub fn something_dangerous(&self) { - unsafe { - // do something that's only memory-safe if `wizz + bang >= 0` - } + pub fn first(&self) -> &T { + unsafe { &*self.data } } } } ``` -The only reason it is sound for `something_dangerous` to be a *safe* method is because the fields `wizz` and `bang` are *not* marked `pub`: outside of `crate_a`, it is impossible to safely initialize these fields in a way that violates the invariant enforced by `Constrained::new`. In other words, `Constrained` is implicitly constructable within `crate_a`, but *not* outside of `crate_a`. Any field that is not marked `pub` of a type should be assumed to be subject to validity invariants that could impact the safety of using that type. +It is sound for `first` to be a *safe* method is because the `from_array` constructor ensures that `data` is safe to dereference, and because `from_array` is the *only* way to safely initialize `NonEmptySlice` outside of `crate_a` (note that `NonEmptySlice`'s fields are *not* `pub`). As a rule: any field that is not marked `pub` should be assumed to be private *because* it is subject to safety invariants. -Unfortunately, field visibility modifiers are not a surefire indicator of whether a type is *fully* implicitly constructable. A type author may restrict the implicit constructability of a type even in situations where all fields of that type *and all fields of those fields) are `pub`; consider: +Unfortunately, field visibility modifiers are not a surefire indicator of whether a type is *fully* implicitly constructable. A type author may restrict the implicit constructability of a type even in situations where all fields of that type (*and all fields of those fields*) are `pub`; consider: ```rust pub mod crate_a { #[repr(C)] - pub struct Constrained(pub private::ConstrainedInner); - - impl Constrained { - /// the sum of `wizz` and `bang` must be greater than or equal to zero. - pub fn new(wizz: i8, bang: u8) -> Self { - assert!((wizz as i16) + (bang as i16) >= 0); - Constrained(private::ConstrainedInner { wizz, bang }) + pub struct NonEmptySlice(pub private::NonEmptySliceInner); + + impl NonEmptySlice { + pub fn from_array(arr: &[T; N], len: usize) -> Self { + assert!(len <= N && len > 0); + Self( + private::NonEmptySliceInner { + data: arr as *const T, + len, + } + ) } - pub fn something_dangerous(&self) { - unsafe { - // do something that's only safe if `self.0.wizz + self.0.bang >= 0` - } + pub fn first(&self) -> &T { + unsafe { &*self.0.data } } } // introduce a private module to avoid `private_in_public` error (E0446): pub(crate) mod private { - /// the sum of `wizz` and `bang` MUST be greater than or equal to zero. #[repr(C)] - pub struct ConstrainedInner { - pub wizz: i8, - pub bang: u8, + pub struct NonEmptySliceInner { + pub data: *const T, + pub len: usize, } } } ``` -In the above example, the definitions of both `Constrained` and its field `ConstrainedInner` are marked `pub`. All fields of both types are marked `pub`. However, `Constrained` is *not* fully implicitly constructible outside of `crate_a`, because the module containing `ConstrainedInner` is not visibile outside of `crate_a`. +In the above example, the definitions of both `NonEmptySlice` and its field `NonEmptySliceInner` are marked `pub`, and all fields of these types are marked `pub`. However, `NonEmptySlice` is *not* fully implicitly constructible outside of `crate_a`, because the module containing `NonEmptySliceInner` is not visibile outside of `crate_a`. #### Constructability and Transmutation Transmutation supplies a mechanism for constructing instances of a type *without* invoking its implicit constructor, nor any constructors defined by the type's author. -In the previous examples, it would be *unsafe* to transmute `0xFF00u16` into `Constrained` outside `crate_a`, because subsequent *safe* use of that value (namely, calling `something_dangerous`) could violate memory safety. (However, it's completely safe to transmute `Constrained` into `0xFF00u16`.) - +In the previous examples, it would be *unsafe* to transmute `0u128` into `NonEmptySlice` outside `crate_a`, because subsequent *safe* use of that value (namely, calling `first`) would memory safety. (However, it's completely safe to transmute `NonEmptySlice` into a `u128`.) For transmutations where the destination type involves mutate-able references, the constructability of the source type is also relevant. Consider: ```rust /* ⚠️ This example intentionally does not compile. */ -let mut x = Constrained::new(0, 0); +let arr = [0u8, 1u8, 2u8]; +let mut x = NonEmptySlice::from_array(&arr, 2); { - let y : &mut u16 = (&mut x).transmute_into(); // Compile Error! - *y = 0xFF00u16; + let y : &mut u128 = (&mut x).transmute_into(); // Compile Error! + *y = 0u128; } -let z : Constrained = x; +let z : NonEmptySlice = x; ``` -If this example did not produce a compile error, the value of `z` would not be a safe instance of its type, `Constrained`. +If this example did not produce a compile error, the value of `z` would not be a safe instance of its type, `NonEmptySlice`, because `z.first()` would dereference a null pointer. ### 📖 When is a transmutation stable? [stability]: #When-is-a-transmutation-stable @@ -864,44 +866,45 @@ This definition is *usually* sufficient for ensuring safety: it is *generally* a pub mod crate_a { #[repr(C)] - pub struct Constrained(pub private::ConstrainedInner); - - impl Constrained { - /// the sum of `wizz` and `bang` must be greater than or equal to zero. - pub fn new(wizz: i8, bang: u8) -> Self { - assert!((wizz as i16) + (bang as i16) >= 0); - Constrained(private::ConstrainedInner { wizz, bang }) + pub struct NonEmptySlice(pub private::NonEmptySliceInner); + + impl NonEmptySlice { + pub fn from_array(arr: &[T; N], len: usize) -> Self { + assert!(len <= N && len > 0); + Self( + private::NonEmptySliceInner { + data: arr as *const T, + len, + } + ) } - pub fn something_dangerous(&self) { - unsafe { - // do something that's only safe if `self.0.wizz + self.0.bang >= 0` - } + pub fn first(&self) -> &T { + unsafe { &*self.0.data } } } // introduce a private module to avoid `private_in_public` error (E0446): pub(crate) mod private { - /// the sum of `wizz` and `bang` MUST be greater than or equal to zero. #[repr(C)] - pub struct ConstrainedInner { - pub wizz: i8, - pub bang: u8, + pub struct NonEmptySliceInner { + pub data: *const T, + pub len: usize, } } } ``` -With this simplified definition of constructability, it is possible for a third-party to define a *safe* constructor of `Constrained` that produces a value is *unsafe* to use: +With this simplified definition of constructability, it is possible for a third-party to define a *safe* constructor of `NonEmptySlice` that produces a value which is *unsafe* to use: ```rust -pub evil_constructor(src: T) -> Constrained +pub evil_constructor(src: T) -> NonEmptySlice where - T: TransmuteInto, + T: TransmuteInto, NeglectStability>, { src.transmute_into() } -evil_constructor(0xFF_00u16).something_dangerous() // muahaha! +evil_constructor(0u128).first() // muahaha! ``` The above code is "safe" because our simplified definition of constructability fails to recognize this pattern of encapsulation, and because `NeglectStability` is a `SafeTransmutationOption`. @@ -913,12 +916,12 @@ By temporarily sacrificing these goals, we may preserve safety solely... ##### ...at the Cost of `NeglectStability` We may preserve safety by demoting `NeglectStability` to `UnsafeTransmutationOption`-status. -In doing so, a third-party is forced to resort to an `unsafe` transmutation to construct `Constrained`; e.g.: +In doing so, a third-party is forced to resort to an `unsafe` transmutation to construct `NonEmptySlice`; e.g.: ```rust -pub evil_constructor(src: T) -> Constrained +pub evil_constructor(src: T) -> NonEmptySlice where - T: TransmuteInto, + T: TransmuteInto, NeglectStability>, { // unsafe because we `NeglectStability` unsafe { src.unsafe_transmute_into() } @@ -931,47 +934,48 @@ pub mod crate_a { #[derive(PromiseTransmutableFrom)] #[repr(C)] - pub struct Constrained(pub private::ConstrainedInner); - - impl Constrained { - /// the sum of `wizz` and `bang` must be greater than or equal to zero. - pub fn new(wizz: i8, bang: u8) -> Self { - assert!((wizz as i16) + (bang as i16) >= 0); - Constrained(private::ConstrainedInner { wizz, bang }) + pub struct NonEmptySlice(pub private::NonEmptySliceInner); + + impl NonEmptySlice { + pub fn from_array(arr: &[T; N], len: usize) -> Self { + assert!(len <= N && len > 0); + Self( + private::NonEmptySliceInner { + data: arr as *const T, + len, + } + ) } - pub fn something_dangerous(&self) { - unsafe { - // do something that's only safe if `self.0.wizz + self.0.bang >= 0` - } + pub fn first(&self) -> &T { + unsafe { &*self.0.data } } } // introduce a private module to avoid `private_in_public` error (E0446): pub(crate) mod private { - /// the sum of `wizz` and `bang` MUST be greater than or equal to zero. #[derive(PromiseTransmutableFrom)] #[repr(C)] - pub struct ConstrainedInner { - pub wizz: i8, - pub bang: u8, + pub struct NonEmptySliceInner { + pub data: *const T, + pub len: usize, } } } ``` -In the above example, the type author declares `Constrained` and `ConstrainedInner` to be stably instantiatable via transmutation. Given this, a third-party no longer needs to resort to `unsafe` code to violate the the invariants on `wizz` and `bang`: +In the above example, the type author declares `NonEmptySlice` and `NonEmptySliceInner` to be stably instantiatable via transmutation. Given this, a third-party no longer needs to resort to `unsafe` code to violate the the invariants on `inner`: ```rust -pub evil_constructor(src: T) -> Constrained +pub evil_constructor(src: T) -> NonEmptySlice where - T: TransmuteInto, + T: TransmuteInto>, { src.transmute_into() } -evil_constructor(0xFF_00u16).something_dangerous() // muahaha! +evil_constructor(0u128).first() // muahaha! ``` -This safety hazard is not materially different from the one that would be induced if the type author implemented `DerefMut` for `Constrained`, or made the `private` module `pub`, or otherwise provided mutable access to `wizz` and `bang`. +This safety hazard is not materially different from the one that would be induced if the type author implemented `DerefMut` for `NonEmptySlice`, or made the `private` module `pub`, or otherwise explicitly provided outsiders with unrestricted mutable access to `data`. ##### Recommendation We recommend that that implementers of this RFC initially simplify constructability by: From 5eafe9fdd8b02f6425db0beaad0b4756cb1fe233 Mon Sep 17 00:00:00 2001 From: Jack Wrenn Date: Tue, 4 Aug 2020 13:13:30 -0400 Subject: [PATCH 06/61] missing word https://github.com/rust-lang/project-safe-transmute/pull/5#discussion_r465200754 --- rfcs/0000-safe-transmute.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rfcs/0000-safe-transmute.md b/rfcs/0000-safe-transmute.md index 4c05483..58f064f 100644 --- a/rfcs/0000-safe-transmute.md +++ b/rfcs/0000-safe-transmute.md @@ -443,7 +443,7 @@ In the above example, the definitions of both `NonEmptySlice` and its field `Non #### Constructability and Transmutation Transmutation supplies a mechanism for constructing instances of a type *without* invoking its implicit constructor, nor any constructors defined by the type's author. -In the previous examples, it would be *unsafe* to transmute `0u128` into `NonEmptySlice` outside `crate_a`, because subsequent *safe* use of that value (namely, calling `first`) would memory safety. (However, it's completely safe to transmute `NonEmptySlice` into a `u128`.) +In the previous examples, it would be *unsafe* to transmute `0u128` into `NonEmptySlice` outside `crate_a`, because subsequent *safe* use of that value (namely, calling `first`) would violate memory safety. (However, it's completely safe to transmute `NonEmptySlice` into a `u128`.) For transmutations where the destination type involves mutate-able references, the constructability of the source type is also relevant. Consider: ```rust From 3a4e5761ae641f074464459915884789ea58a0f7 Mon Sep 17 00:00:00 2001 From: Jack Wrenn Date: Tue, 4 Aug 2020 16:58:35 -0400 Subject: [PATCH 07/61] Apply suggestions from code review Kelly -> Kelley --- rfcs/0000-safe-transmute.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/rfcs/0000-safe-transmute.md b/rfcs/0000-safe-transmute.md index 58f064f..9eccf90 100644 --- a/rfcs/0000-safe-transmute.md +++ b/rfcs/0000-safe-transmute.md @@ -83,11 +83,11 @@ fn main() { } } ``` -This pattern, Kelly notes, is common when interacting with operating system APIs. +This pattern, Kelley notes, is common when interacting with operating system APIs. This rust program compiles, but quietly introduces undefined behavior. `Foo` requires stricter in-memory alignment than `u8`. The equivalent code in Zig produces a compiler error for this reason and requires modification. -And that's not all: Kelly misses that the very layout of `Foo` itself is undefined⁠—it must be annotated with `#[repr(C)]`! +And that's not all: Kelley misses that the very layout of `Foo` itself is undefined⁠—it must be annotated with `#[repr(C)]`! Kelley concludes: > In Zig the problem of alignment is solved completely; the compiler catches all possible alignment issues. In the situation where you need to assert to the compiler that something is more aligned than Zig thinks it is, you can use `@alignCast`. This inserts a cheap safety check in debug mode to make sure the alignment assertion is correct. @@ -100,7 +100,7 @@ This RFC proposes mechanisms that use this information to enable safer transmuta ***TODO: Josh?*** ## Safer Initialization Primitives -The initialization primitives `mem::zeroed` and `mem::uninitialized` are `unsafe` because they may be used to initialized types for which zeroed or uninitialized memory are *not* valid bit-patterns. The `mem::zeroed` function recently gained a dynamically-enforced validity check, but this safety measure isn't wholly satisfactory: the validity properties of `T` are statically known, but the check is dynamic. +The initialization primitives `mem::zeroed` and `mem::uninitialized` are `unsafe` because they may be used to initialize types for which zeroed or uninitialized memory are *not* valid bit-patterns. The `mem::zeroed` function recently gained a dynamically-enforced validity check, but this safety measure isn't wholly satisfactory: the validity properties of `T` are statically known, but the check is dynamic. This RFC proposes mechanisms that would allow these functions to explicitly bound their generic parameter `T` based on its validity properties. We revisit this motivating use-case with our proposed mechanisms near the end of this RFC, [*here*][future-possibility-safe-initialization]. @@ -1607,7 +1607,7 @@ Writing *both* impls (as we do above) is logically nonsense, but is nonetheless ### Case Study: Making Unsafe Rust Safer [case-study-safer-unsafe]: #Case-Study-Making-Unsafe-Rust-Safer -This RFC proposes mechanisms for safe transmutation and safer *unsafe* transmutation. How might [Kelly's motivating example][motivation-safer-unsafe] change in light of these mechanisms? +This RFC proposes mechanisms for safe transmutation and safer *unsafe* transmutation. How might [Kelley's motivating example][motivation-safer-unsafe] change in light of these mechanisms? #### Safer Unsafe For the compiler to accept a use of `unsafe_transmute`, we must: From 00167d659e8c94c7f92468831714df47dadceab8 Mon Sep 17 00:00:00 2001 From: Jack Wrenn Date: Tue, 4 Aug 2020 17:17:46 -0400 Subject: [PATCH 08/61] add lifetime parameter to `NonEmptySlice` https://github.com/rust-lang/project-safe-transmute/pull/5#discussion_r465216812 --- rfcs/0000-safe-transmute.md | 58 +++++++++++++++++++++---------------- 1 file changed, 33 insertions(+), 25 deletions(-) diff --git a/rfcs/0000-safe-transmute.md b/rfcs/0000-safe-transmute.md index 9eccf90..7e19d91 100644 --- a/rfcs/0000-safe-transmute.md +++ b/rfcs/0000-safe-transmute.md @@ -380,22 +380,24 @@ Limiting implicit constructability is the fundamental mechanism with which type pub mod crate_a { #[repr(C)] - pub struct NonEmptySlice { + pub struct NonEmptySlice<'a, T> { data: *const T, len: usize, + lifetime: core::marker::PhantomData<&'a ()>, } - impl NonEmptySlice { - pub fn from_array(arr: &[T; N], len: usize) -> Self { + impl<'a, T> NonEmptySlice<'a, T> { + pub fn from_array(arr: &'a [T; N], len: usize) -> Self { assert!(len <= N); assert!(len > 0); Self { data: arr as *const T, len, + lifetime: core::marker::PhantomData, } } - pub fn first(&self) -> &T { + pub fn first(&self) -> &'a T { unsafe { &*self.data } } } @@ -409,20 +411,21 @@ Unfortunately, field visibility modifiers are not a surefire indicator of whethe pub mod crate_a { #[repr(C)] - pub struct NonEmptySlice(pub private::NonEmptySliceInner); + pub struct NonEmptySlice<'a, T>(pub private::NonEmptySliceInner<'a, T>); - impl NonEmptySlice { - pub fn from_array(arr: &[T; N], len: usize) -> Self { + impl<'a, T> NonEmptySlice<'a, T> { + pub fn from_array(arr: &'a [T; N], len: usize) -> Self { assert!(len <= N && len > 0); Self( private::NonEmptySliceInner { data: arr as *const T, len, + lifetime: core::marker::PhantomData, } ) } - pub fn first(&self) -> &T { + pub fn first(&self) -> &'a T { unsafe { &*self.0.data } } } @@ -430,9 +433,10 @@ pub mod crate_a { // introduce a private module to avoid `private_in_public` error (E0446): pub(crate) mod private { #[repr(C)] - pub struct NonEmptySliceInner { + pub struct NonEmptySliceInner<'a, T> { pub data: *const T, pub len: usize, + pub lifetime: core::marker::PhantomData<&'a ()>, } } @@ -866,20 +870,21 @@ This definition is *usually* sufficient for ensuring safety: it is *generally* a pub mod crate_a { #[repr(C)] - pub struct NonEmptySlice(pub private::NonEmptySliceInner); + pub struct NonEmptySlice<'a, T>(pub private::NonEmptySliceInner<'a, T>); - impl NonEmptySlice { - pub fn from_array(arr: &[T; N], len: usize) -> Self { + impl<'a, T> NonEmptySlice<'a, T> { + pub fn from_array(arr: &'a [T; N], len: usize) -> Self { assert!(len <= N && len > 0); Self( private::NonEmptySliceInner { data: arr as *const T, len, + lifetime: core::marker::PhantomData, } ) } - pub fn first(&self) -> &T { + pub fn first(&self) -> &'a T { unsafe { &*self.0.data } } } @@ -887,9 +892,10 @@ pub mod crate_a { // introduce a private module to avoid `private_in_public` error (E0446): pub(crate) mod private { #[repr(C)] - pub struct NonEmptySliceInner { + pub struct NonEmptySliceInner<'a, T> { pub data: *const T, pub len: usize, + pub lifetime: core::marker::PhantomData<&'a ()>, } } @@ -897,9 +903,9 @@ pub mod crate_a { ``` With this simplified definition of constructability, it is possible for a third-party to define a *safe* constructor of `NonEmptySlice` that produces a value which is *unsafe* to use: ```rust -pub evil_constructor(src: T) -> NonEmptySlice +pub evil_constructor(src: T) -> NonEmptySlice<'static, u8> where - T: TransmuteInto, NeglectStability>, + T: TransmuteInto, NeglectStability>, { src.transmute_into() } @@ -919,9 +925,9 @@ We may preserve safety by demoting `NeglectStability` to `UnsafeTransmutationOpt In doing so, a third-party is forced to resort to an `unsafe` transmutation to construct `NonEmptySlice`; e.g.: ```rust -pub evil_constructor(src: T) -> NonEmptySlice +pub evil_constructor(src: T) -> NonEmptySlice<'static, u8> where - T: TransmuteInto, NeglectStability>, + T: TransmuteInto, NeglectStability>, { // unsafe because we `NeglectStability` unsafe { src.unsafe_transmute_into() } @@ -934,20 +940,21 @@ pub mod crate_a { #[derive(PromiseTransmutableFrom)] #[repr(C)] - pub struct NonEmptySlice(pub private::NonEmptySliceInner); + pub struct NonEmptySlice<'a, T>(pub private::NonEmptySliceInner<'a, T>); - impl NonEmptySlice { - pub fn from_array(arr: &[T; N], len: usize) -> Self { + impl<'a, T> NonEmptySlice<'a, T> { + pub fn from_array(arr: &'a [T; N], len: usize) -> Self { assert!(len <= N && len > 0); Self( private::NonEmptySliceInner { data: arr as *const T, len, + lifetime: core::marker::PhantomData, } ) } - pub fn first(&self) -> &T { + pub fn first(&self) -> &'a T { unsafe { &*self.0.data } } } @@ -956,9 +963,10 @@ pub mod crate_a { pub(crate) mod private { #[derive(PromiseTransmutableFrom)] #[repr(C)] - pub struct NonEmptySliceInner { + pub struct NonEmptySliceInner<'a, T> { pub data: *const T, pub len: usize, + pub lifetime: core::marker::PhantomData<&'a ()>, } } @@ -966,9 +974,9 @@ pub mod crate_a { ``` In the above example, the type author declares `NonEmptySlice` and `NonEmptySliceInner` to be stably instantiatable via transmutation. Given this, a third-party no longer needs to resort to `unsafe` code to violate the the invariants on `inner`: ```rust -pub evil_constructor(src: T) -> NonEmptySlice +pub evil_constructor(src: T) -> NonEmptySlice<'static, u8> where - T: TransmuteInto>, + T: TransmuteInto>, { src.transmute_into() } From cf204266e67301b0feccdc2d26c61faf9a733fb2 Mon Sep 17 00:00:00 2001 From: Jack Wrenn Date: Tue, 4 Aug 2020 17:32:34 -0400 Subject: [PATCH 09/61] `TransmuteFrom` connotes conversion https://github.com/rust-lang/project-safe-transmute/pull/5#discussion_r465342037 --- rfcs/0000-safe-transmute.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rfcs/0000-safe-transmute.md b/rfcs/0000-safe-transmute.md index 7e19d91..ec3c055 100644 --- a/rfcs/0000-safe-transmute.md +++ b/rfcs/0000-safe-transmute.md @@ -1712,7 +1712,7 @@ Some [prior art][prior-art], especially in the crate ecosystem, provides an API The API advocated by this proposal is unopinionated about what transmutations users might wish to do, and what transmutations the compiler is able to reason about. The implementation of this RFC may be initially very simple (and perhaps support no more than the restrictive approaches allow for), but then subsequently grow in sophistication—*without* necessitating public API changes. ### Why *two* traits? -If `TransmuteInto` is implemented in terms of `TransmuteFrom`, why provide it at all? We do so for consistency with libcore's [`From`/`Into`](https://doc.rust-lang.org/stable/rust-by-example/conversion/from_into.html) traits, and because directionality conveys intent: `TransmuteInto` connotes *conversion*, whereas `TransmuteInto` connotes initialization. We believe that the supporting code examples of this RFC demonstrate the explanatory benefits of providing *both* traits. +If `TransmuteInto` is implemented in terms of `TransmuteFrom`, why provide it at all? We do so for consistency with libcore's [`From`/`Into`](https://doc.rust-lang.org/stable/rust-by-example/conversion/from_into.html) traits, and because directionality conveys intent: `TransmuteFrom` connotes *conversion*, whereas `TransmuteInto` connotes initialization. We believe that the supporting code examples of this RFC demonstrate the explanatory benefits of providing *both* traits. ## Rationale: Transmutation Options From a5122b2cd80d86b4c7cd7b552519fa5fe8ebb8a1 Mon Sep 17 00:00:00 2001 From: Jack Wrenn Date: Tue, 4 Aug 2020 17:51:50 -0400 Subject: [PATCH 10/61] extra explanatory comments on Summary example https://github.com/rust-lang/project-safe-transmute/pull/5#discussion_r464500451 --- rfcs/0000-safe-transmute.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/rfcs/0000-safe-transmute.md b/rfcs/0000-safe-transmute.md index ec3c055..e27a4a2 100644 --- a/rfcs/0000-safe-transmute.md +++ b/rfcs/0000-safe-transmute.md @@ -16,20 +16,21 @@ use core::transmute::{ stability::{PromiseTransmutableInto, PromiseTransmutableFrom}, }; -#[derive(PromiseTransmutableInto, PromiseTransmutableFrom)] +#[derive(PromiseTransmutableInto, PromiseTransmutableFrom)] // declare `Foo` to be *stably* transmutable #[repr(C)] pub struct Foo(pub u8, pub u16); -// ^ there's a padding byte here +// ^ there's a padding byte here, between these fields // Transmute fearlessly! let _ : Foo = 64u32.transmute_into(); // Alchemy Achieved! +// ^^^^^^^^^^^^^^ provided by the `TransmuteInto` trait let _ : u32 = Foo(16, 12).transmute_into(); // Compile Error! // error[E0277]: the trait bound `u32: TransmuteFrom` is not satisfied -// --> src/demo.rs:14:27 +// --> src/demo.rs:15:27 // | -// 14 | let _ : u32 = Foo(16, 12).transmute_into(); // Compile Error! +// 15 | let _ : u32 = Foo(16, 12).transmute_into(); // Compile Error! // | ^^^^^^^^^^^^^^ the trait `TransmuteFrom` is not implemented for `u32` // | // = note: required because of the requirements on the impl of `TransmuteInto` for `foo::Foo` From 1a119b21bc3ca7c447c1bd645cb0805d0c2b5b3c Mon Sep 17 00:00:00 2001 From: Jack Wrenn Date: Tue, 4 Aug 2020 18:28:42 -0400 Subject: [PATCH 11/61] clarify why size expansion is dangerous, but leave the door open to it https://github.com/rust-lang/project-safe-transmute/pull/5#discussion_r465307607 It's reasonably* valid to expand size when the bytes of destination type that overhang the source type may be uninitialized; e.g.: [MaybeUninit; 2] to [MaybeUninit; 4] I say "reasonably" valid, because the rule of thumb with `mem::transmute_copy` has always been DON'T EXPAND SIZE. But when you frame transmutation as a `union` with two variants: union Transmute { src: Src, dst: Dst, } then this interpretation of size-expanding transmutes is plausible. --- rfcs/0000-safe-transmute.md | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/rfcs/0000-safe-transmute.md b/rfcs/0000-safe-transmute.md index e27a4a2..663ecc5 100644 --- a/rfcs/0000-safe-transmute.md +++ b/rfcs/0000-safe-transmute.md @@ -271,7 +271,7 @@ let _ : [u8; 16] = [0u8; 32]::default().transmute_into(); ``` This transmute truncates away the final sixteen bytes of the `[u8; 32]` value. -A value may ***not*** be transmuted into a type of greater size: +A value may ***not*** be transmuted into a type of greater size, if doing so would expose uninitialized bytes as initialized: ```rust /* ⚠️ This example intentionally does not compile. */ let _ : [u8; 32] = [0u8; 16]::default().transmute_into(); // Compile Error! @@ -283,11 +283,25 @@ let _ : [u8; 32] = [0u8; 16]::default().transmute_into(); // Compile Error! The [restrictions above that apply to transmuting owned values][transmute-owned] also apply to transmuting references. However, references carry a few additional restrictions. A [sound transmutation] must: + - [preserve or shrink size][reference-size], - [preserve or relax alignment][reference-alignment], - [preserve or shrink lifetimes][reference-lifetimes], - [preserve or shrink uniqueness][reference-mutability], and - and if the destination type is a mutate-able reference, [preserve validity][reference-validity]. +##### Preserve or Shrink Size +[reference-size]: #Preserve-or-Shrink-Size + +You may preserve or decrease the size of the referent type via transmutation: +```rust +let _: &[u8; 3] = (&[0u8; 9]).transmute_into(); +``` + +However, you may **not**, under any circumstances, *increase* the size of the referent type: +```rust +/* ⚠️ This example intentionally does not compile. */ +let _: &[u8; 9] = (&[0u8; 3]).transmute_into(); // Compile Error! +``` ##### Preserve or Relax Alignment [reference-alignment]: #Preserve-or-Relax-Alignment From 575eb971f4cd5131d6ade93dea130cd80d5b7624 Mon Sep 17 00:00:00 2001 From: Jack Wrenn Date: Tue, 4 Aug 2020 18:35:51 -0400 Subject: [PATCH 12/61] missing word https://github.com/rust-lang/project-safe-transmute/pull/5#discussion_r465357068 --- rfcs/0000-safe-transmute.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rfcs/0000-safe-transmute.md b/rfcs/0000-safe-transmute.md index 663ecc5..6861840 100644 --- a/rfcs/0000-safe-transmute.md +++ b/rfcs/0000-safe-transmute.md @@ -1757,7 +1757,7 @@ Our proposed API supports complex, granular stability declarations, all-the-whil This RFC builds on ample [prior art][prior-art] in the crate ecosystem, but these efforts strain against the fundamental limitations of crates. Fundamentally, safe transmutation efforts use traits to expose layout information to the type system. The burden of ensuring safety [is usually either placed entirely on the end-user, or assumed by complex, incomplete proc-macro `derives`][mechanism-manual]. -An exception to this rule is the [typic][crate-typic] crate, which complex, type-level programming to emulate a compiler-supported, "smart" `TransmuteFrom` trait (like the one proposed in this RFC). Nonetheless, [typic][crate-typic] is fundamentally limited: since Rust does not provide a type-level mechanism for reflecting over the structure of arbitrary types, even [typic][crate-typic] cannot judge the safety of a transmutation without special user-added annotations on type definitions. Although [typic][crate-typic] succeeds as a proof-of-concept, its maintainability is questionable, and the error messages it produces are [lovecraftian](https://en.wikipedia.org/wiki/Lovecraftian_horror). +An exception to this rule is the [typic][crate-typic] crate, which utilizes complex, type-level programming to emulate a compiler-supported, "smart" `TransmuteFrom` trait (like the one proposed in this RFC). Nonetheless, [typic][crate-typic] is fundamentally limited: since Rust does not provide a type-level mechanism for reflecting over the structure of arbitrary types, even [typic][crate-typic] cannot judge the safety of a transmutation without special user-added annotations on type definitions. Although [typic][crate-typic] succeeds as a proof-of-concept, its maintainability is questionable, and the error messages it produces are [lovecraftian](https://en.wikipedia.org/wiki/Lovecraftian_horror). The development approaches like [typic][crate-typic]'s could, perhaps, be eased by stabilizing [frunk](https://crates.io/crates/frunk)-like structural reflection, or (better yet) by stabilizing a compiler plugin API for registering "smart" traits like `TransmuteFrom`. However, we suspect that such features would be drastically harder to design and stabilize. From 5297ad691f62df00c138f7b8abd02d82ce3181a3 Mon Sep 17 00:00:00 2001 From: Jack Wrenn Date: Tue, 4 Aug 2020 18:39:47 -0400 Subject: [PATCH 13/61] Fix botched types https://github.com/rust-lang/project-safe-transmute/pull/5#pullrequestreview-461223358 --- rfcs/0000-safe-transmute.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rfcs/0000-safe-transmute.md b/rfcs/0000-safe-transmute.md index 6861840..685b95f 100644 --- a/rfcs/0000-safe-transmute.md +++ b/rfcs/0000-safe-transmute.md @@ -267,14 +267,14 @@ let _ : Packed = Padded::default().transmute_into(); // Compile Error! It's completely sound to transmute into a type with fewer bytes than the source type; e.g.: ```rust -let _ : [u8; 16] = [0u8; 32]::default().transmute_into(); +let _ : [u8; 16] = [u8; 32]::default().transmute_into(); ``` This transmute truncates away the final sixteen bytes of the `[u8; 32]` value. A value may ***not*** be transmuted into a type of greater size, if doing so would expose uninitialized bytes as initialized: ```rust /* ⚠️ This example intentionally does not compile. */ -let _ : [u8; 32] = [0u8; 16]::default().transmute_into(); // Compile Error! +let _ : [u8; 32] = [u8; 16]::default().transmute_into(); // Compile Error! ``` #### Requirements on References From f85600d03f650c419abb849ee8d3db468ad9931c Mon Sep 17 00:00:00 2001 From: Jack Wrenn Date: Wed, 5 Aug 2020 07:16:36 -0400 Subject: [PATCH 14/61] platform stability clarification clarify that platform-unstable types are addressed, but not their stability --- rfcs/0000-safe-transmute.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rfcs/0000-safe-transmute.md b/rfcs/0000-safe-transmute.md index 685b95f..2b452fb 100644 --- a/rfcs/0000-safe-transmute.md +++ b/rfcs/0000-safe-transmute.md @@ -1554,7 +1554,7 @@ The stability declaration traits communicate library layout stability, but not * - All pointer-related primitive types (`usize`, `isize`, `*const T`, `*mut T`, `&T`, `&mut T`) possess platform-dependent layouts; their sizes and alignments are well-defined, but vary between platforms. Concretely, whether `usize` is `TransmuteInto<[u8; 4]>` or `TransmuteInto<[u8; 8]>` will depend on the platform. - The very existence of some types depends on platform, too; e.g., the contents of [`core::arch`](https://doc.rust-lang.org/stable/core/arch/), [`std::os`](https://doc.rust-lang.org/stable/std/os/), and [`core::sync::atomic`](https://doc.rust-lang.org/stable/std/sync/atomic/) all depend on platform. -Expanding this RFC to address such types would introduce considerable additional complexity: +Our proposed stability system is oblivious to the inter-platform variations of these types. Expanding our stability system to be aware of inter-platform variations would introduce considerable additional complexity:
  1. From a47b8855a621f817251d1f9f82ff202b3fa0bd23 Mon Sep 17 00:00:00 2001 From: Jack Wrenn Date: Wed, 5 Aug 2020 09:21:03 -0400 Subject: [PATCH 15/61] neglect::Alignment -> NeglectAlignment --- rfcs/0000-safe-transmute.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rfcs/0000-safe-transmute.md b/rfcs/0000-safe-transmute.md index 2b452fb..38443af 100644 --- a/rfcs/0000-safe-transmute.md +++ b/rfcs/0000-safe-transmute.md @@ -2142,7 +2142,7 @@ pub fn recognize(input: &Matrix) -> usize /// 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: UnsafeTransmuteInto<&'u U, neglect::Alignment>, + &'t T: UnsafeTransmuteInto<&'u U, NeglectAlignment>, { if (src as *const T as usize) % align_of::() != 0 { None From bc1963f74e1e12388888e34ce62176d36bc40207 Mon Sep 17 00:00:00 2001 From: Jack Wrenn Date: Wed, 5 Aug 2020 09:52:31 -0400 Subject: [PATCH 16/61] neglect::Alignment -> NeglectAlignment --- rfcs/0000-safe-transmute.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rfcs/0000-safe-transmute.md b/rfcs/0000-safe-transmute.md index 38443af..5895353 100644 --- a/rfcs/0000-safe-transmute.md +++ b/rfcs/0000-safe-transmute.md @@ -752,7 +752,7 @@ By using the `NeglectAlignment` option, you are committing to ensure that the tr /// 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: TransmuteInto<&'u U, neglect::Alignment>, + &'t T: TransmuteInto<&'u U, NeglectAlignment>, { if (src as *const T as usize) % align_of::() != 0 { None From ed172fe84874c154118f2054435ceb8ff94312e1 Mon Sep 17 00:00:00 2001 From: Jack Wrenn Date: Wed, 5 Aug 2020 12:59:19 -0400 Subject: [PATCH 17/61] `PromiseTransmutable` extension https://github.com/rust-lang/project-safe-transmute/pull/5#discussion_r464522560 --- rfcs/0000-safe-transmute.md | 39 +++++++++++++++++++++++++++++++++---- 1 file changed, 35 insertions(+), 4 deletions(-) diff --git a/rfcs/0000-safe-transmute.md b/rfcs/0000-safe-transmute.md index 5895353..158c386 100644 --- a/rfcs/0000-safe-transmute.md +++ b/rfcs/0000-safe-transmute.md @@ -529,8 +529,9 @@ Why is this the case? The type `::Archetype` exemplifies the furthest extreme of non-breaking changes that could be made to the layout of `Src` that could affect its use as a source type in transmutations. Conversely, `::Archetype` exemplifies the furthest extreme of non-breaking changes that could be made to the layout of `Dst` that could affect its use as a destination type in transmutations. If a transmutation between these extremities is valid, then so is `Src: TransmuteInto`. -#### Common Use-Case: Promising a Fixed Layout -To promise that type's layout is completely fixed, simply `derive` the `PromiseTransmutableFrom` and `PromiseTransmutableInto` traits: +#### Common Use-Case: As-Stable-As-Possible +[stability-common]: #common-use-case-as-stable-as-possible +To promise that all transmutations which are currently safe for your type will remain so in the future, simply annotate your type with: ```rust #[derive(PromiseTransmutableFrom, PromiseTransmutableInto)] #[repr(C)] @@ -571,9 +572,10 @@ const _: () = { } }; ``` +Since deriving *both* of these traits together is, by far, the most common use-case, we [propose][extension-promisetransmutable-shorthand] `#[derive(PromiseTransmutable)]` as an ergonomic shortcut. - -#### Uncommon Use Case: Complex Stability Guarantees +#### Uncommon Use-Case: Weak Stability Guarantees +[stability-uncommon]: #uncommon-use-case-weak-stability-guarantees If you are *most people* and want to declare your types as layout-stable, you should follow the advice in the previous sections. In doing so, you declare that you will *not* modify the layout of your type in virtually any way, except as a breaking change. If your type's fields have simple stability guarantees, this effects the strongest possible declaration of stability: it declares that *all* transmutations that are safe are *also* stable. @@ -1989,6 +1991,35 @@ The omission is intentional. The consequences of such an option are suprising in # Future possibilities [future-possibilities]: #future-possibilities +## Extension: `PromiseTransmutable` Shorthand +[extension-promisetransmutable-shorthand]: #extension-promisetransmutable-shorthand + +We anticipate that *most* users will merely want to [promise][stability-common] that their types are as-stable-as-possible. To do so, this RFC proposes the shorthand: +```rust +#[derive(PromiseTransmutableFrom, PromiseTransmutableInto)] +#[repr(C)] +pub struct Foo(pub Bar, pub Baz); +``` +As a shorthand, this is still rather long. For such users, the separability of `PromiseTransmutableFrom` and `PromiseTransmutableInto` is totally irrelevant. We therefore propose a `derive(PromiseTransmutable)` shorthand, such that this: +```rust +#[derive(PromiseTransmutable)] +#[repr(C)] +pub struct Foo(pub Bar, pub Baz); +``` +...is equivalent to this: +```rust +#[derive(PromiseTransmutableFrom, PromiseTransmutableInto)] +#[repr(C)] +pub struct Foo(pub Bar, pub Baz); +``` + +However, we caution *against* adding a corresponding trait or trait alias; e.g.: +```rust +trait PromiseTransmutable = PromiseTransmutableFrom + PromiseTransmutableInto; +``` +The vast majority of users will *only* confront the stability declaration traits in the context of deriving them; the *only* scenario in which end-users will refer to these traits in a type-context is the [rare use-case][stability-uncommon] of *manually* implementing them. For such users, the separability of `PromiseTransmutableFrom` and `PromiseTransmutableInto` *is* relevant. The availability of a `PromiseTransmutable` trait or trait alias in this scenario would be a distraction, since referring to it in a type-context is almost certainly a misstep. + +We acknowledge that it is unusual for a `derive` macro to not create an item of the same name, but this weirdness is outweighed by the weirdness of the alternative: providing a trait for which there is almost no good use. ## Extension: Byte Transmutation Traits [marker-traits]: #Extension-Byte-Transmutation-Traits From 15fd0e93b9dd73d270cfa062b6ecdd28df3c2aa0 Mon Sep 17 00:00:00 2001 From: Jack Wrenn Date: Wed, 5 Aug 2020 13:08:47 -0400 Subject: [PATCH 18/61] clarify the purpose of "Dissecting Stability" The section describes how user-facing complexity is turned into complexity the type system handles. --- rfcs/0000-safe-transmute.md | 1 + 1 file changed, 1 insertion(+) diff --git a/rfcs/0000-safe-transmute.md b/rfcs/0000-safe-transmute.md index 158c386..02a682f 100644 --- a/rfcs/0000-safe-transmute.md +++ b/rfcs/0000-safe-transmute.md @@ -1024,6 +1024,7 @@ pub unsafe trait PromiseTransmutableInto type Archetype; } ``` +This section gives concrete examples of how misusing these simple, unsafe definitions can lead to unsoundness, and then refines them until they are immune to misuse (and, consequently, *safe*). ### Part 1: Towards Safety From 49465859c96fd4a2c303a0fd519b563958e08af6 Mon Sep 17 00:00:00 2001 From: Jack Wrenn Date: Wed, 5 Aug 2020 16:21:41 -0400 Subject: [PATCH 19/61] rust -> Rust; rustc -> `rustc` Co-authored-by: Zach Lute --- rfcs/0000-safe-transmute.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rfcs/0000-safe-transmute.md b/rfcs/0000-safe-transmute.md index 02a682f..71970cb 100644 --- a/rfcs/0000-safe-transmute.md +++ b/rfcs/0000-safe-transmute.md @@ -86,7 +86,7 @@ fn main() { ``` This pattern, Kelley notes, is common when interacting with operating system APIs. -This rust program compiles, but quietly introduces undefined behavior. `Foo` requires stricter in-memory alignment than `u8`. The equivalent code in Zig produces a compiler error for this reason and requires modification. +This Rust program compiles, but quietly introduces undefined behavior. `Foo` requires stricter in-memory alignment than `u8`. The equivalent code in Zig produces a compiler error for this reason and requires modification. And that's not all: Kelley misses that the very layout of `Foo` itself is undefined⁠—it must be annotated with `#[repr(C)]`! @@ -1764,7 +1764,7 @@ An exception to this rule is the [typic][crate-typic] crate, which utilizes comp The development approaches like [typic][crate-typic]'s could, perhaps, be eased by stabilizing [frunk](https://crates.io/crates/frunk)-like structural reflection, or (better yet) by stabilizing a compiler plugin API for registering "smart" traits like `TransmuteFrom`. However, we suspect that such features would be drastically harder to design and stabilize. -Regardless of approach, almost all [prior art][prior-art] attempts to reproduce knowledge *already* possessed by rustc during the compilation process (i.e., the layout qualities of a concrete type). Emulating the process of layout computation to any degree is an error-prone duplication of effort between rustc and the crate, in a domain where correctness is crucial. +Regardless of approach, almost all [prior art][prior-art] attempts to reproduce knowledge *already* possessed by `rustc` during the compilation process (i.e., the layout qualities of a concrete type). Emulating the process of layout computation to any degree is an error-prone duplication of effort between `rustc` and the crate, in a domain where correctness is crucial. Finally, community-led, crate-based approaches are, inescapably, unauthoritative. These approaches are incapable of fulfilling our motivating goal of providing a *standard* mechanism for programmers to statically ensure that a transmutation is safe, sound, or stable. From 528df3ae454c757879621604b4e6e1326d032f10 Mon Sep 17 00:00:00 2001 From: Jack Wrenn Date: Mon, 10 Aug 2020 14:53:36 -0400 Subject: [PATCH 20/61] fix typo in slice casting extension --- rfcs/0000-safe-transmute.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rfcs/0000-safe-transmute.md b/rfcs/0000-safe-transmute.md index 71970cb..bc71688 100644 --- a/rfcs/0000-safe-transmute.md +++ b/rfcs/0000-safe-transmute.md @@ -2123,7 +2123,7 @@ impl<'t, T> &'t [T] { Neglect: SafeTransmuteOptions, &'t [T; size_of::()]: TransmuteInto<&'u [U; size_of::()], Neglect> { - unsafe { self.unsafe_cast() } + unsafe { self.unsafe_transmute_into() } } /// Unsafely convert a `&[T]` into `&[U]`. From 216232eaf2d922a5c74aeac161701d61b62d7efe Mon Sep 17 00:00:00 2001 From: Jack Wrenn Date: Tue, 11 Aug 2020 16:49:16 -0400 Subject: [PATCH 21/61] fix dead links --- rfcs/0000-safe-transmute.md | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/rfcs/0000-safe-transmute.md b/rfcs/0000-safe-transmute.md index bc71688..69309c4 100644 --- a/rfcs/0000-safe-transmute.md +++ b/rfcs/0000-safe-transmute.md @@ -187,7 +187,7 @@ A safe transmutation is ***stable*** if the authors of the source type and desti ### 📖 When is a transmutation sound? -[sound transmutation]: #When-is-a-transmutation-sound? +[sound transmutation]: #-when-is-a-transmutation-sound A transmutation is ***sound*** if the mere act of transmuting a value from one type to another is not undefined behavior. #### Well-Defined Representation @@ -203,7 +203,7 @@ In contrast, most `struct` and `enum` types defined without an explicit `#[repr( To ensure that types you've define are soundly transmutable, you almost always (with very few exceptions) must mark them with the `#[repr(C)]` attribute. #### Requirements on Owned Values -[transmute-owned]: #transmuting-owned-values +[transmute-owned]: #requirements-on-owned-values Transmutations involving owned values must adhere to two rules to be sound. They must: * [preserve or broaden the bit validity][owned-validity], and @@ -278,7 +278,7 @@ let _ : [u8; 32] = [u8; 16]::default().transmute_into(); // Compile Error! ``` #### Requirements on References -[transmute-references]: #transmuting-references +[transmute-references]: #requirements-on-references The [restrictions above that apply to transmuting owned values][transmute-owned] also apply to transmuting references. However, references carry a few additional restrictions. @@ -479,7 +479,7 @@ let z : NonEmptySlice = x; If this example did not produce a compile error, the value of `z` would not be a safe instance of its type, `NonEmptySlice`, because `z.first()` would dereference a null pointer. ### 📖 When is a transmutation stable? -[stability]: #When-is-a-transmutation-stable +[stability]: #-when-is-a-transmutation-stable Since the soundness and safety of a transmutation is affected by the layouts of the source and destination types, changes to those types' layouts may cause code which previously compiled to produce errors. In other words, transmutation causes a type's layout to become part of that type's API for the purposes of SemVer stability. @@ -2024,7 +2024,6 @@ We acknowledge that it is unusual for a `derive` macro to not create an item of ## Extension: Byte Transmutation Traits [marker-traits]: #Extension-Byte-Transmutation-Traits -[future-possibility-safe-initialization]: #Safe-Initialization The marker traits from [zerocopy][crate-zerocopy] are expressible using `TransmuteFrom` and `TransmuteInto`. In principle, these traits could remain in crates. However, we believe their clarity and broad usefulness may justify their inclusion in `libcore`. From 70f2d9c32ef6f42920fa16f8c0c1f18623277892 Mon Sep 17 00:00:00 2001 From: Jack Wrenn Date: Tue, 11 Aug 2020 17:27:18 -0400 Subject: [PATCH 22/61] slice casting revamp For actually using slice casting to build other abstractions, it's useful to have a trait name to refer to in `where` bounds. --- rfcs/0000-safe-transmute.md | 109 +++++++++++++++++++++++++++++++----- 1 file changed, 96 insertions(+), 13 deletions(-) diff --git a/rfcs/0000-safe-transmute.md b/rfcs/0000-safe-transmute.md index 69309c4..962db7e 100644 --- a/rfcs/0000-safe-transmute.md +++ b/rfcs/0000-safe-transmute.md @@ -2114,31 +2114,114 @@ where ## Extension: Slice Casting Transmuting the contained type of a slice is a [common operation](https://internals.rust-lang.org/t/safe-trasnsmute-for-slices-e-g-u64-u32-particularly-simd-types/2871) in cryptography. Although this RFC does not propose the addition of a concrete method for slice casting, the mechanisms proposed in this RFC make possible sound and complete slice casting abstractions; e.g.: ```rust -impl<'t, T> &'t [T] { +pub mod cast { + use crate::transmute::{ + TransmuteFrom, + options::{SafeTransmuteOptions, UnsafeTransmuteOptions}, + }; + + use core::{ + mem::{size_of, size_of_val}, + slice + }; - /// Safely convert a `&[T]` into `&[U]`. - pub fn transmute_into<'u, U, Neglect>(&self) -> &'u [U] + pub trait CastInto where - Neglect: SafeTransmuteOptions, - &'t [T; size_of::()]: TransmuteInto<&'u [U; size_of::()], Neglect> + Neglect: UnsafeTransmuteOptions, + Dst: CastFrom, + { + fn cast_into(self) -> Dst + where + Self: Sized, + Dst: Sized, + Neglect: SafeTransmuteOptions + { + CastFrom::<_, Neglect>::cast_from(self) + } + + unsafe fn unsafe_cast_into(self) -> Dst + where + Self: Sized, + Dst: Sized, + Neglect: UnsafeTransmuteOptions + { + CastFrom::<_, Neglect>::unsafe_cast_from(self) + } + } + + impl CastInto for Src + where + Neglect: UnsafeTransmuteOptions, + Dst: CastFrom, + {} + + pub trait CastFrom + where + Neglect: UnsafeTransmuteOptions { - unsafe { self.unsafe_transmute_into() } + fn cast_from(src: Src) -> Self + where + Src: Sized, + Self: Sized, + Neglect: SafeTransmuteOptions; + + unsafe fn unsafe_cast_from(src: Src) -> Self + where + Src: Sized, + Self: Sized, + Neglect: UnsafeTransmuteOptions; } - /// Unsafely convert a `&[T]` into `&[U]`. - pub unsafe fn unsafe_transmute_into<'u, U, Neglect>(&self) -> &'u [U] + /// Convert `&[Src]` to `&[Dst]` + impl<'i, 'o, Src, Dst, Neglect> CastFrom<&'i [Src], Neglect> for &'o [Dst] where Neglect: UnsafeTransmuteOptions, - &'t [T; size_of::()]: TransmuteInto<&'u [U; size_of::()], Neglect> + &'o [Dst; size_of::()]: TransmuteFrom<&'i [Src; size_of::()], Neglect> { - let len = size_of_val(self).checked_div(size_of::()).unwrap_or(0); - unsafe { - slice::from_raw_parts(self.as_ptr() as *const U, len) + fn cast_from(src: &'i [Src]) -> &'o [Dst] + where + Neglect: SafeTransmuteOptions, + { + unsafe { CastFrom::<_,Neglect>::unsafe_cast_from(src) } + } + + unsafe fn unsafe_cast_from(src: &'i [Src]) -> &'o [Dst] + where + Neglect: UnsafeTransmuteOptions, + { + let len = size_of_val(src).checked_div(size_of::()).unwrap_or(0); + unsafe { + slice::from_raw_parts(src.as_ptr() as *const Dst, len) + } + } + } + + /// Convert `&mut [Src]` to `&mut [Dst]` + impl<'i, 'o, Src, Dst, Neglect> CastFrom<&'i mut [Src], Neglect> for &'o mut [Dst] + where + Neglect: UnsafeTransmuteOptions, + &'o mut [Dst; size_of::()]: TransmuteFrom<&'i mut [Src; size_of::()], Neglect> + { + fn cast_from(src: &'i mut [Src]) -> &'o mut [Dst] + where + Neglect: SafeTransmuteOptions, + { + unsafe { CastFrom::<_,Neglect>::unsafe_cast_from(src) } + } + + unsafe fn unsafe_cast_from(src: &'i mut [Src]) -> &'o mut [Dst] + where + Neglect: UnsafeTransmuteOptions, + { + let len = size_of_val(src).checked_div(size_of::()).unwrap_or(0); + unsafe { + slice::from_raw_parts_mut(src.as_ptr() as *mut Dst, len) + } } } } ``` -The `TransmuteInto` bound statically ensures that `T` and `U` have compatible alignments, even though `.transmute_into()` isn't called in the body of this function. +Note that `TransmuteFrom` is used as trait bound to ensure safety, but the `transmute_from()` method isn't actually called. ## Extension: `include_data!` [future-possibility-include_data]: #Extension-include_data From 5424df47a074e3dd13c75567ff6876e21b6632aa Mon Sep 17 00:00:00 2001 From: Jack Wrenn Date: Tue, 11 Aug 2020 18:32:33 -0400 Subject: [PATCH 23/61] packet parsing example --- rfcs/0000-safe-transmute.md | 56 ++++++++++++++++++++++++++++++++++++- 1 file changed, 55 insertions(+), 1 deletion(-) diff --git a/rfcs/0000-safe-transmute.md b/rfcs/0000-safe-transmute.md index 962db7e..498a792 100644 --- a/rfcs/0000-safe-transmute.md +++ b/rfcs/0000-safe-transmute.md @@ -1705,7 +1705,59 @@ fn main() { ``` ### Case Study: Abstractions for Packet Parsing -***TODO: josh*** +Using the core mechanisms of this RFC (along with the proposed [slice-casting extension][ext-slice-casting]) it is trivial to safely define useful zero-copy packet-parsing utilities, like those in the [`zerocopy`] and [`packet`] crates; e.g.: +``` +impl<'a> &'a [u8] +{ + /// Analogous to: + /// - https://fuchsia-docs.firebaseapp.com/rust/packet/trait.BufferView.html#method.peek_obj_front + /// - https://fuchsia-docs.firebaseapp.com/rust/packet/trait.BufferView.html#method.take_obj_front + fn take_front<'t, T>(&'a mut self) -> Option<&'a T> + where + Self: CastInto<&'a [T]>, + { + let idx = size_of::(); + let (parsable, remainder) = self.split_at(idx); + *self = remainder; + parsable.cast_into().first() + } + + /// Analogous to: + /// - https://fuchsia-docs.firebaseapp.com/rust/packet/trait.BufferView.html#method.peek_obj_back + /// - https://fuchsia-docs.firebaseapp.com/rust/packet/trait.BufferView.html#method.take_obj_back + fn take_back<'t, T>(&'a mut self) -> Option<&'a T> + where + Self: CastInto<&'a [T]>, + { + let idx = self.len().saturating_sub(size_of::()); + let (remainder, parsable) = self.split_at(idx); + *self = remainder; + parsable.cast_into().first() + } +} +``` +...which then can be used like so: +```rust +pub struct UdpPacket<'a> { + hdr: &'a UdpHeader, + body: &'a [u8], +} + +#[derive(PromiseTransmutableFrom, PromiseTransmutableInto)] +#[repr(C)] +struct UdpHeader { + src_port: [u8; 2], + dst_port: [u8; 2], + length: [u8; 2], + checksum: [u8; 2], +} + +impl<'a> UdpPacket<'a> { + pub fn parse(mut bytes: &'a [u8]) -> Option { + Some(UdpPacket { hdr: bytes.read()?, body: bytes }) + } +} +``` ### Case Study: Abstractions for Pointer Bitpacking [case-study-alignment]: #case-study-abstractions-for-pointer-bitpacking @@ -2112,6 +2164,8 @@ where ``` ## Extension: Slice Casting +[ext-slice-casting]: #extension-slice-casting + Transmuting the contained type of a slice is a [common operation](https://internals.rust-lang.org/t/safe-trasnsmute-for-slices-e-g-u64-u32-particularly-simd-types/2871) in cryptography. Although this RFC does not propose the addition of a concrete method for slice casting, the mechanisms proposed in this RFC make possible sound and complete slice casting abstractions; e.g.: ```rust pub mod cast { From cc016094ea9d2432ae4833da066b4d53594c6cab Mon Sep 17 00:00:00 2001 From: Jack Wrenn Date: Tue, 11 Aug 2020 20:13:26 -0400 Subject: [PATCH 24/61] packet parsing tweaks --- rfcs/0000-safe-transmute.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/rfcs/0000-safe-transmute.md b/rfcs/0000-safe-transmute.md index 498a792..0d43bfe 100644 --- a/rfcs/0000-safe-transmute.md +++ b/rfcs/0000-safe-transmute.md @@ -1706,9 +1706,10 @@ fn main() { ### Case Study: Abstractions for Packet Parsing Using the core mechanisms of this RFC (along with the proposed [slice-casting extension][ext-slice-casting]) it is trivial to safely define useful zero-copy packet-parsing utilities, like those in the [`zerocopy`] and [`packet`] crates; e.g.: -``` +```rust impl<'a> &'a [u8] { + /// Read `&T` off the front of `self`, and shrink the underlying slice. /// Analogous to: /// - https://fuchsia-docs.firebaseapp.com/rust/packet/trait.BufferView.html#method.peek_obj_front /// - https://fuchsia-docs.firebaseapp.com/rust/packet/trait.BufferView.html#method.take_obj_front @@ -1722,6 +1723,7 @@ impl<'a> &'a [u8] parsable.cast_into().first() } + /// Read `&T` off the back of `self`, and shrink the underlying slice. /// Analogous to: /// - https://fuchsia-docs.firebaseapp.com/rust/packet/trait.BufferView.html#method.peek_obj_back /// - https://fuchsia-docs.firebaseapp.com/rust/packet/trait.BufferView.html#method.take_obj_back @@ -1754,7 +1756,7 @@ struct UdpHeader { impl<'a> UdpPacket<'a> { pub fn parse(mut bytes: &'a [u8]) -> Option { - Some(UdpPacket { hdr: bytes.read()?, body: bytes }) + Some(UdpPacket { hdr: {bytes.read()?}, body: bytes }) } } ``` From 4fe45b6d309564dab3f2914d2271f7c2e9eb96ee Mon Sep 17 00:00:00 2001 From: Jack Wrenn Date: Fri, 14 Aug 2020 15:22:10 -0400 Subject: [PATCH 25/61] simplify minimal implementation --- rfcs/0000-safe-transmute.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rfcs/0000-safe-transmute.md b/rfcs/0000-safe-transmute.md index 0d43bfe..311a85f 100644 --- a/rfcs/0000-safe-transmute.md +++ b/rfcs/0000-safe-transmute.md @@ -860,7 +860,7 @@ This [minimal implementation][minimal-impl] is sufficient for convincing the com ### Listing for Initial, Minimal Implementation [minimal-impl]: #Listing-for-Initial-Minimal-Implementation -**[This module listing provides the minimal-viable implementation of this RFC (excepting the automatic derives).](https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=25a83d8d8e33e9f631a64b53e3c39844)** This listing is also the **canonical specification** of this RFC's public API surface. +**[This module listing provides the minimal-viable implementation of this RFC (excepting the automatic derives).](https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=bca1ad92882760e94cd4a79bccf4de30)** This listing is also the **canonical specification** of this RFC's public API surface. ### Towards an Initial, Smart Implementation From b38d1df05e2fc8b827e344b556c92d3309f8f0da Mon Sep 17 00:00:00 2001 From: Jack Wrenn Date: Fri, 14 Aug 2020 15:24:23 -0400 Subject: [PATCH 26/61] fix broken link --- rfcs/0000-safe-transmute.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rfcs/0000-safe-transmute.md b/rfcs/0000-safe-transmute.md index 311a85f..f7a4995 100644 --- a/rfcs/0000-safe-transmute.md +++ b/rfcs/0000-safe-transmute.md @@ -228,7 +228,7 @@ let _ : u8 = Bool::True.transmute_into(); let _ : u8 = Bool::False.transmute_into(); ``` -(Note: #[derive(PromiseTransmutableFrom, PromiseTransmutableInto)] annotation connotes that all aspects of Bool's layout are part of its library stability guarantee.) +(Note: #[derive(PromiseTransmutableFrom, PromiseTransmutableInto)] annotation connotes that all aspects of Bool's layout are part of its library stability guarantee.) ...because all possible instances of `Bool` are also valid instances of [`u8`]. However, transmuting a [`u8`] into a `Bool` is forbidden: From ea047446d650b6aa28eef24e84e3b90a04ea7ffb Mon Sep 17 00:00:00 2001 From: Jack Wrenn Date: Fri, 14 Aug 2020 17:12:46 -0400 Subject: [PATCH 27/61] nix needless `for<'a>` --- rfcs/0000-safe-transmute.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/rfcs/0000-safe-transmute.md b/rfcs/0000-safe-transmute.md index f7a4995..8ecb43d 100644 --- a/rfcs/0000-safe-transmute.md +++ b/rfcs/0000-safe-transmute.md @@ -1464,7 +1464,7 @@ Rather than change the `where` bound of the *definitions* of `PromiseTransmutabl impl<'a, T> PromiseTransmutableFrom for &'a T where T: PromiseTransmutableFrom, - for<'a> &'a Self::Archetype: TransmuteInto<&'a Self, NeglectStability> + &'a Self::Archetype: TransmuteInto<&'a Self, NeglectStability> { type Archetype = &'a T::Archetype; } @@ -1472,7 +1472,7 @@ where impl<'a, T> PromiseTransmutableInto for &'a T where T: PromiseTransmutableInto, - for<'a> &'a Self::Archetype: TransmuteFrom<&'a Self, NeglectStability> + &'a Self::Archetype: TransmuteFrom<&'a Self, NeglectStability> { type Archetype = &'a T::Archetype; } @@ -1500,7 +1500,7 @@ where impl<'a, T> PromiseTransmutableFrom for &'a mut T where T: PromiseTransmutableFrom, - for<'a> &'a mut Self::Archetype: TransmuteInto<&'a mut Self, NeglectStability> + &'a mut Self::Archetype: TransmuteInto<&'a mut Self, NeglectStability> { type Archetype = &'a T::Archetype; } @@ -1508,7 +1508,7 @@ where impl<'a, T> PromiseTransmutableInto for &'a mut T where T: PromiseTransmutableInto, - for<'a> &'a mut Self::Archetype: TransmuteFrom<&'a mut Self, NeglectStability> + &'a mut Self::Archetype: TransmuteFrom<&'a mut Self, NeglectStability> { type Archetype = &'a T::Archetype; } From 0fb4863430e0d4a60d7b3ab322007afb7f74f6ea Mon Sep 17 00:00:00 2001 From: Jack Wrenn Date: Fri, 14 Aug 2020 18:32:04 -0400 Subject: [PATCH 28/61] slice casting tweaks Pedantic changes that make the slice-casting extension closer to something shippable. --- rfcs/0000-safe-transmute.md | 136 +++++++++++++++++++++++------------- 1 file changed, 86 insertions(+), 50 deletions(-) diff --git a/rfcs/0000-safe-transmute.md b/rfcs/0000-safe-transmute.md index 8ecb43d..7b8048d 100644 --- a/rfcs/0000-safe-transmute.md +++ b/rfcs/0000-safe-transmute.md @@ -2168,29 +2168,26 @@ where ## Extension: Slice Casting [ext-slice-casting]: #extension-slice-casting -Transmuting the contained type of a slice is a [common operation](https://internals.rust-lang.org/t/safe-trasnsmute-for-slices-e-g-u64-u32-particularly-simd-types/2871) in cryptography. Although this RFC does not propose the addition of a concrete method for slice casting, the mechanisms proposed in this RFC make possible sound and complete slice casting abstractions; e.g.: +Transmuting the contained type of a slice is a [common operation](https://internals.rust-lang.org/t/safe-trasnsmute-for-slices-e-g-u64-u32-particularly-simd-types/2871) in cryptography and fast packet parsing. Although this RFC does not propose the addition of a concrete method for slice casting, the mechanisms proposed in this RFC make possible sound and complete slice casting abstractions; e.g.: ```rust pub mod cast { - use crate::transmute::{ - TransmuteFrom, - options::{SafeTransmuteOptions, UnsafeTransmuteOptions}, - }; - use core::{ - mem::{size_of, size_of_val}, - slice - }; + #[marker] pub trait SafeCastOptions: UnsafeCastOptions {} + #[marker] pub trait UnsafeCastOptions {} + + impl SafeCastOptions for () {} + impl UnsafeCastOptions for () {} pub trait CastInto where - Neglect: UnsafeTransmuteOptions, Dst: CastFrom, + Neglect: UnsafeCastOptions, { fn cast_into(self) -> Dst where Self: Sized, Dst: Sized, - Neglect: SafeTransmuteOptions + Neglect: SafeCastOptions, { CastFrom::<_, Neglect>::cast_from(self) } @@ -2199,81 +2196,120 @@ pub mod cast { where Self: Sized, Dst: Sized, - Neglect: UnsafeTransmuteOptions + Neglect: UnsafeCastOptions, { CastFrom::<_, Neglect>::unsafe_cast_from(self) } } - + impl CastInto for Src where - Neglect: UnsafeTransmuteOptions, Dst: CastFrom, + Neglect: UnsafeCastOptions, {} pub trait CastFrom where - Neglect: UnsafeTransmuteOptions + Neglect: UnsafeCastOptions, { fn cast_from(src: Src) -> Self where Src: Sized, Self: Sized, - Neglect: SafeTransmuteOptions; + Neglect: SafeCastOptions + { + unsafe { CastFrom::<_,Neglect>::unsafe_cast_from(src) } + } unsafe fn unsafe_cast_from(src: Src) -> Self where Src: Sized, Self: Sized, - Neglect: UnsafeTransmuteOptions; + Neglect: UnsafeCastOptions; } - /// Convert `&[Src]` to `&[Dst]` - impl<'i, 'o, Src, Dst, Neglect> CastFrom<&'i [Src], Neglect> for &'o [Dst] - where - Neglect: UnsafeTransmuteOptions, - &'o [Dst; size_of::()]: TransmuteFrom<&'i [Src; size_of::()], Neglect> - { - fn cast_from(src: &'i [Src]) -> &'o [Dst] - where - Neglect: SafeTransmuteOptions, - { - unsafe { CastFrom::<_,Neglect>::unsafe_cast_from(src) } - } - - unsafe fn unsafe_cast_from(src: &'i [Src]) -> &'o [Dst] + /// Options for casting the contents of slices. + pub mod slice { + use super::{ + CastFrom, + SafeCastOptions, + UnsafeCastOptions, + super::transmute::{ + TransmuteFrom, + options::{SafeTransmuteOptions, UnsafeTransmuteOptions}, + }, + }; + + use core::{ + mem::{size_of, size_of_val}, + slice + }; + + /// All `SafeTransmuteOptions` are `SafeSliceCastOptions`. + pub trait SafeSliceCastOptions + : SafeCastOptions + + SafeTransmuteOptions + + UnsafeSliceCastOptions + {} + + /// All `UnsafeTransmuteOptions` are `UnsafeSliceCastOptions`. + pub trait UnsafeSliceCastOptions + : UnsafeCastOptions + + UnsafeTransmuteOptions + {} + + impl SafeCastOptions for Neglect {} + impl SafeSliceCastOptions for Neglect {} + impl UnsafeCastOptions for Neglect {} + impl UnsafeSliceCastOptions for Neglect {} + + /// Convert `&[Src]` to `&[Dst]` + impl<'i, 'o, Src, Dst, Neglect> CastFrom<&'i [Src], Neglect> for &'o [Dst] where - Neglect: UnsafeTransmuteOptions, + Neglect: UnsafeSliceCastOptions, + &'o [Dst; size_of::()]: TransmuteFrom<&'i [Src; size_of::()], Neglect> { - let len = size_of_val(src).checked_div(size_of::()).unwrap_or(0); - unsafe { - slice::from_raw_parts(src.as_ptr() as *const Dst, len) + unsafe fn unsafe_cast_from(src: &'i [Src]) -> &'o [Dst] + where + Neglect: UnsafeSliceCastOptions, + { + let len = size_of_val(src).checked_div(size_of::()).unwrap_or(0); + unsafe { slice::from_raw_parts(src.as_ptr() as *const Dst, len) } } } - } - /// Convert `&mut [Src]` to `&mut [Dst]` - impl<'i, 'o, Src, Dst, Neglect> CastFrom<&'i mut [Src], Neglect> for &'o mut [Dst] - where - Neglect: UnsafeTransmuteOptions, - &'o mut [Dst; size_of::()]: TransmuteFrom<&'i mut [Src; size_of::()], Neglect> - { - fn cast_from(src: &'i mut [Src]) -> &'o mut [Dst] + /// Convert `&mut [Src]` to `&mut [Dst]` + impl<'i, 'o, Src, Dst, Neglect> CastFrom<&'i mut [Src], Neglect> for &'o mut [Dst] where - Neglect: SafeTransmuteOptions, + Neglect: UnsafeSliceCastOptions, + &'o mut [Dst; size_of::()]: TransmuteFrom<&'i mut [Src; size_of::()], Neglect> { - unsafe { CastFrom::<_,Neglect>::unsafe_cast_from(src) } + unsafe fn unsafe_cast_from(src: &'i mut [Src]) -> &'o mut [Dst] + where + Neglect: UnsafeSliceCastOptions, + { + let len = size_of_val(src).checked_div(size_of::()).unwrap_or(0); + unsafe { slice::from_raw_parts_mut(src.as_ptr() as *mut Dst, len) } + } } - unsafe fn unsafe_cast_from(src: &'i mut [Src]) -> &'o mut [Dst] + /// Convert `&mut [Src]` to `&[Dst]` + impl<'i, 'o, Src, Dst, Neglect> CastFrom<&'i mut [Src], Neglect> for &'o [Dst] where - Neglect: UnsafeTransmuteOptions, + Neglect: UnsafeSliceCastOptions, + &'o mut [Dst; size_of::()]: TransmuteFrom<&'i [Src; size_of::()], Neglect> { - let len = size_of_val(src).checked_div(size_of::()).unwrap_or(0); - unsafe { - slice::from_raw_parts_mut(src.as_ptr() as *mut Dst, len) + unsafe fn unsafe_cast_from(src: &'i mut [Src]) -> &'o [Dst] + where + Neglect: UnsafeSliceCastOptions, + { + let len = size_of_val(src).checked_div(size_of::()).unwrap_or(0); + unsafe { + slice::from_raw_parts(src.as_ptr() as *const Dst, len) + } } } + } } ``` From b7fef2d10f067c3813851408b17e16e783159f5c Mon Sep 17 00:00:00 2001 From: Jack Wrenn Date: Fri, 14 Aug 2020 20:02:36 -0400 Subject: [PATCH 29/61] packet parsing contributions from @joshlf --- rfcs/0000-safe-transmute.md | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/rfcs/0000-safe-transmute.md b/rfcs/0000-safe-transmute.md index 7b8048d..b15a618 100644 --- a/rfcs/0000-safe-transmute.md +++ b/rfcs/0000-safe-transmute.md @@ -97,8 +97,25 @@ This situation is frustrating. Like `zig`, `rustc` *knows* the layout of `Foo`, This RFC proposes mechanisms that use this information to enable safer transmutation. We revisit Kelley's motivating example with these proposed mechanisms near the end of this RFC, [*here*][case-study-safer-unsafe]. -## Efficient Network Parsing -***TODO: Josh?*** +## Efficient Parsing +In languages like C or C++, a common technique for parsing structured data such as network packets or file formats is to declare a type whose fields correspond to the fields of the data being parsed. An input byte array is then parsed by casting a pointer to the array (e.g., `a char *`) to a pointer to the typed representation (e.g., `udp_header_t *`). It is the programmer's responsibility to avoid the many pitfalls that could introduce undefined behavior when using this pattern. + +In Rust, we might employ the same technique: +```rust +struct UdpHeader { + src_port: u16, + dst_port: u16, + length: u16, + checksum: [u8; 2], +} +``` +However, as it stands today, it is still the programmer's responsibility to ensure that a conversion such as `&[u8]` to `&UdpHeader` is sound. In the above example, we can see a number of pitfalls that a programmer must know to avoid: + - The layout of `UdpHeader` is undefined without `#[repr(C)]` + - The conversion of `&[u8]` to `&UdpHeader` is invalid in the general case because the length of the byte slice must be sufficient + - The conversion of `&[u8]` to `&UdpHeader` is invalid in the general case because the alignment of the byte slice must be sufficient (the alignment issue can be avoided by replacing the `u16` fields with `[u8; 2]`) + +This RFC proposes mechanisms that would allow to perform this reference conversion safely, and would fail compilation if any of the above pitfalls were encountered. We revisit this example [here][case-study-parsing]. + ## Safer Initialization Primitives The initialization primitives `mem::zeroed` and `mem::uninitialized` are `unsafe` because they may be used to initialize types for which zeroed or uninitialized memory are *not* valid bit-patterns. The `mem::zeroed` function recently gained a dynamically-enforced validity check, but this safety measure isn't wholly satisfactory: the validity properties of `T` are statically known, but the check is dynamic. @@ -130,7 +147,7 @@ This RFC proposes mechanisms that would eliminate `unsafe` in this example. We r ## SIMD -***TODO:*** https://internals.rust-lang.org/t/pre-rfc-frombits-intobits/7071 +***TODO:*** This might be helpful: https://internals.rust-lang.org/t/pre-rfc-frombits-intobits/7071 ## `Atomic` @@ -1704,7 +1721,8 @@ fn main() { } ``` -### Case Study: Abstractions for Packet Parsing +### Case Study: Abstractions for Fast Parsing +[case-study-parsing]: #case-study-abstractions-for-fast-parsing Using the core mechanisms of this RFC (along with the proposed [slice-casting extension][ext-slice-casting]) it is trivial to safely define useful zero-copy packet-parsing utilities, like those in the [`zerocopy`] and [`packet`] crates; e.g.: ```rust impl<'a> &'a [u8] @@ -1738,7 +1756,7 @@ impl<'a> &'a [u8] } } ``` -...which then can be used like so: +To parse a `UdpPacket` given a byte slice, we split the slice into a slice containing first 8 bytes, and the remainder. We then cast the first slice into a reference to a `UdpHeader`. A `UdpPacket`, then, consists of the reference to the `UdpHeader`, and the remainder slice. Concretely: ```rust pub struct UdpPacket<'a> { hdr: &'a UdpHeader, @@ -1760,6 +1778,7 @@ impl<'a> UdpPacket<'a> { } } ``` +While this example is simple, the technique can be expanded to arbitrarily complex structures. ### Case Study: Abstractions for Pointer Bitpacking [case-study-alignment]: #case-study-abstractions-for-pointer-bitpacking From 41fa3391b158b77316bda66bd9356b42bc3b1061 Mon Sep 17 00:00:00 2001 From: Jack Wrenn Date: Sat, 15 Aug 2020 15:14:18 -0400 Subject: [PATCH 30/61] inline minimal implementation (with minor tweaks) --- rfcs/0000-safe-transmute.md | 372 +++++++++++++++++++++++++++++++++++- 1 file changed, 371 insertions(+), 1 deletion(-) diff --git a/rfcs/0000-safe-transmute.md b/rfcs/0000-safe-transmute.md index b15a618..071a0d2 100644 --- a/rfcs/0000-safe-transmute.md +++ b/rfcs/0000-safe-transmute.md @@ -877,7 +877,377 @@ This [minimal implementation][minimal-impl] is sufficient for convincing the com ### Listing for Initial, Minimal Implementation [minimal-impl]: #Listing-for-Initial-Minimal-Implementation -**[This module listing provides the minimal-viable implementation of this RFC (excepting the automatic derives).](https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=bca1ad92882760e94cd4a79bccf4de30)** This listing is also the **canonical specification** of this RFC's public API surface. +This listing is both a minimal-viable implementation of this RFC (excepting the automatic derives) and the **canonical specification** of this RFC's API surface ([playground](https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=dee36fb5231c9e00b03b424e204cd1a9)): +```rust +#![feature(const_generics)] // for stability declarations on `[T; N]` +#![feature(decl_macro)] // for stub implementations of derives +#![feature(never_type)] // for stability declarations on `!` +#![allow(unused_unsafe, incomplete_features)] + +/// Transmutation conversions. +// suggested location: `core::convert` +pub mod transmute { + + use {options::*, stability::*}; + + /// Reinterprets the bits of a value of one type as another type, safely. + #[inline(always)] + pub /*const*/ fn safe_transmute(src: Src) -> Dst + where + Src: TransmuteInto, + Neglect: SafeTransmuteOptions + { + src.transmute_into() + } + + /// Reinterprets the bits of a value of one type as another type, potentially unsafely. + #[inline(always)] + pub /*const*/ unsafe fn unsafe_transmute(src: Src) -> Dst + where + Src: TransmuteInto, + Neglect: UnsafeTransmuteOptions + { + unsafe { src.unsafe_transmute_into() } + } + + /// `Self: TransmuteInto + where + Neglect: UnsafeTransmuteOptions, + { + /// Reinterpret the bits of a value of one type as another type, safely. + fn transmute_into(self) -> Dst + where + Self: Sized, + Dst: Sized, + Neglect: SafeTransmuteOptions; + + /// Reinterpret the bits of a value of one type as another type, potentially unsafely. + /// + /// The onus is on you to ensure that calling this method is safe. + unsafe fn unsafe_transmute_into(self) -> Dst + where + Self: Sized, + Dst: Sized, + Neglect: UnsafeTransmuteOptions; + } + + unsafe impl TransmuteInto for Src + where + Src: ?Sized, + Dst: ?Sized + TransmuteFrom, + Neglect: UnsafeTransmuteOptions, + { + #[inline(always)] + fn transmute_into(self) -> Dst + where + Self: Sized, + Dst: Sized, + Neglect: SafeTransmuteOptions, + { + Dst::transmute_from(self) + } + + #[inline(always)] + unsafe fn unsafe_transmute_into(self) -> Dst + where + Self: Sized, + Dst: Sized, + Neglect: UnsafeTransmuteOptions, + { + unsafe { Dst::unsafe_transmute_from(self) } + } + } + + /// `Self: TransmuteInto + where + Neglect: UnsafeTransmuteOptions, + { + /// Reinterpret the bits of a value of one type as another type, safely. + #[inline(always)] + fn transmute_from(src: Src) -> Self + where + Src: Sized, + Self: Sized, + Neglect: SafeTransmuteOptions, + { + use core::{mem, ptr}; + unsafe { + let dst = ptr::read(&src as *const Src as *const Self); + mem::forget(src); + dst + } + } + + /// Reinterpret the bits of a value of one type as another type, potentially unsafely. + /// + /// The onus is on you to ensure that calling this function is safe. + #[inline(always)] + unsafe fn unsafe_transmute_from(src: Src) -> Self + where + Src: Sized, + Self: Sized, + Neglect: UnsafeTransmuteOptions, + { + use core::{mem, ptr}; + unsafe { + let dst = ptr::read_unaligned(&src as *const Src as *const Self); + mem::forget(src); + dst + } + } + } + + /// A type is always transmutable from itself. + unsafe impl TransmuteFrom for T {} + + /// A type is *stably* transmutable if... + unsafe impl TransmuteFrom for Dst + where + Src: PromiseTransmutableInto, + Dst: PromiseTransmutableFrom, + ::Archetype: + TransmuteFrom< + ::Archetype, + NeglectStability + > + {} + + /// Traits for declaring the SemVer stability of types. + pub mod stability { + + use super::{TransmuteFrom, TransmuteInto, options::NeglectStability}; + + /// Declare that transmuting `Self` into `Archetype` is SemVer-stable. + /* #[lang = "promise_transmutable_into"] */ + pub trait PromiseTransmutableInto + { + /// The `Archetype` must be safely transmutable from `Self`. + type Archetype + : TransmuteFrom + + PromiseTransmutableInto; + } + + /// Declare that transmuting `Self` from `Archetype` is SemVer-stable. + /* #[lang = "promise_transmutable_from"] */ + pub trait PromiseTransmutableFrom + { + /// The `Archetype` must be safely transmutable into `Self`. + type Archetype + : TransmuteInto + + PromiseTransmutableFrom; + } + + + /// Derive macro generating an impl of the trait `PromiseTransmutableInto`. + //#[rustc_builtin_macro] + pub macro PromiseTransmutableInto($item:item) { + /* compiler built-in */ + } + + /// Derive macro generating an impl of the trait `PromiseTransmutableFrom`. + //#[rustc_builtin_macro] + pub macro PromiseTransmutableFrom($item:item) { + /* compiler built-in */ + } + + + impl PromiseTransmutableInto for ! {type Archetype = Self;} + impl PromiseTransmutableFrom for ! {type Archetype = Self;} + + impl PromiseTransmutableInto for () {type Archetype = Self;} + impl PromiseTransmutableFrom for () {type Archetype = Self;} + + impl PromiseTransmutableInto for f32 {type Archetype = Self;} + impl PromiseTransmutableFrom for f32 {type Archetype = Self;} + impl PromiseTransmutableInto for f64 {type Archetype = Self;} + impl PromiseTransmutableFrom for f64 {type Archetype = Self;} + + impl PromiseTransmutableInto for i8 {type Archetype = Self;} + impl PromiseTransmutableFrom for i8 {type Archetype = Self;} + impl PromiseTransmutableInto for i16 {type Archetype = Self;} + impl PromiseTransmutableFrom for i16 {type Archetype = Self;} + impl PromiseTransmutableInto for i32 {type Archetype = Self;} + impl PromiseTransmutableFrom for i32 {type Archetype = Self;} + impl PromiseTransmutableInto for i64 {type Archetype = Self;} + impl PromiseTransmutableFrom for i64 {type Archetype = Self;} + impl PromiseTransmutableInto for i128 {type Archetype = Self;} + impl PromiseTransmutableFrom for i128 {type Archetype = Self;} + impl PromiseTransmutableInto for isize {type Archetype = Self;} + impl PromiseTransmutableFrom for isize {type Archetype = Self;} + + impl PromiseTransmutableInto for u8 {type Archetype = Self;} + impl PromiseTransmutableFrom for u8 {type Archetype = Self;} + impl PromiseTransmutableInto for u16 {type Archetype = Self;} + impl PromiseTransmutableFrom for u16 {type Archetype = Self;} + impl PromiseTransmutableInto for u32 {type Archetype = Self;} + impl PromiseTransmutableFrom for u32 {type Archetype = Self;} + impl PromiseTransmutableInto for u64 {type Archetype = Self;} + impl PromiseTransmutableFrom for u64 {type Archetype = Self;} + impl PromiseTransmutableInto for u128 {type Archetype = Self;} + impl PromiseTransmutableFrom for u128 {type Archetype = Self;} + impl PromiseTransmutableInto for usize {type Archetype = Self;} + impl PromiseTransmutableFrom for usize {type Archetype = Self;} + + use core::marker::PhantomData; + impl PromiseTransmutableInto for PhantomData { type Archetype = Self; } + impl PromiseTransmutableFrom for PhantomData { type Archetype = Self; } + + + impl PromiseTransmutableInto for [T; N] + where + T: PromiseTransmutableInto, + [T::Archetype; N] + : TransmuteFrom + + PromiseTransmutableInto, + { + type Archetype = [T::Archetype; N]; + } + + impl PromiseTransmutableFrom for [T; N] + where + T: PromiseTransmutableFrom, + [T::Archetype; N] + : TransmuteInto + + PromiseTransmutableFrom, + { + type Archetype = [T::Archetype; N]; + } + + + impl PromiseTransmutableInto for *const T + where + T: PromiseTransmutableInto, + *const T::Archetype + : TransmuteFrom + + PromiseTransmutableInto, + { + type Archetype = *const T::Archetype; + } + + impl PromiseTransmutableFrom for *const T + where + T: PromiseTransmutableFrom, + *const T::Archetype + : TransmuteInto + + PromiseTransmutableFrom, + { + type Archetype = *const T::Archetype; + } + + + impl PromiseTransmutableInto for *mut T + where + T: PromiseTransmutableInto, + *mut T::Archetype + : TransmuteFrom + + PromiseTransmutableInto, + { + type Archetype = *mut T::Archetype; + } + + impl PromiseTransmutableFrom for *mut T + where + T: PromiseTransmutableFrom, + *mut T::Archetype + : TransmuteInto + + PromiseTransmutableFrom, + { + type Archetype = *mut T::Archetype; + } + + + impl<'a, T: ?Sized> PromiseTransmutableInto for &'a T + where + T: PromiseTransmutableInto, + &'a T::Archetype + : TransmuteFrom<&'a T, NeglectStability> + + PromiseTransmutableInto, + { + type Archetype = &'a T::Archetype; + } + + impl<'a, T: ?Sized> PromiseTransmutableFrom for &'a T + where + T: PromiseTransmutableFrom, + &'a T::Archetype + : TransmuteInto<&'a T, NeglectStability> + + PromiseTransmutableFrom, + { + type Archetype = &'a T::Archetype; + } + + impl<'a, T: ?Sized> PromiseTransmutableInto for &'a mut T + where + T: PromiseTransmutableInto, + &'a mut T::Archetype + : TransmuteFrom<&'a mut T, NeglectStability> + + PromiseTransmutableInto, + { + type Archetype = &'a mut T::Archetype; + } + + impl<'a, T: ?Sized> PromiseTransmutableFrom for &'a mut T + where + T: PromiseTransmutableFrom, + &'a mut T::Archetype + : TransmuteInto<&'a mut T, NeglectStability> + + PromiseTransmutableFrom, + { + type Archetype = &'a mut T::Archetype; + } + } + + /// Static checks that may be neglected when determining if a type is `TransmuteFrom` some other type. + pub mod options { + + /// Options that may be used with safe transmutations. + pub trait SafeTransmuteOptions: UnsafeTransmuteOptions + {} + + /// Options that may be used with unsafe transmutations. + pub trait UnsafeTransmuteOptions: private::Sealed + {} + + impl SafeTransmuteOptions for () {} + impl UnsafeTransmuteOptions for () {} + + /// Neglect the stability check of `TransmuteFrom`. + /* #[lang = "neglect_stability"] */ + pub struct NeglectStability; + + // Uncomment this if/when constructibility is fully implemented: + // impl SafeTransmuteOptions for NeglectStability {} + impl UnsafeTransmuteOptions for NeglectStability {} + + /* + pub struct NeglectAlignment; + impl UnsafeTransmuteOptions for NeglectAlignment {} + */ + + /* FILL: Implementations for tuple combinations of options */ + + // prevent third-party implementations of `UnsafeTransmuteOptions` + mod private { + use super::*; + + pub trait Sealed {} + + impl Sealed for () {} + impl Sealed for NeglectStability {} + /* impl Sealed for NeglectAlignment {} */ + + /* FILL: Implementations for tuple combinations of options */ + } + } +} +``` ### Towards an Initial, Smart Implementation From 3d3a3e33febd7ac9bd4a788e47ab1bfe23d514d2 Mon Sep 17 00:00:00 2001 From: Jack Wrenn Date: Sat, 15 Aug 2020 15:16:14 -0400 Subject: [PATCH 31/61] link to packet_formats crate in parsing example --- rfcs/0000-safe-transmute.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rfcs/0000-safe-transmute.md b/rfcs/0000-safe-transmute.md index 071a0d2..bf8ae23 100644 --- a/rfcs/0000-safe-transmute.md +++ b/rfcs/0000-safe-transmute.md @@ -2148,7 +2148,7 @@ impl<'a> UdpPacket<'a> { } } ``` -While this example is simple, the technique can be expanded to arbitrarily complex structures. +While this example is simple, the technique can be [expanded](https://fuchsia-docs.firebaseapp.com/rust/packet_formats/) to arbitrarily complex structures. ### Case Study: Abstractions for Pointer Bitpacking [case-study-alignment]: #case-study-abstractions-for-pointer-bitpacking From b25348c526d2135430ff8d1839f6ee823d132e24 Mon Sep 17 00:00:00 2001 From: Jack Wrenn Date: Tue, 25 Aug 2020 14:22:41 -0400 Subject: [PATCH 32/61] extension: trait gadgets for querying alignment & size --- rfcs/0000-safe-transmute.md | 77 +++++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) diff --git a/rfcs/0000-safe-transmute.md b/rfcs/0000-safe-transmute.md index bf8ae23..a0ca4ca 100644 --- a/rfcs/0000-safe-transmute.md +++ b/rfcs/0000-safe-transmute.md @@ -2465,6 +2465,83 @@ The vast majority of users will *only* confront the stability declaration traits We acknowledge that it is unusual for a `derive` macro to not create an item of the same name, but this weirdness is outweighed by the weirdness of the alternative: providing a trait for which there is almost no good use. +## Extension: Layout Property Traits +Given `TransmuteFrom` and `TransmuteInto`, we can construct bounds that check certain properties of a type by checking if its convertible to another, contrived type. These gadgets are useful (see [`Vec` casting](#todo) for an example), and their usefulness could justify adding them to `libcore`—perhaps in the `mem` module. + +### Querying Alignment +The type `[T; 0]` shares the alignment requirements of `T`, but no other layout properties. A type `&[T; 0]` will only be transmutable to `&[U; 0]`, if the minimum alignment of `T` is greater than that of `U`. We exploit this to define a trait that is implemented for `Self` if its alignment is less-than-or-equal to that of `Rhs`: +```rust +/// Implemented if `align_of::() <= align_of::()` +pub trait AlignLtEq +where + Neglect: UnsafeTransmuteOptions, +{} + +impl AlignLtEq for Lhs +where + Neglect: UnsafeTransmuteOptions, + for<'a> &'a [Lhs; 0]: TransmuteFrom<&'a [Rhs; 0], Neglect> +{} +``` +Furthermore, if the alignment of `Self` is less-than-or-equal to `Rhs`, and the alignment of `Rhs` is less-than-or-equal to `Self`, then the alignments of `Self` and `Rhs` must be equal: +```rust +/// Implemented if `align_of::() == align_of::()` +pub trait AlignEq +where + Neglect: UnsafeTransmuteOptions, +{} + +impl AlignEq for Lhs +where + Neglect: UnsafeTransmuteOptions, + Lhs: AlignLtEq, + Rhs: AlignLtEq, +{} +``` + +### Querying Size +Querying *just* the size of a type is trickier: how might we query the size without implicitly querying the alignment and validity of the bytes that contribute to that size? + +We do so by constructing a contrived container type that neutralizes the alignment and validity aspects of its contents: +```rust +#[derive(PromiseTransmutableFrom, PromiseTransmutableInto)] */ +#[repr(C)] +pub struct Gadget(pub [Align; 0], pub MaybeUninit); +``` +This type will have the size of `Size`, and alignment equal to the maximum of `Align` and `Size`. `MaybeUninit` neutralizes the bit-validity qualities of `Size`. + +We use this `Gadget` to define a trait that is implemented if the size of `Self` is less-than-or-equal to the size of `Rhs`: +```rust +/// Implemented if `size_of::() <= size_of::()` +pub trait SizeLtEq +where + Neglect: UnsafeTransmuteOptions, +{} + +impl SizeLtEq for Lhs +where + Neglect: UnsafeTransmuteOptions, + for<'a> &'a Gadget: TransmuteFrom<&'a Gadget, Neglect>, +{} +``` +This works, because `Gadget` and `Gadget` will have equal alignment, and equal bit-validity (they consist solely of padding bytes). Thus, all that varies between them is their size, and reference transmutations must either preserve or reduce the size of the transmuted type. + +As before, if the size of `Self` is less-than-or-equal to `Rhs`, and the size of `Rhs` is less-than-or-equal to `Self`, then the sizes of `Self` and `Rhs` must be equal: +```rust +/// Implemented if `size_of::() == size_of::()` +pub trait SizeEq +where + Neglect: UnsafeTransmuteOptions, +{} + +impl SizeEq for Lhs +where + Neglect: UnsafeTransmuteOptions, + Lhs: SizeLtEq, + Rhs: SizeLtEq, +{} +``` + ## Extension: Byte Transmutation Traits [marker-traits]: #Extension-Byte-Transmutation-Traits From b2baf68ab2abbfebb04e7dac54f9adb6d816cff5 Mon Sep 17 00:00:00 2001 From: Jack Wrenn Date: Tue, 25 Aug 2020 15:46:12 -0400 Subject: [PATCH 33/61] extension: casting revamp --- rfcs/0000-safe-transmute.md | 296 ++++++++++++++++++++++-------------- 1 file changed, 184 insertions(+), 112 deletions(-) diff --git a/rfcs/0000-safe-transmute.md b/rfcs/0000-safe-transmute.md index a0ca4ca..38e953a 100644 --- a/rfcs/0000-safe-transmute.md +++ b/rfcs/0000-safe-transmute.md @@ -2466,7 +2466,7 @@ The vast majority of users will *only* confront the stability declaration traits We acknowledge that it is unusual for a `derive` macro to not create an item of the same name, but this weirdness is outweighed by the weirdness of the alternative: providing a trait for which there is almost no good use. ## Extension: Layout Property Traits -Given `TransmuteFrom` and `TransmuteInto`, we can construct bounds that check certain properties of a type by checking if its convertible to another, contrived type. These gadgets are useful (see [`Vec` casting](#todo) for an example), and their usefulness could justify adding them to `libcore`—perhaps in the `mem` module. +Given `TransmuteFrom` and `TransmuteInto`, we can construct bounds that check certain properties of a type by checking if its convertible to another, contrived type. These gadgets are useful (see [`Vec` casting][ext-vec-casting] for an example), and their usefulness could justify adding them to `libcore`—perhaps in the `mem` module. ### Querying Alignment The type `[T; 0]` shares the alignment requirements of `T`, but no other layout properties. A type `&[T; 0]` will only be transmutable to `&[U; 0]`, if the minimum alignment of `T` is greater than that of `U`. We exploit this to define a trait that is implemented for `Self` if its alignment is less-than-or-equal to that of `Rhs`: @@ -2631,155 +2631,227 @@ where } ``` -## Extension: Slice Casting -[ext-slice-casting]: #extension-slice-casting +## Extension: Casting +[ext-slice-casting]: #extension-casting +Given `TransmuteFrom`, we can construct safe abstractions for casts of slices and `Vec`s that effectively transmute the *contents* of those types. -Transmuting the contained type of a slice is a [common operation](https://internals.rust-lang.org/t/safe-trasnsmute-for-slices-e-g-u64-u32-particularly-simd-types/2871) in cryptography and fast packet parsing. Although this RFC does not propose the addition of a concrete method for slice casting, the mechanisms proposed in this RFC make possible sound and complete slice casting abstractions; e.g.: +### Slice Casting +A slice cast is an operation that consumes `&'i [Src]` and produces `&'o [Dst]`. This conversion has both static and dynamic components. The length of a `&'i [Src]` is a dynamic quality of values of that type. If ths static sizes of `Src` and `Dst` differ, the length of `&'o [Dst]` will need to differ from that of `&'i [Src]` accordingly. The static component of this conversion is whether the layouts of `Src` and `Dst` even permit such a conversion---we can use `TransmuteFrom` to answer this: *Could a reference to a maximally-long array of `Src` be transmuted to an array of just one `Dst`?* + +Concretely: ```rust -pub mod cast { +fn cast<'i, 'o, Src, Dst>(src: &'i [Src]) -> &'o [Dst] +where + &'o [Dst; 1]: TransmuteFrom<&'i [Src; usize::MAX]> +{ + let len = size_of_val(src).checked_div(size_of::()).unwrap_or(0); + unsafe { slice::from_raw_parts(src.as_ptr() as *const Dst, len) } +} +``` - #[marker] pub trait SafeCastOptions: UnsafeCastOptions {} - #[marker] pub trait UnsafeCastOptions {} +### `Vec` Casting +[ext-vec-casting]: #vec-casting - impl SafeCastOptions for () {} - impl UnsafeCastOptions for () {} +The invariants imposed by [`Vec::from_raw_parts`](https://doc.rust-lang.org/alloc/vec/struct.Vec.html#method.from_raw_parts) are far stricter than those imposed by [`slice::from_raw_parts`](https://doc.rust-lang.org/core/slice/fn.from_raw_parts.html): we may only convert a `Vec` to `Vec` if: - pub trait CastInto - where - Dst: CastFrom, - Neglect: UnsafeCastOptions, - { - fn cast_into(self) -> Dst - where - Self: Sized, - Dst: Sized, - Neglect: SafeCastOptions, - { - CastFrom::<_, Neglect>::cast_from(self) - } + - `U` is transmutable from `T` + - `U` has the same size as `T` + - `U` has the same static alignment as `T` - unsafe fn unsafe_cast_into(self) -> Dst - where - Self: Sized, - Dst: Sized, - Neglect: UnsafeCastOptions, - { - CastFrom::<_, Neglect>::unsafe_cast_from(self) - } - } +Concretely: +```rust +fn cast(src: Vec) -> Vec +where + Dst: TransmuteFrom + + AlignEq, + + SizeEq, +{ + let (ptr, len, cap) = src.into_raw_parts(); + unsafe { Vec::from_raw_parts(ptr as *mut Dst, len, cap) } +} +``` - impl CastInto for Src - where - Dst: CastFrom, - Neglect: UnsafeCastOptions, - {} +### Cast Ergonomics +The above `cast` functions have a major ergonomic drawback: they require callers to reiterate their `where` bounds. The bounds that determine the castability of a type is more ergonomically encapsulated with a *single* trait bound whose name clearly communicates that casting is possible. +Mirroring the design of `TransmuteFrom`, this *could* take the form of a `CastFrom` trait: +```rust +pub mod cast { + + /// Instantiate `Self` from a value of type `Src`. + /// + /// The reciprocal of [CastInto]. pub trait CastFrom where - Neglect: UnsafeCastOptions, + Neglect: options::CastOptions, { + /// Instantiate `Self` by casting a value of type `Src`, safely. fn cast_from(src: Src) -> Self where Src: Sized, Self: Sized, - Neglect: SafeCastOptions + Neglect: options::SafeCastOptions { unsafe { CastFrom::<_,Neglect>::unsafe_cast_from(src) } } + /// Instantiate `Self` by casting a value of type `Src`, potentially safely. unsafe fn unsafe_cast_from(src: Src) -> Self where Src: Sized, Self: Sized, - Neglect: UnsafeCastOptions; + Neglect: options::CastOptions; } - /// Options for casting the contents of slices. - pub mod slice { - use super::{ + /// Options for casting. + pub mod options { + + /// The super-trait of all *safe* casting options. + #[marker] pub trait SafeCastOptions: CastOptions {} + + /// The super-trait of all casting options. + #[marker] pub trait CastOptions {} + + impl SafeCastOptions for () {} + impl CastOptions for () {} + + } +} +``` +...which might then be implemented like so for `Vec`: +```rust +/// A contiguous growable array type with heap-allocated contents, `Vec`. +pub mod vec { + use core::convert::{ + transmute::{ + TransmuteFrom, + options::{SafeTransmuteOptions, UnsafeTransmuteOptions, NeglectAlignment}, + }, + cast::{ CastFrom, - SafeCastOptions, - UnsafeCastOptions, - super::transmute::{ - TransmuteFrom, - options::{SafeTransmuteOptions, UnsafeTransmuteOptions}, + options::{ + SafeCastOptions, + CastOptions, }, - }; - - use core::{ - mem::{size_of, size_of_val}, - slice - }; - - /// All `SafeTransmuteOptions` are `SafeSliceCastOptions`. - pub trait SafeSliceCastOptions - : SafeCastOptions - + SafeTransmuteOptions - + UnsafeSliceCastOptions - {} + }, + }; - /// All `UnsafeTransmuteOptions` are `UnsafeSliceCastOptions`. - pub trait UnsafeSliceCastOptions - : UnsafeCastOptions - + UnsafeTransmuteOptions - {} + /// Safe options for casting `Vec` to `Vec`. + pub trait SafeVecCastOptions + : SafeCastOptions + + SafeTransmuteOptions + + VecCastOptions + {} - impl SafeCastOptions for Neglect {} - impl SafeSliceCastOptions for Neglect {} - impl UnsafeCastOptions for Neglect {} - impl UnsafeSliceCastOptions for Neglect {} + /// Options for casting `Vec` to `Vec`. + pub trait VecCastOptions + : UnsafeTransmuteOptions + + CastOptions + {} - /// Convert `&[Src]` to `&[Dst]` - impl<'i, 'o, Src, Dst, Neglect> CastFrom<&'i [Src], Neglect> for &'o [Dst] - where - Neglect: UnsafeSliceCastOptions, - &'o [Dst; size_of::()]: TransmuteFrom<&'i [Src; size_of::()], Neglect> + impl SafeCastOptions for Neglect {} + impl SafeVecCastOptions for Neglect {} + + impl CastOptions for Neglect {} + impl VecCastOptions for Neglect {} + + + use core::mem::{MaybeUninit, SizeEq, AlignEq}; + + impl CastFrom, Neglect> for Vec + where + Neglect: VecCastOptions, + Dst: TransmuteFrom + + AlignEq + + SizeEq, + { + #[doc(hidden)] + #[inline(always)] + unsafe fn unsafe_cast_from(src: Vec) -> Vec { - unsafe fn unsafe_cast_from(src: &'i [Src]) -> &'o [Dst] - where - Neglect: UnsafeSliceCastOptions, - { - let len = size_of_val(src).checked_div(size_of::()).unwrap_or(0); - unsafe { slice::from_raw_parts(src.as_ptr() as *const Dst, len) } - } + let (ptr, len, cap) = src.into_raw_parts(); + Vec::from_raw_parts(ptr as *mut Dst, len, cap) } + } +} +``` +...and like this for slices: +```rust +pub mod slice { + use core::convert::{ + transmute::{ + TransmuteFrom, + options::{SafeTransmuteOptions, UnsafeTransmuteOptions}, + }, + cast::{ + CastFrom, + options::{ + SafeCastOptions, + CastOptions, + }, + }, + }; - /// Convert `&mut [Src]` to `&mut [Dst]` - impl<'i, 'o, Src, Dst, Neglect> CastFrom<&'i mut [Src], Neglect> for &'o mut [Dst] - where - Neglect: UnsafeSliceCastOptions, - &'o mut [Dst; size_of::()]: TransmuteFrom<&'i mut [Src; size_of::()], Neglect> + use core::{ + mem::{size_of, size_of_val}, + slice + }; + + /// *Safe* options for casting **slices**. + pub trait SafeSliceCastOptions + : SafeCastOptions + + SafeTransmuteOptions + + SliceCastOptions + {} + + /// Options for casting **slices**. + pub trait SliceCastOptions + : CastOptions + + UnsafeTransmuteOptions + {} + + impl SafeCastOptions for Neglect {} + impl SafeSliceCastOptions for Neglect {} + + impl CastOptions for Neglect {} + impl SliceCastOptions for Neglect {} + + + impl<'i, 'o, Src, Dst, Neglect> CastFrom<&'i [Src], Neglect> for &'o [Dst] + where + Neglect: SliceCastOptions, + &'o [Dst; 1]: TransmuteFrom<&'i [Src; usize::MAX], Neglect> + { + unsafe fn unsafe_cast_from(src: &'i [Src]) -> &'o [Dst] { - unsafe fn unsafe_cast_from(src: &'i mut [Src]) -> &'o mut [Dst] - where - Neglect: UnsafeSliceCastOptions, - { - let len = size_of_val(src).checked_div(size_of::()).unwrap_or(0); - unsafe { slice::from_raw_parts_mut(src.as_ptr() as *mut Dst, len) } - } + let len = size_of_val(src).checked_div(size_of::()).unwrap_or(0); + unsafe { slice::from_raw_parts(src.as_ptr() as *const Dst, len) } } + } - /// Convert `&mut [Src]` to `&[Dst]` - impl<'i, 'o, Src, Dst, Neglect> CastFrom<&'i mut [Src], Neglect> for &'o [Dst] - where - Neglect: UnsafeSliceCastOptions, - &'o mut [Dst; size_of::()]: TransmuteFrom<&'i [Src; size_of::()], Neglect> + impl<'i, 'o, Src, Dst, Neglect> CastFrom<&'i mut [Src], Neglect> for &'o mut [Dst] + where + Neglect: SliceCastOptions, + &'o mut [Dst; 1]: TransmuteFrom<&'i mut [Src; usize::MAX], Neglect> + { + unsafe fn unsafe_cast_from(src: &'i mut [Src]) -> &'o mut [Dst] { - unsafe fn unsafe_cast_from(src: &'i mut [Src]) -> &'o [Dst] - where - Neglect: UnsafeSliceCastOptions, - { - let len = size_of_val(src).checked_div(size_of::()).unwrap_or(0); - unsafe { - slice::from_raw_parts(src.as_ptr() as *const Dst, len) - } - } + let len = size_of_val(src).checked_div(size_of::()).unwrap_or(0); + unsafe { slice::from_raw_parts_mut(src.as_ptr() as *mut Dst, len) } } + } + impl<'i, 'o, Src, Dst, Neglect> CastFrom<&'i mut [Src], Neglect> for &'o [Dst] + where + { + unsafe fn unsafe_cast_from(src: &'i mut [Src]) -> &'o [Dst] + { + let len = size_of_val(src).checked_div(size_of::()).unwrap_or(0); + unsafe { slice::from_raw_parts(src.as_ptr() as *const Dst, len) } + } } } ``` -Note that `TransmuteFrom` is used as trait bound to ensure safety, but the `transmute_from()` method isn't actually called. ## Extension: `include_data!` [future-possibility-include_data]: #Extension-include_data @@ -2805,8 +2877,8 @@ pub fn recognize(input: &Matrix) -> usize } ``` -## Extension: Reference Casting -[ext-ref-casting]: #Extension-Reference-Casting +## Possibility: Reference Casting +[ext-ref-casting]: #possibility-Reference-Casting ```rust /// Try to convert a `&T` into `&U`. /// @@ -2826,8 +2898,8 @@ where } ``` -## Extension: Generic Atomics -[future-possibility-generic-atomics]: #extension-generic-atomics +## Possibility: Generic Atomics +[future-possibility-generic-atomics]: #possibility-generic-atomics ```rust type LargestPlatformAtomic = u64; From 781cf1f8825f69e48d9b3fd7e62fbff1f19f8eb1 Mon Sep 17 00:00:00 2001 From: Jack Wrenn Date: Tue, 25 Aug 2020 15:48:24 -0400 Subject: [PATCH 34/61] rename: UnsafeTransmuteOptions -> TransmuteOptions --- rfcs/0000-safe-transmute.md | 100 ++++++++++++++++++------------------ 1 file changed, 50 insertions(+), 50 deletions(-) diff --git a/rfcs/0000-safe-transmute.md b/rfcs/0000-safe-transmute.md index 38e953a..c49e973 100644 --- a/rfcs/0000-safe-transmute.md +++ b/rfcs/0000-safe-transmute.md @@ -646,7 +646,7 @@ Two traits provide mechanisms for transmutation between types: #[lang = "transmute_from"] pub unsafe trait TransmuteFrom where - Neglect: UnsafeTransmuteOptions, + Neglect: TransmuteOptions, { #[inline(always)] fn transmute_from(src: Src) -> Self @@ -663,7 +663,7 @@ where where Src: Sized, Self: Sized, - Neglect: UnsafeTransmuteOptions, + Neglect: TransmuteOptions, { use core::{mem, ptr}; unsafe { @@ -677,7 +677,7 @@ where // implemented in terms of `TransmuteFrom` pub unsafe trait TransmuteInto where - Neglect: UnsafeTransmuteOptions, + Neglect: TransmuteOptions, { fn transmute_into(self) -> Dst where @@ -689,14 +689,14 @@ where where Self: Sized, Dst: Sized, - Neglect: UnsafeTransmuteOptions; + Neglect: TransmuteOptions; } unsafe impl TransmuteInto for Src where Src: ?Sized, Dst: ?Sized + TransmuteFrom, - Neglect: UnsafeTransmuteOptions, + Neglect: TransmuteOptions, { ... } @@ -715,18 +715,18 @@ The default value of the `Neglect` parameter, `()`, statically forbids transmute | `NeglectAlignment` | Safety | `unsafe_transmute_{from,into}` | | `NeglectValidity` | Soundness | `unsafe_transmute_{from,into}` | -`NeglectStabilty` implements the `SafeTransmuteOptions` and `UnsafeTransmuteOptions` marker traits, as it can be used in both safe and unsafe code. The selection of multiple options is encoded by grouping them as a tuple; e.g., `(NeglectAlignment, NeglectValidity)` is a selection of both the `NeglectAlignment` and `NeglectValidity` options. +`NeglectStabilty` implements the `SafeTransmuteOptions` and `TransmuteOptions` marker traits, as it can be used in both safe and unsafe code. The selection of multiple options is encoded by grouping them as a tuple; e.g., `(NeglectAlignment, NeglectValidity)` is a selection of both the `NeglectAlignment` and `NeglectValidity` options. We introduce two marker traits which serve to group together the options that may be used with safe transmutes, and those which may be used with `unsafe` transmutes: ```rust pub trait SafeTransmuteOptions: private::Sealed {} -pub trait UnsafeTransmuteOptions: SafeTransmuteOptions +pub trait TransmuteOptions: SafeTransmuteOptions {} impl SafeTransmuteOptions for () {} -impl UnsafeTransmuteOptions for () {} +impl TransmuteOptions for () {} ``` #### `NeglectStability` @@ -737,7 +737,7 @@ By default, `TransmuteFrom` and `TransmuteInto`'s methods require that the [layo pub struct NeglectStability; impl SafeTransmuteOptions for NeglectStability {} -impl UnsafeTransmuteOptions for NeglectStability {} +impl TransmuteOptions for NeglectStability {} ``` Prior to the adoption of the [stability declaration traits][stability], crate authors documented the layout guarantees of their types with doc comments. The `TransmuteFrom` and `TransmuteInto` traits and methods may be used with these types by requesting that the stability check is neglected; for instance: @@ -760,7 +760,7 @@ By default, `TransmuteFrom` and `TransmuteInto`'s methods require that, when tra ```rust pub struct NeglectAlignment; -impl UnsafeTransmuteOptions for NeglectAlignment {} +impl TransmuteOptions for NeglectAlignment {} ``` By using the `NeglectAlignment` option, you are committing to ensure that the transmuted reference satisfies the alignment requirements of the destination's referent type. For instance: @@ -800,7 +800,7 @@ The `NeglectValidity` option disables this check. ```rust pub struct NeglectValidity; -impl UnsafeTransmuteOptions for NeglectValidity {} +impl TransmuteOptions for NeglectValidity {} ``` By using the `NeglectValidity` option, you are committing to ensure dynamically source value is a valid instance of the destination type. For instance: @@ -858,7 +858,7 @@ The *only* item defined by this RFC requiring special compiler support is `Trans /// A type is transmutable into itself. unsafe impl TransmuteFrom for T where - Neglect: UnsafeTransmuteOptions + Neglect: TransmuteOptions {} /// A transmutation is *stable* if... @@ -905,7 +905,7 @@ pub mod transmute { pub /*const*/ unsafe fn unsafe_transmute(src: Src) -> Dst where Src: TransmuteInto, - Neglect: UnsafeTransmuteOptions + Neglect: TransmuteOptions { unsafe { src.unsafe_transmute_into() } } @@ -915,7 +915,7 @@ pub mod transmute { /// a given set of static checks to `Neglect`. pub unsafe trait TransmuteInto where - Neglect: UnsafeTransmuteOptions, + Neglect: TransmuteOptions, { /// Reinterpret the bits of a value of one type as another type, safely. fn transmute_into(self) -> Dst @@ -931,14 +931,14 @@ pub mod transmute { where Self: Sized, Dst: Sized, - Neglect: UnsafeTransmuteOptions; + Neglect: TransmuteOptions; } unsafe impl TransmuteInto for Src where Src: ?Sized, Dst: ?Sized + TransmuteFrom, - Neglect: UnsafeTransmuteOptions, + Neglect: TransmuteOptions, { #[inline(always)] fn transmute_into(self) -> Dst @@ -955,7 +955,7 @@ pub mod transmute { where Self: Sized, Dst: Sized, - Neglect: UnsafeTransmuteOptions, + Neglect: TransmuteOptions, { unsafe { Dst::unsafe_transmute_from(self) } } @@ -967,7 +967,7 @@ pub mod transmute { /* #[lang = "transmute_from"] */ pub unsafe trait TransmuteFrom where - Neglect: UnsafeTransmuteOptions, + Neglect: TransmuteOptions, { /// Reinterpret the bits of a value of one type as another type, safely. #[inline(always)] @@ -993,7 +993,7 @@ pub mod transmute { where Src: Sized, Self: Sized, - Neglect: UnsafeTransmuteOptions, + Neglect: TransmuteOptions, { use core::{mem, ptr}; unsafe { @@ -1208,15 +1208,15 @@ pub mod transmute { pub mod options { /// Options that may be used with safe transmutations. - pub trait SafeTransmuteOptions: UnsafeTransmuteOptions + pub trait SafeTransmuteOptions: TransmuteOptions {} /// Options that may be used with unsafe transmutations. - pub trait UnsafeTransmuteOptions: private::Sealed + pub trait TransmuteOptions: private::Sealed {} impl SafeTransmuteOptions for () {} - impl UnsafeTransmuteOptions for () {} + impl TransmuteOptions for () {} /// Neglect the stability check of `TransmuteFrom`. /* #[lang = "neglect_stability"] */ @@ -1224,16 +1224,16 @@ pub mod transmute { // Uncomment this if/when constructibility is fully implemented: // impl SafeTransmuteOptions for NeglectStability {} - impl UnsafeTransmuteOptions for NeglectStability {} + impl TransmuteOptions for NeglectStability {} /* pub struct NeglectAlignment; - impl UnsafeTransmuteOptions for NeglectAlignment {} + impl TransmuteOptions for NeglectAlignment {} */ /* FILL: Implementations for tuple combinations of options */ - // prevent third-party implementations of `UnsafeTransmuteOptions` + // prevent third-party implementations of `TransmuteOptions` mod private { use super::*; @@ -1392,7 +1392,7 @@ This safety hazard is not materially different from the one that would be induce ##### Recommendation We recommend that that implementers of this RFC initially simplify constructability by: - adopting our simplified definition of constructability - - demoting `NeglectStability` to unsafe status (i.e., not implementing `SafeTransmuteOptions` for `NeglectStability`; *only* `UnsafeTransmuteOptions`) + - demoting `NeglectStability` to unsafe status (i.e., not implementing `SafeTransmuteOptions` for `NeglectStability`; *only* `TransmuteOptions`) If and when the implementation of `TransmuteFrom` encodes our complete definition of constructability, `NeglectStability` shall become a safe transmute option. @@ -1978,7 +1978,7 @@ To accomodate this, we may modify the definitions of `PromiseTransmutableFrom` a ```rust pub trait PromiseTransmutableFrom where - Neglect: UnsafeTransmuteOptions + Neglect: TransmuteOptions { type Archetype : TransmuteInto> @@ -1987,7 +1987,7 @@ where pub trait PromiseTransmutableInto where - Neglect: UnsafeTransmuteOptions + Neglect: TransmuteOptions { type Archetype : TransmuteFrom> @@ -2399,7 +2399,7 @@ The automatic mechanism proposed by [*Pre-RFC: Safe coercions*][2017-02] exploit Our RFC exploits the related concept of *constructability*, which is a property of a struct, or enum variant (rather than solely a property of fields). However, we recognize that it may be difficult to test for constructability within the trait resolution process. -The simplified definition of *constructability* we propose is the same employed by [typic][crate-typic] (which uses the term "visibility"). [Typic][crate-typic] regards the pub-in-prive soundness hole of the simplified definition to be sufficiently niche that `NeglectStability` remains "safe". However, unlike [typic][crate-typic], we believe that this simplified definition imposes a safety hazard substantial enough to warrant making `NeglectStability` initially an unsafe transmute option. +The simplified definition of *constructability* we propose is the same employed by [typic][crate-typic] (which uses the term "visibility"). [Typic][crate-typic] regards the pub-in-prive soundness hole of the simplified definition to be sufficiently niche that `NeglectStability` remains "safe". However, unlike [typic][crate-typic], we believe that this simplified definition imposes a safety hazard substantial enough to warrant making `NeglectStability` initially usable with *only* unsafe transmutes. Our RFC separates *constructability*, which concerns what aspects of a type's structure are part of its public API, and *stability*, which concerns the aspects of a type's layout that are part of its public API for SemVer purposes. This distinction does not appear in prior work. @@ -2474,12 +2474,12 @@ The type `[T; 0]` shares the alignment requirements of `T`, but no other layout /// Implemented if `align_of::() <= align_of::()` pub trait AlignLtEq where - Neglect: UnsafeTransmuteOptions, + Neglect: TransmuteOptions, {} impl AlignLtEq for Lhs where - Neglect: UnsafeTransmuteOptions, + Neglect: TransmuteOptions, for<'a> &'a [Lhs; 0]: TransmuteFrom<&'a [Rhs; 0], Neglect> {} ``` @@ -2488,12 +2488,12 @@ Furthermore, if the alignment of `Self` is less-than-or-equal to `Rhs`, and the /// Implemented if `align_of::() == align_of::()` pub trait AlignEq where - Neglect: UnsafeTransmuteOptions, + Neglect: TransmuteOptions, {} impl AlignEq for Lhs where - Neglect: UnsafeTransmuteOptions, + Neglect: TransmuteOptions, Lhs: AlignLtEq, Rhs: AlignLtEq, {} @@ -2515,12 +2515,12 @@ We use this `Gadget` to define a trait that is implemented if the size of `Self` /// Implemented if `size_of::() <= size_of::()` pub trait SizeLtEq where - Neglect: UnsafeTransmuteOptions, + Neglect: TransmuteOptions, {} impl SizeLtEq for Lhs where - Neglect: UnsafeTransmuteOptions, + Neglect: TransmuteOptions, for<'a> &'a Gadget: TransmuteFrom<&'a Gadget, Neglect>, {} ``` @@ -2531,12 +2531,12 @@ As before, if the size of `Self` is less-than-or-equal to `Rhs`, and the size of /// Implemented if `size_of::() == size_of::()` pub trait SizeEq where - Neglect: UnsafeTransmuteOptions, + Neglect: TransmuteOptions, {} impl SizeEq for Lhs where - Neglect: UnsafeTransmuteOptions, + Neglect: TransmuteOptions, Lhs: SizeLtEq, Rhs: SizeLtEq, {} @@ -2552,7 +2552,7 @@ Indicates that a type may be transmuted into an appropriately-sized array of byt ```rust pub unsafe trait IntoBytes where - Neglect: UnsafeTransmuteOptions + Neglect: TransmuteOptions {} // `Src` is `IntoBytes` if it can be safely transmuted *into* an @@ -2560,7 +2560,7 @@ where unsafe impl IntoBytes for Src where Src: Sized + TransmuteInto<[u8; size_of::()], Neglect>, - Neglect: UnsafeTransmuteOptions + Neglect: TransmuteOptions {} ``` @@ -2569,7 +2569,7 @@ Indicates that a type may be transmuted from an appropriately-sized array of byt ```rust pub unsafe trait FromBytes where - Neglect: UnsafeTransmuteOptions + Neglect: TransmuteOptions {} // `Dst` is `FromBytes` if it can be safely transmuted *from* an @@ -2577,7 +2577,7 @@ where unsafe impl FromBytes for Dst where Dst: Sized + TransmuteFrom<[u8; size_of::()], Neglect>, - Neglect: UnsafeTransmuteOptions + Neglect: TransmuteOptions {} ``` @@ -2589,7 +2589,7 @@ Indicates that a type may be transmuted from an appropriately-sized array of zer ```rust pub unsafe trait FromZeros where - Neglect: UnsafeTransmuteOptions + Neglect: TransmuteOptions { /// Safely initialize `Self` from zeroed bytes. fn zeroed() -> Self @@ -2613,7 +2613,7 @@ enum Zero { unsafe impl FromZeros for Dst where Dst: Sized + TransmuteFrom<[Zero; size_of::()], Neglect>, - Neglect: UnsafeTransmuteOptions + Neglect: TransmuteOptions { fn zeroed() -> Self where @@ -2624,7 +2624,7 @@ where unsafe fn unsafe_zeroed() -> Self where - Neglect: UnsafeTransmuteOptions + Neglect: TransmuteOptions { [Zero; size_of::].unsafe_transmute_into() } @@ -2725,7 +2725,7 @@ pub mod vec { use core::convert::{ transmute::{ TransmuteFrom, - options::{SafeTransmuteOptions, UnsafeTransmuteOptions, NeglectAlignment}, + options::{SafeTransmuteOptions, TransmuteOptions, NeglectAlignment}, }, cast::{ CastFrom, @@ -2745,7 +2745,7 @@ pub mod vec { /// Options for casting `Vec` to `Vec`. pub trait VecCastOptions - : UnsafeTransmuteOptions + : TransmuteOptions + CastOptions {} @@ -2753,7 +2753,7 @@ pub mod vec { impl SafeVecCastOptions for Neglect {} impl CastOptions for Neglect {} - impl VecCastOptions for Neglect {} + impl VecCastOptions for Neglect {} use core::mem::{MaybeUninit, SizeEq, AlignEq}; @@ -2781,7 +2781,7 @@ pub mod slice { use core::convert::{ transmute::{ TransmuteFrom, - options::{SafeTransmuteOptions, UnsafeTransmuteOptions}, + options::{SafeTransmuteOptions, TransmuteOptions}, }, cast::{ CastFrom, @@ -2807,14 +2807,14 @@ pub mod slice { /// Options for casting **slices**. pub trait SliceCastOptions : CastOptions - + UnsafeTransmuteOptions + + TransmuteOptions {} impl SafeCastOptions for Neglect {} impl SafeSliceCastOptions for Neglect {} impl CastOptions for Neglect {} - impl SliceCastOptions for Neglect {} + impl SliceCastOptions for Neglect {} impl<'i, 'o, Src, Dst, Neglect> CastFrom<&'i [Src], Neglect> for &'o [Dst] From a98df6effaef9ef9563280b2518e19c5e8e703db Mon Sep 17 00:00:00 2001 From: Jack Wrenn Date: Tue, 25 Aug 2020 15:52:29 -0400 Subject: [PATCH 35/61] dim of variation intro --- rfcs/0000-safe-transmute.md | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/rfcs/0000-safe-transmute.md b/rfcs/0000-safe-transmute.md index c49e973..a8f01f7 100644 --- a/rfcs/0000-safe-transmute.md +++ b/rfcs/0000-safe-transmute.md @@ -2236,8 +2236,17 @@ Finally, community-led, crate-based approaches are, inescapably, unauthoritative [2019-12-05-v2]: https://internals.rust-lang.org/t/pre-rfc-v2-safe-transmute/11431 [2020-07]: https://internals.rust-lang.org/t/pre-rfc-explicit-opt-in-oibit-for-truly-pod-data-and-safe-transmutes/2361 - ## Dimensions of Variation +[dimensions-of-variation]: #dimensions-of-variation + +A handful of dimensions of variation charactarize the distinctions between prior art: + - conversion complexity + - conversion fallibility + - source and destination types supported + - implementation mechanism + - stability hazards + +We review each of these dimensions in turn, along with this proposal's location along these dimensions: ### Conversion Complexity Prior work differs in whether it supports complex conversions, or only simple transmutation. [*Pre-RFC FromBits/IntoBits*][2018-03]'s proposed traits include conversion methods that are implemented by type authors. Because end-users provide their own definitions of these methods, they can be defined to do more than just transmutation (e.g., slice casting). (This approach is similar to the [uncon][crate-uncon] crate's [`FromUnchecked`](https://docs.rs/uncon/1.*/uncon/trait.FromUnchecked.html) and [`IntoUnchecked`](https://docs.rs/uncon/1.*/uncon/trait.IntoUnchecked.html) traits, which provide unsafe conversions between types. These traits are safe to implement, but their conversion methods are not.) From 1df275e0ecc938259e16365459fff9f7b87aee4c Mon Sep 17 00:00:00 2001 From: Jack Wrenn Date: Tue, 25 Aug 2020 15:54:19 -0400 Subject: [PATCH 36/61] typo --- rfcs/0000-safe-transmute.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rfcs/0000-safe-transmute.md b/rfcs/0000-safe-transmute.md index a8f01f7..ea4bc23 100644 --- a/rfcs/0000-safe-transmute.md +++ b/rfcs/0000-safe-transmute.md @@ -2515,7 +2515,7 @@ We do so by constructing a contrived container type that neutralizes the alignme ```rust #[derive(PromiseTransmutableFrom, PromiseTransmutableInto)] */ #[repr(C)] -pub struct Gadget(pub [Align; 0], pub MaybeUninit); +pub struct Gadget(pub [Align; 0], pub MaybeUninit); ``` This type will have the size of `Size`, and alignment equal to the maximum of `Align` and `Size`. `MaybeUninit` neutralizes the bit-validity qualities of `Size`. From a1362e9e10ca35c9c54d7ba3747edd891d50eb00 Mon Sep 17 00:00:00 2001 From: Jack Wrenn Date: Tue, 25 Aug 2020 16:04:23 -0400 Subject: [PATCH 37/61] possibility intros --- rfcs/0000-safe-transmute.md | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/rfcs/0000-safe-transmute.md b/rfcs/0000-safe-transmute.md index ea4bc23..dc60293 100644 --- a/rfcs/0000-safe-transmute.md +++ b/rfcs/0000-safe-transmute.md @@ -2444,6 +2444,10 @@ The omission is intentional. The consequences of such an option are suprising in # Future possibilities [future-possibilities]: #future-possibilities +We divide future possibilities into two categories: + - *extensions*, which are highly-developed, + - *possibilities*, which sketch an idea. + ## Extension: `PromiseTransmutable` Shorthand [extension-promisetransmutable-shorthand]: #extension-promisetransmutable-shorthand @@ -2888,6 +2892,8 @@ pub fn recognize(input: &Matrix) -> usize ## Possibility: Reference Casting [ext-ref-casting]: #possibility-Reference-Casting + +The ability to neglect static checks should make possible [bytemuck][crate-bytemuck]-style fallible casts; e.g.: ```rust /// Try to convert a `&T` into `&U`. /// @@ -2910,13 +2916,14 @@ where ## Possibility: Generic Atomics [future-possibility-generic-atomics]: #possibility-generic-atomics +With much additional effort, this proposal could provide a basis for truly a truly generic `Atomic` type: + ```rust type LargestPlatformAtomic = u64; pub struct Atomic where - // ensures that size_of::() <= size_of::(): - MaybeUninit: TransmuteFrom + T: SizeLtEq { v: UnsafeCell } From 7eadf01e562001f41b100b0322005a3b2c635358 Mon Sep 17 00:00:00 2001 From: Jack Wrenn Date: Tue, 25 Aug 2020 16:13:39 -0400 Subject: [PATCH 38/61] simplified formulation of constructability is a compiler stability hazard https://internals.rust-lang.org/t/pre-rfc-safer-transmutation/12926/8 --- rfcs/0000-safe-transmute.md | 1 + 1 file changed, 1 insertion(+) diff --git a/rfcs/0000-safe-transmute.md b/rfcs/0000-safe-transmute.md index dc60293..406e148 100644 --- a/rfcs/0000-safe-transmute.md +++ b/rfcs/0000-safe-transmute.md @@ -1393,6 +1393,7 @@ This safety hazard is not materially different from the one that would be induce We recommend that that implementers of this RFC initially simplify constructability by: - adopting our simplified definition of constructability - demoting `NeglectStability` to unsafe status (i.e., not implementing `SafeTransmuteOptions` for `NeglectStability`; *only* `TransmuteOptions`) + - advise users that implementing the stability declaration traits on types that are not fully-implicitly constructable will be a compiler-error will be a compiler error (i.e., these traits must not be implemented on types exploiting the pub-in-priv trick) If and when the implementation of `TransmuteFrom` encodes our complete definition of constructability, `NeglectStability` shall become a safe transmute option. From d86731c14d1a1ccfd5128d0088185882043de6df Mon Sep 17 00:00:00 2001 From: Jack Wrenn Date: Tue, 25 Aug 2020 16:19:15 -0400 Subject: [PATCH 39/61] typo --- rfcs/0000-safe-transmute.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rfcs/0000-safe-transmute.md b/rfcs/0000-safe-transmute.md index 406e148..1ac82dd 100644 --- a/rfcs/0000-safe-transmute.md +++ b/rfcs/0000-safe-transmute.md @@ -2409,7 +2409,7 @@ The automatic mechanism proposed by [*Pre-RFC: Safe coercions*][2017-02] exploit Our RFC exploits the related concept of *constructability*, which is a property of a struct, or enum variant (rather than solely a property of fields). However, we recognize that it may be difficult to test for constructability within the trait resolution process. -The simplified definition of *constructability* we propose is the same employed by [typic][crate-typic] (which uses the term "visibility"). [Typic][crate-typic] regards the pub-in-prive soundness hole of the simplified definition to be sufficiently niche that `NeglectStability` remains "safe". However, unlike [typic][crate-typic], we believe that this simplified definition imposes a safety hazard substantial enough to warrant making `NeglectStability` initially usable with *only* unsafe transmutes. +The simplified definition of *constructability* we propose is the same employed by [typic][crate-typic] (which uses the term "visibility"). [Typic][crate-typic] regards the pub-in-priv soundness hole of the simplified definition to be sufficiently niche that `NeglectStability` remains "safe". However, unlike [typic][crate-typic], we believe that this simplified definition imposes a safety hazard substantial enough to warrant making `NeglectStability` initially usable with *only* unsafe transmutes. Our RFC separates *constructability*, which concerns what aspects of a type's structure are part of its public API, and *stability*, which concerns the aspects of a type's layout that are part of its public API for SemVer purposes. This distinction does not appear in prior work. From 618c8d53d35f38d934a5619de8f5ff599fd8ae1b Mon Sep 17 00:00:00 2001 From: Jack Wrenn Date: Tue, 25 Aug 2020 17:12:46 -0400 Subject: [PATCH 40/61] fix: uninformative PromiseTransmutableInto::Archetype size trick --- rfcs/0000-safe-transmute.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rfcs/0000-safe-transmute.md b/rfcs/0000-safe-transmute.md index 1ac82dd..06f0d58 100644 --- a/rfcs/0000-safe-transmute.md +++ b/rfcs/0000-safe-transmute.md @@ -632,7 +632,7 @@ Since the `u8` field is private, its valid values are assumed to be constrained You may freely increase the required alignment of your type to any value greater-than-or-equal to the required alignment of your archetype. The minimum alignment requirement permitted by Rust is 1. ###### Archetype Size -You may decrease the size of your type without violating stability to any size less-than-or-equal to the size of your archetype. The minimum size of types in Rust is 0. +You may increase the size of your type without violating stability to any size greater-than-or-equal to the size of your archetype. The minimum size of types in Rust is 0. ###### Archetype Validity & Visibility You may freely make your type more constructible (in terms of both visibility and theoretical validity) than your archetype. A minimally-informative archetype may be constructed using `MaybeUninit` as a building-block, since *any* possible instantiation of a byte is a valid instantiation of `MaybeUninit`. From 98eb1673e14a880ab2baa9791f2a149e5d0efb5a Mon Sep 17 00:00:00 2001 From: Jack Wrenn Date: Tue, 25 Aug 2020 18:00:14 -0400 Subject: [PATCH 41/61] remove last dependency on const-generics! --- rfcs/0000-safe-transmute.md | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/rfcs/0000-safe-transmute.md b/rfcs/0000-safe-transmute.md index 06f0d58..518b560 100644 --- a/rfcs/0000-safe-transmute.md +++ b/rfcs/0000-safe-transmute.md @@ -2645,6 +2645,37 @@ where } ``` +### Removing the Dependency on Const Generics +The obvious implementations of these traits depends on constructing arrays of length `size_of::()`. At the time of writing, this is not permitted by const generics. Can we construct formulations that do not rely on speculative advancements in const generics? + +#### Alternative `FromBytes` +```rust +unsafe impl FromBytes for Dst +where + Dst: TransmuteFrom<[u8; usize::MAX]>, +{} +``` +This bound works because `[u8; usize::MAX]` will be greater-than-or-equal to the maximum object size (i.e., the maximum *possible* size of `Dst`), and because transmutations of values may truncate the source bytes. + +#### Alternative `IntoBytes` +```rust +unsafe impl IntoBytes for Src +where + [Src: usize::MAX]: TransmuteInto<[u8; usize::MAX]>, +{} +``` +Unless `Src` is a zero-sized type, its size will be greater-than-or-equal-to the size of a `u8` (1). + +This breaks down in two scenarios. First, if the size of `Src` is greater than `[u8; usize::MAX]`, then the transmutability of `Src`'s excess bytes will not be assessed. This is impossible: `[u8; usize::MAX]` will be greater-than-or-equal to the maximum object size. Second, if `Src` is a zero-sized-type, this bound will not be satisfied: we cannot construct a `[u8; usize::MAX]` from nothing. + +If `IntoBytes` is declared as a `#[marker]` trait, we could cover this scenario with: +```rust +unsafe impl IntoBytes for Src +where + Src: SizeEq<()>, +{} +``` + ## Extension: Casting [ext-slice-casting]: #extension-casting Given `TransmuteFrom`, we can construct safe abstractions for casts of slices and `Vec`s that effectively transmute the *contents* of those types. From 2f85008e99ea584d3f6bbc3b808ebfae41361c28 Mon Sep 17 00:00:00 2001 From: Jack Wrenn Date: Tue, 25 Aug 2020 18:04:26 -0400 Subject: [PATCH 42/61] nix extra sentence --- rfcs/0000-safe-transmute.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/rfcs/0000-safe-transmute.md b/rfcs/0000-safe-transmute.md index 518b560..4893de9 100644 --- a/rfcs/0000-safe-transmute.md +++ b/rfcs/0000-safe-transmute.md @@ -2664,8 +2664,6 @@ where [Src: usize::MAX]: TransmuteInto<[u8; usize::MAX]>, {} ``` -Unless `Src` is a zero-sized type, its size will be greater-than-or-equal-to the size of a `u8` (1). - This breaks down in two scenarios. First, if the size of `Src` is greater than `[u8; usize::MAX]`, then the transmutability of `Src`'s excess bytes will not be assessed. This is impossible: `[u8; usize::MAX]` will be greater-than-or-equal to the maximum object size. Second, if `Src` is a zero-sized-type, this bound will not be satisfied: we cannot construct a `[u8; usize::MAX]` from nothing. If `IntoBytes` is declared as a `#[marker]` trait, we could cover this scenario with: From c4f5d6030caaa1acd05a3605411f87004535c92c Mon Sep 17 00:00:00 2001 From: Jack Wrenn Date: Thu, 27 Aug 2020 16:31:00 -0400 Subject: [PATCH 43/61] factor out extensions --- rfcs/0000-ext-byte-transmutation.md | 134 +++++++ rfcs/0000-ext-container-casting.md | 299 +++++++++++++++ rfcs/0000-ext-generic-atomic.md | 73 ++++ rfcs/0000-ext-include-data.md | 70 ++++ rfcs/0000-ext-layout-traits.md | 145 ++++++++ rfcs/0000-ext-promise-transmutable.md | 54 +++ rfcs/0000-safe-transmute.md | 511 +------------------------- 7 files changed, 783 insertions(+), 503 deletions(-) create mode 100644 rfcs/0000-ext-byte-transmutation.md create mode 100644 rfcs/0000-ext-container-casting.md create mode 100644 rfcs/0000-ext-generic-atomic.md create mode 100644 rfcs/0000-ext-include-data.md create mode 100644 rfcs/0000-ext-layout-traits.md create mode 100644 rfcs/0000-ext-promise-transmutable.md diff --git a/rfcs/0000-ext-byte-transmutation.md b/rfcs/0000-ext-byte-transmutation.md new file mode 100644 index 0000000..60aabd7 --- /dev/null +++ b/rfcs/0000-ext-byte-transmutation.md @@ -0,0 +1,134 @@ +- Feature Name: byte_transmutations +- Start Date: (fill me in with today's date, YYYY-MM-DD) +- RFC PR: [rust-lang/rfcs#0000](https://github.com/rust-lang/rfcs/pull/0000) +- Rust Issue: [rust-lang/rust#0000](https://github.com/rust-lang/rust/issues/0000) + +[safer transmutation]: 0000-safe-transmute.md +[zerocopy]: https://crates.io/crates/zerocopy +[bytemuck]: https://crates.io/crates/bytemuck + +# Summary +[summary]: #summary + +This is a **library extension** to [safer transmutation] (i.e., it does not require additional compiler support) which introduces traits encoding common transmutation-conversions: *byte* transmutations: + - `FromZeros`, implemented if `Self` is initializable from an equivalently-sized array of zeroed bytes. + - `FromBytes`, implemented if `Self` is transmutable into an equivalently-sized array of initialized bytes. + - `IntoBytes`, implemented if `Self` is transmutable from an equivalently-sized array of initialized bytes. + +# Motivation +[motivation]: #motivation +Transmutations of types to-and-from equivalently-sized buffers of bytes are perhaps the most common use-case of transmutation; e.g., `FromBytes` and `AsBytes` traits of [zerocopy] form the foundation of Fuchsia's Rust networking stack. These traits can be formulated soundly and completely via [safer transmutation], but the obvious formulations aren't viable; e.g.: +```rust +// `Dst` is `FromBytes` if it can be safely transmuted *from* an +// equivalently sized array of `u8`. +unsafe impl FromBytes for Dst +where + Dst: TransmuteFrom<[u8; size_of::()]>, +{} +``` +At the time of writing, `size_of::()` *cannot* appear in this context. Our proposal provides implementation of these traits that do not rely on speculative advancements in const generics. + +Together, `IntoBytes` and `FromBytes` can form the basis of a [bytemuck]-like [`PlainOldData` trait](https://docs.rs/bytemuck/1.*/bytemuck/trait.Pod.html): +```rust +/// Implemented by types that are "plain old data": +pub unsafe trait PlainOldData +where + Neglect: TransmuteOptions +{} + +unsafe impl PlainOldData for T +where + T: FromBytes + IntoBytes, + Neglect: TransmuteOptions +{} +``` + +# Reference-level explanation +[reference-level-explanation]: #reference-level-explanation + +The implementations of these traits using [safer transmutation] follows: + +## `FromZeros` +Indicates that a type may be transmuted from an appropriately-sized array of zeroed bytes. This trait provide a safe alternative to [`mem::zeroed()`](https://doc.rust-lang.org/core/mem/fn.zeroed.html). + +```rust +pub unsafe trait FromZeros +where + Neglect: TransmuteOptions +{ + /// Safely initialize `Self` from zeroed bytes. + fn zeroed() -> Self + where + Neglect: SafeTransmuteOptions; + + /// Unsafely initialize `Self` from zeroed bytes. + fn unsafe_zeroed() -> Self + where + Neglect: TransmuteOptions; +} + +#[derive(Copy, Clone, PromiseTransmutableFrom, PromiseTransmutableInto)] +#[repr(u8)] +enum Zero { + Zero = 0u8 +} + +unsafe impl FromZeros for Dst +where + Dst: TransmuteFrom<[Zero; usize::MAX], Neglect>, + Neglect: TransmuteOptions +{ + fn zeroed() -> Self + where + Neglect: SafeTransmuteOptions + { + [Zero; size_of::].transmute_into() + } + + unsafe fn unsafe_zeroed() -> Self + where + Neglect: TransmuteOptions + { + [Zero; size_of::].unsafe_transmute_into() + } +} +``` + +## `FromBytes` +Indicates that a type may be transmuted from an appropriately-sized array of bytes. +```rust +pub unsafe trait FromBytes +where + Neglect: TransmuteOptions +{} + +unsafe impl FromBytes for Dst +where + Dst: TransmuteFrom<[u8; usize::MAX], Neglect>, + Neglect: TransmuteOptions +{} +``` + +## `IntoBytes` +Indicates that a type may be transmuted into an appropriately-sized array of bytes. +```rust +#[marker] +pub unsafe trait IntoBytes +where + Neglect: TransmuteOptions +{} + +// covers `size_of::() >= 1` +unsafe impl IntoBytes for Src +where + [Src: usize::MAX]: TransmuteInto<[u8; usize::MAX], Neglect>, + Neglect: TransmuteOptions +{} + +// covers `size_of::() == 0` +unsafe impl IntoBytes for Src +where + Src: SizeEq<()>, + Neglect: TransmuteOptions +{} +``` diff --git a/rfcs/0000-ext-container-casting.md b/rfcs/0000-ext-container-casting.md new file mode 100644 index 0000000..b3fb868 --- /dev/null +++ b/rfcs/0000-ext-container-casting.md @@ -0,0 +1,299 @@ +- Feature Name: castfrom +- Start Date: (fill me in with today's date, YYYY-MM-DD) +- RFC PR: [rust-lang/rfcs#0000](https://github.com/rust-lang/rfcs/pull/0000) +- Rust Issue: [rust-lang/rust#0000](https://github.com/rust-lang/rust/issues/0000) + +[safer transmutation]: 0000-safe-transmute.md +[zerocopy]: https://crates.io/crates/zerocopy +[packet]: https://fuchsia-docs.firebaseapp.com/rust/packet + +# Summary +[summary]: #summary + +This is a **library extension** to [safer transmutation] (i.e., it does not require additional compiler support) which introduces traits for transmute-like conversions between container-types like `Vec` and slices. + +# Motivation +[motivation]: #motivation + +The [safer transmutation] API makes it possible to define safe mechanisms for transmuting the contents of slices and `Vec`s. These conversions are not, themselves, sound *transmutations*: neither the layout of slices nor `Vec`s are well-defined. However, they each provide `from_raw_parts` constructors that can be used to effectively transmute their contents. + + +A slice cast is an operation that consumes `&'i [Src]` and produces `&'o [Dst]`. This conversion has both static and dynamic components. The length of a `&'i [Src]` is a dynamic quality of values of that type. If ths static sizes of `Src` and `Dst` differ, the length of `&'o [Dst]` will need to differ from that of `&'i [Src]` accordingly. The static component of this conversion is whether the layouts of `Src` and `Dst` even permit such a conversion---we can use `TransmuteFrom` to answer this: *Could a reference to a maximally-long array of `Src` be transmuted to an array of just one `Dst`?* + +Concretely: +```rust +fn cast<'i, 'o, Src, Dst>(src: &'i [Src]) -> &'o [Dst] +where + &'o [Dst; 1]: TransmuteFrom<&'i [Src; usize::MAX]> +{ + let len = size_of_val(src).checked_div(size_of::()).unwrap_or(0); + unsafe { slice::from_raw_parts(src.as_ptr() as *const Dst, len) } +} +``` + +The invariants imposed by [`Vec::from_raw_parts`](https://doc.rust-lang.org/alloc/vec/struct.Vec.html#method.from_raw_parts) are far stricter than those imposed by [`slice::from_raw_parts`](https://doc.rust-lang.org/core/slice/fn.from_raw_parts.html): we may only convert a `Vec` to `Vec` if: + - `U` is transmutable from `T` + - `U` has the same size as `T` + - `U` has the same static alignment as `T` + +Concretely: +```rust +fn cast(src: Vec) -> Vec +where + Dst: TransmuteFrom + + AlignEq, + + SizeEq, +{ + let (ptr, len, cap) = src.into_raw_parts(); + unsafe { Vec::from_raw_parts(ptr as *mut Dst, len, cap) } +} +``` + +The above `cast` functions have a major ergonomic drawback: they require callers to reiterate their `where` bounds. The bounds that determine the castability of a type is more ergonomically encapsulated with a *single* trait bound whose name clearly communicates that casting is possible. + +# Guide-level explanation +[guide-level-explanation]: #guide-level-explanation + +Mirroring the design of `TransmuteFrom`, this *could* take the form of a `CastFrom` trait: +```rust +pub mod cast { + + /// Instantiate `Self` from a value of type `Src`. + /// + /// The reciprocal of [CastInto]. + pub trait CastFrom + where + Neglect: options::CastOptions, + { + /// Instantiate `Self` by casting a value of type `Src`, safely. + fn cast_from(src: Src) -> Self + where + Src: Sized, + Self: Sized, + Neglect: options::SafeCastOptions + { + unsafe { CastFrom::<_,Neglect>::unsafe_cast_from(src) } + } + + /// Instantiate `Self` by casting a value of type `Src`, potentially safely. + unsafe fn unsafe_cast_from(src: Src) -> Self + where + Src: Sized, + Self: Sized, + Neglect: options::CastOptions; + } + + /// Options for casting. + pub mod options { + + /// The super-trait of all *safe* casting options. + #[marker] pub trait SafeCastOptions: CastOptions {} + + /// The super-trait of all casting options. + #[marker] pub trait CastOptions {} + + impl SafeCastOptions for () {} + impl CastOptions for () {} + + } +} +``` +...for which implementations on `Vec` and slices will be provided. + +Using this foundation, it is trivial to define the abstractions of the [zerocopy] and [packet] crates; e.g.: +```rust +impl<'a> &'a [u8] +{ + /// Read `&T` off the front of `self`, and shrink the underlying slice. + /// Analogous to: + /// - https://fuchsia-docs.firebaseapp.com/rust/packet/trait.BufferView.html#method.peek_obj_front + /// - https://fuchsia-docs.firebaseapp.com/rust/packet/trait.BufferView.html#method.take_obj_front + fn take_front<'t, T>(&'a mut self) -> Option<&'a T> + where + Self: CastInto<&'a [T]>, + { + let idx = size_of::(); + let (parsable, remainder) = self.split_at(idx); + *self = remainder; + parsable.cast_into().first() + } + + /// Read `&T` off the back of `self`, and shrink the underlying slice. + /// Analogous to: + /// - https://fuchsia-docs.firebaseapp.com/rust/packet/trait.BufferView.html#method.peek_obj_back + /// - https://fuchsia-docs.firebaseapp.com/rust/packet/trait.BufferView.html#method.take_obj_back + fn take_back<'t, T>(&'a mut self) -> Option<&'a T> + where + Self: CastInto<&'a [T]>, + { + let idx = self.len().saturating_sub(size_of::()); + let (remainder, parsable) = self.split_at(idx); + *self = remainder; + parsable.cast_into().first() + } +} +``` + +To parse a `UdpPacket` given a byte slice, we split the slice into a slice containing first 8 bytes, and the remainder. We then cast the first slice into a reference to a `UdpHeader`. A `UdpPacket`, then, consists of the reference to the `UdpHeader`, and the remainder slice. Concretely: +```rust +pub struct UdpPacket<'a> { + hdr: &'a UdpHeader, + body: &'a [u8], +} + +#[derive(PromiseTransmutableFrom, PromiseTransmutableInto)] +#[repr(C)] +struct UdpHeader { + src_port: [u8; 2], + dst_port: [u8; 2], + length: [u8; 2], + checksum: [u8; 2], +} + +impl<'a> UdpPacket<'a> { + pub fn parse(mut bytes: &'a [u8]) -> Option { + Some(UdpPacket { hdr: {bytes.take_front()?}, body: bytes }) + } +} +``` +(While this example is simple, the technique can be [expanded](https://fuchsia-docs.firebaseapp.com/rust/packet_formats/) to arbitrarily complex structures.) + + +# Reference-level explanation +[reference-level-explanation]: #reference-level-explanation + +This `CastFrom` trait might then be implemented like so for `Vec`: +```rust +/// A contiguous growable array type with heap-allocated contents, `Vec`. +pub mod vec { + use core::convert::{ + transmute::{ + TransmuteFrom, + options::{SafeTransmuteOptions, TransmuteOptions, NeglectAlignment}, + }, + cast::{ + CastFrom, + options::{ + SafeCastOptions, + CastOptions, + }, + }, + }; + + /// Safe options for casting `Vec` to `Vec`. + pub trait SafeVecCastOptions + : SafeCastOptions + + SafeTransmuteOptions + + VecCastOptions + {} + + /// Options for casting `Vec` to `Vec`. + pub trait VecCastOptions + : TransmuteOptions + + CastOptions + {} + + impl SafeCastOptions for Neglect {} + impl SafeVecCastOptions for Neglect {} + + impl CastOptions for Neglect {} + impl VecCastOptions for Neglect {} + + + use core::mem::{MaybeUninit, SizeEq, AlignEq}; + + impl CastFrom, Neglect> for Vec + where + Neglect: VecCastOptions, + Dst: TransmuteFrom + + AlignEq + + SizeEq, + { + #[doc(hidden)] + #[inline(always)] + unsafe fn unsafe_cast_from(src: Vec) -> Vec + { + let (ptr, len, cap) = src.into_raw_parts(); + Vec::from_raw_parts(ptr as *mut Dst, len, cap) + } + } +} +``` + +...and like so for slices: +```rust +pub mod slice { + use core::convert::{ + transmute::{ + TransmuteFrom, + options::{SafeTransmuteOptions, TransmuteOptions}, + }, + cast::{ + CastFrom, + options::{ + SafeCastOptions, + CastOptions, + }, + }, + }; + + use core::{ + mem::{size_of, size_of_val}, + slice + }; + + /// *Safe* options for casting **slices**. + pub trait SafeSliceCastOptions + : SafeCastOptions + + SafeTransmuteOptions + + SliceCastOptions + {} + + /// Options for casting **slices**. + pub trait SliceCastOptions + : CastOptions + + TransmuteOptions + {} + + impl SafeCastOptions for Neglect {} + impl SafeSliceCastOptions for Neglect {} + + impl CastOptions for Neglect {} + impl SliceCastOptions for Neglect {} + + + impl<'i, 'o, Src, Dst, Neglect> CastFrom<&'i [Src], Neglect> for &'o [Dst] + where + Neglect: SliceCastOptions, + &'o [Dst; 1]: TransmuteFrom<&'i [Src; usize::MAX], Neglect> + { + unsafe fn unsafe_cast_from(src: &'i [Src]) -> &'o [Dst] + { + let len = size_of_val(src).checked_div(size_of::()).unwrap_or(0); + unsafe { slice::from_raw_parts(src.as_ptr() as *const Dst, len) } + } + } + + impl<'i, 'o, Src, Dst, Neglect> CastFrom<&'i mut [Src], Neglect> for &'o mut [Dst] + where + Neglect: SliceCastOptions, + &'o mut [Dst; 1]: TransmuteFrom<&'i mut [Src; usize::MAX], Neglect> + { + unsafe fn unsafe_cast_from(src: &'i mut [Src]) -> &'o mut [Dst] + { + let len = size_of_val(src).checked_div(size_of::()).unwrap_or(0); + unsafe { slice::from_raw_parts_mut(src.as_ptr() as *mut Dst, len) } + } + } + + impl<'i, 'o, Src, Dst, Neglect> CastFrom<&'i mut [Src], Neglect> for &'o [Dst] + where + { + unsafe fn unsafe_cast_from(src: &'i mut [Src]) -> &'o [Dst] + { + let len = size_of_val(src).checked_div(size_of::()).unwrap_or(0); + unsafe { slice::from_raw_parts(src.as_ptr() as *const Dst, len) } + } + } +} +``` diff --git a/rfcs/0000-ext-generic-atomic.md b/rfcs/0000-ext-generic-atomic.md new file mode 100644 index 0000000..667d4e0 --- /dev/null +++ b/rfcs/0000-ext-generic-atomic.md @@ -0,0 +1,73 @@ +- Feature Name: generic_atomic +- Start Date: (fill me in with today's date, YYYY-MM-DD) +- RFC PR: [rust-lang/rfcs#0000](https://github.com/rust-lang/rfcs/pull/0000) +- Rust Issue: [rust-lang/rust#0000](https://github.com/rust-lang/rust/issues/0000) + +[safer transmutation]: 0000-safe-transmute.md + +# Summary +[summary]: #summary + +This is a **library extension** to [safer transmutation] (i.e., it does not require additional compiler support) which introduces `Atomic`. + +# Motivation +[motivation]: #motivation + +Rust defines a dozen `Atomic*` types (`AtomicBool`, `AtomicI8`, `AtomicI16`, `AtomicI32`, `AtomicI64`, `AtomicIsize`, `AtomicPtr`, `AtomicU8`, `AtomicU16`, `AtomicU32`, `AtomicU64`, and `AtomicUsize`). + +This set is large—a distinct `Atomic*` type is required for each primitive type—but incomplete. If one wants atomic operations on their own type, they must define a wrapper around an existing `Atomic*` type of appropriate size and validity, then transmute at API boundaries; e.g.: + +```rust +#[repr(u8)] +enum Trilean { + False, + True, + Unknown, +} + +#[repr(transparent)] +pub AtomicTrilean(AtomicU8); + +impl AtomicTrilean { + + pub const fn new(v: Trilean) -> Self { + AtomicTrilean( + AtomicU8::new( + unsafe { mem::transmute(v) } + )) + } + + ... +} +``` + +The [safer transmutation] mechanisms permit a truly-generic `Atomic` type. + +# Reference-level explanation +[reference-level-explanation]: #reference-level-explanation + +Using the [safe transmutation] mechanisms, `Atomic` might be defined like so: + +```rust +type LargestPlatformAtomic = u64; // platform-dependent + +pub struct Atomic +where + T: SizeLtEq +{ + v: UnsafeCell +} + +impl Atomic +{ + #[inline] + pub fn load(&self, order: Ordering) -> T { + unsafe { atomic_load(self.v.get(), order) } + } + + #[inline] + pub fn store(&self, val: T, order: Ordering) -> T { + unsafe { atomic_store(self.v.get(), val, order) } + } +} +``` \ No newline at end of file diff --git a/rfcs/0000-ext-include-data.md b/rfcs/0000-ext-include-data.md new file mode 100644 index 0000000..63300b3 --- /dev/null +++ b/rfcs/0000-ext-include-data.md @@ -0,0 +1,70 @@ +- Feature Name: include_data +- Start Date: (fill me in with today's date, YYYY-MM-DD) +- RFC PR: [rust-lang/rfcs#0000](https://github.com/rust-lang/rfcs/pull/0000) +- Rust Issue: [rust-lang/rust#0000](https://github.com/rust-lang/rust/issues/0000) + +[safer transmutation]: 0000-safe-transmute.md + +# Summary +[summary]: #summary + +This is a **library extension** to [safer transmutation] (i.e., it does not require additional compiler support) which introduces `include_data!`, a safe, generic alternative to `include_bytes!`. + +# Motivation +[motivation]: #motivation + +Rust's [`include_bytes!` macro](https://doc.rust-lang.org/core/macro.include_bytes.html) lets you statically include the contents of a file into your executable's binary. The builtin is a quick-and-dirty solution for packaging data with your executable, and perhaps even helping the compiler optimize your code. **Unfortunately, it's difficult to use correctly.** Consider: +```rust +pub fn recognize(input: &Matrix) -> usize +{ + static RAW_WEIGHT : &'static [u8; 62_720] = include_bytes!("/weight.bin"); + + static RAW_BIAS : &'static [u8; 80] = include_bytes!("/bias.bin"); + + let WEIGHT: &Matrix = unsafe{ mem::transmute(RAW_WEIGHT) }; + + let BIAS: &Matrix = unsafe{ mem::transmute(RAW_BIAS) }; + + network::recognize(input, WEIGHT, BIAS) +} +``` +This is memory-unsafe, because `RAW_WEIGHT` and `RAW_BIAS` might not meet the alignment requirements of `Matrix`. This fix is subtle: +```rust +pub fn recognize(input: &Matrix) -> usize +{ + static RAW_WEIGHT : &'static [u8; 62_720] = include_bytes!("/weight.bin"); + + static RAW_BIAS : &'static [u8; 80] = include_bytes!("/bias.bin"); + + let WEIGHT: &Matrix = unsafe{ &mem::transmute(*RAW_WEIGHT) }; + + let BIAS: &Matrix = unsafe{ &mem::transmute(*RAW_BIAS) }; + + network::recognize(input, WEIGHT, BIAS) +} +``` +Even still, there is potential for undefined behavior: `mem::transmute` does almost nothing to ensure that the included bytes are a *valid* instance of `Matrix`. This RFC proposes a **safe** `include_transmute!` macro that lacks these disadvantages; e.g.: +```rust +pub fn recognize(input: &Matrix) -> usize +{ + static WEIGHT: &Matrix = include_transmute!("/weight.bin"); + + static BIAS: &Matrix = include_transmute!("/weight.bin"); + + network::recognize(input, &WEIGHT, &BIAS) +} +``` + +# Reference-level explanation +[reference-level-explanation]: #reference-level-explanation + +Using the [safe transmutation] mechanisms, `include_transmute!` might be defined like so: + +```rust +macro_rules! include_transmute { + ($file:expr) => {{ + use core::convert::transmute::*; + &safe_transmute<_, _, ()>(*include_bytes!($file)) + }}; +} +``` \ No newline at end of file diff --git a/rfcs/0000-ext-layout-traits.md b/rfcs/0000-ext-layout-traits.md new file mode 100644 index 0000000..53a3446 --- /dev/null +++ b/rfcs/0000-ext-layout-traits.md @@ -0,0 +1,145 @@ +- Feature Name: layout_traits +- Start Date: (fill me in with today's date, YYYY-MM-DD) +- RFC PR: [rust-lang/rfcs#0000](https://github.com/rust-lang/rfcs/pull/0000) +- Rust Issue: [rust-lang/rust#0000](https://github.com/rust-lang/rust/issues/0000) + +[safer transmutation]: 0000-safe-transmute.md + +# Summary +[summary]: #summary + +This is a **library extension** to [safer transmutation] (i.e., it does not require additional compiler support) which introduces traits that are implemented depending on the sizes and alignments of two given types: + - `mem::AlignLtEq`, implemented for `Lhs` if `align_of::() <= align_of::` + - `mem::AlignEq`, implemented for `Lhs` if `align_of::() == align_of::` + - `mem::SizeLtEq`, implemented for `Lhs` if `size_of::() <= size_of::` + - `mem::SizeEq`, implemented for `Lhs` if `size_of::() == size_of::` + +# Motivation +[motivation]: #motivation + +Some abstractions that depend on a type's layout are more easier thought of in terms of size or alignment constraints than transmutability. For instance: + +## Pointer Bit Packing +A common bit-packing technique involves abusing the relationship between allocations and alignment. If a type is aligned to 2n, then the *n* least significant bits of pointers to that type will equal `0`. These known-zero bits can be packed with data. Since alignment cannot be currently reasoned about at the type-level, it's currently impossible to bound instantiations of a generic parameter based on minimum alignment. + +Using [safer transmutation], we can require that a reference to a generic type `T` has alignment of at least a given value (e.g., `8`) by first defining a ZST with that alignment: +```rust +#[derive(PromiseTransmutableFrom)] +#[repr(align(8)] +struct Aligned8; +``` +and then adding this `where` bound to our abstraction: +```rust +where + &T: TransmuteInto<&Aligned8> +``` + +The intuition behind this trait bound requires a thorough understanding of transmutability. With this RFC, this bound would be more clearly expressed as: +```rust +#[derive(PromiseTransmutableFrom)] +#[repr(align(8)] +struct Aligned8; +``` + +## `Vec` Casting +The invariants imposed by [`Vec::from_raw_parts`](https://doc.rust-lang.org/alloc/vec/struct.Vec.html#method.from_raw_parts) stipulate that we may only convert a `Vec` to `Vec` if: + - `U` is transmutable from `T` + - `U` has the same minimum alignment as `T` + - `U` has the same size as `T` + +With this RFC, these requirements may be written directly: +```rust +fn cast(src: Vec) -> Vec +where + Dst: TransmuteFrom + + AlignEq, + + SizeEq, +{ + let (ptr, len, cap) = src.into_raw_parts(); + unsafe { Vec::from_raw_parts(ptr as *mut Dst, len, cap) } +} +``` + +# Reference-level explanation +[reference-level-explanation]: #reference-level-explanation + +Given `TransmuteFrom` and `TransmuteInto`, we can construct bounds that check certain properties of a type by checking if its convertible to another, contrived type. These gadgets are useful, but subtle to formulate. + +## Querying Alignment +The type `[T; 0]` shares the alignment requirements of `T`, but no other layout properties. A type `&[T; 0]` will only be transmutable to `&[U; 0]`, if the minimum alignment of `T` is greater than that of `U`. We exploit this to define a trait that is implemented for `Self` if its alignment is less-than-or-equal to that of `Rhs`: +```rust +/// Implemented if `align_of::() <= align_of::()` +pub trait AlignLtEq +where + Neglect: TransmuteOptions, +{} + +impl AlignLtEq for Lhs +where + Neglect: TransmuteOptions, + for<'a> &'a [Lhs; 0]: TransmuteFrom<&'a [Rhs; 0], Neglect> +{} +``` +Furthermore, if the alignment of `Self` is less-than-or-equal to `Rhs`, and the alignment of `Rhs` is less-than-or-equal to `Self`, then the alignments of `Self` and `Rhs` must be equal: +```rust +/// Implemented if `align_of::() == align_of::()` +pub trait AlignEq +where + Neglect: TransmuteOptions, +{} + +impl AlignEq for Lhs +where + Neglect: TransmuteOptions, + Lhs: AlignLtEq, + Rhs: AlignLtEq, +{} +``` + +## Querying Size +Querying *just* the size of a type is trickier: how might we query the size without implicitly querying the alignment and validity of the bytes that contribute to that size? + +We do so by constructing a contrived container type that neutralizes the alignment and validity aspects of its contents: +```rust +#[derive(PromiseTransmutableFrom, PromiseTransmutableInto)] */ +#[repr(C)] +struct Gadget(pub [Align; 0], pub MaybeUninit); +``` +This type will have the size of `Size`, and alignment equal to the maximum of `Align` and `Size`. `MaybeUninit` neutralizes the bit-validity qualities of `Size`. + +We use this `Gadget` to define a trait that is implemented if the size of `Self` is less-than-or-equal to the size of `Rhs`: +```rust +/// Implemented if `size_of::() <= size_of::()` +pub trait SizeLtEq +where + Neglect: TransmuteOptions, +{} + +impl SizeLtEq for Lhs +where + Neglect: TransmuteOptions, + for<'a> &'a Gadget: TransmuteFrom<&'a Gadget, Neglect>, +{} +``` +This works, because `Gadget` and `Gadget` will have equal alignment, and equal bit-validity (they consist solely of padding bytes). Thus, all that varies between them is their size, and reference transmutations must either preserve or reduce the size of the transmuted type. + +As before, if the size of `Self` is less-than-or-equal to `Rhs`, and the size of `Rhs` is less-than-or-equal to `Self`, then the sizes of `Self` and `Rhs` must be equal: +```rust +/// Implemented if `size_of::() == size_of::()` +pub trait SizeEq +where + Neglect: TransmuteOptions, +{} + +impl SizeEq for Lhs +where + Neglect: TransmuteOptions, + Lhs: SizeLtEq, + Rhs: SizeLtEq, +{} +``` + +# Prior art +[prior-art]: #prior-art + +The [FromBits](https://github.com/joshlf/rfcs/blob/joshlf/from-bits/text/0000-from-bits.md#sizeleq-and-alignleq) pre-RFC, recommends adding two similar traits: `AlignLeq` and `SizeLeq`. This pre-RFC envisions these traits as requiring additional compiler support; our proposal formulates equivalent traits just using [safer transmutation]. diff --git a/rfcs/0000-ext-promise-transmutable.md b/rfcs/0000-ext-promise-transmutable.md new file mode 100644 index 0000000..99c0e99 --- /dev/null +++ b/rfcs/0000-ext-promise-transmutable.md @@ -0,0 +1,54 @@ +- Feature Name: promise_transmutable +- Start Date: (fill me in with today's date, YYYY-MM-DD) +- RFC PR: [rust-lang/rfcs#0000](https://github.com/rust-lang/rfcs/pull/0000) +- Rust Issue: [rust-lang/rust#0000](https://github.com/rust-lang/rust/issues/0000) + +[safer transmutation]: 0000-safe-transmute.md +[zerocopy]: https://crates.io/crates/zerocopy +[bytemuck]: https://crates.io/crates/bytemuck + +# Summary +[summary]: #summary + +This is a compiler-suported extension to [safer transmutation], adding `#[derive(PromiseTransmutable)]`, which expands to `#[derive(PromiseTransmutableFrom, PromiseTransmutableInto)]`. + +# Motivation +[motivation]: #motivation + +We anticipate that *most* users will merely want to promise that their types are as-stable-as-possible. To do so, [safer transmutation] provides this shorthand: +```rust +#[derive(PromiseTransmutableFrom, PromiseTransmutableInto)] +#[repr(C)] +pub struct Foo(pub Bar, pub Baz); +``` +As a shorthand, this is still rather long. For such users, the separability of `PromiseTransmutableFrom` and `PromiseTransmutableInto` is totally irrelevant. + +# Guide-level explanation +[guide-level-explanation]: #guide-level-explanation + +We propose a `derive(PromiseTransmutable)` shorthand, such that this: +```rust +#[derive(PromiseTransmutable)] +#[repr(C)] +pub struct Foo(pub Bar, pub Baz); +``` +...is equivalent to this: +```rust +#[derive(PromiseTransmutableFrom, PromiseTransmutableInto)] +#[repr(C)] +pub struct Foo(pub Bar, pub Baz); +``` + +# Reference-level explanation +[reference-level-explanation]: #reference-level-explanation + +We caution *against* adding a corresponding trait or trait alias; e.g.: +```rust +trait PromiseTransmutable = PromiseTransmutableFrom + PromiseTransmutableInto; +``` +The vast majority of users will *only* confront the stability declaration traits in the context of deriving them; the *only* scenario in which end-users will refer to these traits in a type-context is the rare use-case of *manually* implementing them. For such users, the separability of `PromiseTransmutableFrom` and `PromiseTransmutableInto` *is* relevant. The availability of a `PromiseTransmutable` trait or trait alias in this scenario would be a distraction, since referring to it in a type-context is almost certainly a misstep. + +# Drawbacks +[drawbacks]: #drawbacks + +We acknowledge that it is unusual for a `derive` macro to not implement an item of the same name, but this weirdness is outweighed by the weirdness of the alternative: providing a trait for which there is almost no good use. diff --git a/rfcs/0000-safe-transmute.md b/rfcs/0000-safe-transmute.md index 4893de9..0dc9562 100644 --- a/rfcs/0000-safe-transmute.md +++ b/rfcs/0000-safe-transmute.md @@ -756,6 +756,8 @@ Neglecting stability over-eagerly cannot cause unsoundness or unsafety. For this By using the `NeglectStability` option to transmute types you do not own, you are committing to ensure that your reliance on these types' layouts is consistent with their documented stability guarantees. #### `NeglectAlignment` +[ext-ref-casting]: #NeglectAlignment + By default, `TransmuteFrom` and `TransmuteInto`'s methods require that, when transmuting references, the minimum alignment of the destination's referent type is no greater than the minimum alignment of the source's referent type. The `NeglectAlignment` option disables this requirement. ```rust pub struct NeglectAlignment; @@ -2452,522 +2454,25 @@ We divide future possibilities into two categories: ## Extension: `PromiseTransmutable` Shorthand [extension-promisetransmutable-shorthand]: #extension-promisetransmutable-shorthand -We anticipate that *most* users will merely want to [promise][stability-common] that their types are as-stable-as-possible. To do so, this RFC proposes the shorthand: -```rust -#[derive(PromiseTransmutableFrom, PromiseTransmutableInto)] -#[repr(C)] -pub struct Foo(pub Bar, pub Baz); -``` -As a shorthand, this is still rather long. For such users, the separability of `PromiseTransmutableFrom` and `PromiseTransmutableInto` is totally irrelevant. We therefore propose a `derive(PromiseTransmutable)` shorthand, such that this: -```rust -#[derive(PromiseTransmutable)] -#[repr(C)] -pub struct Foo(pub Bar, pub Baz); -``` -...is equivalent to this: -```rust -#[derive(PromiseTransmutableFrom, PromiseTransmutableInto)] -#[repr(C)] -pub struct Foo(pub Bar, pub Baz); -``` - -However, we caution *against* adding a corresponding trait or trait alias; e.g.: -```rust -trait PromiseTransmutable = PromiseTransmutableFrom + PromiseTransmutableInto; -``` -The vast majority of users will *only* confront the stability declaration traits in the context of deriving them; the *only* scenario in which end-users will refer to these traits in a type-context is the [rare use-case][stability-uncommon] of *manually* implementing them. For such users, the separability of `PromiseTransmutableFrom` and `PromiseTransmutableInto` *is* relevant. The availability of a `PromiseTransmutable` trait or trait alias in this scenario would be a distraction, since referring to it in a type-context is almost certainly a misstep. - -We acknowledge that it is unusual for a `derive` macro to not create an item of the same name, but this weirdness is outweighed by the weirdness of the alternative: providing a trait for which there is almost no good use. +See [here](0000-ext-promise-transmutable.md). ## Extension: Layout Property Traits -Given `TransmuteFrom` and `TransmuteInto`, we can construct bounds that check certain properties of a type by checking if its convertible to another, contrived type. These gadgets are useful (see [`Vec` casting][ext-vec-casting] for an example), and their usefulness could justify adding them to `libcore`—perhaps in the `mem` module. - -### Querying Alignment -The type `[T; 0]` shares the alignment requirements of `T`, but no other layout properties. A type `&[T; 0]` will only be transmutable to `&[U; 0]`, if the minimum alignment of `T` is greater than that of `U`. We exploit this to define a trait that is implemented for `Self` if its alignment is less-than-or-equal to that of `Rhs`: -```rust -/// Implemented if `align_of::() <= align_of::()` -pub trait AlignLtEq -where - Neglect: TransmuteOptions, -{} - -impl AlignLtEq for Lhs -where - Neglect: TransmuteOptions, - for<'a> &'a [Lhs; 0]: TransmuteFrom<&'a [Rhs; 0], Neglect> -{} -``` -Furthermore, if the alignment of `Self` is less-than-or-equal to `Rhs`, and the alignment of `Rhs` is less-than-or-equal to `Self`, then the alignments of `Self` and `Rhs` must be equal: -```rust -/// Implemented if `align_of::() == align_of::()` -pub trait AlignEq -where - Neglect: TransmuteOptions, -{} - -impl AlignEq for Lhs -where - Neglect: TransmuteOptions, - Lhs: AlignLtEq, - Rhs: AlignLtEq, -{} -``` - -### Querying Size -Querying *just* the size of a type is trickier: how might we query the size without implicitly querying the alignment and validity of the bytes that contribute to that size? - -We do so by constructing a contrived container type that neutralizes the alignment and validity aspects of its contents: -```rust -#[derive(PromiseTransmutableFrom, PromiseTransmutableInto)] */ -#[repr(C)] -pub struct Gadget(pub [Align; 0], pub MaybeUninit); -``` -This type will have the size of `Size`, and alignment equal to the maximum of `Align` and `Size`. `MaybeUninit` neutralizes the bit-validity qualities of `Size`. - -We use this `Gadget` to define a trait that is implemented if the size of `Self` is less-than-or-equal to the size of `Rhs`: -```rust -/// Implemented if `size_of::() <= size_of::()` -pub trait SizeLtEq -where - Neglect: TransmuteOptions, -{} - -impl SizeLtEq for Lhs -where - Neglect: TransmuteOptions, - for<'a> &'a Gadget: TransmuteFrom<&'a Gadget, Neglect>, -{} -``` -This works, because `Gadget` and `Gadget` will have equal alignment, and equal bit-validity (they consist solely of padding bytes). Thus, all that varies between them is their size, and reference transmutations must either preserve or reduce the size of the transmuted type. - -As before, if the size of `Self` is less-than-or-equal to `Rhs`, and the size of `Rhs` is less-than-or-equal to `Self`, then the sizes of `Self` and `Rhs` must be equal: -```rust -/// Implemented if `size_of::() == size_of::()` -pub trait SizeEq -where - Neglect: TransmuteOptions, -{} - -impl SizeEq for Lhs -where - Neglect: TransmuteOptions, - Lhs: SizeLtEq, - Rhs: SizeLtEq, -{} -``` - -## Extension: Byte Transmutation Traits -[marker-traits]: #Extension-Byte-Transmutation-Traits - -The marker traits from [zerocopy][crate-zerocopy] are expressible using `TransmuteFrom` and `TransmuteInto`. In principle, these traits could remain in crates. However, we believe their clarity and broad usefulness may justify their inclusion in `libcore`. - -### `IntoBytes` -Indicates that a type may be transmuted into an appropriately-sized array of bytes. -```rust -pub unsafe trait IntoBytes -where - Neglect: TransmuteOptions -{} - -// `Src` is `IntoBytes` if it can be safely transmuted *into* an -// equivalently sized array of `u8`. -unsafe impl IntoBytes for Src -where - Src: Sized + TransmuteInto<[u8; size_of::()], Neglect>, - Neglect: TransmuteOptions -{} -``` - -### `FromBytes` -Indicates that a type may be transmuted from an appropriately-sized array of bytes. -```rust -pub unsafe trait FromBytes -where - Neglect: TransmuteOptions -{} - -// `Dst` is `FromBytes` if it can be safely transmuted *from* an -// equivalently sized array of `u8`. -unsafe impl FromBytes for Dst -where - Dst: Sized + TransmuteFrom<[u8; size_of::()], Neglect>, - Neglect: TransmuteOptions -{} -``` -### `FromZeros` -[future-possibility-safe-initialization]: #FromZeros - -Indicates that a type may be transmuted from an appropriately-sized array of zeroed bytes. Equivalently, `FromZeros` indicates that a type may be safely initialized with `mem::zeroed`. - -```rust -pub unsafe trait FromZeros -where - Neglect: TransmuteOptions -{ - /// Safely initialize `Self` from zeroed bytes. - fn zeroed() -> Self - where - Neglect: SafeTransmuteOptions; - - /// Unsafely initialize `Self` from zeroed bytes. - fn unsafe_zeroed() -> Self - where - Neglect: SafeTransmuteOptions; -} +See [here](0000-ext-layout-traits.md). -#[derive(Copy)] -#[repr(u8)] -enum Zero { - Zero = 0u8 -} - -// `T` is `FromZeros` if it can be safely transmuted *from* an -// equivalently sized array of zeroed bytes. -unsafe impl FromZeros for Dst -where - Dst: Sized + TransmuteFrom<[Zero; size_of::()], Neglect>, - Neglect: TransmuteOptions -{ - fn zeroed() -> Self - where - Neglect: SafeTransmuteOptions - { - [Zero; size_of::].transmute_into() - } - - unsafe fn unsafe_zeroed() -> Self - where - Neglect: TransmuteOptions - { - [Zero; size_of::].unsafe_transmute_into() - } -} -``` - -### Removing the Dependency on Const Generics -The obvious implementations of these traits depends on constructing arrays of length `size_of::()`. At the time of writing, this is not permitted by const generics. Can we construct formulations that do not rely on speculative advancements in const generics? - -#### Alternative `FromBytes` -```rust -unsafe impl FromBytes for Dst -where - Dst: TransmuteFrom<[u8; usize::MAX]>, -{} -``` -This bound works because `[u8; usize::MAX]` will be greater-than-or-equal to the maximum object size (i.e., the maximum *possible* size of `Dst`), and because transmutations of values may truncate the source bytes. - -#### Alternative `IntoBytes` -```rust -unsafe impl IntoBytes for Src -where - [Src: usize::MAX]: TransmuteInto<[u8; usize::MAX]>, -{} -``` -This breaks down in two scenarios. First, if the size of `Src` is greater than `[u8; usize::MAX]`, then the transmutability of `Src`'s excess bytes will not be assessed. This is impossible: `[u8; usize::MAX]` will be greater-than-or-equal to the maximum object size. Second, if `Src` is a zero-sized-type, this bound will not be satisfied: we cannot construct a `[u8; usize::MAX]` from nothing. - -If `IntoBytes` is declared as a `#[marker]` trait, we could cover this scenario with: -```rust -unsafe impl IntoBytes for Src -where - Src: SizeEq<()>, -{} -``` ## Extension: Casting [ext-slice-casting]: #extension-casting -Given `TransmuteFrom`, we can construct safe abstractions for casts of slices and `Vec`s that effectively transmute the *contents* of those types. - -### Slice Casting -A slice cast is an operation that consumes `&'i [Src]` and produces `&'o [Dst]`. This conversion has both static and dynamic components. The length of a `&'i [Src]` is a dynamic quality of values of that type. If ths static sizes of `Src` and `Dst` differ, the length of `&'o [Dst]` will need to differ from that of `&'i [Src]` accordingly. The static component of this conversion is whether the layouts of `Src` and `Dst` even permit such a conversion---we can use `TransmuteFrom` to answer this: *Could a reference to a maximally-long array of `Src` be transmuted to an array of just one `Dst`?* - -Concretely: -```rust -fn cast<'i, 'o, Src, Dst>(src: &'i [Src]) -> &'o [Dst] -where - &'o [Dst; 1]: TransmuteFrom<&'i [Src; usize::MAX]> -{ - let len = size_of_val(src).checked_div(size_of::()).unwrap_or(0); - unsafe { slice::from_raw_parts(src.as_ptr() as *const Dst, len) } -} -``` - -### `Vec` Casting -[ext-vec-casting]: #vec-casting - -The invariants imposed by [`Vec::from_raw_parts`](https://doc.rust-lang.org/alloc/vec/struct.Vec.html#method.from_raw_parts) are far stricter than those imposed by [`slice::from_raw_parts`](https://doc.rust-lang.org/core/slice/fn.from_raw_parts.html): we may only convert a `Vec` to `Vec` if: - - - `U` is transmutable from `T` - - `U` has the same size as `T` - - `U` has the same static alignment as `T` - -Concretely: -```rust -fn cast(src: Vec) -> Vec -where - Dst: TransmuteFrom - + AlignEq, - + SizeEq, -{ - let (ptr, len, cap) = src.into_raw_parts(); - unsafe { Vec::from_raw_parts(ptr as *mut Dst, len, cap) } -} -``` - -### Cast Ergonomics -The above `cast` functions have a major ergonomic drawback: they require callers to reiterate their `where` bounds. The bounds that determine the castability of a type is more ergonomically encapsulated with a *single* trait bound whose name clearly communicates that casting is possible. - -Mirroring the design of `TransmuteFrom`, this *could* take the form of a `CastFrom` trait: -```rust -pub mod cast { - - /// Instantiate `Self` from a value of type `Src`. - /// - /// The reciprocal of [CastInto]. - pub trait CastFrom - where - Neglect: options::CastOptions, - { - /// Instantiate `Self` by casting a value of type `Src`, safely. - fn cast_from(src: Src) -> Self - where - Src: Sized, - Self: Sized, - Neglect: options::SafeCastOptions - { - unsafe { CastFrom::<_,Neglect>::unsafe_cast_from(src) } - } - - /// Instantiate `Self` by casting a value of type `Src`, potentially safely. - unsafe fn unsafe_cast_from(src: Src) -> Self - where - Src: Sized, - Self: Sized, - Neglect: options::CastOptions; - } - - /// Options for casting. - pub mod options { - - /// The super-trait of all *safe* casting options. - #[marker] pub trait SafeCastOptions: CastOptions {} - - /// The super-trait of all casting options. - #[marker] pub trait CastOptions {} - - impl SafeCastOptions for () {} - impl CastOptions for () {} - - } -} -``` -...which might then be implemented like so for `Vec`: -```rust -/// A contiguous growable array type with heap-allocated contents, `Vec`. -pub mod vec { - use core::convert::{ - transmute::{ - TransmuteFrom, - options::{SafeTransmuteOptions, TransmuteOptions, NeglectAlignment}, - }, - cast::{ - CastFrom, - options::{ - SafeCastOptions, - CastOptions, - }, - }, - }; - - /// Safe options for casting `Vec` to `Vec`. - pub trait SafeVecCastOptions - : SafeCastOptions - + SafeTransmuteOptions - + VecCastOptions - {} - - /// Options for casting `Vec` to `Vec`. - pub trait VecCastOptions - : TransmuteOptions - + CastOptions - {} - - impl SafeCastOptions for Neglect {} - impl SafeVecCastOptions for Neglect {} - - impl CastOptions for Neglect {} - impl VecCastOptions for Neglect {} - - - use core::mem::{MaybeUninit, SizeEq, AlignEq}; - - impl CastFrom, Neglect> for Vec - where - Neglect: VecCastOptions, - Dst: TransmuteFrom - + AlignEq - + SizeEq, - { - #[doc(hidden)] - #[inline(always)] - unsafe fn unsafe_cast_from(src: Vec) -> Vec - { - let (ptr, len, cap) = src.into_raw_parts(); - Vec::from_raw_parts(ptr as *mut Dst, len, cap) - } - } -} -``` -...and like this for slices: -```rust -pub mod slice { - use core::convert::{ - transmute::{ - TransmuteFrom, - options::{SafeTransmuteOptions, TransmuteOptions}, - }, - cast::{ - CastFrom, - options::{ - SafeCastOptions, - CastOptions, - }, - }, - }; - - use core::{ - mem::{size_of, size_of_val}, - slice - }; - - /// *Safe* options for casting **slices**. - pub trait SafeSliceCastOptions - : SafeCastOptions - + SafeTransmuteOptions - + SliceCastOptions - {} +[ext-vec-casting]: #extension-casting - /// Options for casting **slices**. - pub trait SliceCastOptions - : CastOptions - + TransmuteOptions - {} - - impl SafeCastOptions for Neglect {} - impl SafeSliceCastOptions for Neglect {} - - impl CastOptions for Neglect {} - impl SliceCastOptions for Neglect {} - - - impl<'i, 'o, Src, Dst, Neglect> CastFrom<&'i [Src], Neglect> for &'o [Dst] - where - Neglect: SliceCastOptions, - &'o [Dst; 1]: TransmuteFrom<&'i [Src; usize::MAX], Neglect> - { - unsafe fn unsafe_cast_from(src: &'i [Src]) -> &'o [Dst] - { - let len = size_of_val(src).checked_div(size_of::()).unwrap_or(0); - unsafe { slice::from_raw_parts(src.as_ptr() as *const Dst, len) } - } - } - - impl<'i, 'o, Src, Dst, Neglect> CastFrom<&'i mut [Src], Neglect> for &'o mut [Dst] - where - Neglect: SliceCastOptions, - &'o mut [Dst; 1]: TransmuteFrom<&'i mut [Src; usize::MAX], Neglect> - { - unsafe fn unsafe_cast_from(src: &'i mut [Src]) -> &'o mut [Dst] - { - let len = size_of_val(src).checked_div(size_of::()).unwrap_or(0); - unsafe { slice::from_raw_parts_mut(src.as_ptr() as *mut Dst, len) } - } - } - - impl<'i, 'o, Src, Dst, Neglect> CastFrom<&'i mut [Src], Neglect> for &'o [Dst] - where - { - unsafe fn unsafe_cast_from(src: &'i mut [Src]) -> &'o [Dst] - { - let len = size_of_val(src).checked_div(size_of::()).unwrap_or(0); - unsafe { slice::from_raw_parts(src.as_ptr() as *const Dst, len) } - } - } -} -``` +See [here](0000-ext-container-casting.md). ## Extension: `include_data!` [future-possibility-include_data]: #Extension-include_data -With the mechanisms proposed in this RFC, we may define a structured-data alternative to `include_bytes`: -```rust -macro_rules! include_data { - ($file:expr) => {{ - use core::transmute::TransmuteInto; - include_bytes!($file).transmute_into() - }}; -} -``` -...and, with this, replace all instances of `unsafe` in our [earlier][motivation-including-data] motivating example: -```rust -pub fn recognize(input: &Matrix) -> usize -{ - static WEIGHT: &Matrix = include_data!("/weight.bin"); - - static BIAS: &Matrix = include_data!("/bias.bin") - - network::recognize(input, WEIGHT, BIAS) -} -``` - -## Possibility: Reference Casting -[ext-ref-casting]: #possibility-Reference-Casting - -The ability to neglect static checks should make possible [bytemuck][crate-bytemuck]-style fallible casts; e.g.: -```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: UnsafeTransmuteInto<&'u U, NeglectAlignment>, -{ - if (src as *const T as usize) % align_of::() != 0 { - None - } else { - // Safe because we dynamically enforce the alignment - // requirement, whose static check we chose to neglect. - Some(unsafe { src.unsafe_transmute_into() }) - } -} -``` +See [here](0000-ext-include-data.md). ## Possibility: Generic Atomics [future-possibility-generic-atomics]: #possibility-generic-atomics -With much additional effort, this proposal could provide a basis for truly a truly generic `Atomic` type: - -```rust -type LargestPlatformAtomic = u64; - -pub struct Atomic -where - T: SizeLtEq -{ - v: UnsafeCell -} - -impl Atomic -{ - #[inline] - pub fn load(&self, order: Ordering) -> T { - unsafe { atomic_load(self.v.get(), order) } - } - - #[inline] - pub fn store(&self, val: T, order: Ordering) -> T { - unsafe { atomic_store(self.v.get(), val, order) } - } -} -``` +See [here](0000-ext-generic-atomic.md). \ No newline at end of file From 1fb9949162fef090e995808c2613f58cb721320c Mon Sep 17 00:00:00 2001 From: Jack Wrenn Date: Thu, 27 Aug 2020 17:03:57 -0400 Subject: [PATCH 44/61] rework motivation; cut case studies --- rfcs/0000-safe-transmute.md | 298 +++--------------------------------- 1 file changed, 23 insertions(+), 275 deletions(-) diff --git a/rfcs/0000-safe-transmute.md b/rfcs/0000-safe-transmute.md index 0dc9562..1b29f39 100644 --- a/rfcs/0000-safe-transmute.md +++ b/rfcs/0000-safe-transmute.md @@ -1,6 +1,6 @@ -# Safe Transmute RFC +# Safer Transmute RFC -- Feature Name: `safe_transmute` +- Feature Name: `safer_transmute` - Start Date: (fill me in with today's date, YYYY-MM-DD) - RFC PR: [rust-lang/rfcs#0000](https://github.com/rust-lang/rfcs/pull/0000) - Rust Issue: [rust-lang/rust#0000](https://github.com/rust-lang/rust/issues/0000) @@ -40,13 +40,22 @@ let _ : u32 = Foo(16, 12).transmute_into(); // Compile Error! # Motivation [motivation]: #motivation -We foresee a range of practical consequences of fulfilling these goals, including [making unsafe Rust safer](#making-unsafe-rust-safer), providing [safer initialization primitives](#safer-initialization-primitives), and [generic atomics](#atomict). We explore these use-cases, and more, in this section. +Byte-reinterpretation conversions (such as those performed by `mem::transmute`, `mem::transmute_copy`, pointer casts, and `union`s) are invaluable in high performance contexts, are `unsafe`, and easy to get wrong. This RFC provides mechanisms that make many currently-unsafe transmutations entirely safe. For transmutations that are not entirely safe, this RFC's mechanisms make mistakes harder to make. -A *comprehensive* approach to safe transmutation provides benefits beyond the mere act of transmutation; namely: +This RFC's comprehensive approach provides additional benefits beyond the mere act of transmutation; namely: - [authoritatively codifies language layout guarantees](#codifying-language-layout-guarantees) - [allows crate authors to codify their types' layout stability guarantees](#expressing-library-layout-guarantees) - [allows crate authors to codify their abstractions' layout requirements](#expressing-layout-requirements) +Given the expressive foundation provided by this RFC, we also envision a range of future possibilities that will *not* require additional compiler support, including: + - [safe slice and `Vec` casting](0000-ext-container-casting.md) + - [a unified, generic `Atomic` type](0000-ext-generic-atomic.md) + - [a safe, generic alternative to `include_bytes!`](0000-ext-include-data.md) + - [traits for asserting the size and alignment relationships of types](0000-ext-layout-traits.md) + - [zerocopy-style traits for safe initialization](0000-ext-byte-transmutation.md) + - [bytemuck-style mechanisms for fallible reference casting][ext-ref-casting] + + ## Codifying Language Layout Guarantees Documentation of Rust's layout guarantees for a type are often spread across countless issues, pull requests, RFCs and various official resources. It can be very difficult to get a straight answer. When transmutation is involved, users must reason about the *combined* layout properties of the source and destination types. @@ -64,122 +73,7 @@ Similarly, there is no canonical way for crate authors to declare the layout req For instance, a common bit-packing technique involves abusing the relationship between allocations and alignment. If a type is aligned to 2n, then the *n* least significant bits of pointers to that type will equal `0`. These known-zero bits can be packed with data. Since alignment cannot be currently reasoned about at the type-level, it's currently impossible to bound instantiations of a generic parameter based on minimum alignment. -The mechanisms proposed by the RFC enable this. We return to this example near the end of the RFC, [here][case-study-alignment]. - -## Making Unsafe Rust Safer -[motivation-safer-unsafe]: #Making-Unsafe-Rust-Safer -In the blog post [*Unsafe Zig is Safer than Unsafe Rust*](https://andrewkelley.me/post/unsafe-zig-safer-than-unsafe-rust.html), Andrew Kelley asks readers to consider the following Rust code: - -```rust -struct Foo { - a: i32, - b: i32, -} - -fn main() { - unsafe { - let mut array: [u8; 1024] = [1; 1024]; - let foo = mem::transmute::<&mut u8, &mut Foo>(&mut array[0]); - foo.a += 1; - } -} -``` -This pattern, Kelley notes, is common when interacting with operating system APIs. - -This Rust program compiles, but quietly introduces undefined behavior. `Foo` requires stricter in-memory alignment than `u8`. The equivalent code in Zig produces a compiler error for this reason and requires modification. - -And that's not all: Kelley misses that the very layout of `Foo` itself is undefined⁠—it must be annotated with `#[repr(C)]`! - -Kelley concludes: -> In Zig the problem of alignment is solved completely; the compiler catches all possible alignment issues. In the situation where you need to assert to the compiler that something is more aligned than Zig thinks it is, you can use `@alignCast`. This inserts a cheap safety check in debug mode to make sure the alignment assertion is correct. - -This situation is frustrating. Like `zig`, `rustc` *knows* the layout of `Foo`, it *knows* that the layout of `Foo` is unspecified because it lacks the `#[repr(C)]` attribute, and it *knows* the minimum alignment requirements of `Foo` are stricter than that of `u8`. Why doesn't it also help us avoid UB? - -This RFC proposes mechanisms that use this information to enable safer transmutation. We revisit Kelley's motivating example with these proposed mechanisms near the end of this RFC, [*here*][case-study-safer-unsafe]. - -## Efficient Parsing -In languages like C or C++, a common technique for parsing structured data such as network packets or file formats is to declare a type whose fields correspond to the fields of the data being parsed. An input byte array is then parsed by casting a pointer to the array (e.g., `a char *`) to a pointer to the typed representation (e.g., `udp_header_t *`). It is the programmer's responsibility to avoid the many pitfalls that could introduce undefined behavior when using this pattern. - -In Rust, we might employ the same technique: -```rust -struct UdpHeader { - src_port: u16, - dst_port: u16, - length: u16, - checksum: [u8; 2], -} -``` -However, as it stands today, it is still the programmer's responsibility to ensure that a conversion such as `&[u8]` to `&UdpHeader` is sound. In the above example, we can see a number of pitfalls that a programmer must know to avoid: - - The layout of `UdpHeader` is undefined without `#[repr(C)]` - - The conversion of `&[u8]` to `&UdpHeader` is invalid in the general case because the length of the byte slice must be sufficient - - The conversion of `&[u8]` to `&UdpHeader` is invalid in the general case because the alignment of the byte slice must be sufficient (the alignment issue can be avoided by replacing the `u16` fields with `[u8; 2]`) - -This RFC proposes mechanisms that would allow to perform this reference conversion safely, and would fail compilation if any of the above pitfalls were encountered. We revisit this example [here][case-study-parsing]. - - -## Safer Initialization Primitives -The initialization primitives `mem::zeroed` and `mem::uninitialized` are `unsafe` because they may be used to initialize types for which zeroed or uninitialized memory are *not* valid bit-patterns. The `mem::zeroed` function recently gained a dynamically-enforced validity check, but this safety measure isn't wholly satisfactory: the validity properties of `T` are statically known, but the check is dynamic. - -This RFC proposes mechanisms that would allow these functions to explicitly bound their generic parameter `T` based on its validity properties. We revisit this motivating use-case with our proposed mechanisms near the end of this RFC, [*here*][future-possibility-safe-initialization]. - -## Including Structured Data -[motivation-including-data]: #Including-Structured-Data - -[`include_bytes`]: https://doc.rust-lang.org/core/macro.include_bytes.html - -The [`include_bytes`] macro statically includes the contents of another file as a byte array, producing a value of type `&'static [u8; N]`. Often, these bytes correspond to structured data and a `transmute` must be used to deserialize the bytes into useful form; e.g.: -```rust -pub fn recognize(input: &Matrix) -> usize -{ - static RAW_WEIGHT : &'static [u8; 62_720] = include_bytes!("/weight.bin"); - - static RAW_BIAS : &'static [u8; 80] = include_bytes!("/bias.bin"); - - let WEIGHT: &Matrix = unsafe{ mem::transmute(RAW_WEIGHT) }; - - let BIAS: &Matrix = unsafe{ mem::transmute(RAW_BIAS) }; - - network::recognize(input, WEIGHT, BIAS) -} -``` - -This RFC proposes mechanisms that would eliminate `unsafe` in this example. We revisit this motivating use-case with our proposed mechanisms near the end of this RFC, [*here*][future-possibility-include_data]. - - -## SIMD -***TODO:*** This might be helpful: https://internals.rust-lang.org/t/pre-rfc-frombits-intobits/7071 - - -## `Atomic` -Rust defines a dozen `Atomic*` types (`AtomicBool`, `AtomicI8`, `AtomicI16`, `AtomicI32`, `AtomicI64`, `AtomicIsize`, `AtomicPtr`, `AtomicU8`, `AtomicU16`, `AtomicU32`, `AtomicU64`, and `AtomicUsize`). - -This set is large—a distinct `Atomic*` type is required for each primitive type—but incomplete. If one wants atomic operations on their own type, they must define a wrapper around an existing `Atomic*` type of appropriate size and validity, then transmute at API boundaries; e.g.: - -```rust -#[repr(u8)] -enum Trilean { - False, - True, - Unknown, -} - -#[repr(transparent)] -pub AtomicTrilean(AtomicU8); - -impl AtomicTrilean { - - pub const fn new(v: Trilean) -> Self { - AtomicTrilean( - AtomicU8::new( - unsafe { mem::transmute(v) } - )) - } - - ... -} -``` - -The mechanisms proposed by this RFC would eliminate this pattern, permitting truly generic `Atomic` types. We revisit this motivating use-case with these proposed mechanisms near the end of this RFC, [*here*][future-possibility-generic-atomics]. +The mechanisms proposed by the RFC enable this, see [here](0000-ext-layout-traits.md). # Guide-level explanation [guide-level-explanation]: #guide-level-explanation @@ -2018,155 +1912,6 @@ Writing *both* impls (as we do above) is logically nonsense, but is nonetheless # Rationale and alternatives [rationale-and-alternatives]: #rationale-and-alternatives -## Case Studies - -### Case Study: Making Unsafe Rust Safer -[case-study-safer-unsafe]: #Case-Study-Making-Unsafe-Rust-Safer - -This RFC proposes mechanisms for safe transmutation and safer *unsafe* transmutation. How might [Kelley's motivating example][motivation-safer-unsafe] change in light of these mechanisms? - -#### Safer Unsafe -For the compiler to accept a use of `unsafe_transmute`, we must: - - use `unsafe_transmute` with the `NeglectAlignment` option - - annotate `Foo` with `#[repr(C)]` so its layout is defined - - annotate `Foo` with `#[derive(PromiseTransmutableFrom,PromiseTransmutableInto)]` to indicate its layout is library-stable (alternatively, we could also use the `NeglectStability` option) - -The sum of these changes: -```rust -use core::transmute::{ - unsafe_transmute, - options::NeglectAlignment, - stability::FixedLayout, -}; - - -#[derive(PromiseTransmutableFrom, PromiseTransmutableInto)] -#[repr(C)] -pub struct Foo { - a: i32, - b: i32, -} - -fn main() { - let mut array: [u8; 1024] = [1; 1024]; - - let mut foo = unsafe { - // check alignment ourselves - assert_eq!((src as *const u8 as usize) % align_of::(), 0); - - // transmute `&mut array` to `&mut Foo` - // permitted becasue we promise to check the alignment ourselves - let foo = unsafe_transmute::<&mut [u8; 1024], &mut Foo, NeglectAlignment>(&mut array); - }; - - foo.a += 1; -} -``` - -#### No Unsafe Needed! -Better yet, we may use a completely *safe* transmute if we statically align the byte array: - -```rust -use core::transmute::{ - TransmuteInto, - options::NeglectAlignment, - stability::FixedLayout, -}; - - -#[derive(PromiseTransmutableFrom, PromiseTransmutableInto)] -#[repr(C)] -struct Foo { - a: i32, - b: i32, -} - -#[derive(PromiseTransmutableFrom, PromiseTransmutableInto)] -#[repr(C, align(4)] -struct Aligned([u8; 1024]); - -fn main() { - let mut array: Aligned = Aligned([1; 1024]); - - let foo : &mut Foo = (&mut array[0]).transmute_into(); - - foo.a += 1; -} -``` - -### Case Study: Abstractions for Fast Parsing -[case-study-parsing]: #case-study-abstractions-for-fast-parsing -Using the core mechanisms of this RFC (along with the proposed [slice-casting extension][ext-slice-casting]) it is trivial to safely define useful zero-copy packet-parsing utilities, like those in the [`zerocopy`] and [`packet`] crates; e.g.: -```rust -impl<'a> &'a [u8] -{ - /// Read `&T` off the front of `self`, and shrink the underlying slice. - /// Analogous to: - /// - https://fuchsia-docs.firebaseapp.com/rust/packet/trait.BufferView.html#method.peek_obj_front - /// - https://fuchsia-docs.firebaseapp.com/rust/packet/trait.BufferView.html#method.take_obj_front - fn take_front<'t, T>(&'a mut self) -> Option<&'a T> - where - Self: CastInto<&'a [T]>, - { - let idx = size_of::(); - let (parsable, remainder) = self.split_at(idx); - *self = remainder; - parsable.cast_into().first() - } - - /// Read `&T` off the back of `self`, and shrink the underlying slice. - /// Analogous to: - /// - https://fuchsia-docs.firebaseapp.com/rust/packet/trait.BufferView.html#method.peek_obj_back - /// - https://fuchsia-docs.firebaseapp.com/rust/packet/trait.BufferView.html#method.take_obj_back - fn take_back<'t, T>(&'a mut self) -> Option<&'a T> - where - Self: CastInto<&'a [T]>, - { - let idx = self.len().saturating_sub(size_of::()); - let (remainder, parsable) = self.split_at(idx); - *self = remainder; - parsable.cast_into().first() - } -} -``` -To parse a `UdpPacket` given a byte slice, we split the slice into a slice containing first 8 bytes, and the remainder. We then cast the first slice into a reference to a `UdpHeader`. A `UdpPacket`, then, consists of the reference to the `UdpHeader`, and the remainder slice. Concretely: -```rust -pub struct UdpPacket<'a> { - hdr: &'a UdpHeader, - body: &'a [u8], -} - -#[derive(PromiseTransmutableFrom, PromiseTransmutableInto)] -#[repr(C)] -struct UdpHeader { - src_port: [u8; 2], - dst_port: [u8; 2], - length: [u8; 2], - checksum: [u8; 2], -} - -impl<'a> UdpPacket<'a> { - pub fn parse(mut bytes: &'a [u8]) -> Option { - Some(UdpPacket { hdr: {bytes.read()?}, body: bytes }) - } -} -``` -While this example is simple, the technique can be [expanded](https://fuchsia-docs.firebaseapp.com/rust/packet_formats/) to arbitrarily complex structures. - -### Case Study: Abstractions for Pointer Bitpacking -[case-study-alignment]: #case-study-abstractions-for-pointer-bitpacking - -A [previous motivating example](#expressing-layout-requirements) involved a generic abstraction only usable with types meeting certain layout requirements: pointer bitpacking. Using this RFC's mechanisms, we can require that a reference to a generic type `T` has alignment of at least a given value (e.g., `8`) by first defining a ZST with that alignment: -```rust -#[derive(PromiseTransmutableFrom)] -#[repr(align(8)] -struct Aligned8; -``` -and then using this `where` bound: -```rust -where - &T: TransmuteInto<&Aligned8> -``` ## Rationale: `TransmuteFrom`/`TransmuteInto` @@ -2447,10 +2192,6 @@ The omission is intentional. The consequences of such an option are suprising in # Future possibilities [future-possibilities]: #future-possibilities -We divide future possibilities into two categories: - - *extensions*, which are highly-developed, - - *possibilities*, which sketch an idea. - ## Extension: `PromiseTransmutable` Shorthand [extension-promisetransmutable-shorthand]: #extension-promisetransmutable-shorthand @@ -2460,6 +2201,11 @@ See [here](0000-ext-promise-transmutable.md). See [here](0000-ext-layout-traits.md). +## Extension: Byte Transmutation Traits and Safe Initialization +[extension-zerocopy]: #extension-byte-transmutation-traits-and-safe-initialization + +See [here](0000-ext-byte-transmutation.md). + ## Extension: Casting [ext-slice-casting]: #extension-casting @@ -2467,12 +2213,14 @@ See [here](0000-ext-layout-traits.md). See [here](0000-ext-container-casting.md). + ## Extension: `include_data!` [future-possibility-include_data]: #Extension-include_data See [here](0000-ext-include-data.md). -## Possibility: Generic Atomics + +## Extension: Generic Atomics [future-possibility-generic-atomics]: #possibility-generic-atomics -See [here](0000-ext-generic-atomic.md). \ No newline at end of file +See [here](0000-ext-generic-atomic.md). From 579a7a0dcde10366ba73738a64b89e4a43b1f504 Mon Sep 17 00:00:00 2001 From: Jack Wrenn Date: Fri, 28 Aug 2020 15:52:52 -0400 Subject: [PATCH 45/61] lingering Type -> Archetype --- rfcs/0000-safe-transmute.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rfcs/0000-safe-transmute.md b/rfcs/0000-safe-transmute.md index 1b29f39..dce12e6 100644 --- a/rfcs/0000-safe-transmute.md +++ b/rfcs/0000-safe-transmute.md @@ -499,7 +499,7 @@ However, if you anticipate that you will need to make changes to your type's lay struct FooArchetype(pub [MaybeUninit; 5]); impl PromiseTransmutableInto for Foo { - type Type = [MaybeUninit; 5]; + type Archetype = [MaybeUninit; 5]; } ``` @@ -1556,7 +1556,7 @@ enum Boolean { } unsafe impl PromiseTransmutableInto for Boolean { - type Type = u8; + type Archetype = u8; } ``` From 8295f5b0d71921a696eda422513b0b8c6e7799d1 Mon Sep 17 00:00:00 2001 From: Jack Wrenn Date: Fri, 28 Aug 2020 15:54:56 -0400 Subject: [PATCH 46/61] abusing -> using https://github.com/rust-lang/project-safe-transmute/pull/5#discussion_r478336699 Co-authored-by: Florian Gilcher --- rfcs/0000-safe-transmute.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rfcs/0000-safe-transmute.md b/rfcs/0000-safe-transmute.md index dce12e6..bb81bdb 100644 --- a/rfcs/0000-safe-transmute.md +++ b/rfcs/0000-safe-transmute.md @@ -71,7 +71,7 @@ This RFC proposes simple-but-powerful [mechanisms][stability] for declaring layo ## Expressing Layout Requirements Similarly, there is no canonical way for crate authors to declare the layout requirements of generic abstractions over types that have certain layout properties. -For instance, a common bit-packing technique involves abusing the relationship between allocations and alignment. If a type is aligned to 2n, then the *n* least significant bits of pointers to that type will equal `0`. These known-zero bits can be packed with data. Since alignment cannot be currently reasoned about at the type-level, it's currently impossible to bound instantiations of a generic parameter based on minimum alignment. +For instance, a common bit-packing technique involves using the relationship between allocations and alignment. If a type is aligned to 2n, then the *n* least significant bits of pointers to that type will equal `0`. These known-zero bits can be packed with data. Since alignment cannot be currently reasoned about at the type-level, it's currently impossible to bound instantiations of a generic parameter based on minimum alignment. The mechanisms proposed by the RFC enable this, see [here](0000-ext-layout-traits.md). From 2eaa50556071ac5bbe7f743a0779a3142c887ad9 Mon Sep 17 00:00:00 2001 From: Jack Wrenn Date: Fri, 28 Aug 2020 16:19:01 -0400 Subject: [PATCH 47/61] update minimal impl --- rfcs/0000-safe-transmute.md | 75 +++++++++++++++++-------------------- 1 file changed, 34 insertions(+), 41 deletions(-) diff --git a/rfcs/0000-safe-transmute.md b/rfcs/0000-safe-transmute.md index bb81bdb..89d8321 100644 --- a/rfcs/0000-safe-transmute.md +++ b/rfcs/0000-safe-transmute.md @@ -559,11 +559,15 @@ where Self: Sized, Neglect: TransmuteOptions, { - use core::{mem, ptr}; + use core::mem::ManuallyDrop; + + union Transmute { + src: ManuallyDrop, + dst: ManuallyDrop, + } + unsafe { - let dst = ptr::read_unaligned(&src as *const Src as *const Self); - mem::forget(src); - dst + ManuallyDrop::into_inner(Transmute { src: ManuallyDrop::new(src) }.dst) } } } @@ -773,8 +777,9 @@ This [minimal implementation][minimal-impl] is sufficient for convincing the com ### Listing for Initial, Minimal Implementation [minimal-impl]: #Listing-for-Initial-Minimal-Implementation -This listing is both a minimal-viable implementation of this RFC (excepting the automatic derives) and the **canonical specification** of this RFC's API surface ([playground](https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=dee36fb5231c9e00b03b424e204cd1a9)): +This listing is both a minimal implementation of this RFC (excepting the automatic derives) and the **canonical specification** of this RFC's API surface ([playground](https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=32a32adf2de2adf47d79edf4463bae47)): ```rust +#![feature(untagged_unions,const_fn,const_fn_union)] // for the impl of transmute free functions #![feature(const_generics)] // for stability declarations on `[T; N]` #![feature(decl_macro)] // for stub implementations of derives #![feature(never_type)] // for stability declarations on `!` @@ -787,23 +792,36 @@ pub mod transmute { use {options::*, stability::*}; /// Reinterprets the bits of a value of one type as another type, safely. + /// + /// Use `()` for `Neglect` if you do not wish to neglect any static checks. #[inline(always)] - pub /*const*/ fn safe_transmute(src: Src) -> Dst + pub const fn safe_transmute(src: Src) -> Dst where - Src: TransmuteInto, + Dst: TransmuteFrom, Neglect: SafeTransmuteOptions { - src.transmute_into() + unsafe { unsafe_transmute::(src) } } /// Reinterprets the bits of a value of one type as another type, potentially unsafely. + /// + /// The onus is on you to ensure that calling this method is safe. #[inline(always)] - pub /*const*/ unsafe fn unsafe_transmute(src: Src) -> Dst + pub const unsafe fn unsafe_transmute(src: Src) -> Dst where - Src: TransmuteInto, + Dst: TransmuteFrom, Neglect: TransmuteOptions { - unsafe { src.unsafe_transmute_into() } + use core::mem::ManuallyDrop; + + union Transmute { + src: ManuallyDrop, + dst: ManuallyDrop, + } + + unsafe { + ManuallyDrop::into_inner(Transmute { src: ManuallyDrop::new(src) }.dst) + } } /// `Self: TransmuteInto where Neglect: TransmuteOptions, @@ -873,12 +890,7 @@ pub mod transmute { Self: Sized, Neglect: SafeTransmuteOptions, { - use core::{mem, ptr}; - unsafe { - let dst = ptr::read(&src as *const Src as *const Self); - mem::forget(src); - dst - } + unsafe { Self::unsafe_transmute_from(src) } } /// Reinterpret the bits of a value of one type as another type, potentially unsafely. @@ -891,16 +903,12 @@ pub mod transmute { Self: Sized, Neglect: TransmuteOptions, { - use core::{mem, ptr}; - unsafe { - let dst = ptr::read_unaligned(&src as *const Src as *const Self); - mem::forget(src); - dst - } + unsafe_transmute::(src) } } /// A type is always transmutable from itself. + // This impl will be replaced with a compiler-supported for arbitrary source and destination types. unsafe impl TransmuteFrom for T {} /// A type is *stably* transmutable if... @@ -921,7 +929,6 @@ pub mod transmute { use super::{TransmuteFrom, TransmuteInto, options::NeglectStability}; /// Declare that transmuting `Self` into `Archetype` is SemVer-stable. - /* #[lang = "promise_transmutable_into"] */ pub trait PromiseTransmutableInto { /// The `Archetype` must be safely transmutable from `Self`. @@ -931,7 +938,6 @@ pub mod transmute { } /// Declare that transmuting `Self` from `Archetype` is SemVer-stable. - /* #[lang = "promise_transmutable_from"] */ pub trait PromiseTransmutableFrom { /// The `Archetype` must be safely transmutable into `Self`. @@ -1104,31 +1110,21 @@ pub mod transmute { pub mod options { /// Options that may be used with safe transmutations. - pub trait SafeTransmuteOptions: TransmuteOptions - {} + pub trait SafeTransmuteOptions: TransmuteOptions {} /// Options that may be used with unsafe transmutations. - pub trait TransmuteOptions: private::Sealed - {} + pub trait TransmuteOptions: private::Sealed {} impl SafeTransmuteOptions for () {} impl TransmuteOptions for () {} /// Neglect the stability check of `TransmuteFrom`. - /* #[lang = "neglect_stability"] */ pub struct NeglectStability; // Uncomment this if/when constructibility is fully implemented: // impl SafeTransmuteOptions for NeglectStability {} impl TransmuteOptions for NeglectStability {} - /* - pub struct NeglectAlignment; - impl TransmuteOptions for NeglectAlignment {} - */ - - /* FILL: Implementations for tuple combinations of options */ - // prevent third-party implementations of `TransmuteOptions` mod private { use super::*; @@ -1137,9 +1133,6 @@ pub mod transmute { impl Sealed for () {} impl Sealed for NeglectStability {} - /* impl Sealed for NeglectAlignment {} */ - - /* FILL: Implementations for tuple combinations of options */ } } } From 9081e53f148b0d8ed9b62d71205a1871a04ff506 Mon Sep 17 00:00:00 2001 From: Jack Wrenn Date: Fri, 28 Aug 2020 17:18:01 -0400 Subject: [PATCH 48/61] transmutation definition --- rfcs/0000-safe-transmute.md | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/rfcs/0000-safe-transmute.md b/rfcs/0000-safe-transmute.md index 89d8321..7b35280 100644 --- a/rfcs/0000-safe-transmute.md +++ b/rfcs/0000-safe-transmute.md @@ -81,7 +81,21 @@ The mechanisms proposed by the RFC enable this, see [here](0000-ext-layout-trait ## Terminology & Concepts ### 📖 Transmutation -**Transmutation** is the act of reinterpreting the bytes corresponding to a value of one type as if they corresponded to a different type. +**Transmutation** is the act of reinterpreting the bytes corresponding to a value of one type as if they corresponded to a different type. Concretely: +``` +unsafe fn transmute(src: Src) -> Dst +{ + union Transmute { + src: ManuallyDrop, + dst: ManuallyDrop, + } + + ManuallyDrop::into_inner(Transmute { src: ManuallyDrop::new(src) }.dst) +} +``` + +### 📖 Safer Transmutation +By **safer transmutation** we mean: *what `where` bound could we add to `transmute` restricts its type parameters `Src` and `Dst` in ways that statically limit the function's misuse?* Our answer to this question will ensure that transmutations are, by default, *sound*, *safe* and *stable*. ### 📖 Soundness A transmutation is ***sound*** if the mere act of transmuting a value from one type to another is not compiler undefined behavior. From 099896aa7d83915e9486b9d51c7ad18a64ab3eb8 Mon Sep 17 00:00:00 2001 From: Jack Wrenn Date: Fri, 28 Aug 2020 17:18:42 -0400 Subject: [PATCH 49/61] union repr --- rfcs/0000-safe-transmute.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/rfcs/0000-safe-transmute.md b/rfcs/0000-safe-transmute.md index 7b35280..8cd38cd 100644 --- a/rfcs/0000-safe-transmute.md +++ b/rfcs/0000-safe-transmute.md @@ -81,10 +81,12 @@ The mechanisms proposed by the RFC enable this, see [here](0000-ext-layout-trait ## Terminology & Concepts ### 📖 Transmutation -**Transmutation** is the act of reinterpreting the bytes corresponding to a value of one type as if they corresponded to a different type. Concretely: +**Transmutation** is the act of reinterpreting the bytes corresponding to a value of one type as if they corresponded to a different type. Concretely, we mean the behavior of this function: ``` +#[inline(always)] unsafe fn transmute(src: Src) -> Dst { + #[repr(C)] union Transmute { src: ManuallyDrop, dst: ManuallyDrop, @@ -575,6 +577,7 @@ where { use core::mem::ManuallyDrop; + #[repr(C)] union Transmute { src: ManuallyDrop, dst: ManuallyDrop, @@ -791,7 +794,7 @@ This [minimal implementation][minimal-impl] is sufficient for convincing the com ### Listing for Initial, Minimal Implementation [minimal-impl]: #Listing-for-Initial-Minimal-Implementation -This listing is both a minimal implementation of this RFC (excepting the automatic derives) and the **canonical specification** of this RFC's API surface ([playground](https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=32a32adf2de2adf47d79edf4463bae47)): +This listing is both a minimal implementation of this RFC (excepting the automatic derives) and the **canonical specification** of this RFC's API surface ([playground](https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=22a5ae2d502ede826392ea4044d48b84)): ```rust #![feature(untagged_unions,const_fn,const_fn_union)] // for the impl of transmute free functions #![feature(const_generics)] // for stability declarations on `[T; N]` @@ -828,6 +831,7 @@ pub mod transmute { { use core::mem::ManuallyDrop; + #[repr(C)] union Transmute { src: ManuallyDrop, dst: ManuallyDrop, From 7fe214621af8ed8188c52e5847e92ece560a75e0 Mon Sep 17 00:00:00 2001 From: Jack Wrenn Date: Fri, 28 Aug 2020 17:24:16 -0400 Subject: [PATCH 50/61] REMOVE Dissecting Stability --- rfcs/0000-safe-transmute.md | 538 ------------------------------------ 1 file changed, 538 deletions(-) diff --git a/rfcs/0000-safe-transmute.md b/rfcs/0000-safe-transmute.md index 8cd38cd..3ab1b28 100644 --- a/rfcs/0000-safe-transmute.md +++ b/rfcs/0000-safe-transmute.md @@ -1305,544 +1305,6 @@ We recommend that that implementers of this RFC initially simplify constructabil If and when the implementation of `TransmuteFrom` encodes our complete definition of constructability, `NeglectStability` shall become a safe transmute option. -## Dissecting Stability - -In this section, we iteratively review the design of the stability declaration traits, starting with a simplest-possible design: -```rust -pub unsafe trait PromiseTransmutableFrom -{ - type Archetype; -} - -pub unsafe trait PromiseTransmutableInto -{ - type Archetype; -} -``` -This section gives concrete examples of how misusing these simple, unsafe definitions can lead to unsoundness, and then refines them until they are immune to misuse (and, consequently, *safe*). - -### Part 1: Towards Safety - -#### Forbidding Archetypes with Nonsense Validity -The simplest definitions of the stability declaration traits *must* be `unsafe` because `Archetype` can be instantiated in ways that undermine soundness; e.g.: -```rust -unsafe impl PromiseTransmutableInto for u8 { - type Archetype = u16; // <- Nonsense! -} - -unsafe impl PromiseTransmutableFrom for u16 { - type Archetype = u16; -} -``` -These declarations subvert safety! Given these declarations, Rust will accept `u8: TransmuteInto`. This is problematic: `u8` is smaller than `u16`. - ---- - -
    -Why would this transmutation be accepted? (Click to Expand) - -Let's solve this trait bound step-by-step: -```rust -/// 0. Initial equation: -u8: - TransmuteInto<&'a Aligned8> -``` - -First, recall that a `Src` type is *stably* transmutable into a `Dst` type *only if* `::Archetype` is transmutable into `::Archetype`, notwithstanding stability. The layouts of `Src` and `Dst` themselves are, in theory, immaterial — all that matters are the layouts of their stability archetypes. Formally: -```rust -𝙞𝙛 - ::Archetype: - TransmuteInto< - ::Archetype, - NeglectStability - > -𝙩𝙝𝙚𝙣 - Src: TransmuteInto -``` - -We apply this rule to our initial equation: -```rust -𝙞𝙛 - ::Archetype: - TransmuteInto< - ::Archetype, - NeglectStability - > -𝙩𝙝𝙚𝙣 - u8: TransmuteInto -``` - -Next, recall the definitions of `PromiseTransmutableInto` for `u8` and `PromiseTransmutableFrom` for `u16`: -```rust -unsafe impl PromiseTransmutableInto for u8 { - type Archetype = u16; // <- Nonsense! -} - -unsafe impl PromiseTransmutableFrom for u16 { - type Archetype = u16; -} -``` -We apply these definitions to evaluate `::Archetype` and `::Archetype`: -```rust -𝙞𝙛 - u16: - TransmuteInto< - u16, - NeglectStability - > -𝙩𝙝𝙚𝙣 - u8: TransmuteInto -``` -Since `u16` can be safely transmuted into `u16`, the compiler accepts the transmutation of `u8` into `u16`! - -
    - ---- - -We can prevent nonsense archetypes by demanding that `Archetype` is transmutable into `Self` (notwithstanding stability): -```rust -pub unsafe trait PromiseTransmutableFrom -where - Self::Archetype: TransmuteInto, -{ - type Archetype; -} - -pub unsafe trait PromiseTransmutableInto -where - Self::Archetype: TransmuteFrom, -{ - type Archetype; -} -``` - -Our previous attempt to skirt soundness now produces a compile error: -```rust -unsafe impl PromiseTransmutableInto for u8 { - type Archetype = u16; - // ^^^ the trait `TransmuteInto` is not implemented for `u16` -} -``` - -#### Forbidding Archetypes with Nonsense Alignment -Unfortunately, the above definition is still flawed. Consider these stability declarations: - -```rust -#[derive(PromiseTransmutableFrom)] -#[repr(C)] -pub struct Aligned1; - -#[derive(PromiseTransmutableInto)] -#[repr(C, align(8))] -pub struct Aligned8; - -unsafe impl PromiseTransmutableInto for Aligned1 { - type Archetype = Aligned8; -} - -unsafe impl PromiseTransmutableFrom for Aligned8 { - type Archetype = Aligned1; -} -``` - -These declarations subvert safety! Given these declarations, Rust would accept the bound `&Aligned1: TransmuteInto<&Aligned8>`. This is problematic: `Aligned8` has *stricter* alignment requirements than `Aligned1`. - ---- - -
    -Why would this transmutation be accepted? (Click to Expand) - -Let's solve this trait bound step-by-step: -```rust -/// 0. Initial equation: -for<'a> &'a Aligned1: - TransmuteInto<&'a Aligned8> -``` - -As before, we first apply the stability rule to our equation: -```rust -𝙞𝙛 - <&'a Aligned1 as PromiseTransmutableInto>::Archetype: - TransmuteInto< - <&'a Aligned8 as PromiseTransmutableFrom>::Archetype, - NeglectStability - > -𝙩𝙝𝙚𝙣 - for<'a> &'a Aligned1: - TransmuteInto<&'a Aligned8> -``` - -Next, recall the definitions of `PromiseTransmutableInto` and `PromiseTransmutableFrom` for `&T`: -```rust -unsafe impl<'a, T> PromiseTransmutableFrom for &'a T -where - T: PromiseTransmutableFrom -{ - type Archetype = &'a T::Archetype; -} - -unsafe impl<'a, T> PromiseTransmutableInto for &'a T -where - T: PromiseTransmutableInto -{ - type Archetype = &'a T::Archetype; -} -``` -We apply these definitions to evaluate `<&_ as PromiseTransmutableInto>::Archetype` and `<&_ as PromiseTransmutableFrom>::Archetype`: -```rust -𝙞𝙛 - &'a ::Archetype: - TransmuteInto< - &'a ::Archetype, - NeglectStability - > -𝙩𝙝𝙚𝙣 - for<'a> &'a Aligned1: - TransmuteInto<&'a Aligned8> -``` - -Finally, recall the definitions of `PromiseTransmutableInto` for `Aligned1` and `PromiseTransmutableFrom` for `Aligned8`: -```rust -unsafe impl PromiseTransmutableInto for Aligned1 { - type Archetype = Aligned8; -} - -unsafe impl PromiseTransmutableFrom for Aligned8 { - type Archetype = Aligned1; -} -``` -We apply these definitions to evaluate `::Archetype` and `::Archetype`: -```rust -𝙞𝙛 - &'a Aligned8: - TransmuteInto< - &'a Aligned1, - NeglectStability - > -𝙩𝙝𝙚𝙣 - for<'a> &'a Aligned1: - TransmuteInto<&'a Aligned8> -``` -Since `&'a Aligned8` can be safely transmuted into `&'a Aligned1`, the compiler accepts the transmutation of `&'a Aligned1` into `&'a Aligned1`! - -
    - ---- - -This safety hazard can be prevented by rewriting our `where` bound like so: -```rust -pub unsafe trait PromiseTransmutableFrom -where - Self::Archetype: TransmuteInto, - for<'a> &'a Self::Archetype: TransmuteInto<&'a Self, NeglectStability> -{ - type Archetype; -} - -pub unsafe trait PromiseTransmutableInto -where - Self::Archetype: TransmuteFrom, - for<'a> &'a Self::Archetype: TransmuteFrom<&'a Self, NeglectStability> -{ - type Archetype; -} -``` - -Our previous attempt to skirt safety now produces a compiler error: -```rust -unsafe impl PromiseTransmutableInto for Aligned1 { - type Archetype = Aligned8; - // ^^^^^^^^ the trait `TransmuteFrom<&'_ Aligned1, NeglectStability>` is not implemented for `&'_ Aligned8` -} -``` - - -#### Forbidding Archetypes with Nonsense Validity Under Mutate-able Reference Transmutation -Unfortunately, the above definition is still flawed. Consider these stability declarations: -```rust -#[repr(u8)] -enum Boolean { - False = 0, - True = 1, -} - -unsafe impl PromiseTransmutableInto for Boolean { - type Archetype = u8; -} -``` - -Given these declarations, Rust would accept the bound `&mut Boolean: TransmuteInto<&mut u8>`. This is problematic: transmutations involving mutate-able references must not expand bit-validity. - ---- - -
    -Why would this transmutation be accepted? (Click to Expand) - -Why would this transmutation be accepted? Let's solve this trait bound step-by-step: -```rust -/// 0. Initial equation: -for<'a> &'a mut Boolean: - TransmuteInto<&'a mut u8> -``` - -As before, we first apply the stability rule to our equation: -```rust -𝙞𝙛 - for<'a> <&'a mut Boolean as PromiseTransmutableInto>::Archetype: - TransmuteInto< - <&'a mut u8 as PromiseTransmutableFrom>::Archetype, - NeglectStability - > -𝙩𝙝𝙚𝙣 - for<'a> '&mut Boolean: - TransmuteInto<&'a mut u8> -``` - -Next, recall the definitions of `PromiseTransmutableInto` and `PromiseTransmutableFrom` for `&mut T`: -```rust -unsafe impl<'a, T> PromiseTransmutableFrom for &'a mut T -where - T: PromiseTransmutableFrom -{ - type Archetype = &'a T::Archetype; -} - -unsafe impl<'a, T> PromiseTransmutableInto for &'a mut T -where - T: PromiseTransmutableInto -{ - type Archetype = &'a T::Archetype; -} -``` -We apply these definitions to evaluate `<& mut _ as PromiseTransmutableInto>::Archetype` and `<& mut _ as PromiseTransmutableFrom>::Archetype`: -```rust -𝙞𝙛 - for<'a> &'a mut ::Archetype: - TransmuteInto< - &'a mut ::Archetype, - NeglectStability - > -𝙩𝙝𝙚𝙣 - for<'a> '&mut Boolean: - TransmuteInto<&'a mut u8> -``` - -Finally, recall the definitions of `PromiseTransmutableInto` for `Boolean` and `PromiseTransmutableFrom` for `u8`: -```rust -unsafe impl PromiseTransmutableInto for Boolean { - type Archetype = u8; -} - -unsafe impl PromiseTransmutableFrom for u8 { - type Archetype = Self; -} -``` -We apply these definitions to evaluate `::Archetype` and `::Archetype`: -```rust -𝙞𝙛 - for<'a> &'a mut u8: - TransmuteInto< - &'a mut u8, - NeglectStability - > -𝙩𝙝𝙚𝙣 - for<'a> '&mut Boolean: - TransmuteInto<&'a mut u8> -``` -Since `&'a mut u8` can be safely transmuted into `&'a mut u8`, the compiler accepts the transmutation of `&mut Boolean` into `&mut u8`. - -
    - ---- - -This safety hazard can be prevented by rewriting our `where` bounds like so: -```rust -pub trait PromiseTransmutableFrom -where - Self::Archetype: TransmuteInto, - for<'a> &'a Self::Archetype: TransmuteInto<&'a Self, NeglectStability>, - for<'a> &'a mut Self::Archetype: TransmuteInto<&'a mut Self, NeglectStability> -{ - type Archetype; -} - -pub trait PromiseTransmutableInto -where - Self::Archetype: TransmutFrom, - for<'a> &'a Self::Archetype: TransmuteFrom<&'a Self, NeglectStability>, - for<'a> &'a mut Self::Archetype: TransmuteFrom<&'a mut Self, NeglectStability> -{ - type Archetype; -} -``` - -Our previous attempt to skirt safety now produces a compiler error: -```rust -impl PromiseTransmutableInto for Boolean { - type Archetype = u8; - // ^^ the trait `TransmuteFrom<&'_ mut u8, NeglectStability>` is not implemented for `&'_ mut Boolean` -} -``` - -### Part 2: Towards Flexibility -In the previous two sections, we bypassed the safety effects of the `PromiseTransmutable{From,Into}` traits' `where` bounds by defining archetypes whose quirks were only apparent under a *more* restrictive transmutation than that enforced by the `where` bound. - -We cannot play the same trick to bypass safety with our latest `where` bound. Between transmutations involving values, shared references and unique references, the rules governing transmutations involving unique references are the most restrictive. - -However, this safety comes at the expense of flexibility. Recall our last 'dangerous' archetype: -```rust -impl PromiseTransmutableInto for Boolean { - type Archetype = u8; - // ^^ the trait `TransmuteFrom<&'_ mut u8, NeglectStability>` is not implemented for `&'_ mut Boolean` -} -``` -This archetype is sensible in the context of value transmutations. It's sensible in the context of immutable reference transmutations. The *only* context in which it is *not* sensible are mutate-able reference transmutations! Our changes achieved safety at the significant expense of flexibility. - -In this section, we begin at our previous value-safe definition of the stability declaration traits: -```rust -pub unsafe trait PromiseTransmutableFrom -where - Self::Archetype: TransmuteInto, -{ - type Archetype; -} - -pub unsafe trait PromiseTransmutableInto -where - Self::Archetype: TransmuteFrom, -{ - type Archetype; -} -``` -...and solve its reference-unsafety in a manner that preserves flexibility. - -#### Compartmentalizing Shared Reference Transmutation Stability -Reconsider these stability declarations: -```rust -#[derive(PromiseTransmutableFrom)] -#[repr(C)] -pub struct Aligned1; - -#[derive(PromiseTransmutableInto)] -#[repr(C, align(8))] -pub struct Aligned8; - -unsafe impl PromiseTransmutableInto for Aligned1 { - type Archetype = Aligned8; -} - -unsafe impl PromiseTransmutableFrom for Aligned8 { - type Archetype = Aligned1; -} -``` - -Given these declarations, Rust would accept the bound `&Aligned1: TransmuteInto<&Aligned8>`. This is problematic: `Aligned8` has *stricter* alignment requirements than `Aligned1`. - -In our derivation of why the compiler accepted this transmutation, we referred to the stability declarations of `&T`: -```rust -impl<'a, T> PromiseTransmutableFrom for &'a T -where - T: PromiseTransmutableFrom, -{ - type Archetype = &'a T::Archetype; -} - -impl<'a, T> PromiseTransmutableInto for &'a T -where - T: PromiseTransmutableInto -{ - type Archetype = &'a T::Archetype; -} -``` -Rather than change the `where` bound of the *definitions* of `PromiseTransmutableFrom` and `PromiseTransmutableInto`, we can simply change the `where` bound of those two *implementations* of the traits: -```rust -impl<'a, T> PromiseTransmutableFrom for &'a T -where - T: PromiseTransmutableFrom, - &'a Self::Archetype: TransmuteInto<&'a Self, NeglectStability> -{ - type Archetype = &'a T::Archetype; -} - -impl<'a, T> PromiseTransmutableInto for &'a T -where - T: PromiseTransmutableInto, - &'a Self::Archetype: TransmuteFrom<&'a Self, NeglectStability> -{ - type Archetype = &'a T::Archetype; -} -``` - -#### Compartmentalizing Unique Reference Transmutation Stability -Likewise, for unique references, we go from this: -```rust -impl<'a, T> PromiseTransmutableFrom for &'a mut T -where - T: PromiseTransmutableFrom -{ - type Archetype = &'a mut T::Archetype; -} - -impl<'a, T> PromiseTransmutableInto for &'a mut T -where - T: PromiseTransmutableInto -{ - type Archetype = &'a mut T::Archetype; -} -``` -...to this: -```rust -impl<'a, T> PromiseTransmutableFrom for &'a mut T -where - T: PromiseTransmutableFrom, - &'a mut Self::Archetype: TransmuteInto<&'a mut Self, NeglectStability> -{ - type Archetype = &'a T::Archetype; -} - -impl<'a, T> PromiseTransmutableInto for &'a mut T -where - T: PromiseTransmutableInto, - &'a mut Self::Archetype: TransmuteFrom<&'a mut Self, NeglectStability> -{ - type Archetype = &'a T::Archetype; -} -``` - -### Part 3: Towards Sensibility - -In the previous two parts, we developed safe-and-flexible stability declaration traits. While we cannot make unsafe archetype choices, we can still make *silly* archetype choices. For instance, we can declare the stability of our types in terms of types that are *not* stable: - -```rust -#[repr(u8)] -pub struct Foo { ... } - -#[repr(u8)] -pub struct Bar { ... } - -impl PromiseTransmutableInto for Foo { - type Archetype = Bar; -} -``` - -We can prevent these sorts of silly archetypes by requiring that `Archetype` itself is stable, and that its stability archetype is not transitively defined: - -```rust -pub trait PromiseTransmutableFrom -{ - type Archetype - : TransmuteInto - + PromiseTransmutableFrom; -} - -pub trait PromiseTransmutableInto -{ - type Archetype - : TransmuteFrom - + PromiseTransmutableInto; -} -``` - # Drawbacks [drawbacks]: #drawbacks From ed02a09f01ba84cecf0f8ce817508a90b1dae770 Mon Sep 17 00:00:00 2001 From: Jack Wrenn Date: Fri, 28 Aug 2020 17:27:50 -0400 Subject: [PATCH 51/61] REMOVE Uncommon Use-Case: Weak Stability Guarantees --- rfcs/0000-safe-transmute.md | 46 ------------------------------------- 1 file changed, 46 deletions(-) diff --git a/rfcs/0000-safe-transmute.md b/rfcs/0000-safe-transmute.md index 3ab1b28..e481225 100644 --- a/rfcs/0000-safe-transmute.md +++ b/rfcs/0000-safe-transmute.md @@ -501,52 +501,6 @@ const _: () = { ``` Since deriving *both* of these traits together is, by far, the most common use-case, we [propose][extension-promisetransmutable-shorthand] `#[derive(PromiseTransmutable)]` as an ergonomic shortcut. -#### Uncommon Use-Case: Weak Stability Guarantees -[stability-uncommon]: #uncommon-use-case-weak-stability-guarantees - -If you are *most people* and want to declare your types as layout-stable, you should follow the advice in the previous sections. In doing so, you declare that you will *not* modify the layout of your type in virtually any way, except as a breaking change. If your type's fields have simple stability guarantees, this effects the strongest possible declaration of stability: it declares that *all* transmutations that are safe are *also* stable. - -However, if you anticipate that you will need to make changes to your type's layouts in a non-breaking way, you can craft uninformative archetypes that effect *weaker* promises of stability. With uninformative archetypes, only a *subset* of transmutations that are safe are also considered stable. For instance, you may use `PromiseTransmutableInto` to specify an lower-bound for the size of a type, but *not* make promises about its bit-validity: - -```rust -#[repr(C)] pub struct Foo(u8, u8, u8, u8, u8, u8); - -#[repr(C)] -struct FooArchetype(pub [MaybeUninit; 5]); - -impl PromiseTransmutableInto for Foo { - type Archetype = [MaybeUninit; 5]; -} -``` - -##### Crafting Uninformative `PromiseTransmutableFrom` Archetypes - -###### Archetype Alignment -You may freely decrease the required alignment of your type to any value less-than-or-equal to the required alignment of your archetype. The maximum alignment requirement permitted by Rust is 229. - -###### Archetype Size -You may decrease the size of your type without violating stability to any size less-than-or-equal to the size of your archetype. Unfortunately, the maximum size of types in Rust is [architecture-dependent](https://github.com/rust-lang/rust/blob/63b441aafbf52d6ba789ecc478455800c1a48df9/src/librustc_target/abi/mod.rs#L179-L197). For the greatest portability, your archetype should be no larger than 215. An archetype of this size provides (un)reasonable future flexibility. - -###### Archetype Validity & Visibility -You may freely make your type *less* constructible (in terms of both theoretical validity and constructor visibility) than your archetype. - -A minimally-informative archetype may be constructed using this as a building-block: -```rust -struct None(u8); -``` -Since the `u8` field is private, its valid values are assumed to be constrained by library invariants. - -##### Crafting Uninformative `PromiseTransmutableInto` Archetypes - -###### Archetype Alignment -You may freely increase the required alignment of your type to any value greater-than-or-equal to the required alignment of your archetype. The minimum alignment requirement permitted by Rust is 1. - -###### Archetype Size -You may increase the size of your type without violating stability to any size greater-than-or-equal to the size of your archetype. The minimum size of types in Rust is 0. - -###### Archetype Validity & Visibility -You may freely make your type more constructible (in terms of both visibility and theoretical validity) than your archetype. A minimally-informative archetype may be constructed using `MaybeUninit` as a building-block, since *any* possible instantiation of a byte is a valid instantiation of `MaybeUninit`. - ## Mechanisms of Transmutation From c0063c45b055f47087bd9d475a9cd54a337a7813 Mon Sep 17 00:00:00 2001 From: Jack Wrenn Date: Fri, 28 Aug 2020 17:32:14 -0400 Subject: [PATCH 52/61] syntax highlighting --- rfcs/0000-safe-transmute.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rfcs/0000-safe-transmute.md b/rfcs/0000-safe-transmute.md index e481225..e5aae81 100644 --- a/rfcs/0000-safe-transmute.md +++ b/rfcs/0000-safe-transmute.md @@ -82,7 +82,7 @@ The mechanisms proposed by the RFC enable this, see [here](0000-ext-layout-trait ### 📖 Transmutation **Transmutation** is the act of reinterpreting the bytes corresponding to a value of one type as if they corresponded to a different type. Concretely, we mean the behavior of this function: -``` +```rust #[inline(always)] unsafe fn transmute(src: Src) -> Dst { From 079324e63ad4e39406eea05a1985f4d948f515db Mon Sep 17 00:00:00 2001 From: Jack Wrenn Date: Fri, 28 Aug 2020 18:36:02 -0400 Subject: [PATCH 53/61] rework stability rationale --- rfcs/0000-safe-transmute.md | 62 ++++++++++++++++++++++++++++++++----- 1 file changed, 54 insertions(+), 8 deletions(-) diff --git a/rfcs/0000-safe-transmute.md b/rfcs/0000-safe-transmute.md index e5aae81..65cf870 100644 --- a/rfcs/0000-safe-transmute.md +++ b/rfcs/0000-safe-transmute.md @@ -1121,7 +1121,7 @@ This initial smart implementation may be made simpler by: - simplifying constructability #### Simplifying Constructability -The safety property of constructability defined in the guidance-level examplation of this RFC describes a platonic ideal of the property. +The safety property of constructability defined in the guidance-level explanation of this RFC describes a platonic ideal of the property. However, we recognize that this definition poses implementation challenges: In our definition of constructability, answering the question of whether a struct or enum variant is constructible depends on *where* that question is being asked. Consequently, answering whether a given type `Src` is `TransmutableInto` a given type `Dst` will depend on *where* that question is posed. @@ -1362,16 +1362,62 @@ We could not identify any advantages to representing options with const-generics ## Rationale: Stability -### Distinguishing Stability from Possibility -[Prior art][mechanism-manual] which requires an author's manual implementation of conversion traits conflates stability with possibility; i.e., there may be types for which a transmutation is known to be valid for which the authors do not implement the required traits for safe transmutation. For such types, transmuters must fall all the way back to the *wildly* unsafe `mem::transmute`; there is no middle-ground where transmuters can retain all static guarantees *besides* stability. By distinguishing possibility from stability, our proposal provides this middle ground. -### Distinguishing Stability from `#[repr(C)]` -In our proposal, `#[repr(C)]` does not connote any promises of layout stability for SemVer purposes. It's [been suggested](https://rust-lang.zulipchat.com/#narrow/stream/216762-project-safe-transmute/topic/RFC.3A.20Stability.20Declaration.20Traits/near/204011238) that the presence of `#[repr(C)]` *already* connotes total layout stability; if so, explicit stability declaration traits are superfluous. However, we are unaware of any documentation indicating that `#[repr(C)]` carries this implication. (It's also [been suggested](https://rust-lang.zulipchat.com/#narrow/stream/216762-project-safe-transmute/topic/typic/near/201165897) that `#[repr(C)]` should *not* connote stability, because that would pose a stability hazard. We agree with this assessment.) +### Why do we need a stability system? -### Permitting Granular Stability Declarations -The [stability declaration traits][stability] permit granular and incomplete promises of layout stability (e.g., guaranteeing the size and validity qualities of a type, but *not* its alignment. An alternative formulation of stability might be all-or-nothing (i.e., equivalent to only being able to write `Archetype = Self`). However, members of the safe-transmute working group have [expressed](https://rust-lang.zulipchat.com/#narrow/stream/216762-project-safe-transmute/topic/Transmutability.20Intrinsic/near/202712834) an interest in granular stability declarations. +At least two requirements necessitate the presence of a stability system: -Our proposed API supports complex, granular stability declarations, all-the-while retaining simplicity in simple use-cases (namely, one can simply the stability declaration traits). +#### Mitigating a Unique Stability Hazard +The usual rules of SemVer stability dictate that if a trait is is implemented in a version `m.a.b`, it will *continue* to be implemented for all versions `m.x.y`, where `x ≥ a` and `y ≥ b`. **`TransmuteFrom` is the exception to this rule**. It would be irresponsible to do nothing to mitigate this stability hazard. + +The compromise made by this RFC is that **`TransmuteFrom` should be stable-by-default**: + - `Dst: TransmuteFrom` follows the usual SemVer rules, + - `Dst: TransmuteFrom` *does not*. + +#### Mitigating a Possible Safety Hazard +The [simplified formulation of constructability](#simplifying-constructability) provides an initially-simpler implementation path at the cost of a soundness hole. There are three possible mitigations: + - *Pretend it does not exist.* Intentional soundness holes would not bode well for this RFC's acceptance. + - *Only provide unsafe transmutation; not safe transmutation.* This option fails to remove any `unsafe` blocks from end-users' code. + - *Allow safe transmutations only when the type authors have promised they will not create a situation that would compromise safety.* **We recommend this option.** + +This promise is inherently one of stability—the type author is vowing that they will not change the implementation of their type in a way that violates the no-pub-in-priv safety invariant of safe transmutation. + + +### Why this *particular* stability system? +The proposed stability system is both simple, flexible, and extensible. Whereas ensuring the soundness and safety of `TransmuteFrom` requires non-trivial compiler support, stability does not—it is realized as merely two normal traits and an `impl`: +```rust +pub trait PromiseTransmutableFrom +{ + type Archetype + : TransmuteInto + + PromiseTransmutableFrom; +} + +pub trait PromiseTransmutableInto +{ + type Archetype + : TransmuteFrom + + PromiseTransmutableInto; +} + +unsafe impl TransmuteFrom for Dst +where + Src: PromiseTransmutableInto, + Dst: PromiseTransmutableFrom, + + ::Archetype: + TransmuteFrom< + ::Archetype, + NeglectStability> +{} +``` +This formulation is flexible: by writing custom `Archetype`s, the [stability declaration traits][stability] make possible granular and incomplete promises of layout stability (e.g., guaranteeing the size and validity qualities of a type, but *not* its alignment. Members of the safe-transmute working group have [expressed](https://rust-lang.zulipchat.com/#narrow/stream/216762-project-safe-transmute/topic/Transmutability.20Intrinsic/near/202712834) an interest in granular stability declarations. + +Finally, this formulation is extensible. The range of advance use-cases permitted by these traits is constrained only by the set of possible `Archetype`s, which, in turn, is constrained by the completeness of `TransmuteFrom`. As the implementation of `TransmuteFrom` becomes more complete, so too will the range of advance use-cases accommodated by these traits. + + +### Couldn't `#[repr(C)]` denote stability? +In our proposal, `#[repr(C)]` does not connote any promises of transmutation stability for SemVer purposes. It has [been suggested](https://rust-lang.zulipchat.com/#narrow/stream/216762-project-safe-transmute/topic/RFC.3A.20Stability.20Declaration.20Traits/near/204011238) that the presence of `#[repr(C)]` *already* connotes total transmutation stability; i.e., that the type's author promises that the type's size and alignment and bit-validity will remain static. If this is true, then an additional stability mechanism is perhaps superfluous. However, we are unaware of any authoritative documentation indicating that `#[repr(C)]` carries this implication. Treating `#[repr(C)]` as an indicator of transmutation stability [would](https://rust-lang.zulipchat.com/#narrow/stream/216762-project-safe-transmute/topic/typic/near/201165897) thus pose a stability hazard. ## Alternative: Implementing this RFC in a Crate From 19e629ea08537cd9d72ff240cff5b4eb022cc2bc Mon Sep 17 00:00:00 2001 From: Jack Wrenn Date: Fri, 28 Aug 2020 18:39:16 -0400 Subject: [PATCH 54/61] note on endianness --- rfcs/0000-safe-transmute.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/rfcs/0000-safe-transmute.md b/rfcs/0000-safe-transmute.md index 65cf870..77d47ce 100644 --- a/rfcs/0000-safe-transmute.md +++ b/rfcs/0000-safe-transmute.md @@ -1265,6 +1265,7 @@ If and when the implementation of `TransmuteFrom` encodes our complete definitio ## No Notion of Platform Stability The stability declaration traits communicate library layout stability, but not *platform* layout stability. A transmutation is platform-stable if it compiling one one platform implies it will compile on all other platforms. Unfortunately, platform-unstable types are common; e.g.: +- All primitive number types have platform-dependent [endianness](https://en.wikipedia.org/wiki/Endianness). - All pointer-related primitive types (`usize`, `isize`, `*const T`, `*mut T`, `&T`, `&mut T`) possess platform-dependent layouts; their sizes and alignments are well-defined, but vary between platforms. Concretely, whether `usize` is `TransmuteInto<[u8; 4]>` or `TransmuteInto<[u8; 8]>` will depend on the platform. - The very existence of some types depends on platform, too; e.g., the contents of [`core::arch`](https://doc.rust-lang.org/stable/core/arch/), [`std::os`](https://doc.rust-lang.org/stable/std/os/), and [`core::sync::atomic`](https://doc.rust-lang.org/stable/std/sync/atomic/) all depend on platform. @@ -1298,7 +1299,7 @@ The issues of platform layout stability exposed by this RFC are not fundamentall The model of stability proposed by this RFC frames stability as a quality of *safe* transmutations. A type author cannot specify stability archetypes for *unsafe* transmutations, and it is reasonable to want to do so. -To accomodate this, we may modify the definitions of `PromiseTransmutableFrom` and `PromiseTransmutableInto` to consume an optional `Neglect` parameter, to allow for stability declarations for unsafe transmutations: +To accommodate this, we may modify the definitions of `PromiseTransmutableFrom` and `PromiseTransmutableInto` to consume an optional `Neglect` parameter, to allow for stability declarations for unsafe transmutations: ```rust pub trait PromiseTransmutableFrom where From aadfec70e2e6487c6739be90a40e319d80da1f86 Mon Sep 17 00:00:00 2001 From: Jack Wrenn Date: Fri, 28 Aug 2020 21:09:27 -0400 Subject: [PATCH 55/61] undefined *or* unspecified behavior --- rfcs/0000-safe-transmute.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rfcs/0000-safe-transmute.md b/rfcs/0000-safe-transmute.md index 77d47ce..237a71e 100644 --- a/rfcs/0000-safe-transmute.md +++ b/rfcs/0000-safe-transmute.md @@ -100,7 +100,7 @@ unsafe fn transmute(src: Src) -> Dst By **safer transmutation** we mean: *what `where` bound could we add to `transmute` restricts its type parameters `Src` and `Dst` in ways that statically limit the function's misuse?* Our answer to this question will ensure that transmutations are, by default, *sound*, *safe* and *stable*. ### 📖 Soundness -A transmutation is ***sound*** if the mere act of transmuting a value from one type to another is not compiler undefined behavior. +A transmutation is ***sound*** if the mere act of transmuting a value from one type to another is not unspecified or undefined behavior. ### 📖 Safety A sound transmutation is ***safe*** if *using* the transmuted value cannot violate memory safety. @@ -115,7 +115,7 @@ A safe transmutation is ***stable*** if the authors of the source type and desti ### 📖 When is a transmutation sound? [sound transmutation]: #-when-is-a-transmutation-sound -A transmutation is ***sound*** if the mere act of transmuting a value from one type to another is not undefined behavior. +A transmutation is ***sound*** if the mere act of transmuting a value from one type to another is not unspecified or undefined behavior. #### Well-Defined Representation [`u8`]: core::u8 From 9ed6b6deb12ad32f3d025c24a698032e10e433d8 Mon Sep 17 00:00:00 2001 From: Jack Wrenn Date: Mon, 31 Aug 2020 16:39:02 -0400 Subject: [PATCH 56/61] improve stability explanation --- rfcs/0000-safe-transmute.md | 108 ++++++++++++++++++++++++------------ 1 file changed, 72 insertions(+), 36 deletions(-) diff --git a/rfcs/0000-safe-transmute.md b/rfcs/0000-safe-transmute.md index 237a71e..677bf4a 100644 --- a/rfcs/0000-safe-transmute.md +++ b/rfcs/0000-safe-transmute.md @@ -438,23 +438,25 @@ By implementing `PromiseTransmutableFrom`, you promise that your type is guarant You are free to change the layout of your type however you like between minor crate versions so long as that change does not violates these promises. These two traits are capable of expressing simple and complex stability guarantees. #### Stability & Transmutation -A `Src` type is *stably* transmutable into a `Dst` type *only if* `::Archetype` is transmutable, stability notwithstanding, into `::Archetype`; formally: +Together with the `PromiseTransmutableFrom` and `PromiseTransmutableInto` traits, this impl of `TransmuteFrom` constitutes the formal definition of transmutation stability: ```rust unsafe impl TransmuteFrom for Dst where Src: PromiseTransmutableInto, Dst: PromiseTransmutableFrom, - ::Archetype: - TransmuteFrom< - ::Archetype, - NeglectStability - > + + Dst::Archetype: TransmuteFrom {} ``` +Why is this safe? Can we really safely judge whether `Dst` is transmutable from `Src` by assessing the transmutability of two different types? Yes! Transmutability is *transitive*. Concretely, if we can safely transmute: + - `Src` to `Src::Archetype` (enforced by `Src: PromiseTransmutableInto`), and + - `Dst::Archetype` to `Dst` (enforced by `Dst: PromiseTransmutableFrom`), and + - `Src::Archetype` to `Dst::Archetype` (enforced by `Dst::Archetype: TransmuteFrom`), -Why is this the case? - -The type `::Archetype` exemplifies the furthest extreme of non-breaking changes that could be made to the layout of `Src` that could affect its use as a source type in transmutations. Conversely, `::Archetype` exemplifies the furthest extreme of non-breaking changes that could be made to the layout of `Dst` that could affect its use as a destination type in transmutations. If a transmutation between these extremities is valid, then so is `Src: TransmuteInto`. +...then it follows that we can safely transmute `Src` to `Dst` in three steps: + 1. we transmute `Src` to `Src::Archetype`, + 2. we transmute `Src::Archetype` to `Dst::Archetype`, + 3. we transmute `Dst::Archetype` to `Dst`. #### Common Use-Case: As-Stable-As-Possible [stability-common]: #common-use-case-as-stable-as-possible @@ -479,6 +481,10 @@ const _: () = { pub ::Archetype, ); + impl PromiseTransmutableFrom for TransmutableFromArchetype { + type Archetype = Self; + } + impl PromiseTransmutableFrom for Foo { type Archetype = TransmutableFromArchetype; } @@ -494,6 +500,10 @@ const _: () = { pub ::Archetype, ); + impl PromiseTransmutableFrom for TransmutableIntoArchetype { + type Archetype = Self; + } + impl PromiseTransmutableInto for Foo { type Archetype = TransmutableIntoArchetype; } @@ -502,6 +512,57 @@ const _: () = { Since deriving *both* of these traits together is, by far, the most common use-case, we [propose][extension-promisetransmutable-shorthand] `#[derive(PromiseTransmutable)]` as an ergonomic shortcut. +#### Uncommon Use-Case: Weak Stability Guarantees +[stability-uncommon]: #uncommon-use-case-weak-stability-guarantees + +We also can specify *custom* `Archetype`s to finely constrain the set of transmutations we are willing to make stability promises for. Consider, for instance, if we want to leave ourselves the future leeway to change the alignment of a type `Foo` without making a SemVer major change: +```rust +#[repr(C)] +pub struct Foo(pub Bar, pub Baz); +``` +The alignment of `Foo` affects transmutability of `&Foo`. A `&Foo` cannot be safely transmuted from a `&Bar` if the alignment requirements of `Foo` exceed those of `Bar`. If we don't want to promise that `&Foo` is stably transmutable from virtually *any* `Bar`, we simply make `Foo`'s `PromiseTransmutableFrom::Archetype` a type with maximally strict alignment requirements: +```rust +const _: () = { + use core::transmute::stability::PromiseTransmutableFrom; + + #[repr(C, align(536870912))] + pub struct TransmutableFromArchetype( + pub ::Archetype, + pub ::Archetype, + ); + + impl PromiseTransmutableFrom for TransmutableFromArchetype { + type Archetype = Self; + } + + impl PromiseTransmutableFrom for Foo { + type Archetype = TransmutableFromArchetype; + } +}; +``` +Conversely, a `&Foo` cannot be safely transmuted *into* a `&Bar` if the alignment requirements of `Bar` exceed those of `Foo`. We reduce this set of stable transmutations by making `PromiseTransmutableFrom::Archetype` a type with minimal alignment requirements: +```rust +const _: () = { + use core::transmute::stability::PromiseTransmutableInto; + + #[repr(C, packed(1))] + pub struct TransmutableIntoArchetype( + pub ::Archetype, + pub ::Archetype, + ); + + impl PromiseTransmutableInto for TransmutableIntoArchetype { + type Archetype = Self; + } + + impl PromiseTransmutableInto for Foo { + type Archetype = TransmutableIntoArchetype; + } +}; +``` +Given these two stability promises, we are free to modify the alignment of `Foo` in SemVer-minor changes without running any risk of breaking dependent crates. + + ## Mechanisms of Transmutation Two traits provide mechanisms for transmutation between types: @@ -1385,33 +1446,8 @@ This promise is inherently one of stability—the type author is vowing that the ### Why this *particular* stability system? -The proposed stability system is both simple, flexible, and extensible. Whereas ensuring the soundness and safety of `TransmuteFrom` requires non-trivial compiler support, stability does not—it is realized as merely two normal traits and an `impl`: -```rust -pub trait PromiseTransmutableFrom -{ - type Archetype - : TransmuteInto - + PromiseTransmutableFrom; -} - -pub trait PromiseTransmutableInto -{ - type Archetype - : TransmuteFrom - + PromiseTransmutableInto; -} +The proposed stability system is both simple, flexible, and extensible. Whereas ensuring the soundness and safety of `TransmuteFrom` requires non-trivial compiler support, stability does not—it is realized as merely two normal traits and an `impl`. -unsafe impl TransmuteFrom for Dst -where - Src: PromiseTransmutableInto, - Dst: PromiseTransmutableFrom, - - ::Archetype: - TransmuteFrom< - ::Archetype, - NeglectStability> -{} -``` This formulation is flexible: by writing custom `Archetype`s, the [stability declaration traits][stability] make possible granular and incomplete promises of layout stability (e.g., guaranteeing the size and validity qualities of a type, but *not* its alignment. Members of the safe-transmute working group have [expressed](https://rust-lang.zulipchat.com/#narrow/stream/216762-project-safe-transmute/topic/Transmutability.20Intrinsic/near/202712834) an interest in granular stability declarations. Finally, this formulation is extensible. The range of advance use-cases permitted by these traits is constrained only by the set of possible `Archetype`s, which, in turn, is constrained by the completeness of `TransmuteFrom`. As the implementation of `TransmuteFrom` becomes more complete, so too will the range of advance use-cases accommodated by these traits. @@ -1695,6 +1731,6 @@ See [here](0000-ext-include-data.md). ## Extension: Generic Atomics -[future-possibility-generic-atomics]: #possibility-generic-atomics +[future-possibility-generic-atomics]: #extension-generic-atomics See [here](0000-ext-generic-atomic.md). From 93d83941341ee9e764f12cc9cc2bab8f990b0642 Mon Sep 17 00:00:00 2001 From: Jack Wrenn Date: Mon, 31 Aug 2020 16:47:40 -0400 Subject: [PATCH 57/61] add explanatory note to error message --- rfcs/0000-safe-transmute.md | 1 + 1 file changed, 1 insertion(+) diff --git a/rfcs/0000-safe-transmute.md b/rfcs/0000-safe-transmute.md index 677bf4a..1f3b894 100644 --- a/rfcs/0000-safe-transmute.md +++ b/rfcs/0000-safe-transmute.md @@ -34,6 +34,7 @@ let _ : u32 = Foo(16, 12).transmute_into(); // Compile Error! // | ^^^^^^^^^^^^^^ the trait `TransmuteFrom` is not implemented for `u32` // | // = note: required because of the requirements on the impl of `TransmuteInto` for `foo::Foo` +// = note: byte 8 of the source type may be uninitialized; byte 8 of the destination type cannot be uninitialized. ``` From b150c3f5865cb9105d0073bf7b96202623b0bff3 Mon Sep 17 00:00:00 2001 From: Jack Wrenn Date: Mon, 31 Aug 2020 17:04:49 -0400 Subject: [PATCH 58/61] prior art: haskell --- rfcs/0000-safe-transmute.md | 35 ++++++++++++++++++++++++++++++----- 1 file changed, 30 insertions(+), 5 deletions(-) diff --git a/rfcs/0000-safe-transmute.md b/rfcs/0000-safe-transmute.md index 1f3b894..d30fb3d 100644 --- a/rfcs/0000-safe-transmute.md +++ b/rfcs/0000-safe-transmute.md @@ -1495,10 +1495,10 @@ Finally, community-led, crate-based approaches are, inescapably, unauthoritative [2019-12-05-v2]: https://internals.rust-lang.org/t/pre-rfc-v2-safe-transmute/11431 [2020-07]: https://internals.rust-lang.org/t/pre-rfc-explicit-opt-in-oibit-for-truly-pod-data-and-safe-transmutes/2361 -## Dimensions of Variation -[dimensions-of-variation]: #dimensions-of-variation +## Prior Art: Rust +[prior-art-rust]: #prior-art-rust -A handful of dimensions of variation charactarize the distinctions between prior art: +A handful of dimensions of variation characterize the distinctions between prior art in Rust: - conversion complexity - conversion fallibility - source and destination types supported @@ -1518,7 +1518,7 @@ The signal and transmutability and mechanism are, in principle, separable. The [ Prior work differs in whether it supports only infallible conversions, or fallible conversions, too. The [convute][crate-convute] crate's [`TryTransmute`](https://docs.rs/convute/0.2.0/convute/marker/trait.TryTransmute.html) trait provides a method, `can_transmute`, that returns true a transmutation from `Self` to `T` is valid for a particular value of `&self`. An early version of [typic][crate-typic] abstracted a similar mechanism into an [`Invariants`](https://docs.rs/typic/0.1.0/typic/transmute/trait.Invariants.html) trait, with additional facilities for error reporting. [*Draft-RFC: `Compatible`/`TryCompatible`*][2019-12-05-gnzlbg] employs a similar mechanism to typic. Typic removed support for fallible transmutation after reckoning with several challenges: -- The causes of uncertain failure could be languaged-imposed (e.g., alignment or validity requirements), or library imposed (i.e., invariants placed on a structure's private fields). +- The causes of uncertain failure could be language-imposed (e.g., alignment or validity requirements), or library imposed (i.e., invariants placed on a structure's private fields). - The points of uncertain failures could be arbitrarily 'deep' into the fields of a type. - Error reporting incurs a runtime cost commensurate with the detail of the reporting, but the detail of reporting required by end-user depends on use-case, not just type. For instance: for some use-cases it may be necessary to know where and why a byte was not a valid `bool`; in others it may be sufficient to know simply *whether* an error occurred. @@ -1641,7 +1641,6 @@ We believe that the implementation burden these approaches place on end-users, a - These approaches conflate transmutation stability with transmutation safety. An end-user wishing to transmute a type for which its author has *not* manually implemented the applicable traits must resort to the wildly unsafe `mem::transmute`. - #### Automatic Automatic approaches implement the transmutation traits without user intervention, whenever it is sound to do so. This is the approach taken by our RFC. Automatic mechanisms appear in at least four prior language proposals: - [*Pre-RFC: Safe coercions*][2017-02] @@ -1671,6 +1670,32 @@ The simplified definition of *constructability* we propose is the same employed Our RFC separates *constructability*, which concerns what aspects of a type's structure are part of its public API, and *stability*, which concerns the aspects of a type's layout that are part of its public API for SemVer purposes. This distinction does not appear in prior work. + +## Prior Art: Haskell + +Haskell's [`Coercible`](https://hackage.haskell.org/package/base-4.14.0.0/docs/Data-Coerce.html#t:Coercible) typeclass is implemented for all types `A` and `B` when the compiler can infer that they have the same representation. As with our proposal's `TransmuteFrom` trait, instances of this typeclass are created "on-the-fly" by the compiler. `Coercible` primarily provides a safe means to convert to-and-from newtypes, and does not seek to answer, for instance, if two `u8`s are interchangeable with a `u16`. + +Haskell takes an algebraic approach to this problem, reasoning at the level of type definitions, not type layouts. However, not all type parameters have an impact on1 a type's layout; for instance: +```rust +#[repr(C)] +struct Bar(PhantomData); + +#[repr(transparent)] +struct Foo(T, Bar); +``` +`Foo`'s layout is impacted solely by `T`, not `U`, but this isn't necessarily clear by looking at the definition of `Foo`. To reason about these scenarios, Haskell introduces the concept of type parameter [*roles*](https://gitlab.haskell.org/ghc/ghc/-/wikis/roles)—labels that denote the relationship of a type parameter to coercibility. + +Our RFC does not need the concept of roles, because it does not attempt to abstractly reason about type definitions. Rather, it reasons about type *layouts*. This example, for instance, does not pose a challenge to our proposal: +```rust +trait SomeTrait { type AssociatedType; } + +#[repr(C)] +struct MyStruct(pub T, pub T::AssociatedType); +``` +For a *particular* `T`, `MyStruct` will have a *particular* layout. Our proposed `TransmuteFrom` trait reasons about the +*layouts* of types (which are fully concrete), not the *definitions* (which may be somewhat abstract). + + # Unresolved questions [unresolved-questions]: #unresolved-questions From 9fe396edccc291eba29834e5c9b45ccf3e53049f Mon Sep 17 00:00:00 2001 From: Jack Wrenn Date: Mon, 31 Aug 2020 17:05:26 -0400 Subject: [PATCH 59/61] typo: TransmutableFromArchetype -> TransmutableIntoArchetype --- rfcs/0000-safe-transmute.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rfcs/0000-safe-transmute.md b/rfcs/0000-safe-transmute.md index d30fb3d..c671777 100644 --- a/rfcs/0000-safe-transmute.md +++ b/rfcs/0000-safe-transmute.md @@ -548,8 +548,8 @@ const _: () = { #[repr(C, packed(1))] pub struct TransmutableIntoArchetype( - pub ::Archetype, - pub ::Archetype, + pub ::Archetype, + pub ::Archetype, ); impl PromiseTransmutableInto for TransmutableIntoArchetype { From 61448fbae00d85b0e80621983c6a8a0f49e2052a Mon Sep 17 00:00:00 2001 From: Jack Wrenn Date: Mon, 31 Aug 2020 17:09:25 -0400 Subject: [PATCH 60/61] change casting section heading --- rfcs/0000-safe-transmute.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/rfcs/0000-safe-transmute.md b/rfcs/0000-safe-transmute.md index c671777..a620a8d 100644 --- a/rfcs/0000-safe-transmute.md +++ b/rfcs/0000-safe-transmute.md @@ -1743,9 +1743,9 @@ See [here](0000-ext-layout-traits.md). See [here](0000-ext-byte-transmutation.md). -## Extension: Casting -[ext-slice-casting]: #extension-casting -[ext-vec-casting]: #extension-casting +## Extension: Slice and `Vec` Casting +[ext-slice-casting]: #extension-slice-and-vec-casting +[ext-vec-casting]: #extension-slice-and-vec-casting See [here](0000-ext-container-casting.md). From 3217d4a894b044f598434a5c7cf7766362a15729 Mon Sep 17 00:00:00 2001 From: Jack Wrenn Date: Mon, 31 Aug 2020 17:14:55 -0400 Subject: [PATCH 61/61] Move one of `Archetype`'s bounds to `where` This improves the rendering of rustdoc, and leaves us free to remove that bound if need be without violating stability. --- rfcs/0000-safe-transmute.md | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/rfcs/0000-safe-transmute.md b/rfcs/0000-safe-transmute.md index a620a8d..7e89b02 100644 --- a/rfcs/0000-safe-transmute.md +++ b/rfcs/0000-safe-transmute.md @@ -418,17 +418,17 @@ The question is, then: *how can the author of a type reason about transmutations You may declare the stability guarantees of your type by implementing one or both of two traits: ```rust pub trait PromiseTransmutableFrom +where + Self::Archetype: PromiseTransmutableFrom { - type Archetype - : TransmuteInto - + PromiseTransmutableFrom; + type Archetype: TransmuteInto } pub trait PromiseTransmutableInto +where + Self::Archetype: PromiseTransmutableInto, { - type Archetype - : TransmuteFrom - + PromiseTransmutableInto; + type Archetype: TransmuteFrom } ``` @@ -810,13 +810,13 @@ This [minimal implementation][minimal-impl] is sufficient for convincing the com ### Listing for Initial, Minimal Implementation [minimal-impl]: #Listing-for-Initial-Minimal-Implementation -This listing is both a minimal implementation of this RFC (excepting the automatic derives) and the **canonical specification** of this RFC's API surface ([playground](https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=22a5ae2d502ede826392ea4044d48b84)): +This listing is both a minimal implementation of this RFC (excepting the automatic derives) and the **canonical specification** of this RFC's API surface ([playground](https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=1ff1700e6dba595f1e600d20da5d6387)): ```rust #![feature(untagged_unions,const_fn,const_fn_union)] // for the impl of transmute free functions #![feature(const_generics)] // for stability declarations on `[T; N]` #![feature(decl_macro)] // for stub implementations of derives #![feature(never_type)] // for stability declarations on `!` -#![allow(unused_unsafe, incomplete_features)] +#![allow(warnings)] /// Transmutation conversions. // suggested location: `core::convert` @@ -964,20 +964,20 @@ pub mod transmute { /// Declare that transmuting `Self` into `Archetype` is SemVer-stable. pub trait PromiseTransmutableInto + where + Self::Archetype: PromiseTransmutableInto { /// The `Archetype` must be safely transmutable from `Self`. - type Archetype - : TransmuteFrom - + PromiseTransmutableInto; + type Archetype: TransmuteFrom; } /// Declare that transmuting `Self` from `Archetype` is SemVer-stable. pub trait PromiseTransmutableFrom + where + Self::Archetype: PromiseTransmutableFrom { /// The `Archetype` must be safely transmutable into `Self`. - type Archetype - : TransmuteInto - + PromiseTransmutableFrom; + type Archetype: TransmuteInto; }