Skip to content

Commit 7ee68d7

Browse files
authored
Rollup merge of rust-lang#54125 - varkor:less-conservative-uninhabitedness-check, r=nikomatsakis
Less conservative uninhabitedness check Extends the uninhabitedness check to structs, non-empty enums, tuples and arrays. Pulled out of rust-lang#47291 and rust-lang#50262. Fixes rust-lang#54586. r? @nikomatsakis
2 parents f99911a + f912fda commit 7ee68d7

File tree

12 files changed

+92
-106
lines changed

12 files changed

+92
-106
lines changed

src/librustc/cfg/construct.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -415,8 +415,7 @@ impl<'a, 'tcx> CFGBuilder<'a, 'tcx> {
415415
args: I) -> CFGIndex {
416416
let func_or_rcvr_exit = self.expr(func_or_rcvr, pred);
417417
let ret = self.straightline(call_expr, func_or_rcvr_exit, args);
418-
// FIXME(canndrew): This is_never should probably be an is_uninhabited.
419-
if self.tables.expr_ty(call_expr).is_never() {
418+
if self.tables.expr_ty(call_expr).conservative_is_uninhabited(self.tcx) {
420419
self.add_unreachable_node()
421420
} else {
422421
ret

src/librustc/middle/liveness.rs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1197,8 +1197,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
11971197
}
11981198

11991199
hir::ExprKind::Call(ref f, ref args) => {
1200-
// FIXME(canndrew): This is_never should really be an is_uninhabited
1201-
let succ = if self.tables.expr_ty(expr).is_never() {
1200+
let succ = if self.tables.expr_ty(expr).conservative_is_uninhabited(self.ir.tcx) {
12021201
self.s.exit_ln
12031202
} else {
12041203
succ
@@ -1208,8 +1207,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
12081207
}
12091208

12101209
hir::ExprKind::MethodCall(.., ref args) => {
1211-
// FIXME(canndrew): This is_never should really be an is_uninhabited
1212-
let succ = if self.tables.expr_ty(expr).is_never() {
1210+
let succ = if self.tables.expr_ty(expr).conservative_is_uninhabited(self.ir.tcx) {
12131211
self.s.exit_ln
12141212
} else {
12151213
succ

src/librustc/ty/layout.rs

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,14 @@ fn layout_raw<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
190190

191191
ty::tls::enter_context(&icx, |_| {
192192
let cx = LayoutCx { tcx, param_env };
193-
cx.layout_raw_uncached(ty)
193+
let layout = cx.layout_raw_uncached(ty);
194+
// Type-level uninhabitedness should always imply ABI uninhabitedness.
195+
if let Ok(layout) = layout {
196+
if ty.conservative_is_uninhabited(tcx) {
197+
assert!(layout.abi.is_uninhabited());
198+
}
199+
}
200+
layout
194201
})
195202
})
196203
}
@@ -205,12 +212,11 @@ pub fn provide(providers: &mut ty::query::Providers<'_>) {
205212
#[derive(Copy, Clone)]
206213
pub struct LayoutCx<'tcx, C> {
207214
pub tcx: C,
208-
pub param_env: ty::ParamEnv<'tcx>
215+
pub param_env: ty::ParamEnv<'tcx>,
209216
}
210217

211218
impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> {
212-
fn layout_raw_uncached(self, ty: Ty<'tcx>)
213-
-> Result<&'tcx LayoutDetails, LayoutError<'tcx>> {
219+
fn layout_raw_uncached(self, ty: Ty<'tcx>) -> Result<&'tcx LayoutDetails, LayoutError<'tcx>> {
214220
let tcx = self.tcx;
215221
let param_env = self.param_env;
216222
let dl = self.data_layout();
@@ -555,13 +561,19 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> {
555561
let size = element.size.checked_mul(count, dl)
556562
.ok_or(LayoutError::SizeOverflow(ty))?;
557563

564+
let abi = if count != 0 && ty.conservative_is_uninhabited(tcx) {
565+
Abi::Uninhabited
566+
} else {
567+
Abi::Aggregate { sized: true }
568+
};
569+
558570
tcx.intern_layout(LayoutDetails {
559571
variants: Variants::Single { index: 0 },
560572
fields: FieldPlacement::Array {
561573
stride: element.size,
562574
count
563575
},
564-
abi: Abi::Aggregate { sized: true },
576+
abi,
565577
align: element.align,
566578
size
567579
})

src/librustc/ty/sty.rs

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1515,6 +1515,51 @@ impl<'a, 'gcx, 'tcx> TyS<'tcx> {
15151515
}
15161516
}
15171517

1518+
/// Checks whether a type is definitely uninhabited. This is
1519+
/// conservative: for some types that are uninhabited we return `false`,
1520+
/// but we only return `true` for types that are definitely uninhabited.
1521+
/// `ty.conservative_is_uninhabited` implies that any value of type `ty`
1522+
/// will be `Abi::Uninhabited`. (Note that uninhabited types may have nonzero
1523+
/// size, to account for partial initialisation. See #49298 for details.)
1524+
pub fn conservative_is_uninhabited(&self, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> bool {
1525+
// FIXME(varkor): we can make this less conversative by substituting concrete
1526+
// type arguments.
1527+
match self.sty {
1528+
ty::Never => true,
1529+
ty::Adt(def, _) if def.is_union() => {
1530+
// For now, `union`s are never considered uninhabited.
1531+
false
1532+
}
1533+
ty::Adt(def, _) => {
1534+
// Any ADT is uninhabited if either:
1535+
// (a) It has no variants (i.e. an empty `enum`);
1536+
// (b) Each of its variants (a single one in the case of a `struct`) has at least
1537+
// one uninhabited field.
1538+
def.variants.iter().all(|var| {
1539+
var.fields.iter().any(|field| {
1540+
tcx.type_of(field.did).conservative_is_uninhabited(tcx)
1541+
})
1542+
})
1543+
}
1544+
ty::Tuple(tys) => tys.iter().any(|ty| ty.conservative_is_uninhabited(tcx)),
1545+
ty::Array(ty, len) => {
1546+
match len.val.try_to_scalar() {
1547+
// If the array is definitely non-empty, it's uninhabited if
1548+
// the type of its elements is uninhabited.
1549+
Some(n) if !n.is_null() => ty.conservative_is_uninhabited(tcx),
1550+
_ => false
1551+
}
1552+
}
1553+
ty::Ref(..) => {
1554+
// References to uninitialised memory is valid for any type, including
1555+
// uninhabited types, in unsafe code, so we treat all references as
1556+
// inhabited.
1557+
false
1558+
}
1559+
_ => false,
1560+
}
1561+
}
1562+
15181563
pub fn is_primitive(&self) -> bool {
15191564
match self.sty {
15201565
Bool | Char | Int(_) | Uint(_) | Float(_) => true,

src/librustc_mir/borrow_check/nll/type_check/mod.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1562,8 +1562,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
15621562
}
15631563
}
15641564
None => {
1565-
// FIXME(canndrew): This is_never should probably be an is_uninhabited
1566-
if !sig.output().is_never() {
1565+
if !sig.output().conservative_is_uninhabited(self.tcx()) {
15671566
span_mirbug!(self, term, "call to converging function {:?} w/o dest", sig);
15681567
}
15691568
}

src/librustc_mir/build/expr/into.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -275,8 +275,6 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
275275
exit_block.unit()
276276
}
277277
ExprKind::Call { ty, fun, args, from_hir_call } => {
278-
// FIXME(canndrew): This is_never should probably be an is_uninhabited
279-
let diverges = expr.ty.is_never();
280278
let intrinsic = match ty.sty {
281279
ty::FnDef(def_id, _) => {
282280
let f = ty.fn_sig(this.hir.tcx());
@@ -332,7 +330,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
332330
func: fun,
333331
args,
334332
cleanup: Some(cleanup),
335-
destination: if diverges {
333+
destination: if expr.ty.conservative_is_uninhabited(this.hir.tcx()) {
336334
None
337335
} else {
338336
Some((destination.clone(), success))

src/librustc_mir/hair/pattern/check_match.rs

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,7 @@ impl<'a, 'tcx> MatchVisitor<'a, 'tcx> {
229229
let scrutinee_is_uninhabited = if self.tcx.features().exhaustive_patterns {
230230
self.tcx.is_ty_uninhabited_from(module, pat_ty)
231231
} else {
232-
self.conservative_is_uninhabited(pat_ty)
232+
pat_ty.conservative_is_uninhabited(self.tcx)
233233
};
234234
if !scrutinee_is_uninhabited {
235235
// We know the type is inhabited, so this must be wrong
@@ -257,15 +257,6 @@ impl<'a, 'tcx> MatchVisitor<'a, 'tcx> {
257257
})
258258
}
259259

260-
fn conservative_is_uninhabited(&self, scrutinee_ty: Ty<'tcx>) -> bool {
261-
// "rustc-1.0-style" uncontentious uninhabitableness check
262-
match scrutinee_ty.sty {
263-
ty::Never => true,
264-
ty::Adt(def, _) => def.variants.is_empty(),
265-
_ => false
266-
}
267-
}
268-
269260
fn check_irrefutable(&self, pat: &'tcx Pat, origin: &str) {
270261
let module = self.tcx.hir.get_module_parent(pat.id);
271262
MatchCheckCtxt::create_and_enter(self.tcx, module, |ref mut cx| {

src/test/debuginfo/nil-enum.rs

Lines changed: 6 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,24 @@
1-
// Copyright 2013-2014 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-
// NOTE Instantiating an empty enum is UB. This test may break in the future.
12-
13-
// LLDB can't handle zero-sized values
1+
// LLDB can't handle zero-sized values.
142
// ignore-lldb
153

16-
174
// compile-flags:-g
185
// gdb-command:run
196

20-
// gdb-command:print first
7+
// gdb-command:print *first
218
// gdbg-check:$1 = {<No data fields>}
229
// gdbr-check:$1 = <error reading variable>
2310

24-
// gdb-command:print second
25-
// gdbg-check:$2 = {<No data fields>}
26-
// gdbr-check:$2 = <error reading variable>
27-
2811
#![allow(unused_variables)]
2912
#![feature(omit_gdb_pretty_printer_section)]
3013
#![feature(maybe_uninit)]
3114
#![omit_gdb_pretty_printer_section]
3215

33-
use std::mem::MaybeUninit;
34-
35-
enum ANilEnum {}
36-
enum AnotherNilEnum {}
16+
enum Void {}
3717

38-
// This test relies on gdbg printing the string "{<No data fields>}" for empty
39-
// structs (which may change some time)
40-
// The error from gdbr is expected since nil enums are not supposed to exist.
4118
fn main() {
42-
unsafe {
43-
let first: ANilEnum = MaybeUninit::uninitialized().into_inner();
44-
let second: AnotherNilEnum = MaybeUninit::uninitialized().into_inner();
19+
let first: *const Void = 1 as *const _;
4520

46-
zzz(); // #break
47-
}
21+
zzz(); // #break
4822
}
4923

50-
fn zzz() {()}
24+
fn zzz() {}
Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,22 @@
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-
111
#![feature(const_transmute)]
122

133
use std::mem;
144

155
#[derive(Copy, Clone)]
166
enum Bar {}
177

18-
const BAD_BAD_BAD: Bar = unsafe { mem::transmute(()) };
8+
union TransmuteUnion<A: Clone + Copy, B: Clone + Copy> {
9+
a: A,
10+
b: B,
11+
}
12+
13+
const BAD_BAD_BAD: Bar = unsafe { (TransmuteUnion::<(), Bar> { a: () }).b };
1914
//~^ ERROR this constant likely exhibits undefined behavior
2015

2116
const BAD_BAD_REF: &Bar = unsafe { mem::transmute(1usize) };
2217
//~^ ERROR this constant likely exhibits undefined behavior
2318

24-
const BAD_BAD_ARRAY: [Bar; 1] = unsafe { mem::transmute(()) };
19+
const BAD_BAD_ARRAY: [Bar; 1] = unsafe { (TransmuteUnion::<(), [Bar; 1]> { a: () }).b };
2520
//~^ ERROR this constant likely exhibits undefined behavior
2621

27-
fn main() {
28-
}
22+
fn main() {}

src/test/ui/consts/const-eval/ub-uninhabit.stderr

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,24 @@
11
error[E0080]: this constant likely exhibits undefined behavior
2-
--> $DIR/ub-uninhabit.rs:18:1
2+
--> $DIR/ub-uninhabit.rs:13:1
33
|
4-
LL | const BAD_BAD_BAD: Bar = unsafe { mem::transmute(()) };
5-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a value of an uninhabited type
4+
LL | const BAD_BAD_BAD: Bar = unsafe { (TransmuteUnion::<(), Bar> { a: () }).b };
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a value of an uninhabited type
66
|
77
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rust compiler repository if you believe it should not be considered undefined behavior
88

99
error[E0080]: this constant likely exhibits undefined behavior
10-
--> $DIR/ub-uninhabit.rs:21:1
10+
--> $DIR/ub-uninhabit.rs:16:1
1111
|
1212
LL | const BAD_BAD_REF: &Bar = unsafe { mem::transmute(1usize) };
1313
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a value of an uninhabited type at .<deref>
1414
|
1515
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rust compiler repository if you believe it should not be considered undefined behavior
1616

1717
error[E0080]: this constant likely exhibits undefined behavior
18-
--> $DIR/ub-uninhabit.rs:24:1
18+
--> $DIR/ub-uninhabit.rs:19:1
1919
|
20-
LL | const BAD_BAD_ARRAY: [Bar; 1] = unsafe { mem::transmute(()) };
21-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a value of an uninhabited type at [0]
20+
LL | const BAD_BAD_ARRAY: [Bar; 1] = unsafe { (TransmuteUnion::<(), [Bar; 1]> { a: () }).b };
21+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a value of an uninhabited type
2222
|
2323
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rust compiler repository if you believe it should not be considered undefined behavior
2424

src/test/ui/uninhabited/uninhabited-matches-feature-gated.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,10 @@ fn main() {
2020
let _ = match x {}; //~ ERROR non-exhaustive
2121

2222
let x: (Void,) = unsafe { std::mem::uninitialized() };
23-
let _ = match x {}; //~ ERROR non-exhaustive
23+
let _ = match x {}; // okay
2424

2525
let x: [Void; 1] = unsafe { std::mem::uninitialized() };
26-
let _ = match x {}; //~ ERROR non-exhaustive
26+
let _ = match x {}; // okay
2727

2828
let x: &[Void] = unsafe { std::mem::uninitialized() };
2929
let _ = match x { //~ ERROR non-exhaustive

src/test/ui/uninhabited/uninhabited-matches-feature-gated.stderr

Lines changed: 1 addition & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -16,30 +16,6 @@ help: Please ensure that all possible cases are being handled; possibly adding w
1616
LL | let _ = match x {}; //~ ERROR non-exhaustive
1717
| ^
1818

19-
error[E0004]: non-exhaustive patterns: type (Void,) is non-empty
20-
--> $DIR/uninhabited-matches-feature-gated.rs:23:19
21-
|
22-
LL | let _ = match x {}; //~ ERROR non-exhaustive
23-
| ^
24-
|
25-
help: Please ensure that all possible cases are being handled; possibly adding wildcards or more match arms.
26-
--> $DIR/uninhabited-matches-feature-gated.rs:23:19
27-
|
28-
LL | let _ = match x {}; //~ ERROR non-exhaustive
29-
| ^
30-
31-
error[E0004]: non-exhaustive patterns: type [Void; 1] is non-empty
32-
--> $DIR/uninhabited-matches-feature-gated.rs:26:19
33-
|
34-
LL | let _ = match x {}; //~ ERROR non-exhaustive
35-
| ^
36-
|
37-
help: Please ensure that all possible cases are being handled; possibly adding wildcards or more match arms.
38-
--> $DIR/uninhabited-matches-feature-gated.rs:26:19
39-
|
40-
LL | let _ = match x {}; //~ ERROR non-exhaustive
41-
| ^
42-
4319
error[E0004]: non-exhaustive patterns: `&[_]` not covered
4420
--> $DIR/uninhabited-matches-feature-gated.rs:29:19
4521
|
@@ -58,7 +34,7 @@ error[E0005]: refutable pattern in local binding: `Err(_)` not covered
5834
LL | let Ok(x) = x;
5935
| ^^^^^ pattern `Err(_)` not covered
6036

61-
error: aborting due to 7 previous errors
37+
error: aborting due to 5 previous errors
6238

6339
Some errors occurred: E0004, E0005.
6440
For more information about an error, try `rustc --explain E0004`.

0 commit comments

Comments
 (0)