1
Fork 0

panic when calling MaybeUninhabited::into_inner on uninhabited type

This commit is contained in:
Ralf Jung 2018-12-27 09:40:33 +01:00
parent a7be40c65a
commit fff905bc69
5 changed files with 99 additions and 51 deletions

View file

@ -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,

View file

@ -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()
}
@ -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()
}
@ -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)
}

View file

@ -500,53 +500,62 @@ 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);
// emit a panic or a NOP for `panic_if_uninhabited`
if intrinsic == Some("panic_if_uninhabited") {
let ty = match callee.layout.ty.sty {
ty::FnDef(_, substs) => {
substs.type_at(0)
}
_ => bug!("{} is not callable as intrinsic", callee.layout.ty)
};
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 {} 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"),
);
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);
// 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,
);
// 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;
}

View file

@ -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()),

View file

@ -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};
@ -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)
);
@ -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)
);
@ -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)
);
@ -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)
);
@ -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)
);
@ -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)
);