Skip to content

Commit ac7b552

Browse files
committed
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
1 parent 91376f4 commit ac7b552

17 files changed

+173
-31
lines changed

compiler/rustc_ty_utils/src/instance.rs

+5
Original file line numberDiff line numberDiff line change
@@ -363,6 +363,11 @@ fn resolve_associated_item<'tcx>(
363363
tcx.item_name(trait_item_id)
364364
),
365365
}
366+
} else if tcx.is_lang_item(trait_ref.def_id, LangItem::TransmuteTrait) {
367+
let name = tcx.item_name(trait_item_id);
368+
assert_eq!(name, sym::transmute);
369+
let args = tcx.erase_regions(rcvr_args);
370+
Some(ty::Instance::new(trait_item_id, args))
366371
} else {
367372
Instance::try_resolve_item_for_coroutine(tcx, trait_item_id, trait_id, rcvr_args)
368373
}

library/core/src/mem/transmutability.rs

+151-14
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,79 @@
11
use crate::marker::{ConstParamTy_, UnsizedConstParamTy};
22

3-
/// Are values of a type transmutable into values of another type?
3+
/// Marks that `Src` is transmutable into `Self`.
44
///
5-
/// This trait is implemented on-the-fly by the compiler for types `Src` and `Self` when the bits of
6-
/// any value of type `Self` are safely transmutable into a value of type `Dst`, in a given `Context`,
7-
/// notwithstanding whatever safety checks you have asked the compiler to [`Assume`] are satisfied.
5+
/// # Implementation
6+
///
7+
/// This trait cannot be implemented explicitly. It is implemented on-the-fly by
8+
/// the compiler for all types `Src` and `Self` such that, given a set of safety
9+
/// obligations on the programmer (see [`Assume`]), the compiler has proved that
10+
/// the bits of a value of type `Src` can be soundly reinterpreted as a `Self`.
11+
///
12+
/// Specifically, this trait models (and
13+
/// [implements](BikeshedIntrinsicFrom::transmute)) the semantics of
14+
/// transmute-via-union; i.e.:
15+
///
16+
/// ```rust
17+
/// pub unsafe fn transmute_via_union<Src, Dst>(src: Src) -> Dst {
18+
/// use core::mem::ManuallyDrop;
19+
///
20+
/// #[repr(C)]
21+
/// union Transmute<Src, Dst> {
22+
/// src: ManuallyDrop<Src>,
23+
/// dst: ManuallyDrop<Dst>,
24+
/// }
25+
///
26+
/// let transmute = Transmute {
27+
/// src: ManuallyDrop::new(src),
28+
/// };
29+
///
30+
/// let dst = transmute.dst;
31+
///
32+
/// ManuallyDrop::into_inner(dst)
33+
/// }
34+
/// ```
35+
///
36+
/// Note that this construction supports some transmutations forbidden by
37+
/// [`mem::transmute_copy`](super::transmute_copy); namely, transmutations that
38+
/// extend the bits of `Src` with trailing padding to fill trailing
39+
/// uninitialized bytes of `Self`; e.g.:
40+
///
41+
/// ```
42+
/// #![feature(transmutability)]
43+
///
44+
/// use core::mem::{Assume, BikeshedIntrinsicFrom};
45+
///
46+
/// let src = 42u8; // size = 1
47+
///
48+
/// #[repr(C, align(2))]
49+
/// struct Dst(u8); // size = 2
50+
//
51+
/// let _ = unsafe { <Dst as BikeshedIntrinsicFrom<u8, { Assume::SAFETY
52+
/// }>>::transmute(src) };
53+
/// ```
54+
///
55+
/// ## Portability
56+
///
57+
/// Implementations of this trait do not provide any guarantee of portability
58+
/// across toolchains, targets or compilations. This trait may be implemented
59+
/// for certain combinations of `Src`, `Self` and `ASSUME` on some toolchains,
60+
/// targets or compilations, but not others. For example, if the layouts of
61+
/// `Src` or `Self` are non-deterministic, the presence or absence of an
62+
/// implementation of this trait may also be non-deterministic. Even if `Src`
63+
/// and `Self` have deterministic layouts (e.g., they are `repr(C)` structs),
64+
/// Rust does not specify the alignments of its primitive integer types, and
65+
/// layouts that involve these types may vary across toolchains, targets or
66+
/// compilations.
67+
///
68+
/// ## Stability
69+
///
70+
/// Implementations of this trait do not provide any guarantee of SemVer
71+
/// stability across the crate versions that define the `Src` and `Self` types.
72+
/// If SemVer stability is crucial to your application, you must consult the
73+
/// documentation of `Src` and `Self`s' defining crates. Note that the presence
74+
/// of `repr(C)`, alone, does not carry a safety invariant of SemVer stability.
75+
/// Furthermore, stability does not imply portability. For example, the size of
76+
/// `usize` is stable, but not portable.
877
#[unstable(feature = "transmutability", issue = "99571")]
978
#[lang = "transmute_trait"]
1079
#[rustc_deny_explicit_impl(implement_via_object = false)]
@@ -13,28 +82,96 @@ pub unsafe trait BikeshedIntrinsicFrom<Src, const ASSUME: Assume = { Assume::NOT
1382
where
1483
Src: ?Sized,
1584
{
85+
/// Transmutes a `Src` value into a `Self`.
86+
///
87+
/// # Safety
88+
///
89+
/// The safety obligations of the caller depend on the value of `ASSUME`:
90+
/// - If [`ASSUME.alignment`](Assume::alignment), the caller must guarantee
91+
/// that the addresses of references in the returned `Self` satisfy the
92+
/// alignment requirements of their referent types.
93+
/// - If [`ASSUME.lifetimes`](Assume::lifetimes), the caller must guarantee
94+
/// that references in the returned `Self` will not outlive their
95+
/// referents.
96+
/// - If [`ASSUME.safety`](Assume::safety), the returned value might not
97+
/// satisfy the library safety invariants of `Self`, and the caller must
98+
/// guarantee that undefined behavior does not arise from uses of the
99+
/// returned value.
100+
/// - If [`ASSUME.validity`](Assume::validity), the caller must guarantee
101+
/// that `src` is a bit-valid instance of `Self`.
102+
///
103+
/// When satisfying the above obligations (if any), the caller must *not*
104+
/// assume that this trait provides any inherent guarantee of layout
105+
/// [portability](#portability) or [stability](#stability).
106+
unsafe fn transmute(src: Src) -> Self
107+
where
108+
Src: Sized,
109+
Self: Sized,
110+
{
111+
use super::ManuallyDrop;
112+
113+
#[repr(C)]
114+
union Transmute<Src, Dst> {
115+
src: ManuallyDrop<Src>,
116+
dst: ManuallyDrop<Dst>,
117+
}
118+
119+
let transmute = Transmute { src: ManuallyDrop::new(src) };
120+
121+
// SAFETY: It is safe to reinterpret the bits of `src` as a value of
122+
// type `Self`, because, by combination of invariant on this trait and
123+
// contract on the caller, `src` has been proven to satisfy both the
124+
// language and library invariants of `Self`. For all invariants not
125+
// `ASSUME`'d by the caller, the safety obligation is supplied by the
126+
// compiler. Conversely, for all invariants `ASSUME`'d by the caller,
127+
// the safety obligation is supplied by contract on the caller.
128+
let dst = unsafe { transmute.dst };
129+
130+
ManuallyDrop::into_inner(dst)
131+
}
16132
}
17133

18-
/// What transmutation safety conditions shall the compiler assume that *you* are checking?
134+
/// Configurable proof assumptions of [`BikeshedIntrinsicFrom`].
135+
///
136+
/// When `false`, the respective proof obligation belongs to the compiler. When
137+
/// `true`, the onus of the safety proof belongs to the programmer.
138+
/// [`BikeshedIntrinsicFrom`].
19139
#[unstable(feature = "transmutability", issue = "99571")]
20140
#[lang = "transmute_opts"]
21141
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
22142
pub struct Assume {
23-
/// When `true`, the compiler assumes that *you* are ensuring (either dynamically or statically) that
24-
/// destination referents do not have stricter alignment requirements than source referents.
143+
/// When `false`, [`BikeshedIntrinsicFrom`] is not implemented for
144+
/// transmutations that might violate the the alignment requirements of
145+
/// references.
146+
///
147+
/// When `true`, [`BikeshedIntrinsicFrom`] assumes that *you* have ensured
148+
/// that that references in the transmuted value satisfy the alignment
149+
/// requirements of their referent types.
25150
pub alignment: bool,
26151

27-
/// When `true`, the compiler assume that *you* are ensuring that lifetimes are not extended in a manner
28-
/// that violates Rust's memory model.
152+
/// When `false`, [`BikeshedIntrinsicFrom`] is not implemented for
153+
/// transmutations that extend the lifetimes of references.
154+
///
155+
/// When `true`, [`BikeshedIntrinsicFrom`] assumes that *you* have ensured
156+
/// that that references in the transmuted value do not outlive their
157+
/// referents.
29158
pub lifetimes: bool,
30159

31-
/// When `true`, the compiler assumes that *you* have ensured that no
32-
/// unsoundness will arise from violating the safety invariants of the
33-
/// destination type (and sometimes of the source type, too).
160+
/// When `false`, [`BikeshedIntrinsicFrom`] is not implemented for
161+
/// transmutations that might violate the library safety invariants of the
162+
/// return type.
163+
///
164+
/// When `true`, [`BikeshedIntrinsicFrom`] assumes that *you* have ensured
165+
/// that undefined behavior does not arise from using the returned value.
34166
pub safety: bool,
35167

36-
/// When `true`, the compiler assumes that *you* are ensuring that the source type is actually a valid
37-
/// instance of the destination type.
168+
/// When `false`, [`BikeshedIntrinsicFrom`] is not implemented for
169+
/// transmutations that might violate the language-level bit-validity
170+
/// invariant of the return type.
171+
///
172+
/// When `true`, [`BikeshedIntrinsicFrom`] assumes that *you* have ensured
173+
/// that the value being transmuted is a bit-valid instance of the return
174+
/// type.
38175
pub validity: bool,
39176
}
40177

tests/mir-opt/issue_72181_1.main.built.after.mir

+1-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ fn main() -> () {
1919
StorageLive(_2);
2020
StorageLive(_3);
2121
_3 = ();
22-
_2 = transmute::<(), Void>(move _3) -> bb4;
22+
_2 = std::intrinsics::transmute::<(), Void>(move _3) -> bb4;
2323
}
2424

2525
bb1: {

tests/mir-opt/lower_intrinsics.transmute_inhabited.LowerIntrinsics.panic-abort.diff

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
bb0: {
1010
StorageLive(_2);
1111
_2 = _1;
12-
- _0 = transmute::<std::cmp::Ordering, i8>(move _2) -> [return: bb1, unwind unreachable];
12+
- _0 = std::intrinsics::transmute::<std::cmp::Ordering, i8>(move _2) -> [return: bb1, unwind unreachable];
1313
+ _0 = move _2 as i8 (Transmute);
1414
+ goto -> bb1;
1515
}

tests/mir-opt/lower_intrinsics.transmute_inhabited.LowerIntrinsics.panic-unwind.diff

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
bb0: {
1010
StorageLive(_2);
1111
_2 = _1;
12-
- _0 = transmute::<std::cmp::Ordering, i8>(move _2) -> [return: bb1, unwind unreachable];
12+
- _0 = std::intrinsics::transmute::<std::cmp::Ordering, i8>(move _2) -> [return: bb1, unwind unreachable];
1313
+ _0 = move _2 as i8 (Transmute);
1414
+ goto -> bb1;
1515
}

tests/mir-opt/lower_intrinsics.transmute_ref_dst.LowerIntrinsics.panic-abort.diff

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
bb0: {
1010
StorageLive(_2);
1111
_2 = _1;
12-
- _0 = transmute::<&T, *const T>(move _2) -> [return: bb1, unwind unreachable];
12+
- _0 = std::intrinsics::transmute::<&T, *const T>(move _2) -> [return: bb1, unwind unreachable];
1313
+ _0 = move _2 as *const T (Transmute);
1414
+ goto -> bb1;
1515
}

tests/mir-opt/lower_intrinsics.transmute_ref_dst.LowerIntrinsics.panic-unwind.diff

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
bb0: {
1010
StorageLive(_2);
1111
_2 = _1;
12-
- _0 = transmute::<&T, *const T>(move _2) -> [return: bb1, unwind unreachable];
12+
- _0 = std::intrinsics::transmute::<&T, *const T>(move _2) -> [return: bb1, unwind unreachable];
1313
+ _0 = move _2 as *const T (Transmute);
1414
+ goto -> bb1;
1515
}

tests/mir-opt/lower_intrinsics.transmute_to_box_uninhabited.LowerIntrinsics.panic-abort.diff

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
bb0: {
1313
StorageLive(_1);
14-
- _1 = transmute::<usize, Box<Never>>(const 1_usize) -> [return: bb1, unwind unreachable];
14+
- _1 = std::intrinsics::transmute::<usize, Box<Never>>(const 1_usize) -> [return: bb1, unwind unreachable];
1515
+ _1 = const 1_usize as std::boxed::Box<Never> (Transmute);
1616
+ goto -> bb1;
1717
}

tests/mir-opt/lower_intrinsics.transmute_to_box_uninhabited.LowerIntrinsics.panic-unwind.diff

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
bb0: {
1313
StorageLive(_1);
14-
- _1 = transmute::<usize, Box<Never>>(const 1_usize) -> [return: bb1, unwind unreachable];
14+
- _1 = std::intrinsics::transmute::<usize, Box<Never>>(const 1_usize) -> [return: bb1, unwind unreachable];
1515
+ _1 = const 1_usize as std::boxed::Box<Never> (Transmute);
1616
+ goto -> bb1;
1717
}

tests/mir-opt/lower_intrinsics.transmute_to_mut_uninhabited.LowerIntrinsics.panic-abort.diff

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
bb0: {
1212
StorageLive(_1);
13-
- _1 = transmute::<usize, &mut Never>(const 1_usize) -> [return: bb1, unwind unreachable];
13+
- _1 = std::intrinsics::transmute::<usize, &mut Never>(const 1_usize) -> [return: bb1, unwind unreachable];
1414
+ _1 = const 1_usize as &mut Never (Transmute);
1515
+ goto -> bb1;
1616
}

tests/mir-opt/lower_intrinsics.transmute_to_mut_uninhabited.LowerIntrinsics.panic-unwind.diff

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
bb0: {
1212
StorageLive(_1);
13-
- _1 = transmute::<usize, &mut Never>(const 1_usize) -> [return: bb1, unwind unreachable];
13+
- _1 = std::intrinsics::transmute::<usize, &mut Never>(const 1_usize) -> [return: bb1, unwind unreachable];
1414
+ _1 = const 1_usize as &mut Never (Transmute);
1515
+ goto -> bb1;
1616
}

tests/mir-opt/lower_intrinsics.transmute_to_ref_uninhabited.LowerIntrinsics.panic-abort.diff

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
bb0: {
1212
StorageLive(_1);
13-
- _1 = transmute::<usize, &Never>(const 1_usize) -> [return: bb1, unwind unreachable];
13+
- _1 = std::intrinsics::transmute::<usize, &Never>(const 1_usize) -> [return: bb1, unwind unreachable];
1414
+ _1 = const 1_usize as &Never (Transmute);
1515
+ goto -> bb1;
1616
}

tests/mir-opt/lower_intrinsics.transmute_to_ref_uninhabited.LowerIntrinsics.panic-unwind.diff

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
bb0: {
1212
StorageLive(_1);
13-
- _1 = transmute::<usize, &Never>(const 1_usize) -> [return: bb1, unwind unreachable];
13+
- _1 = std::intrinsics::transmute::<usize, &Never>(const 1_usize) -> [return: bb1, unwind unreachable];
1414
+ _1 = const 1_usize as &Never (Transmute);
1515
+ goto -> bb1;
1616
}

tests/mir-opt/lower_intrinsics.transmute_uninhabited.LowerIntrinsics.panic-abort.diff

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
bb0: {
1010
StorageLive(_2);
1111
_2 = _1;
12-
- _0 = transmute::<(), Never>(move _2) -> unwind unreachable;
12+
- _0 = std::intrinsics::transmute::<(), Never>(move _2) -> unwind unreachable;
1313
+ _0 = move _2 as Never (Transmute);
1414
+ unreachable;
1515
}

tests/mir-opt/lower_intrinsics.transmute_uninhabited.LowerIntrinsics.panic-unwind.diff

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
bb0: {
1010
StorageLive(_2);
1111
_2 = _1;
12-
- _0 = transmute::<(), Never>(move _2) -> unwind unreachable;
12+
- _0 = std::intrinsics::transmute::<(), Never>(move _2) -> unwind unreachable;
1313
+ _0 = move _2 as Never (Transmute);
1414
+ unreachable;
1515
}

tests/ui/closures/coerce-unsafe-to-closure.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
error[E0277]: expected a `FnOnce(&str)` closure, found `unsafe extern "rust-intrinsic" fn(_) -> _ {transmute::<_, _>}`
1+
error[E0277]: expected a `FnOnce(&str)` closure, found `unsafe extern "rust-intrinsic" fn(_) -> _ {std::intrinsics::transmute::<_, _>}`
22
--> $DIR/coerce-unsafe-to-closure.rs:2:44
33
|
44
LL | let x: Option<&[u8]> = Some("foo").map(std::mem::transmute);
55
| --- ^^^^^^^^^^^^^^^^^^^ call the function in a closure: `|| unsafe { /* code */ }`
66
| |
77
| required by a bound introduced by this call
88
|
9-
= help: the trait `FnOnce(&str)` is not implemented for fn item `unsafe extern "rust-intrinsic" fn(_) -> _ {transmute::<_, _>}`
9+
= help: the trait `FnOnce(&str)` is not implemented for fn item `unsafe extern "rust-intrinsic" fn(_) -> _ {std::intrinsics::transmute::<_, _>}`
1010
= note: unsafe function cannot be called generically without an unsafe block
1111
note: required by a bound in `Option::<T>::map`
1212
--> $SRC_DIR/core/src/option.rs:LL:COL

tests/ui/intrinsics/reify-intrinsic.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@ LL | let _: unsafe extern "rust-intrinsic" fn(isize) -> usize = std::mem::tr
77
| expected due to this
88
|
99
= note: expected fn pointer `unsafe extern "rust-intrinsic" fn(isize) -> usize`
10-
found fn item `unsafe extern "rust-intrinsic" fn(_) -> _ {transmute::<_, _>}`
10+
found fn item `unsafe extern "rust-intrinsic" fn(_) -> _ {std::intrinsics::transmute::<_, _>}`
1111

12-
error[E0606]: casting `unsafe extern "rust-intrinsic" fn(_) -> _ {transmute::<_, _>}` as `unsafe extern "rust-intrinsic" fn(isize) -> usize` is invalid
12+
error[E0606]: casting `unsafe extern "rust-intrinsic" fn(_) -> _ {std::intrinsics::transmute::<_, _>}` as `unsafe extern "rust-intrinsic" fn(isize) -> usize` is invalid
1313
--> $DIR/reify-intrinsic.rs:11:13
1414
|
1515
LL | let _ = std::mem::transmute as unsafe extern "rust-intrinsic" fn(isize) -> usize;

0 commit comments

Comments
 (0)