mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-01 06:51:58 +00:00
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:
commit
de3d640f59
@ -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;
|
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
|
// See issue #49298 for more details on the need to leave space
|
||||||
// for non-ZST uninhabited data (mostly partial initialization).
|
// for non-ZST uninhabited data (mostly partial initialization).
|
||||||
let absent = |fields: &[TyLayout<'_>]| {
|
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());
|
let is_zst = fields.iter().all(|f| f.is_zst());
|
||||||
uninhabited && is_zst
|
uninhabited && is_zst
|
||||||
};
|
};
|
||||||
@ -872,7 +872,7 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> {
|
|||||||
_ => Abi::Aggregate { sized: true },
|
_ => 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;
|
abi = Abi::Uninhabited;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -900,7 +900,7 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> {
|
|||||||
let discr_type = def.repr.discr_type();
|
let discr_type = def.repr.discr_type();
|
||||||
let bits = Integer::from_attr(tcx, discr_type).size().bits();
|
let bits = Integer::from_attr(tcx, discr_type).size().bits();
|
||||||
for (i, discr) in def.discriminants(tcx).enumerate() {
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
let mut x = discr.val as i128;
|
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;
|
abi = Abi::Uninhabited;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -279,7 +279,7 @@ pub fn create_function_debug_context(
|
|||||||
}
|
}
|
||||||
None => {}
|
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;
|
flags = flags | DIFlags::FlagNoReturn;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,7 +23,7 @@
|
|||||||
use llvm;
|
use llvm;
|
||||||
use llvm::AttributePlace::Function;
|
use llvm::AttributePlace::Function;
|
||||||
use rustc::ty::{self, Ty};
|
use rustc::ty::{self, Ty};
|
||||||
use rustc::ty::layout::{self, LayoutOf};
|
use rustc::ty::layout::LayoutOf;
|
||||||
use rustc::session::config::Sanitizer;
|
use rustc::session::config::Sanitizer;
|
||||||
use rustc_data_structures::small_c_str::SmallCStr;
|
use rustc_data_structures::small_c_str::SmallCStr;
|
||||||
use rustc_target::spec::PanicStrategy;
|
use rustc_target::spec::PanicStrategy;
|
||||||
@ -137,7 +137,7 @@ pub fn declare_fn(
|
|||||||
let fty = FnType::new(cx, sig, &[]);
|
let fty = FnType::new(cx, sig, &[]);
|
||||||
let llfn = declare_raw_fn(cx, name, fty.llvm_cconv(), fty.llvm_type(cx));
|
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);
|
llvm::Attribute::NoReturn.apply_llfn(Function, llfn);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -482,6 +482,54 @@ impl FunctionCx<'a, 'll, 'tcx> {
|
|||||||
_ => FnType::new(bx.cx, sig, &extra_args)
|
_ => 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.
|
// 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 arg_count = fn_ty.args.len() + fn_ty.ret.is_indirect() as usize;
|
||||||
let mut llargs = Vec::with_capacity(arg_count);
|
let mut llargs = Vec::with_capacity(arg_count);
|
||||||
|
@ -275,7 +275,7 @@ impl PlaceRef<'ll, 'tcx> {
|
|||||||
/// Obtain the actual discriminant of a value.
|
/// Obtain the actual discriminant of a value.
|
||||||
pub fn codegen_get_discr(self, bx: &Builder<'a, 'll, 'tcx>, cast_to: Ty<'tcx>) -> &'ll 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);
|
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);
|
return C_undef(cast_to);
|
||||||
}
|
}
|
||||||
match self.layout.variants {
|
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
|
/// Set the discriminant for a new value of the given case of the given
|
||||||
/// representation.
|
/// representation.
|
||||||
pub fn codegen_set_discr(&self, bx: &Builder<'a, 'll, 'tcx>, variant_index: usize) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
match self.layout.variants {
|
match self.layout.variants {
|
||||||
|
@ -290,7 +290,7 @@ impl FunctionCx<'a, 'll, 'tcx> {
|
|||||||
mir::CastKind::Misc => {
|
mir::CastKind::Misc => {
|
||||||
assert!(cast.is_llvm_immediate());
|
assert!(cast.is_llvm_immediate());
|
||||||
let ll_t_out = cast.immediate_llvm_type(bx.cx);
|
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 {
|
return (bx, OperandRef {
|
||||||
val: OperandValue::Immediate(C_undef(ll_t_out)),
|
val: OperandValue::Immediate(C_undef(ll_t_out)),
|
||||||
layout: cast,
|
layout: cast,
|
||||||
|
@ -514,7 +514,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M>
|
|||||||
rval: OpTy<'tcx>,
|
rval: OpTy<'tcx>,
|
||||||
) -> EvalResult<'tcx, (u128, usize)> {
|
) -> EvalResult<'tcx, (u128, usize)> {
|
||||||
trace!("read_discriminant_value {:#?}", rval.layout);
|
trace!("read_discriminant_value {:#?}", rval.layout);
|
||||||
if rval.layout.abi == layout::Abi::Uninhabited {
|
if rval.layout.abi.is_uninhabited() {
|
||||||
return err!(Unreachable);
|
return err!(Unreachable);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -802,6 +802,14 @@ impl Abi {
|
|||||||
_ => false,
|
_ => 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)]
|
#[derive(PartialEq, Eq, Hash, Debug)]
|
||||||
|
23
src/test/codegen/box-maybe-uninit.rs
Normal file
23
src/test/codegen/box-maybe-uninit.rs
Normal 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())
|
||||||
|
}
|
@ -8,6 +8,8 @@
|
|||||||
// option. This file may not be copied, modified, or distributed
|
// option. This file may not be copied, modified, or distributed
|
||||||
// except according to those terms.
|
// 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
|
// LLDB can't handle zero-sized values
|
||||||
// ignore-lldb
|
// ignore-lldb
|
||||||
|
|
||||||
@ -25,8 +27,11 @@
|
|||||||
|
|
||||||
#![allow(unused_variables)]
|
#![allow(unused_variables)]
|
||||||
#![feature(omit_gdb_pretty_printer_section)]
|
#![feature(omit_gdb_pretty_printer_section)]
|
||||||
|
#![feature(maybe_uninit)]
|
||||||
#![omit_gdb_pretty_printer_section]
|
#![omit_gdb_pretty_printer_section]
|
||||||
|
|
||||||
|
use std::mem::MaybeUninit;
|
||||||
|
|
||||||
enum ANilEnum {}
|
enum ANilEnum {}
|
||||||
enum AnotherNilEnum {}
|
enum AnotherNilEnum {}
|
||||||
|
|
||||||
@ -35,8 +40,8 @@ enum AnotherNilEnum {}
|
|||||||
// The error from gdbr is expected since nil enums are not supposed to exist.
|
// The error from gdbr is expected since nil enums are not supposed to exist.
|
||||||
fn main() {
|
fn main() {
|
||||||
unsafe {
|
unsafe {
|
||||||
let first: ANilEnum = ::std::mem::zeroed();
|
let first: ANilEnum = MaybeUninit::uninitialized().into_inner();
|
||||||
let second: AnotherNilEnum = ::std::mem::zeroed();
|
let second: AnotherNilEnum = MaybeUninit::uninitialized().into_inner();
|
||||||
|
|
||||||
zzz(); // #break
|
zzz(); // #break
|
||||||
}
|
}
|
||||||
|
83
src/test/run-pass/panic-uninitialized-zeroed.rs
Normal file
83
src/test/run-pass/panic-uninitialized-zeroed.rs
Normal 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)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user