diff --git a/src/libcore/intrinsics.rs b/src/libcore/intrinsics.rs index 7508257f7806f..4f5310f5285c9 100644 --- a/src/libcore/intrinsics.rs +++ b/src/libcore/intrinsics.rs @@ -690,6 +690,11 @@ extern "rust-intrinsic" { /// crate it is invoked in. pub fn type_id() -> 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(); + /// Creates a value initialized to zero. /// /// `init` is unsafe because it returns a zeroed-out datum, diff --git a/src/libcore/mem.rs b/src/libcore/mem.rs index c024868714cab..8fcbb73d9ce46 100644 --- a/src/libcore/mem.rs +++ b/src/libcore/mem.rs @@ -492,6 +492,8 @@ pub const fn needs_drop() -> 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 { + #[cfg(not(stage0))] + intrinsics::panic_if_uninhabited::(); intrinsics::init() } @@ -624,6 +626,8 @@ pub unsafe fn zeroed() -> 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 { + #[cfg(not(stage0))] + intrinsics::panic_if_uninhabited::(); intrinsics::uninit() } @@ -1128,6 +1132,8 @@ impl MaybeUninit { #[unstable(feature = "maybe_uninit", issue = "53491")] #[inline(always)] pub unsafe fn into_inner(self) -> T { + #[cfg(not(stage0))] + intrinsics::panic_if_uninhabited::(); ManuallyDrop::into_inner(self.value) } diff --git a/src/librustc_codegen_ssa/mir/block.rs b/src/librustc_codegen_ssa/mir/block.rs index 7397c722df6c9..aa82c853257a3 100644 --- a/src/librustc_codegen_ssa/mir/block.rs +++ b/src/librustc_codegen_ssa/mir/block.rs @@ -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; } diff --git a/src/librustc_typeck/check/intrinsic.rs b/src/librustc_typeck/check/intrinsic.rs index a36b21921436e..821c30b4fa709 100644 --- a/src/librustc_typeck/check/intrinsic.rs +++ b/src/librustc_typeck/check/intrinsic.rs @@ -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()), diff --git a/src/test/run-pass/panic-uninitialized-zeroed.rs b/src/test/run-pass/panic-uninitialized-zeroed.rs index c806bb97c1519..d47ff6c630d11 100644 --- a/src/test/run-pass/panic-uninitialized-zeroed.rs +++ b/src/test/run-pass/panic-uninitialized-zeroed.rs @@ -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::().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::().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::().map(|s| { + s == "Attempted to instantiate uninhabited type !" })), Some(true) ); @@ -38,7 +47,7 @@ fn main() { panic::catch_unwind(|| { mem::uninitialized::() }).err().and_then(|a| a.downcast_ref::().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::() }).err().and_then(|a| a.downcast_ref::().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::::uninitialized().into_inner() + }).err().and_then(|a| a.downcast_ref::().map(|s| { + s == "Attempted to instantiate uninhabited type Foo" })), Some(true) ); @@ -56,7 +74,7 @@ fn main() { panic::catch_unwind(|| { mem::uninitialized::() }).err().and_then(|a| a.downcast_ref::().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::() }).err().and_then(|a| a.downcast_ref::().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::::uninitialized().into_inner() + }).err().and_then(|a| a.downcast_ref::().map(|s| { + s == "Attempted to instantiate uninhabited type Bar" })), Some(true) );