Skip to content

Commit b8c67c6

Browse files
committed
Auto merge of #139916 - RalfJung:intrinsic-wrappers, r=<try>
make std::intrinsic 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 efcbb94 + 5d60226 commit b8c67c6

15 files changed

+337
-353
lines changed

library/core/src/intrinsics/mod.rs

+25-293
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;
@@ -74,6 +73,7 @@ pub mod simd;
7473
#[cfg(all(target_has_atomic = "8", target_has_atomic = "32", target_has_atomic = "ptr"))]
7574
use crate::sync::atomic::{self, AtomicBool, AtomicI32, AtomicIsize, AtomicU32, Ordering};
7675

76+
/// This is an accidentally-stable alias to [`ptr::drop_in_place`]; use that instead.
7777
#[stable(feature = "drop_in_place", since = "1.8.0")]
7878
#[rustc_allowed_through_unstable_modules = "import this function via `std::ptr` instead"]
7979
#[deprecated(note = "no longer an intrinsic - use `ptr::drop_in_place` directly", since = "1.52.0")]
@@ -3339,7 +3339,7 @@ pub const unsafe fn typed_swap_nonoverlapping<T>(x: *mut T, y: *mut T) {
33393339
/// `#[inline]`), gating assertions on `ub_checks()` rather than `cfg!(ub_checks)` means that
33403340
/// assertions are enabled whenever the *user crate* has UB checks enabled. However, if the
33413341
/// user has UB checks disabled, the checks will still get optimized out. This intrinsic is
3342-
/// primarily used by [`ub_checks::assert_unsafe_precondition`].
3342+
/// primarily used by [`crate::ub_checks::assert_unsafe_precondition`].
33433343
#[rustc_intrinsic_const_stable_indirect] // just for UB checks
33443344
#[inline(always)]
33453345
#[rustc_intrinsic]
@@ -3628,306 +3628,38 @@ impl<P: ?Sized, T: ptr::Thin> AggregateRawPtr<*mut T> for *mut P {
36283628
#[rustc_intrinsic]
36293629
pub const fn ptr_metadata<P: ptr::Pointee<Metadata = M> + ?Sized, M>(ptr: *const P) -> M;
36303630

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

3757-
/// Copies `count * size_of::<T>()` bytes from `src` to `dst`. The source
3758-
/// and destination may overlap.
3759-
///
3760-
/// If the source and destination will *never* overlap,
3761-
/// [`copy_nonoverlapping`] can be used instead.
3762-
///
3763-
/// `copy` is semantically equivalent to C's [`memmove`], but
3764-
/// with the source and destination arguments swapped,
3765-
/// and `count` counting the number of `T`s instead of bytes.
3766-
/// Copying takes place as if the bytes were copied from `src`
3767-
/// to a temporary array and then copied from the array to `dst`.
3768-
///
3769-
/// The copy is "untyped" in the sense that data may be uninitialized or otherwise violate the
3770-
/// requirements of `T`. The initialization state is preserved exactly.
3771-
///
3772-
/// [`memmove`]: https://en.cppreference.com/w/c/string/byte/memmove
3773-
///
3774-
/// # Safety
3775-
///
3776-
/// Behavior is undefined if any of the following conditions are violated:
3777-
///
3778-
/// * `src` must be [valid] for reads of `count * size_of::<T>()` bytes.
3779-
///
3780-
/// * `dst` must be [valid] for writes of `count * size_of::<T>()` bytes, and must remain valid even
3781-
/// when `src` is read for `count * size_of::<T>()` bytes. (This means if the memory ranges
3782-
/// overlap, the `dst` pointer must not be invalidated by `src` reads.)
3783-
///
3784-
/// * Both `src` and `dst` must be properly aligned.
3785-
///
3786-
/// Like [`read`], `copy` creates a bitwise copy of `T`, regardless of
3787-
/// whether `T` is [`Copy`]. If `T` is not [`Copy`], using both the values
3788-
/// in the region beginning at `*src` and the region beginning at `*dst` can
3789-
/// [violate memory safety][read-ownership].
3790-
///
3791-
/// Note that even if the effectively copied size (`count * size_of::<T>()`) is
3792-
/// `0`, the pointers must be properly aligned.
3793-
///
3794-
/// [`read`]: crate::ptr::read
3795-
/// [read-ownership]: crate::ptr::read#ownership-of-the-returned-value
3796-
/// [valid]: crate::ptr#safety
3797-
///
3798-
/// # Examples
3799-
///
3800-
/// Efficiently create a Rust vector from an unsafe buffer:
3801-
///
3802-
/// ```
3803-
/// use std::ptr;
3804-
///
3805-
/// /// # Safety
3806-
/// ///
3807-
/// /// * `ptr` must be correctly aligned for its type and non-zero.
3808-
/// /// * `ptr` must be valid for reads of `elts` contiguous elements of type `T`.
3809-
/// /// * Those elements must not be used after calling this function unless `T: Copy`.
3810-
/// # #[allow(dead_code)]
3811-
/// unsafe fn from_buf_raw<T>(ptr: *const T, elts: usize) -> Vec<T> {
3812-
/// let mut dst = Vec::with_capacity(elts);
3813-
///
3814-
/// // SAFETY: Our precondition ensures the source is aligned and valid,
3815-
/// // and `Vec::with_capacity` ensures that we have usable space to write them.
3816-
/// unsafe { ptr::copy(ptr, dst.as_mut_ptr(), elts); }
3817-
///
3818-
/// // SAFETY: We created it with this much capacity earlier,
3819-
/// // and the previous `copy` has initialized these elements.
3820-
/// unsafe { dst.set_len(elts); }
3821-
/// dst
3822-
/// }
3823-
/// ```
3824-
#[doc(alias = "memmove")]
3642+
/// This is an accidentally-stable alias to [`ptr::copy`]; use that instead.
3643+
// Note (intentionally not in the doc comment): `ptr::copy` adds some extra
3644+
// debug assertions; if you are writing compiler tests or code inside the standard library
3645+
// that wants to avoid those debug assertions, directly call this intrinsic instead.
38253646
#[stable(feature = "rust1", since = "1.0.0")]
38263647
#[rustc_allowed_through_unstable_modules = "import this function via `std::ptr` instead"]
38273648
#[rustc_const_stable(feature = "const_intrinsic_copy", since = "1.83.0")]
3828-
#[inline(always)]
3829-
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
3830-
#[rustc_diagnostic_item = "ptr_copy"]
3831-
pub const unsafe fn copy<T>(src: *const T, dst: *mut T, count: usize) {
3832-
#[rustc_intrinsic_const_stable_indirect]
3833-
#[rustc_nounwind]
3834-
#[rustc_intrinsic]
3835-
const unsafe fn copy<T>(src: *const T, dst: *mut T, count: usize);
3836-
3837-
// SAFETY: the safety contract for `copy` must be upheld by the caller.
3838-
unsafe {
3839-
ub_checks::assert_unsafe_precondition!(
3840-
check_language_ub,
3841-
"ptr::copy requires that both pointer arguments are aligned and non-null",
3842-
(
3843-
src: *const () = src as *const (),
3844-
dst: *mut () = dst as *mut (),
3845-
align: usize = align_of::<T>(),
3846-
zero_size: bool = T::IS_ZST || count == 0,
3847-
) =>
3848-
ub_checks::maybe_is_aligned_and_not_null(src, align, zero_size)
3849-
&& ub_checks::maybe_is_aligned_and_not_null(dst, align, zero_size)
3850-
);
3851-
copy(src, dst, count)
3852-
}
3853-
}
3649+
#[rustc_nounwind]
3650+
#[rustc_intrinsic]
3651+
pub const unsafe fn copy<T>(src: *const T, dst: *mut T, count: usize);
38543652

3855-
/// Sets `count * size_of::<T>()` bytes of memory starting at `dst` to
3856-
/// `val`.
3857-
///
3858-
/// `write_bytes` is similar to C's [`memset`], but sets `count *
3859-
/// size_of::<T>()` bytes to `val`.
3860-
///
3861-
/// [`memset`]: https://en.cppreference.com/w/c/string/byte/memset
3862-
///
3863-
/// # Safety
3864-
///
3865-
/// Behavior is undefined if any of the following conditions are violated:
3866-
///
3867-
/// * `dst` must be [valid] for writes of `count * size_of::<T>()` bytes.
3868-
///
3869-
/// * `dst` must be properly aligned.
3870-
///
3871-
/// Note that even if the effectively copied size (`count * size_of::<T>()`) is
3872-
/// `0`, the pointer must be properly aligned.
3873-
///
3874-
/// Additionally, note that changing `*dst` in this way can easily lead to undefined behavior (UB)
3875-
/// later if the written bytes are not a valid representation of some `T`. For instance, the
3876-
/// following is an **incorrect** use of this function:
3877-
///
3878-
/// ```rust,no_run
3879-
/// unsafe {
3880-
/// let mut value: u8 = 0;
3881-
/// let ptr: *mut bool = &mut value as *mut u8 as *mut bool;
3882-
/// let _bool = ptr.read(); // This is fine, `ptr` points to a valid `bool`.
3883-
/// ptr.write_bytes(42u8, 1); // This function itself does not cause UB...
3884-
/// let _bool = ptr.read(); // ...but it makes this operation UB! ⚠️
3885-
/// }
3886-
/// ```
3887-
///
3888-
/// [valid]: crate::ptr#safety
3889-
///
3890-
/// # Examples
3891-
///
3892-
/// Basic usage:
3893-
///
3894-
/// ```
3895-
/// use std::ptr;
3896-
///
3897-
/// let mut vec = vec![0u32; 4];
3898-
/// unsafe {
3899-
/// let vec_ptr = vec.as_mut_ptr();
3900-
/// ptr::write_bytes(vec_ptr, 0xfe, 2);
3901-
/// }
3902-
/// assert_eq!(vec, [0xfefefefe, 0xfefefefe, 0, 0]);
3903-
/// ```
3904-
#[doc(alias = "memset")]
3653+
/// This is an accidentally-stable alias to [`ptr::write_bytes`]; use that instead.
3654+
// Note (intentionally not in the doc comment): `ptr::write_bytes` adds some extra
3655+
// debug assertions; if you are writing compiler tests or code inside the standard library
3656+
// that wants to avoid those debug assertions, directly call this intrinsic instead.
39053657
#[stable(feature = "rust1", since = "1.0.0")]
39063658
#[rustc_allowed_through_unstable_modules = "import this function via `std::ptr` instead"]
3907-
#[rustc_const_stable(feature = "const_ptr_write", since = "1.83.0")]
3908-
#[inline(always)]
3909-
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
3910-
#[rustc_diagnostic_item = "ptr_write_bytes"]
3911-
pub const unsafe fn write_bytes<T>(dst: *mut T, val: u8, count: usize) {
3912-
#[rustc_intrinsic_const_stable_indirect]
3913-
#[rustc_nounwind]
3914-
#[rustc_intrinsic]
3915-
const unsafe fn write_bytes<T>(dst: *mut T, val: u8, count: usize);
3916-
3917-
// SAFETY: the safety contract for `write_bytes` must be upheld by the caller.
3918-
unsafe {
3919-
ub_checks::assert_unsafe_precondition!(
3920-
check_language_ub,
3921-
"ptr::write_bytes requires that the destination pointer is aligned and non-null",
3922-
(
3923-
addr: *const () = dst as *const (),
3924-
align: usize = align_of::<T>(),
3925-
zero_size: bool = T::IS_ZST || count == 0,
3926-
) => ub_checks::maybe_is_aligned_and_not_null(addr, align, zero_size)
3927-
);
3928-
write_bytes(dst, val, count)
3929-
}
3930-
}
3659+
#[rustc_const_stable(feature = "const_intrinsic_copy", since = "1.83.0")]
3660+
#[rustc_nounwind]
3661+
#[rustc_intrinsic]
3662+
pub const unsafe fn write_bytes<T>(dst: *mut T, val: u8, count: usize);
39313663

39323664
/// Returns the minimum of two `f16` values.
39333665
///

library/core/src/mem/mod.rs

+2
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)