From 43dfc04bb5ffecde2fa6987bdb3389314a5c040d Mon Sep 17 00:00:00 2001 From: Jack Wrenn Date: Mon, 12 Aug 2024 22:52:59 +0000 Subject: [PATCH] document & impl the transmutation modeled by `BikeshedIntrinsicFrom` Documents that `BikeshedIntrinsicFrom` models transmute-via-union, which is slightly more expressive than the transmute-via-cast implemented by `transmute_copy`. Additionally, we provide an implementation of transmute-via-union as a method on the `BikeshedIntrinsicFrom` trait with additional documentation on the boundary between trait invariants and caller obligations. Whether or not transmute-via-union is the right kind of transmute to model remains up for discussion [1]. Regardless, it seems wise to document the present behavior. [1] https://rust-lang.zulipchat.com/#narrow/stream/216762-project-safe-transmute/topic/What.20'kind'.20of.20transmute.20to.20model.3F/near/426331967 --- compiler/rustc_ty_utils/src/instance.rs | 4 + library/core/src/mem/transmutability.rs | 165 ++++++++++++++++-- .../closures/coerce-unsafe-to-closure.stderr | 4 +- tests/ui/intrinsics/reify-intrinsic.stderr | 4 +- 4 files changed, 159 insertions(+), 18 deletions(-) diff --git a/compiler/rustc_ty_utils/src/instance.rs b/compiler/rustc_ty_utils/src/instance.rs index 43e491387091e..817c8395b914b 100644 --- a/compiler/rustc_ty_utils/src/instance.rs +++ b/compiler/rustc_ty_utils/src/instance.rs @@ -363,6 +363,10 @@ fn resolve_associated_item<'tcx>( tcx.item_name(trait_item_id) ), } + } else if tcx.is_lang_item(trait_ref.def_id, LangItem::TransmuteTrait) { + let name = tcx.item_name(trait_item_id); + assert_eq!(name, sym::transmute); + Some(ty::Instance::new(trait_item_id, rcvr_args)) } else { Instance::try_resolve_item_for_coroutine(tcx, trait_item_id, trait_id, rcvr_args) } diff --git a/library/core/src/mem/transmutability.rs b/library/core/src/mem/transmutability.rs index ea73c5b80ba44..696e3d54daf03 100644 --- a/library/core/src/mem/transmutability.rs +++ b/library/core/src/mem/transmutability.rs @@ -1,10 +1,79 @@ use crate::marker::{ConstParamTy_, UnsizedConstParamTy}; -/// Are values of a type transmutable into values of another type? +/// Marks that `Src` is transmutable into `Self`. /// -/// This trait is implemented on-the-fly by the compiler for types `Src` and `Self` when the bits of -/// any value of type `Self` are safely transmutable into a value of type `Dst`, in a given `Context`, -/// notwithstanding whatever safety checks you have asked the compiler to [`Assume`] are satisfied. +/// # Implementation +/// +/// This trait cannot be implemented explicitly. It is implemented on-the-fly by +/// the compiler for all types `Src` and `Self` such that, given a set of safety +/// obligations on the programmer (see [`Assume`]), the compiler has proved that +/// the bits of a value of type `Src` can be soundly reinterpreted as a `Self`. +/// +/// Specifically, this trait models (and +/// [implements](BikeshedIntrinsicFrom::transmute)) the semantics of +/// transmute-via-union; i.e.: +/// +/// ```rust +/// pub unsafe fn transmute_via_union(src: Src) -> Dst { +/// use core::mem::ManuallyDrop; +/// +/// #[repr(C)] +/// union Transmute { +/// src: ManuallyDrop, +/// dst: ManuallyDrop, +/// } +/// +/// let transmute = Transmute { +/// src: ManuallyDrop::new(src), +/// }; +/// +/// let dst = transmute.dst; +/// +/// ManuallyDrop::into_inner(dst) +/// } +/// ``` +/// +/// Note that this construction supports some transmutations forbidden by +/// [`mem::transmute_copy`](super::transmute_copy); namely, transmutations that +/// extend the bits of `Src` with trailing padding to fill trailing +/// uninitialized bytes of `Self`; e.g.: +/// +/// ``` +/// #![feature(transmutability)] +/// +/// use core::mem::{Assume, BikeshedIntrinsicFrom}; +/// +/// let src = 42u8; // size = 1 +/// +/// #[repr(C, align(2))] +/// struct Dst(u8); // size = 2 +// +/// let _ = unsafe { >::transmute(src) }; +/// ``` +/// +/// ## Portability +/// +/// Implementations of this trait do not provide any guarantee of portability +/// across toolchains, targets or compilations. This trait may be implemented +/// for certain combinations of `Src`, `Self` and `ASSUME` on some toolchains, +/// targets or compilations, but not others. For example, if the layouts of +/// `Src` or `Self` are non-deterministic, the presence or absence of an +/// implementation of this trait may also be non-deterministic. Even if `Src` +/// and `Self` have deterministic layouts (e.g., they are `repr(C)` structs), +/// Rust does not specify the alignments of its primitive integer types, and +/// layouts that involve these types may vary across toolchains, targets or +/// compilations. +/// +/// ## Stability +/// +/// Implementations of this trait do not provide any guarantee of SemVer +/// stability across the crate versions that define the `Src` and `Self` types. +/// If SemVer stability is crucial to your application, you must consult the +/// documentation of `Src` and `Self`s' defining crates. Note that the presence +/// of `repr(C)`, alone, does not carry a safety invariant of SemVer stability. +/// Furthermore, stability does not imply portability. For example, the size of +/// `usize` is stable, but not portable. #[unstable(feature = "transmutability", issue = "99571")] #[lang = "transmute_trait"] #[rustc_deny_explicit_impl(implement_via_object = false)] @@ -13,28 +82,96 @@ pub unsafe trait BikeshedIntrinsicFrom Self + where + Src: Sized, + Self: Sized, + { + use super::ManuallyDrop; + + #[repr(C)] + union Transmute { + src: ManuallyDrop, + dst: ManuallyDrop, + } + + let transmute = Transmute { src: ManuallyDrop::new(src) }; + + // SAFETY: It is safe to reinterpret the bits of `src` as a value of + // type `Self`, because, by combination of invariant on this trait and + // contract on the caller, `src` has been proven to satisfy both the + // language and library invariants of `Self`. For all invariants not + // `ASSUME`'d by the caller, the safety obligation is supplied by the + // compiler. Conversely, for all invariants `ASSUME`'d by the caller, + // the safety obligation is supplied by contract on the caller. + let dst = unsafe { transmute.dst }; + + ManuallyDrop::into_inner(dst) + } } -/// What transmutation safety conditions shall the compiler assume that *you* are checking? +/// Configurable proof assumptions of [`BikeshedIntrinsicFrom`]. +/// +/// When `false`, the respective proof obligation belongs to the compiler. When +/// `true`, the onus of the safety proof belongs to the programmer. +/// [`BikeshedIntrinsicFrom`]. #[unstable(feature = "transmutability", issue = "99571")] #[lang = "transmute_opts"] #[derive(PartialEq, Eq, Clone, Copy, Debug)] pub struct Assume { - /// When `true`, the compiler assumes that *you* are ensuring (either dynamically or statically) that - /// destination referents do not have stricter alignment requirements than source referents. + /// When `false`, [`BikeshedIntrinsicFrom`] is not implemented for + /// transmutations that might violate the the alignment requirements of + /// references. + /// + /// When `true`, [`BikeshedIntrinsicFrom`] assumes that *you* have ensured + /// that that references in the transmuted value satisfy the alignment + /// requirements of their referent types. pub alignment: bool, - /// When `true`, the compiler assume that *you* are ensuring that lifetimes are not extended in a manner - /// that violates Rust's memory model. + /// When `false`, [`BikeshedIntrinsicFrom`] is not implemented for + /// transmutations that extend the lifetimes of references. + /// + /// When `true`, [`BikeshedIntrinsicFrom`] assumes that *you* have ensured + /// that that references in the transmuted value do not outlive their + /// referents. pub lifetimes: bool, - /// When `true`, the compiler assumes that *you* have ensured that no - /// unsoundness will arise from violating the safety invariants of the - /// destination type (and sometimes of the source type, too). + /// When `false`, [`BikeshedIntrinsicFrom`] is not implemented for + /// transmutations that might violate the library safety invariants of the + /// return type. + /// + /// When `true`, [`BikeshedIntrinsicFrom`] assumes that *you* have ensured + /// that undefined behavior does not arise from using the returned value. pub safety: bool, - /// When `true`, the compiler assumes that *you* are ensuring that the source type is actually a valid - /// instance of the destination type. + /// When `false`, [`BikeshedIntrinsicFrom`] is not implemented for + /// transmutations that might violate the language-level bit-validity + /// invariant of the return type. + /// + /// When `true`, [`BikeshedIntrinsicFrom`] assumes that *you* have ensured + /// that the value being transmuted is a bit-valid instance of the return + /// type. pub validity: bool, } diff --git a/tests/ui/closures/coerce-unsafe-to-closure.stderr b/tests/ui/closures/coerce-unsafe-to-closure.stderr index cb718ca160f8b..2538fc0361c37 100644 --- a/tests/ui/closures/coerce-unsafe-to-closure.stderr +++ b/tests/ui/closures/coerce-unsafe-to-closure.stderr @@ -1,4 +1,4 @@ -error[E0277]: expected a `FnOnce(&str)` closure, found `unsafe extern "rust-intrinsic" fn(_) -> _ {transmute::<_, _>}` +error[E0277]: expected a `FnOnce(&str)` closure, found `unsafe extern "rust-intrinsic" fn(_) -> _ {std::intrinsics::transmute::<_, _>}` --> $DIR/coerce-unsafe-to-closure.rs:2:44 | LL | let x: Option<&[u8]> = Some("foo").map(std::mem::transmute); @@ -6,7 +6,7 @@ LL | let x: Option<&[u8]> = Some("foo").map(std::mem::transmute); | | | required by a bound introduced by this call | - = help: the trait `FnOnce(&str)` is not implemented for fn item `unsafe extern "rust-intrinsic" fn(_) -> _ {transmute::<_, _>}` + = help: the trait `FnOnce(&str)` is not implemented for fn item `unsafe extern "rust-intrinsic" fn(_) -> _ {std::intrinsics::transmute::<_, _>}` = note: unsafe function cannot be called generically without an unsafe block note: required by a bound in `Option::::map` --> $SRC_DIR/core/src/option.rs:LL:COL diff --git a/tests/ui/intrinsics/reify-intrinsic.stderr b/tests/ui/intrinsics/reify-intrinsic.stderr index 0119a1a6650d0..21b7bf4e1cb83 100644 --- a/tests/ui/intrinsics/reify-intrinsic.stderr +++ b/tests/ui/intrinsics/reify-intrinsic.stderr @@ -7,9 +7,9 @@ LL | let _: unsafe extern "rust-intrinsic" fn(isize) -> usize = std::mem::tr | expected due to this | = note: expected fn pointer `unsafe extern "rust-intrinsic" fn(isize) -> usize` - found fn item `unsafe extern "rust-intrinsic" fn(_) -> _ {transmute::<_, _>}` + found fn item `unsafe extern "rust-intrinsic" fn(_) -> _ {std::intrinsics::transmute::<_, _>}` -error[E0606]: casting `unsafe extern "rust-intrinsic" fn(_) -> _ {transmute::<_, _>}` as `unsafe extern "rust-intrinsic" fn(isize) -> usize` is invalid +error[E0606]: casting `unsafe extern "rust-intrinsic" fn(_) -> _ {std::intrinsics::transmute::<_, _>}` as `unsafe extern "rust-intrinsic" fn(isize) -> usize` is invalid --> $DIR/reify-intrinsic.rs:11:13 | LL | let _ = std::mem::transmute as unsafe extern "rust-intrinsic" fn(isize) -> usize;