Skip to content

Commit de3d640

Browse files
committed
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 #53508. This is likely not the one that introduced the perf regression, but just to be sure I'll do a perf run anyway.
2 parents 6188c58 + dd65d73 commit de3d640

File tree

11 files changed

+181
-14
lines changed

11 files changed

+181
-14
lines changed

src/librustc/ty/layout.rs

+5-5
Original file line numberDiff line numberDiff line change
@@ -449,7 +449,7 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> {
449449
}
450450
}
451451

452-
if sized && fields.iter().any(|f| f.abi == Abi::Uninhabited) {
452+
if sized && fields.iter().any(|f| f.abi.is_uninhabited()) {
453453
abi = Abi::Uninhabited;
454454
}
455455

@@ -724,7 +724,7 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> {
724724
// See issue #49298 for more details on the need to leave space
725725
// for non-ZST uninhabited data (mostly partial initialization).
726726
let absent = |fields: &[TyLayout<'_>]| {
727-
let uninhabited = fields.iter().any(|f| f.abi == Abi::Uninhabited);
727+
let uninhabited = fields.iter().any(|f| f.abi.is_uninhabited());
728728
let is_zst = fields.iter().all(|f| f.is_zst());
729729
uninhabited && is_zst
730730
};
@@ -872,7 +872,7 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> {
872872
_ => Abi::Aggregate { sized: true },
873873
};
874874

875-
if st.iter().all(|v| v.abi == Abi::Uninhabited) {
875+
if st.iter().all(|v| v.abi.is_uninhabited()) {
876876
abi = Abi::Uninhabited;
877877
}
878878

@@ -900,7 +900,7 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> {
900900
let discr_type = def.repr.discr_type();
901901
let bits = Integer::from_attr(tcx, discr_type).size().bits();
902902
for (i, discr) in def.discriminants(tcx).enumerate() {
903-
if variants[i].iter().any(|f| f.abi == Abi::Uninhabited) {
903+
if variants[i].iter().any(|f| f.abi.is_uninhabited()) {
904904
continue;
905905
}
906906
let mut x = discr.val as i128;
@@ -1096,7 +1096,7 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> {
10961096
}
10971097
}
10981098

1099-
if layout_variants.iter().all(|v| v.abi == Abi::Uninhabited) {
1099+
if layout_variants.iter().all(|v| v.abi.is_uninhabited()) {
11001100
abi = Abi::Uninhabited;
11011101
}
11021102

src/librustc_codegen_llvm/debuginfo/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -279,7 +279,7 @@ pub fn create_function_debug_context(
279279
}
280280
None => {}
281281
};
282-
if cx.layout_of(sig.output()).abi == ty::layout::Abi::Uninhabited {
282+
if cx.layout_of(sig.output()).abi.is_uninhabited() {
283283
flags = flags | DIFlags::FlagNoReturn;
284284
}
285285

src/librustc_codegen_llvm/declare.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
use llvm;
2424
use llvm::AttributePlace::Function;
2525
use rustc::ty::{self, Ty};
26-
use rustc::ty::layout::{self, LayoutOf};
26+
use rustc::ty::layout::LayoutOf;
2727
use rustc::session::config::Sanitizer;
2828
use rustc_data_structures::small_c_str::SmallCStr;
2929
use rustc_target::spec::PanicStrategy;
@@ -137,7 +137,7 @@ pub fn declare_fn(
137137
let fty = FnType::new(cx, sig, &[]);
138138
let llfn = declare_raw_fn(cx, name, fty.llvm_cconv(), fty.llvm_type(cx));
139139

140-
if cx.layout_of(sig.output()).abi == layout::Abi::Uninhabited {
140+
if cx.layout_of(sig.output()).abi.is_uninhabited() {
141141
llvm::Attribute::NoReturn.apply_llfn(Function, llfn);
142142
}
143143

src/librustc_codegen_llvm/mir/block.rs

+48
Original file line numberDiff line numberDiff line change
@@ -482,6 +482,54 @@ impl FunctionCx<'a, 'll, 'tcx> {
482482
_ => FnType::new(bx.cx, sig, &extra_args)
483483
};
484484

485+
// emit a panic instead of instantiating an uninhabited type
486+
if (intrinsic == Some("init") || intrinsic == Some("uninit")) &&
487+
fn_ty.ret.layout.abi.is_uninhabited()
488+
{
489+
let loc = bx.sess().source_map().lookup_char_pos(span.lo());
490+
let filename = Symbol::intern(&loc.file.name.to_string()).as_str();
491+
let filename = C_str_slice(bx.cx, filename);
492+
let line = C_u32(bx.cx, loc.line as u32);
493+
let col = C_u32(bx.cx, loc.col.to_usize() as u32 + 1);
494+
let align = tcx.data_layout.aggregate_align
495+
.max(tcx.data_layout.i32_align)
496+
.max(tcx.data_layout.pointer_align);
497+
498+
let str = format!(
499+
"Attempted to instantiate uninhabited type {} using mem::{}",
500+
sig.output(),
501+
if intrinsic == Some("init") { "zeroed" } else { "uninitialized" }
502+
);
503+
let msg_str = Symbol::intern(&str).as_str();
504+
let msg_str = C_str_slice(bx.cx, msg_str);
505+
let msg_file_line_col = C_struct(bx.cx,
506+
&[msg_str, filename, line, col],
507+
false);
508+
let msg_file_line_col = consts::addr_of(bx.cx,
509+
msg_file_line_col,
510+
align,
511+
Some("panic_loc"));
512+
513+
// Obtain the panic entry point.
514+
let def_id =
515+
common::langcall(bx.tcx(), Some(span), "", lang_items::PanicFnLangItem);
516+
let instance = ty::Instance::mono(bx.tcx(), def_id);
517+
let fn_ty = FnType::of_instance(bx.cx, &instance);
518+
let llfn = callee::get_fn(bx.cx, instance);
519+
520+
// Codegen the actual panic invoke/call.
521+
do_call(
522+
self,
523+
bx,
524+
fn_ty,
525+
llfn,
526+
&[msg_file_line_col],
527+
destination.as_ref().map(|(_, bb)| (ReturnDest::Nothing, *bb)),
528+
cleanup,
529+
);
530+
return;
531+
}
532+
485533
// The arguments we'll be passing. Plus one to account for outptr, if used.
486534
let arg_count = fn_ty.args.len() + fn_ty.ret.is_indirect() as usize;
487535
let mut llargs = Vec::with_capacity(arg_count);

src/librustc_codegen_llvm/mir/place.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -275,7 +275,7 @@ impl PlaceRef<'ll, 'tcx> {
275275
/// Obtain the actual discriminant of a value.
276276
pub fn codegen_get_discr(self, bx: &Builder<'a, 'll, 'tcx>, cast_to: Ty<'tcx>) -> &'ll Value {
277277
let cast_to = bx.cx.layout_of(cast_to).immediate_llvm_type(bx.cx);
278-
if self.layout.abi == layout::Abi::Uninhabited {
278+
if self.layout.abi.is_uninhabited() {
279279
return C_undef(cast_to);
280280
}
281281
match self.layout.variants {
@@ -338,7 +338,7 @@ impl PlaceRef<'ll, 'tcx> {
338338
/// Set the discriminant for a new value of the given case of the given
339339
/// representation.
340340
pub fn codegen_set_discr(&self, bx: &Builder<'a, 'll, 'tcx>, variant_index: usize) {
341-
if self.layout.for_variant(bx.cx, variant_index).abi == layout::Abi::Uninhabited {
341+
if self.layout.for_variant(bx.cx, variant_index).abi.is_uninhabited() {
342342
return;
343343
}
344344
match self.layout.variants {

src/librustc_codegen_llvm/mir/rvalue.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -290,7 +290,7 @@ impl FunctionCx<'a, 'll, 'tcx> {
290290
mir::CastKind::Misc => {
291291
assert!(cast.is_llvm_immediate());
292292
let ll_t_out = cast.immediate_llvm_type(bx.cx);
293-
if operand.layout.abi == layout::Abi::Uninhabited {
293+
if operand.layout.abi.is_uninhabited() {
294294
return (bx, OperandRef {
295295
val: OperandValue::Immediate(C_undef(ll_t_out)),
296296
layout: cast,

src/librustc_mir/interpret/operand.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -514,7 +514,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M>
514514
rval: OpTy<'tcx>,
515515
) -> EvalResult<'tcx, (u128, usize)> {
516516
trace!("read_discriminant_value {:#?}", rval.layout);
517-
if rval.layout.abi == layout::Abi::Uninhabited {
517+
if rval.layout.abi.is_uninhabited() {
518518
return err!(Unreachable);
519519
}
520520

src/librustc_target/abi/mod.rs

+8
Original file line numberDiff line numberDiff line change
@@ -802,6 +802,14 @@ impl Abi {
802802
_ => false,
803803
}
804804
}
805+
806+
/// Returns true if this is an uninhabited type
807+
pub fn is_uninhabited(&self) -> bool {
808+
match *self {
809+
Abi::Uninhabited => true,
810+
_ => false,
811+
}
812+
}
805813
}
806814

807815
#[derive(PartialEq, Eq, Hash, Debug)]

src/test/codegen/box-maybe-uninit.rs

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

src/test/debuginfo/nil-enum.rs

+7-2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11+
// NOTE Instantiating an empty enum is UB. This test may break in the future.
12+
1113
// LLDB can't handle zero-sized values
1214
// ignore-lldb
1315

@@ -25,8 +27,11 @@
2527

2628
#![allow(unused_variables)]
2729
#![feature(omit_gdb_pretty_printer_section)]
30+
#![feature(maybe_uninit)]
2831
#![omit_gdb_pretty_printer_section]
2932

33+
use std::mem::MaybeUninit;
34+
3035
enum ANilEnum {}
3136
enum AnotherNilEnum {}
3237

@@ -35,8 +40,8 @@ enum AnotherNilEnum {}
3540
// The error from gdbr is expected since nil enums are not supposed to exist.
3641
fn main() {
3742
unsafe {
38-
let first: ANilEnum = ::std::mem::zeroed();
39-
let second: AnotherNilEnum = ::std::mem::zeroed();
43+
let first: ANilEnum = MaybeUninit::uninitialized().into_inner();
44+
let second: AnotherNilEnum = MaybeUninit::uninitialized().into_inner();
4045

4146
zzz(); // #break
4247
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// ignore-wasm32-bare always compiled as panic=abort right now and this requires unwinding
12+
// This test checks that instantiating an uninhabited type via `mem::{uninitialized,zeroed}` results
13+
// in a runtime panic.
14+
15+
#![feature(never_type)]
16+
17+
use std::{mem, panic};
18+
19+
#[allow(dead_code)]
20+
struct Foo {
21+
x: u8,
22+
y: !,
23+
}
24+
25+
enum Bar {}
26+
27+
fn main() {
28+
unsafe {
29+
assert_eq!(
30+
panic::catch_unwind(|| {
31+
mem::uninitialized::<!>()
32+
}).err().and_then(|a| a.downcast_ref::<String>().map(|s| {
33+
s == "Attempted to instantiate uninhabited type ! using mem::uninitialized"
34+
})),
35+
Some(true)
36+
);
37+
38+
assert_eq!(
39+
panic::catch_unwind(|| {
40+
mem::zeroed::<!>()
41+
}).err().and_then(|a| a.downcast_ref::<String>().map(|s| {
42+
s == "Attempted to instantiate uninhabited type ! using mem::zeroed"
43+
})),
44+
Some(true)
45+
);
46+
47+
assert_eq!(
48+
panic::catch_unwind(|| {
49+
mem::uninitialized::<Foo>()
50+
}).err().and_then(|a| a.downcast_ref::<String>().map(|s| {
51+
s == "Attempted to instantiate uninhabited type Foo using mem::uninitialized"
52+
})),
53+
Some(true)
54+
);
55+
56+
assert_eq!(
57+
panic::catch_unwind(|| {
58+
mem::zeroed::<Foo>()
59+
}).err().and_then(|a| a.downcast_ref::<String>().map(|s| {
60+
s == "Attempted to instantiate uninhabited type Foo using mem::zeroed"
61+
})),
62+
Some(true)
63+
);
64+
65+
assert_eq!(
66+
panic::catch_unwind(|| {
67+
mem::uninitialized::<Bar>()
68+
}).err().and_then(|a| a.downcast_ref::<String>().map(|s| {
69+
s == "Attempted to instantiate uninhabited type Bar using mem::uninitialized"
70+
})),
71+
Some(true)
72+
);
73+
74+
assert_eq!(
75+
panic::catch_unwind(|| {
76+
mem::zeroed::<Bar>()
77+
}).err().and_then(|a| a.downcast_ref::<String>().map(|s| {
78+
s == "Attempted to instantiate uninhabited type Bar using mem::zeroed"
79+
})),
80+
Some(true)
81+
);
82+
}
83+
}

0 commit comments

Comments
 (0)