Rollup merge of #69922 - RalfJung:less-intrinsic, r=oli-obk
implement zeroed and uninitialized with MaybeUninit This is the second attempt of doing such a change (first PR: https://github.com/rust-lang/rust/pull/62150). The last change [got reverted](https://github.com/rust-lang/rust/pull/63343) because it [caused](https://github.com/rust-lang/rust/issues/62825) some [issues](https://github.com/rust-lang/rust/issues/52898#issuecomment-512182438) in [code that incorrectly used these functions](https://github.com/erlepereira/x11-rs/issues/99). Since then, the [problematic code has been fixed](https://github.com/erlepereira/x11-rs/pull/101), and rustc [gained a lint](https://github.com/rust-lang/rust/pull/63346) that is able to detect many misuses of these functions statically and a [dynamic check that panics](https://github.com/rust-lang/rust/pull/66059) instead of causing UB for some incorrect uses. Fixes https://github.com/rust-lang/rust/issues/62825
This commit is contained in:
commit
7a7ca8238f
@ -1027,46 +1027,8 @@ extern "rust-intrinsic" {
|
|||||||
#[rustc_const_unstable(feature = "const_caller_location", issue = "47809")]
|
#[rustc_const_unstable(feature = "const_caller_location", issue = "47809")]
|
||||||
pub fn caller_location() -> &'static crate::panic::Location<'static>;
|
pub fn caller_location() -> &'static crate::panic::Location<'static>;
|
||||||
|
|
||||||
/// Creates a value initialized to zero.
|
|
||||||
///
|
|
||||||
/// `init` is unsafe because it returns a zeroed-out datum,
|
|
||||||
/// which is unsafe unless `T` is `Copy`. Also, even if T is
|
|
||||||
/// `Copy`, an all-zero value may not correspond to any legitimate
|
|
||||||
/// state for the type in question.
|
|
||||||
///
|
|
||||||
/// The stabilized version of this intrinsic is
|
|
||||||
/// [`std::mem::zeroed`](../../std/mem/fn.zeroed.html).
|
|
||||||
#[unstable(
|
|
||||||
feature = "core_intrinsics",
|
|
||||||
reason = "intrinsics are unlikely to ever be stabilized, instead \
|
|
||||||
they should be used through stabilized interfaces \
|
|
||||||
in the rest of the standard library",
|
|
||||||
issue = "none"
|
|
||||||
)]
|
|
||||||
#[rustc_deprecated(reason = "superseded by MaybeUninit, removal planned", since = "1.38.0")]
|
|
||||||
pub fn init<T>() -> T;
|
|
||||||
|
|
||||||
/// Creates an uninitialized value.
|
|
||||||
///
|
|
||||||
/// `uninit` is unsafe because there is no guarantee of what its
|
|
||||||
/// contents are. In particular its drop-flag may be set to any
|
|
||||||
/// state, which means it may claim either dropped or
|
|
||||||
/// undropped. In the general case one must use `ptr::write` to
|
|
||||||
/// initialize memory previous set to the result of `uninit`.
|
|
||||||
///
|
|
||||||
/// The stabilized version of this intrinsic is
|
|
||||||
/// [`std::mem::MaybeUninit`](../../std/mem/union.MaybeUninit.html).
|
|
||||||
#[unstable(
|
|
||||||
feature = "core_intrinsics",
|
|
||||||
reason = "intrinsics are unlikely to ever be stabilized, instead \
|
|
||||||
they should be used through stabilized interfaces \
|
|
||||||
in the rest of the standard library",
|
|
||||||
issue = "none"
|
|
||||||
)]
|
|
||||||
#[rustc_deprecated(reason = "superseded by MaybeUninit, removal planned", since = "1.38.0")]
|
|
||||||
pub fn uninit<T>() -> T;
|
|
||||||
|
|
||||||
/// Moves a value out of scope without running drop glue.
|
/// Moves a value out of scope without running drop glue.
|
||||||
|
/// This exists solely for `mem::forget_unsized`; normal `forget` uses `ManuallyDrop` instead.
|
||||||
pub fn forget<T: ?Sized>(_: T);
|
pub fn forget<T: ?Sized>(_: T);
|
||||||
|
|
||||||
/// Reinterprets the bits of a value of one type as another type.
|
/// Reinterprets the bits of a value of one type as another type.
|
||||||
|
@ -490,7 +490,7 @@ pub const fn needs_drop<T>() -> bool {
|
|||||||
///
|
///
|
||||||
/// let _x: &i32 = unsafe { mem::zeroed() }; // Undefined behavior!
|
/// let _x: &i32 = unsafe { mem::zeroed() }; // Undefined behavior!
|
||||||
/// ```
|
/// ```
|
||||||
#[inline]
|
#[inline(always)]
|
||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
#[stable(feature = "rust1", since = "1.0.0")]
|
||||||
#[allow(deprecated_in_future)]
|
#[allow(deprecated_in_future)]
|
||||||
#[allow(deprecated)]
|
#[allow(deprecated)]
|
||||||
@ -500,7 +500,7 @@ pub unsafe fn zeroed<T>() -> T {
|
|||||||
intrinsics::assert_zero_valid::<T>();
|
intrinsics::assert_zero_valid::<T>();
|
||||||
#[cfg(bootstrap)]
|
#[cfg(bootstrap)]
|
||||||
intrinsics::panic_if_uninhabited::<T>();
|
intrinsics::panic_if_uninhabited::<T>();
|
||||||
intrinsics::init()
|
MaybeUninit::zeroed().assume_init()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Bypasses Rust's normal memory-initialization checks by pretending to
|
/// Bypasses Rust's normal memory-initialization checks by pretending to
|
||||||
@ -525,7 +525,7 @@ pub unsafe fn zeroed<T>() -> T {
|
|||||||
/// [uninit]: union.MaybeUninit.html#method.uninit
|
/// [uninit]: union.MaybeUninit.html#method.uninit
|
||||||
/// [assume_init]: union.MaybeUninit.html#method.assume_init
|
/// [assume_init]: union.MaybeUninit.html#method.assume_init
|
||||||
/// [inv]: union.MaybeUninit.html#initialization-invariant
|
/// [inv]: union.MaybeUninit.html#initialization-invariant
|
||||||
#[inline]
|
#[inline(always)]
|
||||||
#[rustc_deprecated(since = "1.39.0", reason = "use `mem::MaybeUninit` instead")]
|
#[rustc_deprecated(since = "1.39.0", reason = "use `mem::MaybeUninit` instead")]
|
||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
#[stable(feature = "rust1", since = "1.0.0")]
|
||||||
#[allow(deprecated_in_future)]
|
#[allow(deprecated_in_future)]
|
||||||
@ -536,7 +536,7 @@ pub unsafe fn uninitialized<T>() -> T {
|
|||||||
intrinsics::assert_uninit_valid::<T>();
|
intrinsics::assert_uninit_valid::<T>();
|
||||||
#[cfg(bootstrap)]
|
#[cfg(bootstrap)]
|
||||||
intrinsics::panic_if_uninhabited::<T>();
|
intrinsics::panic_if_uninhabited::<T>();
|
||||||
intrinsics::uninit()
|
MaybeUninit::uninit().assume_init()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Swaps the values at two mutable locations, without deinitializing either one.
|
/// Swaps the values at two mutable locations, without deinitializing either one.
|
||||||
|
@ -195,26 +195,8 @@ impl IntrinsicCallMethods<'tcx> for Builder<'a, 'll, 'tcx> {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
OperandRef::from_const(self, ty_name, ret_ty).immediate_or_packed_pair(self)
|
OperandRef::from_const(self, ty_name, ret_ty).immediate_or_packed_pair(self)
|
||||||
}
|
}
|
||||||
"init" => {
|
// Effectively no-op
|
||||||
let ty = substs.type_at(0);
|
"forget" => {
|
||||||
if !self.layout_of(ty).is_zst() {
|
|
||||||
// Just zero out the stack slot.
|
|
||||||
// If we store a zero constant, LLVM will drown in vreg allocation for large
|
|
||||||
// data structures, and the generated code will be awful. (A telltale sign of
|
|
||||||
// this is large quantities of `mov [byte ptr foo],0` in the generated code.)
|
|
||||||
memset_intrinsic(
|
|
||||||
self,
|
|
||||||
false,
|
|
||||||
ty,
|
|
||||||
llresult,
|
|
||||||
self.const_u8(0),
|
|
||||||
self.const_usize(1),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Effectively no-ops
|
|
||||||
"uninit" | "forget" => {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
"offset" => {
|
"offset" => {
|
||||||
|
@ -150,8 +150,6 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) {
|
|||||||
"assert_inhabited" | "assert_zero_valid" | "assert_uninit_valid" => {
|
"assert_inhabited" | "assert_zero_valid" | "assert_uninit_valid" => {
|
||||||
(1, Vec::new(), tcx.mk_unit())
|
(1, Vec::new(), tcx.mk_unit())
|
||||||
}
|
}
|
||||||
"init" => (1, Vec::new(), param(0)),
|
|
||||||
"uninit" => (1, Vec::new(), param(0)),
|
|
||||||
"forget" => (1, vec![param(0)], tcx.mk_unit()),
|
"forget" => (1, vec![param(0)], tcx.mk_unit()),
|
||||||
"transmute" => (2, vec![param(0)], param(1)),
|
"transmute" => (2, vec![param(0)], param(1)),
|
||||||
"move_val_init" => (1, vec![tcx.mk_mut_ptr(param(0)), param(0)], tcx.mk_unit()),
|
"move_val_init" => (1, vec![tcx.mk_mut_ptr(param(0)), param(0)], tcx.mk_unit()),
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
// compile-flags: -O
|
||||||
// run-pass
|
// run-pass
|
||||||
|
|
||||||
#![allow(unused_must_use)]
|
#![allow(unused_must_use)]
|
||||||
@ -10,17 +11,13 @@
|
|||||||
|
|
||||||
#![feature(intrinsics)]
|
#![feature(intrinsics)]
|
||||||
|
|
||||||
use std::thread;
|
use std::{mem, thread};
|
||||||
|
|
||||||
extern "rust-intrinsic" {
|
|
||||||
pub fn init<T>() -> T;
|
|
||||||
}
|
|
||||||
|
|
||||||
const SIZE: usize = 1024 * 1024;
|
const SIZE: usize = 1024 * 1024;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
// do the test in a new thread to avoid (spurious?) stack overflows
|
// do the test in a new thread to avoid (spurious?) stack overflows
|
||||||
thread::spawn(|| {
|
thread::spawn(|| {
|
||||||
let _memory: [u8; SIZE] = unsafe { init() };
|
let _memory: [u8; SIZE] = unsafe { mem::zeroed() };
|
||||||
}).join();
|
}).join();
|
||||||
}
|
}
|
||||||
|
@ -1,9 +0,0 @@
|
|||||||
#![allow(deprecated)]
|
|
||||||
#![feature(core_intrinsics)]
|
|
||||||
|
|
||||||
use std::intrinsics::{init};
|
|
||||||
|
|
||||||
// Test that the `init` intrinsic is really unsafe
|
|
||||||
pub fn main() {
|
|
||||||
let stuff = init::<isize>(); //~ ERROR call to unsafe function is unsafe
|
|
||||||
}
|
|
@ -1,11 +0,0 @@
|
|||||||
error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
|
|
||||||
--> $DIR/init-unsafe.rs:8:17
|
|
||||||
|
|
|
||||||
LL | let stuff = init::<isize>();
|
|
||||||
| ^^^^^^^^^^^^^^^ call to unsafe function
|
|
||||||
|
|
|
||||||
= note: consult the function's documentation for information on how to avoid undefined behavior
|
|
||||||
|
|
||||||
error: aborting due to previous error
|
|
||||||
|
|
||||||
For more information about this error, try `rustc --explain E0133`.
|
|
@ -5,7 +5,6 @@
|
|||||||
|
|
||||||
mod rusti {
|
mod rusti {
|
||||||
extern "rust-intrinsic" {
|
extern "rust-intrinsic" {
|
||||||
pub fn init<T>() -> T;
|
|
||||||
pub fn move_val_init<T>(dst: *mut T, src: T);
|
pub fn move_val_init<T>(dst: *mut T, src: T);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -15,17 +14,17 @@ pub fn main() {
|
|||||||
// sanity check
|
// sanity check
|
||||||
check_drops_state(0, None);
|
check_drops_state(0, None);
|
||||||
|
|
||||||
let mut x: Box<D> = box D(1);
|
let mut x: Option<Box<D>> = Some(box D(1));
|
||||||
assert_eq!(x.0, 1);
|
assert_eq!(x.as_ref().unwrap().0, 1);
|
||||||
|
|
||||||
// A normal overwrite, to demonstrate `check_drops_state`.
|
// A normal overwrite, to demonstrate `check_drops_state`.
|
||||||
x = box D(2);
|
x = Some(box D(2));
|
||||||
|
|
||||||
// At this point, one destructor has run, because the
|
// At this point, one destructor has run, because the
|
||||||
// overwrite of `x` drops its initial value.
|
// overwrite of `x` drops its initial value.
|
||||||
check_drops_state(1, Some(1));
|
check_drops_state(1, Some(1));
|
||||||
|
|
||||||
let mut y: Box<D> = rusti::init();
|
let mut y: Option<Box<D>> = std::mem::zeroed();
|
||||||
|
|
||||||
// An initial binding does not overwrite anything.
|
// An initial binding does not overwrite anything.
|
||||||
check_drops_state(1, Some(1));
|
check_drops_state(1, Some(1));
|
||||||
@ -51,9 +50,9 @@ pub fn main() {
|
|||||||
// during such a destructor call. We do so after the end of
|
// during such a destructor call. We do so after the end of
|
||||||
// this scope.
|
// this scope.
|
||||||
|
|
||||||
assert_eq!(y.0, 2);
|
assert_eq!(y.as_ref().unwrap().0, 2);
|
||||||
y.0 = 3;
|
y.as_mut().unwrap().0 = 3;
|
||||||
assert_eq!(y.0, 3);
|
assert_eq!(y.as_ref().unwrap().0, 3);
|
||||||
|
|
||||||
check_drops_state(1, Some(1));
|
check_drops_state(1, Some(1));
|
||||||
}
|
}
|
||||||
|
@ -1,13 +0,0 @@
|
|||||||
// run-pass
|
|
||||||
// pretty-expanded FIXME #23616
|
|
||||||
|
|
||||||
#![feature(intrinsics)]
|
|
||||||
|
|
||||||
mod rusti {
|
|
||||||
extern "rust-intrinsic" {
|
|
||||||
pub fn uninit<T>() -> T;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn main() {
|
|
||||||
let _a : isize = unsafe {rusti::uninit()};
|
|
||||||
}
|
|
@ -10,5 +10,5 @@ use proc_macro::TokenStream;
|
|||||||
#[proc_macro_derive(Unstable)]
|
#[proc_macro_derive(Unstable)]
|
||||||
pub fn derive(_input: TokenStream) -> TokenStream {
|
pub fn derive(_input: TokenStream) -> TokenStream {
|
||||||
|
|
||||||
"unsafe fn foo() -> u32 { ::std::intrinsics::init() }".parse().unwrap()
|
"unsafe fn foo() -> u32 { ::std::intrinsics::abort() }".parse().unwrap()
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user