Auto merge of #54667 - RalfJung:maybe-uninit, r=pnkfelix

Panic when using mem::uninitialized or mem::zeroed on an uninhabited type

All code by @japaric. This re-submits one half of https://github.com/rust-lang/rust/pull/53508. This is likely not the one that introduced the perf regression, but just to be sure I'll do a perf run anyway.
This commit is contained in:
bors 2018-10-01 14:58:24 +00:00
commit de3d640f59
11 changed files with 181 additions and 14 deletions

View File

@ -449,7 +449,7 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> {
}
}
if sized && fields.iter().any(|f| f.abi == Abi::Uninhabited) {
if sized && fields.iter().any(|f| f.abi.is_uninhabited()) {
abi = Abi::Uninhabited;
}
@ -724,7 +724,7 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> {
// See issue #49298 for more details on the need to leave space
// for non-ZST uninhabited data (mostly partial initialization).
let absent = |fields: &[TyLayout<'_>]| {
let uninhabited = fields.iter().any(|f| f.abi == Abi::Uninhabited);
let uninhabited = fields.iter().any(|f| f.abi.is_uninhabited());
let is_zst = fields.iter().all(|f| f.is_zst());
uninhabited && is_zst
};
@ -872,7 +872,7 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> {
_ => Abi::Aggregate { sized: true },
};
if st.iter().all(|v| v.abi == Abi::Uninhabited) {
if st.iter().all(|v| v.abi.is_uninhabited()) {
abi = Abi::Uninhabited;
}
@ -900,7 +900,7 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> {
let discr_type = def.repr.discr_type();
let bits = Integer::from_attr(tcx, discr_type).size().bits();
for (i, discr) in def.discriminants(tcx).enumerate() {
if variants[i].iter().any(|f| f.abi == Abi::Uninhabited) {
if variants[i].iter().any(|f| f.abi.is_uninhabited()) {
continue;
}
let mut x = discr.val as i128;
@ -1096,7 +1096,7 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> {
}
}
if layout_variants.iter().all(|v| v.abi == Abi::Uninhabited) {
if layout_variants.iter().all(|v| v.abi.is_uninhabited()) {
abi = Abi::Uninhabited;
}

View File

@ -279,7 +279,7 @@ pub fn create_function_debug_context(
}
None => {}
};
if cx.layout_of(sig.output()).abi == ty::layout::Abi::Uninhabited {
if cx.layout_of(sig.output()).abi.is_uninhabited() {
flags = flags | DIFlags::FlagNoReturn;
}

View File

@ -23,7 +23,7 @@
use llvm;
use llvm::AttributePlace::Function;
use rustc::ty::{self, Ty};
use rustc::ty::layout::{self, LayoutOf};
use rustc::ty::layout::LayoutOf;
use rustc::session::config::Sanitizer;
use rustc_data_structures::small_c_str::SmallCStr;
use rustc_target::spec::PanicStrategy;
@ -137,7 +137,7 @@ pub fn declare_fn(
let fty = FnType::new(cx, sig, &[]);
let llfn = declare_raw_fn(cx, name, fty.llvm_cconv(), fty.llvm_type(cx));
if cx.layout_of(sig.output()).abi == layout::Abi::Uninhabited {
if cx.layout_of(sig.output()).abi.is_uninhabited() {
llvm::Attribute::NoReturn.apply_llfn(Function, llfn);
}

View File

@ -482,6 +482,54 @@ impl FunctionCx<'a, 'll, 'tcx> {
_ => FnType::new(bx.cx, 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 = C_str_slice(bx.cx, filename);
let line = C_u32(bx.cx, loc.line as u32);
let col = C_u32(bx.cx, loc.col.to_usize() as u32 + 1);
let align = tcx.data_layout.aggregate_align
.max(tcx.data_layout.i32_align)
.max(tcx.data_layout.pointer_align);
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 = C_str_slice(bx.cx, msg_str);
let msg_file_line_col = C_struct(bx.cx,
&[msg_str, filename, line, col],
false);
let msg_file_line_col = consts::addr_of(bx.cx,
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 = FnType::of_instance(bx.cx, &instance);
let llfn = callee::get_fn(bx.cx, instance);
// Codegen the actual panic invoke/call.
do_call(
self,
bx,
fn_ty,
llfn,
&[msg_file_line_col],
destination.as_ref().map(|(_, bb)| (ReturnDest::Nothing, *bb)),
cleanup,
);
return;
}
// The arguments we'll be passing. Plus one to account for outptr, if used.
let arg_count = fn_ty.args.len() + fn_ty.ret.is_indirect() as usize;
let mut llargs = Vec::with_capacity(arg_count);

View File

@ -275,7 +275,7 @@ impl PlaceRef<'ll, 'tcx> {
/// Obtain the actual discriminant of a value.
pub fn codegen_get_discr(self, bx: &Builder<'a, 'll, 'tcx>, cast_to: Ty<'tcx>) -> &'ll Value {
let cast_to = bx.cx.layout_of(cast_to).immediate_llvm_type(bx.cx);
if self.layout.abi == layout::Abi::Uninhabited {
if self.layout.abi.is_uninhabited() {
return C_undef(cast_to);
}
match self.layout.variants {
@ -338,7 +338,7 @@ impl PlaceRef<'ll, 'tcx> {
/// Set the discriminant for a new value of the given case of the given
/// representation.
pub fn codegen_set_discr(&self, bx: &Builder<'a, 'll, 'tcx>, variant_index: usize) {
if self.layout.for_variant(bx.cx, variant_index).abi == layout::Abi::Uninhabited {
if self.layout.for_variant(bx.cx, variant_index).abi.is_uninhabited() {
return;
}
match self.layout.variants {

View File

@ -290,7 +290,7 @@ impl FunctionCx<'a, 'll, 'tcx> {
mir::CastKind::Misc => {
assert!(cast.is_llvm_immediate());
let ll_t_out = cast.immediate_llvm_type(bx.cx);
if operand.layout.abi == layout::Abi::Uninhabited {
if operand.layout.abi.is_uninhabited() {
return (bx, OperandRef {
val: OperandValue::Immediate(C_undef(ll_t_out)),
layout: cast,

View File

@ -514,7 +514,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M>
rval: OpTy<'tcx>,
) -> EvalResult<'tcx, (u128, usize)> {
trace!("read_discriminant_value {:#?}", rval.layout);
if rval.layout.abi == layout::Abi::Uninhabited {
if rval.layout.abi.is_uninhabited() {
return err!(Unreachable);
}

View File

@ -802,6 +802,14 @@ impl Abi {
_ => false,
}
}
/// Returns true if this is an uninhabited type
pub fn is_uninhabited(&self) -> bool {
match *self {
Abi::Uninhabited => true,
_ => false,
}
}
}
#[derive(PartialEq, Eq, Hash, Debug)]

View File

@ -0,0 +1,23 @@
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// compile-flags: -O
#![crate_type="lib"]
#![feature(maybe_uninit)]
use std::mem::MaybeUninit;
// Boxing a `MaybeUninit` value should not copy junk from the stack
#[no_mangle]
pub fn box_uninitialized() -> Box<MaybeUninit<usize>> {
// CHECK-LABEL: @box_uninitialized
// CHECK-NOT: store
Box::new(MaybeUninit::uninitialized())
}

View File

@ -8,6 +8,8 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// NOTE Instantiating an empty enum is UB. This test may break in the future.
// LLDB can't handle zero-sized values
// ignore-lldb
@ -25,8 +27,11 @@
#![allow(unused_variables)]
#![feature(omit_gdb_pretty_printer_section)]
#![feature(maybe_uninit)]
#![omit_gdb_pretty_printer_section]
use std::mem::MaybeUninit;
enum ANilEnum {}
enum AnotherNilEnum {}
@ -35,8 +40,8 @@ enum AnotherNilEnum {}
// The error from gdbr is expected since nil enums are not supposed to exist.
fn main() {
unsafe {
let first: ANilEnum = ::std::mem::zeroed();
let second: AnotherNilEnum = ::std::mem::zeroed();
let first: ANilEnum = MaybeUninit::uninitialized().into_inner();
let second: AnotherNilEnum = MaybeUninit::uninitialized().into_inner();
zzz(); // #break
}

View File

@ -0,0 +1,83 @@
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// ignore-wasm32-bare always compiled as panic=abort right now and this requires unwinding
// This test checks that instantiating an uninhabited type via `mem::{uninitialized,zeroed}` results
// in a runtime panic.
#![feature(never_type)]
use std::{mem, panic};
#[allow(dead_code)]
struct Foo {
x: u8,
y: !,
}
enum Bar {}
fn main() {
unsafe {
assert_eq!(
panic::catch_unwind(|| {
mem::uninitialized::<!>()
}).err().and_then(|a| a.downcast_ref::<String>().map(|s| {
s == "Attempted to instantiate uninhabited type ! using mem::uninitialized"
})),
Some(true)
);
assert_eq!(
panic::catch_unwind(|| {
mem::zeroed::<!>()
}).err().and_then(|a| a.downcast_ref::<String>().map(|s| {
s == "Attempted to instantiate uninhabited type ! using mem::zeroed"
})),
Some(true)
);
assert_eq!(
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"
})),
Some(true)
);
assert_eq!(
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"
})),
Some(true)
);
assert_eq!(
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"
})),
Some(true)
);
assert_eq!(
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"
})),
Some(true)
);
}
}