Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 30 additions & 5 deletions library/core/src/mem/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ use crate::clone::TrivialClone;
use crate::cmp::Ordering;
use crate::marker::{Destruct, DiscriminantKind};
use crate::panic::const_assert;
use crate::ub_checks::assert_unsafe_precondition;
use crate::{clone, cmp, fmt, hash, intrinsics, ptr};

mod alignment;
Expand Down Expand Up @@ -1040,6 +1041,18 @@ pub const fn copy<T: Copy>(x: &T) -> T {
///
/// [ub]: ../../reference/behavior-considered-undefined.html
///
/// If you have a raw pointer instead of a reference, you might be looking for
/// [`ptr::read_unaligned`]`::<Dst>(ptr.cast::<Dst>())` instead.
///
/// # Safety
///
/// - Requires `size_of_val::<Src>(src) >= size_of::<Dst>()`
/// - The first `size_of::<Dst>()` bytes behind `src` must be *readable*
/// - The first `size_of::<Dst>()` bytes behind `src` must be *valid*
/// when interpreted as a `Dst`.
/// - All safety invariants of the `Dst` type must be upheld. (For example,
/// `{ transmute_copy::<String, String>(&x); x; }` is UB for the double-drop.)
///
/// # Examples
///
/// ```
Expand All @@ -1064,20 +1077,32 @@ pub const fn copy<T: Copy>(x: &T) -> T {
///
/// // The contents of 'foo_array' should not have changed
/// assert_eq!(foo_array, [10]);
///
/// let bytes: &[u8] = &[1, 2, 3, 4, 5, 6, 7];
/// assert_eq!(
/// unsafe { mem::transmute_copy::<[u8], u32>(bytes) },
/// u32::from_ne_bytes(*bytes.first_chunk().unwrap()),
/// );
/// ```
#[inline]
#[must_use]
#[track_caller]
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_const_stable(feature = "const_transmute_copy", since = "1.74.0")]
pub const unsafe fn transmute_copy<Src, Dst>(src: &Src) -> Dst {
assert!(
size_of::<Src>() >= size_of::<Dst>(),
"cannot transmute_copy if Dst is larger than Src"
pub const unsafe fn transmute_copy<Src: ?Sized, Dst>(src: &Src) -> Dst {
// library UB because it's possible for the `Src` to be only a subset of the allocation
// and thus for a failure to not be immediate language UB
assert_unsafe_precondition!(
check_library_ub,
"cannot transmute_copy if Dst is larger than Src",
(
src_size: usize = size_of_val::<Src>(src),
dst_size: usize = Dst::SIZE,
) => src_size >= dst_size
);

// If Dst has a higher alignment requirement, src might not be suitably aligned.
if align_of::<Dst>() > align_of::<Src>() {
if align_of::<Dst>() > align_of_val::<Src>(src) {
// SAFETY: `src` is a reference which is guaranteed to be valid for reads.
// The caller must guarantee that the actual transmutation is safe.
unsafe { ptr::read_unaligned(src as *const Src as *const Dst) }
Expand Down
26 changes: 0 additions & 26 deletions library/coretests/tests/mem.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,32 +138,6 @@ fn test_transmute_copy_unaligned() {
assert_eq!(0_u64, unsafe { transmute_copy(&u.b) });
}

#[test]
#[cfg(panic = "unwind")]
fn test_transmute_copy_grow_panics() {
use std::panic;

let err = panic::catch_unwind(panic::AssertUnwindSafe(|| unsafe {
let _unused: u64 = transmute_copy(&1_u8);
}));

match err {
Ok(_) => unreachable!(),
Err(payload) => {
payload
.downcast::<&'static str>()
.and_then(|s| {
if *s == "cannot transmute_copy if Dst is larger than Src" {
Ok(s)
} else {
Err(s)
}
})
.unwrap_or_else(|p| panic::resume_unwind(p));
}
}
}

#[test]
#[allow(dead_code)]
fn test_discriminant_send_sync() {
Expand Down
9 changes: 9 additions & 0 deletions tests/ui/precondition-checks/transmute_copy.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
//@ run-crash
//@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes
//@ error-pattern: unsafe precondition(s) violated: cannot transmute_copy if Dst is larger than Src

fn main() {
unsafe {
let _unused: u64 = std::mem::transmute_copy(&1_u8);
}
}
Loading