Skip to content

Commit f8e9e76

Browse files
committed
Auto merge of #139916 - RalfJung:intrinsic-wrappers, r=Mark-Simulacrum
make std::intrinsics functions actually be intrinsics Most of the functions in `std::intrinsics` are actually intrinsics, but some are not: for historical reasons, `std::intrinsics::{copy,copy_nonoverlapping,write_bytes}` are accessible on stable, and the versions in `std::ptr` are just re-exports. These functions are not intrinsics, but wrappers around the intrinsic, because they add extra debug assertions. This PR makes the functions in `std::intrinsics` actually be intrinsics. - The advantage is that we can now use it in tests that need to directly call the intrinsic, thus removing a footgun for compiler development. We also remove the extended user-facing doc comments of these functions out of a file that should be largely internal documentation. - The downside is that if users are using those functions directly, they will not get the debug assertions any more. Note however that those users are already ignoring a deprecation warning, so I think this is fine. Furthermore, if someone imports the `intrinsic` name of this function and turns that into a function pointer, that will no longer work, since only the wrapper functions can be turned into a function pointer. I would be rather surprised if anyone did this, though... and again, they must have already ignored a deprecation warning. Still, seems worth a crater run, if there's general agreement that we want to go ahead with this change. (`intrinsics::drop_in_place` also remains not-an-intrinsic, which bugs me, but oh well, not much we can do about it; we can't remove it from the module as the path is accidentally-stable.) Cc `@rust-lang/libs-api` `@saethlin`
2 parents a8e4c68 + a29756d commit f8e9e76

15 files changed

+336
-353
lines changed

library/core/src/intrinsics/mod.rs

Lines changed: 24 additions & 293 deletions
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,7 @@
6262
#![allow(missing_docs)]
6363

6464
use crate::marker::{DiscriminantKind, Tuple};
65-
use crate::mem::SizedTypeProperties;
66-
use crate::{ptr, ub_checks};
65+
use crate::ptr;
6766

6867
pub mod fallback;
6968
pub mod mir;
@@ -3317,7 +3316,7 @@ pub const unsafe fn typed_swap_nonoverlapping<T>(x: *mut T, y: *mut T) {
33173316
/// `#[inline]`), gating assertions on `ub_checks()` rather than `cfg!(ub_checks)` means that
33183317
/// assertions are enabled whenever the *user crate* has UB checks enabled. However, if the
33193318
/// user has UB checks disabled, the checks will still get optimized out. This intrinsic is
3320-
/// primarily used by [`ub_checks::assert_unsafe_precondition`].
3319+
/// primarily used by [`crate::ub_checks::assert_unsafe_precondition`].
33213320
#[rustc_intrinsic_const_stable_indirect] // just for UB checks
33223321
#[inline(always)]
33233322
#[rustc_intrinsic]
@@ -3595,306 +3594,38 @@ impl<P: ?Sized, T: ptr::Thin> AggregateRawPtr<*mut T> for *mut P {
35953594
#[rustc_intrinsic]
35963595
pub const fn ptr_metadata<P: ptr::Pointee<Metadata = M> + ?Sized, M>(ptr: *const P) -> M;
35973596

3598-
// Some functions are defined here because they accidentally got made
3599-
// available in this module on stable. See <https://github.com/rust-lang/rust/issues/15702>.
3600-
// (`transmute` also falls into this category, but it cannot be wrapped due to the
3601-
// check that `T` and `U` have the same size.)
3602-
3603-
/// Copies `count * size_of::<T>()` bytes from `src` to `dst`. The source
3604-
/// and destination must *not* overlap.
3605-
///
3606-
/// For regions of memory which might overlap, use [`copy`] instead.
3607-
///
3608-
/// `copy_nonoverlapping` is semantically equivalent to C's [`memcpy`], but
3609-
/// with the source and destination arguments swapped,
3610-
/// and `count` counting the number of `T`s instead of bytes.
3611-
///
3612-
/// The copy is "untyped" in the sense that data may be uninitialized or otherwise violate the
3613-
/// requirements of `T`. The initialization state is preserved exactly.
3614-
///
3615-
/// [`memcpy`]: https://en.cppreference.com/w/c/string/byte/memcpy
3616-
///
3617-
/// # Safety
3618-
///
3619-
/// Behavior is undefined if any of the following conditions are violated:
3620-
///
3621-
/// * `src` must be [valid] for reads of `count * size_of::<T>()` bytes.
3622-
///
3623-
/// * `dst` must be [valid] for writes of `count * size_of::<T>()` bytes.
3624-
///
3625-
/// * Both `src` and `dst` must be properly aligned.
3626-
///
3627-
/// * The region of memory beginning at `src` with a size of `count *
3628-
/// size_of::<T>()` bytes must *not* overlap with the region of memory
3629-
/// beginning at `dst` with the same size.
3630-
///
3631-
/// Like [`read`], `copy_nonoverlapping` creates a bitwise copy of `T`, regardless of
3632-
/// whether `T` is [`Copy`]. If `T` is not [`Copy`], using *both* the values
3633-
/// in the region beginning at `*src` and the region beginning at `*dst` can
3634-
/// [violate memory safety][read-ownership].
3635-
///
3636-
/// Note that even if the effectively copied size (`count * size_of::<T>()`) is
3637-
/// `0`, the pointers must be properly aligned.
3638-
///
3639-
/// [`read`]: crate::ptr::read
3640-
/// [read-ownership]: crate::ptr::read#ownership-of-the-returned-value
3641-
/// [valid]: crate::ptr#safety
3642-
///
3643-
/// # Examples
3644-
///
3645-
/// Manually implement [`Vec::append`]:
3646-
///
3647-
/// ```
3648-
/// use std::ptr;
3649-
///
3650-
/// /// Moves all the elements of `src` into `dst`, leaving `src` empty.
3651-
/// fn append<T>(dst: &mut Vec<T>, src: &mut Vec<T>) {
3652-
/// let src_len = src.len();
3653-
/// let dst_len = dst.len();
3654-
///
3655-
/// // Ensure that `dst` has enough capacity to hold all of `src`.
3656-
/// dst.reserve(src_len);
3657-
///
3658-
/// unsafe {
3659-
/// // The call to add is always safe because `Vec` will never
3660-
/// // allocate more than `isize::MAX` bytes.
3661-
/// let dst_ptr = dst.as_mut_ptr().add(dst_len);
3662-
/// let src_ptr = src.as_ptr();
3663-
///
3664-
/// // Truncate `src` without dropping its contents. We do this first,
3665-
/// // to avoid problems in case something further down panics.
3666-
/// src.set_len(0);
3667-
///
3668-
/// // The two regions cannot overlap because mutable references do
3669-
/// // not alias, and two different vectors cannot own the same
3670-
/// // memory.
3671-
/// ptr::copy_nonoverlapping(src_ptr, dst_ptr, src_len);
3672-
///
3673-
/// // Notify `dst` that it now holds the contents of `src`.
3674-
/// dst.set_len(dst_len + src_len);
3675-
/// }
3676-
/// }
3677-
///
3678-
/// let mut a = vec!['r'];
3679-
/// let mut b = vec!['u', 's', 't'];
3680-
///
3681-
/// append(&mut a, &mut b);
3682-
///
3683-
/// assert_eq!(a, &['r', 'u', 's', 't']);
3684-
/// assert!(b.is_empty());
3685-
/// ```
3686-
///
3687-
/// [`Vec::append`]: ../../std/vec/struct.Vec.html#method.append
3688-
#[doc(alias = "memcpy")]
3597+
/// This is an accidentally-stable alias to [`ptr::copy_nonoverlapping`]; use that instead.
3598+
// Note (intentionally not in the doc comment): `ptr::copy_nonoverlapping` adds some extra
3599+
// debug assertions; if you are writing compiler tests or code inside the standard library
3600+
// that wants to avoid those debug assertions, directly call this intrinsic instead.
36893601
#[stable(feature = "rust1", since = "1.0.0")]
36903602
#[rustc_allowed_through_unstable_modules = "import this function via `std::ptr` instead"]
36913603
#[rustc_const_stable(feature = "const_intrinsic_copy", since = "1.83.0")]
3692-
#[inline(always)]
3693-
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
3694-
#[rustc_diagnostic_item = "ptr_copy_nonoverlapping"]
3695-
pub const unsafe fn copy_nonoverlapping<T>(src: *const T, dst: *mut T, count: usize) {
3696-
#[rustc_intrinsic_const_stable_indirect]
3697-
#[rustc_nounwind]
3698-
#[rustc_intrinsic]
3699-
const unsafe fn copy_nonoverlapping<T>(src: *const T, dst: *mut T, count: usize);
3700-
3701-
ub_checks::assert_unsafe_precondition!(
3702-
check_language_ub,
3703-
"ptr::copy_nonoverlapping requires that both pointer arguments are aligned and non-null \
3704-
and the specified memory ranges do not overlap",
3705-
(
3706-
src: *const () = src as *const (),
3707-
dst: *mut () = dst as *mut (),
3708-
size: usize = size_of::<T>(),
3709-
align: usize = align_of::<T>(),
3710-
count: usize = count,
3711-
) => {
3712-
let zero_size = count == 0 || size == 0;
3713-
ub_checks::maybe_is_aligned_and_not_null(src, align, zero_size)
3714-
&& ub_checks::maybe_is_aligned_and_not_null(dst, align, zero_size)
3715-
&& ub_checks::maybe_is_nonoverlapping(src, dst, size, count)
3716-
}
3717-
);
3718-
3719-
// SAFETY: the safety contract for `copy_nonoverlapping` must be
3720-
// upheld by the caller.
3721-
unsafe { copy_nonoverlapping(src, dst, count) }
3722-
}
3604+
#[rustc_nounwind]
3605+
#[rustc_intrinsic]
3606+
pub const unsafe fn copy_nonoverlapping<T>(src: *const T, dst: *mut T, count: usize);
37233607

3724-
/// Copies `count * size_of::<T>()` bytes from `src` to `dst`. The source
3725-
/// and destination may overlap.
3726-
///
3727-
/// If the source and destination will *never* overlap,
3728-
/// [`copy_nonoverlapping`] can be used instead.
3729-
///
3730-
/// `copy` is semantically equivalent to C's [`memmove`], but
3731-
/// with the source and destination arguments swapped,
3732-
/// and `count` counting the number of `T`s instead of bytes.
3733-
/// Copying takes place as if the bytes were copied from `src`
3734-
/// to a temporary array and then copied from the array to `dst`.
3735-
///
3736-
/// The copy is "untyped" in the sense that data may be uninitialized or otherwise violate the
3737-
/// requirements of `T`. The initialization state is preserved exactly.
3738-
///
3739-
/// [`memmove`]: https://en.cppreference.com/w/c/string/byte/memmove
3740-
///
3741-
/// # Safety
3742-
///
3743-
/// Behavior is undefined if any of the following conditions are violated:
3744-
///
3745-
/// * `src` must be [valid] for reads of `count * size_of::<T>()` bytes.
3746-
///
3747-
/// * `dst` must be [valid] for writes of `count * size_of::<T>()` bytes, and must remain valid even
3748-
/// when `src` is read for `count * size_of::<T>()` bytes. (This means if the memory ranges
3749-
/// overlap, the `dst` pointer must not be invalidated by `src` reads.)
3750-
///
3751-
/// * Both `src` and `dst` must be properly aligned.
3752-
///
3753-
/// Like [`read`], `copy` creates a bitwise copy of `T`, regardless of
3754-
/// whether `T` is [`Copy`]. If `T` is not [`Copy`], using both the values
3755-
/// in the region beginning at `*src` and the region beginning at `*dst` can
3756-
/// [violate memory safety][read-ownership].
3757-
///
3758-
/// Note that even if the effectively copied size (`count * size_of::<T>()`) is
3759-
/// `0`, the pointers must be properly aligned.
3760-
///
3761-
/// [`read`]: crate::ptr::read
3762-
/// [read-ownership]: crate::ptr::read#ownership-of-the-returned-value
3763-
/// [valid]: crate::ptr#safety
3764-
///
3765-
/// # Examples
3766-
///
3767-
/// Efficiently create a Rust vector from an unsafe buffer:
3768-
///
3769-
/// ```
3770-
/// use std::ptr;
3771-
///
3772-
/// /// # Safety
3773-
/// ///
3774-
/// /// * `ptr` must be correctly aligned for its type and non-zero.
3775-
/// /// * `ptr` must be valid for reads of `elts` contiguous elements of type `T`.
3776-
/// /// * Those elements must not be used after calling this function unless `T: Copy`.
3777-
/// # #[allow(dead_code)]
3778-
/// unsafe fn from_buf_raw<T>(ptr: *const T, elts: usize) -> Vec<T> {
3779-
/// let mut dst = Vec::with_capacity(elts);
3780-
///
3781-
/// // SAFETY: Our precondition ensures the source is aligned and valid,
3782-
/// // and `Vec::with_capacity` ensures that we have usable space to write them.
3783-
/// unsafe { ptr::copy(ptr, dst.as_mut_ptr(), elts); }
3784-
///
3785-
/// // SAFETY: We created it with this much capacity earlier,
3786-
/// // and the previous `copy` has initialized these elements.
3787-
/// unsafe { dst.set_len(elts); }
3788-
/// dst
3789-
/// }
3790-
/// ```
3791-
#[doc(alias = "memmove")]
3608+
/// This is an accidentally-stable alias to [`ptr::copy`]; use that instead.
3609+
// Note (intentionally not in the doc comment): `ptr::copy` adds some extra
3610+
// debug assertions; if you are writing compiler tests or code inside the standard library
3611+
// that wants to avoid those debug assertions, directly call this intrinsic instead.
37923612
#[stable(feature = "rust1", since = "1.0.0")]
37933613
#[rustc_allowed_through_unstable_modules = "import this function via `std::ptr` instead"]
37943614
#[rustc_const_stable(feature = "const_intrinsic_copy", since = "1.83.0")]
3795-
#[inline(always)]
3796-
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
3797-
#[rustc_diagnostic_item = "ptr_copy"]
3798-
pub const unsafe fn copy<T>(src: *const T, dst: *mut T, count: usize) {
3799-
#[rustc_intrinsic_const_stable_indirect]
3800-
#[rustc_nounwind]
3801-
#[rustc_intrinsic]
3802-
const unsafe fn copy<T>(src: *const T, dst: *mut T, count: usize);
3803-
3804-
// SAFETY: the safety contract for `copy` must be upheld by the caller.
3805-
unsafe {
3806-
ub_checks::assert_unsafe_precondition!(
3807-
check_language_ub,
3808-
"ptr::copy requires that both pointer arguments are aligned and non-null",
3809-
(
3810-
src: *const () = src as *const (),
3811-
dst: *mut () = dst as *mut (),
3812-
align: usize = align_of::<T>(),
3813-
zero_size: bool = T::IS_ZST || count == 0,
3814-
) =>
3815-
ub_checks::maybe_is_aligned_and_not_null(src, align, zero_size)
3816-
&& ub_checks::maybe_is_aligned_and_not_null(dst, align, zero_size)
3817-
);
3818-
copy(src, dst, count)
3819-
}
3820-
}
3615+
#[rustc_nounwind]
3616+
#[rustc_intrinsic]
3617+
pub const unsafe fn copy<T>(src: *const T, dst: *mut T, count: usize);
38213618

3822-
/// Sets `count * size_of::<T>()` bytes of memory starting at `dst` to
3823-
/// `val`.
3824-
///
3825-
/// `write_bytes` is similar to C's [`memset`], but sets `count *
3826-
/// size_of::<T>()` bytes to `val`.
3827-
///
3828-
/// [`memset`]: https://en.cppreference.com/w/c/string/byte/memset
3829-
///
3830-
/// # Safety
3831-
///
3832-
/// Behavior is undefined if any of the following conditions are violated:
3833-
///
3834-
/// * `dst` must be [valid] for writes of `count * size_of::<T>()` bytes.
3835-
///
3836-
/// * `dst` must be properly aligned.
3837-
///
3838-
/// Note that even if the effectively copied size (`count * size_of::<T>()`) is
3839-
/// `0`, the pointer must be properly aligned.
3840-
///
3841-
/// Additionally, note that changing `*dst` in this way can easily lead to undefined behavior (UB)
3842-
/// later if the written bytes are not a valid representation of some `T`. For instance, the
3843-
/// following is an **incorrect** use of this function:
3844-
///
3845-
/// ```rust,no_run
3846-
/// unsafe {
3847-
/// let mut value: u8 = 0;
3848-
/// let ptr: *mut bool = &mut value as *mut u8 as *mut bool;
3849-
/// let _bool = ptr.read(); // This is fine, `ptr` points to a valid `bool`.
3850-
/// ptr.write_bytes(42u8, 1); // This function itself does not cause UB...
3851-
/// let _bool = ptr.read(); // ...but it makes this operation UB! ⚠️
3852-
/// }
3853-
/// ```
3854-
///
3855-
/// [valid]: crate::ptr#safety
3856-
///
3857-
/// # Examples
3858-
///
3859-
/// Basic usage:
3860-
///
3861-
/// ```
3862-
/// use std::ptr;
3863-
///
3864-
/// let mut vec = vec![0u32; 4];
3865-
/// unsafe {
3866-
/// let vec_ptr = vec.as_mut_ptr();
3867-
/// ptr::write_bytes(vec_ptr, 0xfe, 2);
3868-
/// }
3869-
/// assert_eq!(vec, [0xfefefefe, 0xfefefefe, 0, 0]);
3870-
/// ```
3871-
#[doc(alias = "memset")]
3619+
/// This is an accidentally-stable alias to [`ptr::write_bytes`]; use that instead.
3620+
// Note (intentionally not in the doc comment): `ptr::write_bytes` adds some extra
3621+
// debug assertions; if you are writing compiler tests or code inside the standard library
3622+
// that wants to avoid those debug assertions, directly call this intrinsic instead.
38723623
#[stable(feature = "rust1", since = "1.0.0")]
38733624
#[rustc_allowed_through_unstable_modules = "import this function via `std::ptr` instead"]
3874-
#[rustc_const_stable(feature = "const_ptr_write", since = "1.83.0")]
3875-
#[inline(always)]
3876-
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
3877-
#[rustc_diagnostic_item = "ptr_write_bytes"]
3878-
pub const unsafe fn write_bytes<T>(dst: *mut T, val: u8, count: usize) {
3879-
#[rustc_intrinsic_const_stable_indirect]
3880-
#[rustc_nounwind]
3881-
#[rustc_intrinsic]
3882-
const unsafe fn write_bytes<T>(dst: *mut T, val: u8, count: usize);
3883-
3884-
// SAFETY: the safety contract for `write_bytes` must be upheld by the caller.
3885-
unsafe {
3886-
ub_checks::assert_unsafe_precondition!(
3887-
check_language_ub,
3888-
"ptr::write_bytes requires that the destination pointer is aligned and non-null",
3889-
(
3890-
addr: *const () = dst as *const (),
3891-
align: usize = align_of::<T>(),
3892-
zero_size: bool = T::IS_ZST || count == 0,
3893-
) => ub_checks::maybe_is_aligned_and_not_null(addr, align, zero_size)
3894-
);
3895-
write_bytes(dst, val, count)
3896-
}
3897-
}
3625+
#[rustc_const_stable(feature = "const_intrinsic_copy", since = "1.83.0")]
3626+
#[rustc_nounwind]
3627+
#[rustc_intrinsic]
3628+
pub const unsafe fn write_bytes<T>(dst: *mut T, val: u8, count: usize);
38983629

38993630
/// Returns the minimum (IEEE 754-2008 minNum) of two `f16` values.
39003631
///

library/core/src/mem/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ mod transmutability;
2121
#[unstable(feature = "transmutability", issue = "99571")]
2222
pub use transmutability::{Assume, TransmuteFrom};
2323

24+
// This one has to be a re-export (rather than wrapping the underlying intrinsic) so that we can do
25+
// the special magic "types have equal size" check at the call site.
2426
#[stable(feature = "rust1", since = "1.0.0")]
2527
#[doc(inline)]
2628
pub use crate::intrinsics::transmute;

0 commit comments

Comments
 (0)