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

panic when calling MaybeUninhabited::into_inner on uninhabited type #57145

Merged
merged 2 commits into from
Jan 5, 2019
Merged
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
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