Skip to content

Commit

Permalink
Auto merge of #57145 - RalfJung:panic-if-uninhabited, r=alexcrichton
Browse files Browse the repository at this point in the history
panic when calling MaybeUninhabited::into_inner on uninhabited type

I do this by adding an internal-only intrinsic `panic_if_uninhabited`. I have no idea what I am doing here, just mindlessly copying code around, so please review carefully!
  • Loading branch information
bors committed Jan 5, 2019
2 parents 6861426 + c118b17 commit 8c2d0f4
Show file tree
Hide file tree
Showing 5 changed files with 96 additions and 53 deletions.
5 changes: 5 additions & 0 deletions src/libcore/intrinsics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -690,6 +690,11 @@ extern "rust-intrinsic" {
/// crate it is invoked in.
pub fn type_id<T: ?Sized + 'static>() -> u64;

/// A guard for unsafe functions that cannot ever be executed if `T` is uninhabited:
/// This will statically either panic, or do nothing.
#[cfg(not(stage0))]
pub fn panic_if_uninhabited<T>();

/// Creates a value initialized to zero.
///
/// `init` is unsafe because it returns a zeroed-out datum,
Expand Down
6 changes: 6 additions & 0 deletions src/libcore/mem.rs
Original file line number Diff line number Diff line change
Expand Up @@ -492,6 +492,8 @@ pub const fn needs_drop<T>() -> bool {
#[rustc_deprecated(since = "2.0.0", reason = "use `mem::MaybeUninit::zeroed` instead")]
#[stable(feature = "rust1", since = "1.0.0")]
pub unsafe fn zeroed<T>() -> T {
#[cfg(not(stage0))]
intrinsics::panic_if_uninhabited::<T>();
intrinsics::init()
}

Expand Down Expand Up @@ -624,6 +626,8 @@ pub unsafe fn zeroed<T>() -> T {
#[rustc_deprecated(since = "2.0.0", reason = "use `mem::MaybeUninit::uninitialized` instead")]
#[stable(feature = "rust1", since = "1.0.0")]
pub unsafe fn uninitialized<T>() -> T {
#[cfg(not(stage0))]
intrinsics::panic_if_uninhabited::<T>();
intrinsics::uninit()
}

Expand Down Expand Up @@ -1128,6 +1132,8 @@ impl<T> MaybeUninit<T> {
#[unstable(feature = "maybe_uninit", issue = "53491")]
#[inline(always)]
pub unsafe fn into_inner(self) -> T {
#[cfg(not(stage0))]
intrinsics::panic_if_uninhabited::<T>();
ManuallyDrop::into_inner(self.value)
}

Expand Down
96 changes: 50 additions & 46 deletions src/librustc_codegen_ssa/mir/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -500,53 +500,57 @@ impl<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
_ => bx.new_fn_type(sig, &extra_args)
};

// emit a panic instead of instantiating an uninhabited type
if (intrinsic == Some("init") || intrinsic == Some("uninit")) &&
fn_ty.ret.layout.abi.is_uninhabited()
{
let loc = bx.sess().source_map().lookup_char_pos(span.lo());
let filename = Symbol::intern(&loc.file.name.to_string()).as_str();
let filename = bx.const_str_slice(filename);
let line = bx.const_u32(loc.line as u32);
let col = bx.const_u32(loc.col.to_usize() as u32 + 1);
let align = tcx.data_layout.aggregate_align.abi
.max(tcx.data_layout.i32_align.abi)
.max(tcx.data_layout.pointer_align.abi);

let str = format!(
"Attempted to instantiate uninhabited type {} using mem::{}",
sig.output(),
if intrinsic == Some("init") { "zeroed" } else { "uninitialized" }
);
let msg_str = Symbol::intern(&str).as_str();
let msg_str = bx.const_str_slice(msg_str);
let msg_file_line_col = bx.const_struct(
&[msg_str, filename, line, col],
false,
);
let msg_file_line_col = bx.static_addr_of(
msg_file_line_col,
align,
Some("panic_loc"),
);
// emit a panic or a NOP for `panic_if_uninhabited`
if intrinsic == Some("panic_if_uninhabited") {
let ty = instance.unwrap().substs.type_at(0);
let layout = bx.layout_of(ty);
if layout.abi.is_uninhabited() {
let loc = bx.sess().source_map().lookup_char_pos(span.lo());
let filename = Symbol::intern(&loc.file.name.to_string()).as_str();
let filename = bx.const_str_slice(filename);
let line = bx.const_u32(loc.line as u32);
let col = bx.const_u32(loc.col.to_usize() as u32 + 1);
let align = tcx.data_layout.aggregate_align.abi
.max(tcx.data_layout.i32_align.abi)
.max(tcx.data_layout.pointer_align.abi);

let str = format!(
"Attempted to instantiate uninhabited type {}",
ty
);
let msg_str = Symbol::intern(&str).as_str();
let msg_str = bx.const_str_slice(msg_str);
let msg_file_line_col = bx.const_struct(
&[msg_str, filename, line, col],
false,
);
let msg_file_line_col = bx.static_addr_of(
msg_file_line_col,
align,
Some("panic_loc"),
);

// Obtain the panic entry point.
let def_id =
common::langcall(bx.tcx(), Some(span), "", lang_items::PanicFnLangItem);
let instance = ty::Instance::mono(bx.tcx(), def_id);
let fn_ty = bx.fn_type_of_instance(&instance);
let llfn = bx.get_fn(instance);

// Codegen the actual panic invoke/call.
do_call(
self,
&mut bx,
fn_ty,
llfn,
&[msg_file_line_col],
destination.as_ref().map(|(_, bb)| (ReturnDest::Nothing, *bb)),
cleanup,
);
// Obtain the panic entry point.
let def_id =
common::langcall(bx.tcx(), Some(span), "", lang_items::PanicFnLangItem);
let instance = ty::Instance::mono(bx.tcx(), def_id);
let fn_ty = bx.fn_type_of_instance(&instance);
let llfn = bx.get_fn(instance);

// Codegen the actual panic invoke/call.
do_call(
self,
&mut bx,
fn_ty,
llfn,
&[msg_file_line_col],
destination.as_ref().map(|(_, bb)| (ReturnDest::Nothing, *bb)),
cleanup,
);
} else {
// a NOP
funclet_br(self, &mut bx, destination.as_ref().unwrap().1);
}
return;
}

Expand Down
1 change: 1 addition & 0 deletions src/librustc_typeck/check/intrinsic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ pub fn check_intrinsic_type<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
], tcx.types.usize)
}
"rustc_peek" => (1, vec![param(0)], param(0)),
"panic_if_uninhabited" => (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()),
Expand Down
41 changes: 34 additions & 7 deletions src/test/run-pass/panic-uninitialized-zeroed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// This test checks that instantiating an uninhabited type via `mem::{uninitialized,zeroed}` results
// in a runtime panic.

#![feature(never_type)]
#![feature(never_type, maybe_uninit)]

use std::{mem, panic};

Expand All @@ -20,7 +20,7 @@ fn main() {
panic::catch_unwind(|| {
mem::uninitialized::<!>()
}).err().and_then(|a| a.downcast_ref::<String>().map(|s| {
s == "Attempted to instantiate uninhabited type ! using mem::uninitialized"
s == "Attempted to instantiate uninhabited type !"
})),
Some(true)
);
Expand All @@ -29,7 +29,16 @@ fn main() {
panic::catch_unwind(|| {
mem::zeroed::<!>()
}).err().and_then(|a| a.downcast_ref::<String>().map(|s| {
s == "Attempted to instantiate uninhabited type ! using mem::zeroed"
s == "Attempted to instantiate uninhabited type !"
})),
Some(true)
);

assert_eq!(
panic::catch_unwind(|| {
mem::MaybeUninit::<!>::uninitialized().into_inner()
}).err().and_then(|a| a.downcast_ref::<String>().map(|s| {
s == "Attempted to instantiate uninhabited type !"
})),
Some(true)
);
Expand All @@ -38,7 +47,7 @@ fn main() {
panic::catch_unwind(|| {
mem::uninitialized::<Foo>()
}).err().and_then(|a| a.downcast_ref::<String>().map(|s| {
s == "Attempted to instantiate uninhabited type Foo using mem::uninitialized"
s == "Attempted to instantiate uninhabited type Foo"
})),
Some(true)
);
Expand All @@ -47,7 +56,16 @@ fn main() {
panic::catch_unwind(|| {
mem::zeroed::<Foo>()
}).err().and_then(|a| a.downcast_ref::<String>().map(|s| {
s == "Attempted to instantiate uninhabited type Foo using mem::zeroed"
s == "Attempted to instantiate uninhabited type Foo"
})),
Some(true)
);

assert_eq!(
panic::catch_unwind(|| {
mem::MaybeUninit::<Foo>::uninitialized().into_inner()
}).err().and_then(|a| a.downcast_ref::<String>().map(|s| {
s == "Attempted to instantiate uninhabited type Foo"
})),
Some(true)
);
Expand All @@ -56,7 +74,7 @@ fn main() {
panic::catch_unwind(|| {
mem::uninitialized::<Bar>()
}).err().and_then(|a| a.downcast_ref::<String>().map(|s| {
s == "Attempted to instantiate uninhabited type Bar using mem::uninitialized"
s == "Attempted to instantiate uninhabited type Bar"
})),
Some(true)
);
Expand All @@ -65,7 +83,16 @@ fn main() {
panic::catch_unwind(|| {
mem::zeroed::<Bar>()
}).err().and_then(|a| a.downcast_ref::<String>().map(|s| {
s == "Attempted to instantiate uninhabited type Bar using mem::zeroed"
s == "Attempted to instantiate uninhabited type Bar"
})),
Some(true)
);

assert_eq!(
panic::catch_unwind(|| {
mem::MaybeUninit::<Bar>::uninitialized().into_inner()
}).err().and_then(|a| a.downcast_ref::<String>().map(|s| {
s == "Attempted to instantiate uninhabited type Bar"
})),
Some(true)
);
Expand Down

0 comments on commit 8c2d0f4

Please sign in to comment.