Skip to content

Commit

Permalink
Rollup merge of #69922 - RalfJung:less-intrinsic, r=oli-obk
Browse files Browse the repository at this point in the history
implement zeroed and uninitialized with MaybeUninit

This is the second attempt of doing such a change (first PR: #62150). The last change [got reverted](#63343) because it [caused](#62825) some [issues](#52898 (comment)) in [code that incorrectly used these functions](AltF02/x11-rs#99).

Since then, the [problematic code has been fixed](AltF02/x11-rs#101), and rustc [gained a lint](#63346) that is able to detect many misuses of these functions statically and a [dynamic check that panics](#66059) instead of causing UB for some incorrect uses.

Fixes #62825
  • Loading branch information
Centril authored Mar 17, 2020
2 parents c4a900f + a2160e6 commit 7a7ca82
Show file tree
Hide file tree
Showing 10 changed files with 18 additions and 113 deletions.
40 changes: 1 addition & 39 deletions src/libcore/intrinsics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1027,46 +1027,8 @@ extern "rust-intrinsic" {
#[rustc_const_unstable(feature = "const_caller_location", issue = "47809")]
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.
/// This exists solely for `mem::forget_unsized`; normal `forget` uses `ManuallyDrop` instead.
pub fn forget<T: ?Sized>(_: T);

/// Reinterprets the bits of a value of one type as another type.
Expand Down
8 changes: 4 additions & 4 deletions src/libcore/mem/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -490,7 +490,7 @@ pub const fn needs_drop<T>() -> bool {
///
/// let _x: &i32 = unsafe { mem::zeroed() }; // Undefined behavior!
/// ```
#[inline]
#[inline(always)]
#[stable(feature = "rust1", since = "1.0.0")]
#[allow(deprecated_in_future)]
#[allow(deprecated)]
Expand All @@ -500,7 +500,7 @@ pub unsafe fn zeroed<T>() -> T {
intrinsics::assert_zero_valid::<T>();
#[cfg(bootstrap)]
intrinsics::panic_if_uninhabited::<T>();
intrinsics::init()
MaybeUninit::zeroed().assume_init()
}

/// Bypasses Rust's normal memory-initialization checks by pretending to
Expand All @@ -525,7 +525,7 @@ pub unsafe fn zeroed<T>() -> T {
/// [uninit]: union.MaybeUninit.html#method.uninit
/// [assume_init]: union.MaybeUninit.html#method.assume_init
/// [inv]: union.MaybeUninit.html#initialization-invariant
#[inline]
#[inline(always)]
#[rustc_deprecated(since = "1.39.0", reason = "use `mem::MaybeUninit` instead")]
#[stable(feature = "rust1", since = "1.0.0")]
#[allow(deprecated_in_future)]
Expand All @@ -536,7 +536,7 @@ pub unsafe fn uninitialized<T>() -> T {
intrinsics::assert_uninit_valid::<T>();
#[cfg(bootstrap)]
intrinsics::panic_if_uninhabited::<T>();
intrinsics::uninit()
MaybeUninit::uninit().assume_init()
}

/// Swaps the values at two mutable locations, without deinitializing either one.
Expand Down
22 changes: 2 additions & 20 deletions src/librustc_codegen_llvm/intrinsic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -195,26 +195,8 @@ impl IntrinsicCallMethods<'tcx> for Builder<'a, 'll, 'tcx> {
.unwrap();
OperandRef::from_const(self, ty_name, ret_ty).immediate_or_packed_pair(self)
}
"init" => {
let ty = substs.type_at(0);
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" => {
// Effectively no-op
"forget" => {
return;
}
"offset" => {
Expand Down
2 changes: 0 additions & 2 deletions src/librustc_typeck/check/intrinsic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -150,8 +150,6 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) {
"assert_inhabited" | "assert_zero_valid" | "assert_uninit_valid" => {
(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()),
"transmute" => (2, vec![param(0)], param(1)),
"move_val_init" => (1, vec![tcx.mk_mut_ptr(param(0)), param(0)], tcx.mk_unit()),
Expand Down
9 changes: 3 additions & 6 deletions src/test/ui/init-large-type.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// compile-flags: -O
// run-pass

#![allow(unused_must_use)]
Expand All @@ -10,17 +11,13 @@

#![feature(intrinsics)]

use std::thread;

extern "rust-intrinsic" {
pub fn init<T>() -> T;
}
use std::{mem, thread};

const SIZE: usize = 1024 * 1024;

fn main() {
// do the test in a new thread to avoid (spurious?) stack overflows
thread::spawn(|| {
let _memory: [u8; SIZE] = unsafe { init() };
let _memory: [u8; SIZE] = unsafe { mem::zeroed() };
}).join();
}
9 changes: 0 additions & 9 deletions src/test/ui/init-unsafe.rs

This file was deleted.

11 changes: 0 additions & 11 deletions src/test/ui/init-unsafe.stderr

This file was deleted.

15 changes: 7 additions & 8 deletions src/test/ui/intrinsics/intrinsic-move-val.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@

mod rusti {
extern "rust-intrinsic" {
pub fn init<T>() -> T;
pub fn move_val_init<T>(dst: *mut T, src: T);
}
}
Expand All @@ -15,17 +14,17 @@ pub fn main() {
// sanity check
check_drops_state(0, None);

let mut x: Box<D> = box D(1);
assert_eq!(x.0, 1);
let mut x: Option<Box<D>> = Some(box D(1));
assert_eq!(x.as_ref().unwrap().0, 1);

// 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
// overwrite of `x` drops its initial value.
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.
check_drops_state(1, Some(1));
Expand All @@ -51,9 +50,9 @@ pub fn main() {
// during such a destructor call. We do so after the end of
// this scope.

assert_eq!(y.0, 2);
y.0 = 3;
assert_eq!(y.0, 3);
assert_eq!(y.as_ref().unwrap().0, 2);
y.as_mut().unwrap().0 = 3;
assert_eq!(y.as_ref().unwrap().0, 3);

check_drops_state(1, Some(1));
}
Expand Down
13 changes: 0 additions & 13 deletions src/test/ui/intrinsics/intrinsic-uninit.rs

This file was deleted.

2 changes: 1 addition & 1 deletion src/test/ui/proc-macro/auxiliary/derive-unstable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,5 @@ use proc_macro::TokenStream;
#[proc_macro_derive(Unstable)]
pub fn derive(_input: TokenStream) -> TokenStream {

"unsafe fn foo() -> u32 { ::std::intrinsics::init() }".parse().unwrap()
"unsafe fn foo() -> u32 { ::std::intrinsics::abort() }".parse().unwrap()
}

0 comments on commit 7a7ca82

Please sign in to comment.