Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

turn mem::uninitialized into a constant function #50150

Closed
wants to merge 1 commit into from
Closed
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
96 changes: 96 additions & 0 deletions src/libcore/mem.rs
Original file line number Diff line number Diff line change
Expand Up @@ -606,12 +606,108 @@ pub unsafe fn zeroed<T>() -> T {
/// [copy]: ../intrinsics/fn.copy.html
/// [copy_no]: ../intrinsics/fn.copy_nonoverlapping.html
/// [`Drop`]: ../ops/trait.Drop.html
#[cfg(stage0)]
#[inline]
#[stable(feature = "rust1", since = "1.0.0")]
pub unsafe fn uninitialized<T>() -> T {
intrinsics::uninit()
}

/// Bypasses Rust's normal memory-initialization checks by pretending to
/// produce a value of type `T`, while doing nothing at all.
///
/// **This is incredibly dangerous and should not be done lightly. Deeply
/// consider initializing your memory with a default value instead.**
///
/// This is useful for [FFI] functions and initializing arrays sometimes,
/// but should generally be avoided.
///
/// [FFI]: ../../book/first-edition/ffi.html
///
/// # Undefined behavior
///
/// It is [undefined behavior][ub] to read uninitialized memory, even just an
/// uninitialized boolean. For instance, if you branch on the value of such
/// a boolean, your program may take one, both, or neither of the branches.
///
/// Writing to the uninitialized value is similarly dangerous. Rust believes the
/// value is initialized, and will therefore try to [`Drop`] the uninitialized
/// value and its fields if you try to overwrite it in a normal manner. The only way
/// to safely initialize an uninitialized value is with [`ptr::write`][write],
/// [`ptr::copy`][copy], or [`ptr::copy_nonoverlapping`][copy_no].
///
/// If the value does implement [`Drop`], it must be initialized before
/// it goes out of scope (and therefore would be dropped). Note that this
/// includes a `panic` occurring and unwinding the stack suddenly.
///
/// # Examples
///
/// Here's how to safely initialize an array of [`Vec`]s.
///
/// ```
/// use std::mem;
/// use std::ptr;
///
/// // Only declare the array. This safely leaves it
/// // uninitialized in a way that Rust will track for us.
/// // However we can't initialize it element-by-element
/// // safely, and we can't use the `[value; 1000]`
/// // constructor because it only works with `Copy` data.
/// let mut data: [Vec<u32>; 1000];
///
/// unsafe {
/// // So we need to do this to initialize it.
/// data = mem::uninitialized();
///
/// // DANGER ZONE: if anything panics or otherwise
/// // incorrectly reads the array here, we will have
/// // Undefined Behavior.
///
/// // It's ok to mutably iterate the data, since this
/// // doesn't involve reading it at all.
/// // (ptr and len are statically known for arrays)
/// for elem in &mut data[..] {
/// // *elem = Vec::new() would try to drop the
/// // uninitialized memory at `elem` -- bad!
/// //
/// // Vec::new doesn't allocate or do really
/// // anything. It's only safe to call here
/// // because we know it won't panic.
/// ptr::write(elem, Vec::new());
/// }
///
/// // SAFE ZONE: everything is initialized.
/// }
///
/// println!("{:?}", &data[0]);
/// ```
///
/// This example emphasizes exactly how delicate and dangerous using `mem::uninitialized`
/// can be. Note that the [`vec!`] macro *does* let you initialize every element with a
/// value that is only [`Clone`], so the following is semantically equivalent and
/// vastly less dangerous, as long as you can live with an extra heap
/// allocation:
///
/// ```
/// let data: Vec<Vec<u32>> = vec![Vec::new(); 1000];
/// println!("{:?}", &data[0]);
/// ```
///
/// [`Vec`]: ../../std/vec/struct.Vec.html
/// [`vec!`]: ../../std/macro.vec.html
/// [`Clone`]: ../../std/clone/trait.Clone.html
/// [ub]: ../../reference/behavior-considered-undefined.html
/// [write]: ../ptr/fn.write.html
/// [copy]: ../intrinsics/fn.copy.html
/// [copy_no]: ../intrinsics/fn.copy_nonoverlapping.html
/// [`Drop`]: ../ops/trait.Drop.html
#[inline]
#[cfg(not(stage0))]
#[stable(feature = "rust1", since = "1.0.0")]
pub const unsafe fn uninitialized<T>() -> T {
intrinsics::uninit()
}

/// Swaps the values at two mutable locations, without deinitializing either one.
///
/// # Examples
Expand Down
22 changes: 22 additions & 0 deletions src/librustc_mir/interpret/const_eval.rs
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,28 @@ impl<'mir, 'tcx> super::Machine<'mir, 'tcx> for CompileTimeEvaluator {
ecx.write_primval(dest, PrimVal::from_u128(type_id), dest_layout.ty)?;
}

"uninit" => {
let size = dest_layout.size.bytes();
let uninit = |this: &mut EvalContext<'a, 'mir, 'tcx, Self>, val: Value| match val {
Value::ByRef(ptr, ..) => {
this.memory.mark_definedness(ptr, size, false)?;
Ok(val)
}
_ => Ok(Value::ByVal(PrimVal::Undef)),
};
match dest {
Place::Local { frame, local } => ecx.modify_local(frame, local, uninit)?,
Place::Ptr {
ptr,
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I copy pasted this code from src/tools/miri. The miri version had ptr: PtrAndAlign { ptr, aligned: true } in the pattern, but that got lost in translation. How do I check that ptr is aligned in this context? Pointer API didn't have any method to check for alignment AFAICT.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you use the old code? You could try looking again, now that miri has been updated.

extra: PlaceExtra::None,
..
} => ecx.memory.mark_definedness(ptr, size, false)?,
Place::Ptr { .. } => {
bug!("uninit intrinsic tried to write to fat or unaligned ptr target")
}
}
}

name => return Err(ConstEvalError::NeedsRfc(format!("calling intrinsic `{}`", name)).into()),
}

Expand Down
4 changes: 3 additions & 1 deletion src/librustc_mir/transform/qualify_consts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -869,7 +869,9 @@ This does not pose a problem by itself because they can't be accessed directly."
Abi::PlatformIntrinsic => {
assert!(!self.tcx.is_const_fn(def_id));
match &self.tcx.item_name(def_id)[..] {
"size_of" | "min_align_of" | "type_id" => is_const_fn = Some(def_id),
"size_of" | "min_align_of" | "type_id" | "uninit" => {
is_const_fn = Some(def_id)
},

name if name.starts_with("simd_shuffle") => {
is_shuffle = true;
Expand Down