Skip to content

Commit 5cad9f0

Browse files
committed
Auto merge of #54125 - varkor:less-conservative-uninhabitedness-check, r=<try>
Less conservative uninhabitedness check Extends the uninhabitedness check to structs, non-empty enums, tuples and arrays. Pulled out of #47291 and #50262. Blocked on #54123. r? @nikomatsakis
2 parents c3a1a0d + 1abd0d8 commit 5cad9f0

File tree

12 files changed

+61
-80
lines changed

12 files changed

+61
-80
lines changed

src/librustc/cfg/construct.rs

+1-2
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

+2-4
Original file line numberDiff line numberDiff line change
@@ -1209,8 +1209,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
12091209
}
12101210

12111211
hir::ExprKind::Call(ref f, ref args) => {
1212-
// FIXME(canndrew): This is_never should really be an is_uninhabited
1213-
let succ = if self.tables.expr_ty(expr).is_never() {
1212+
let succ = if self.tables.expr_ty(expr).conservative_is_uninhabited(self.ir.tcx) {
12141213
self.s.exit_ln
12151214
} else {
12161215
succ
@@ -1220,8 +1219,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
12201219
}
12211220

12221221
hir::ExprKind::MethodCall(.., ref args) => {
1223-
// FIXME(canndrew): This is_never should really be an is_uninhabited
1224-
let succ = if self.tables.expr_ty(expr).is_never() {
1222+
let succ = if self.tables.expr_ty(expr).conservative_is_uninhabited(self.ir.tcx) {
12251223
self.s.exit_ln
12261224
} else {
12271225
succ

src/librustc/ty/sty.rs

+40
Original file line numberDiff line numberDiff line change
@@ -1489,6 +1489,46 @@ impl<'a, 'gcx, 'tcx> TyS<'tcx> {
14891489
}
14901490
}
14911491

1492+
pub fn conservative_is_uninhabited(&self, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> bool {
1493+
// Checks whether a type is definitely uninhabited. This is
1494+
// conservative: for some types that are uninhabited we return `false`,
1495+
// but we only return `true` for types that are definitely uninhabited.
1496+
match self.sty {
1497+
ty::Never => true,
1498+
ty::Adt(def, _) if def.is_union() => {
1499+
// For now, `union`s are never considered uninhabited.
1500+
false
1501+
}
1502+
ty::Adt(def, _) => {
1503+
// Any ADT is uninhabited if either:
1504+
// (a) It has no variants (i.e. an empty `enum`);
1505+
// (b) Each of its variants (a single one in the case of a `struct`) has at least
1506+
// one uninhabited field.
1507+
def.variants.iter().all(|var| {
1508+
var.fields.iter().any(|field| {
1509+
tcx.type_of(field.did).conservative_is_uninhabited(tcx)
1510+
})
1511+
})
1512+
}
1513+
ty::Tuple(tys) => tys.iter().any(|ty| ty.conservative_is_uninhabited(tcx)),
1514+
ty::Array(ty, len) => {
1515+
match len.val.try_to_scalar() {
1516+
// If the array is definitely non-empty, it's uninhabited if
1517+
// the type of its elements is uninhabited.
1518+
Some(n) if !n.is_null() => ty.conservative_is_uninhabited(tcx),
1519+
_ => false
1520+
}
1521+
}
1522+
ty::Ref(..) => {
1523+
// References to uninitialised memory is valid for any type, including
1524+
// uninhabited types, in unsafe code, so we treat all references as
1525+
// inhabited.
1526+
false
1527+
}
1528+
_ => false,
1529+
}
1530+
}
1531+
14921532
pub fn is_primitive(&self) -> bool {
14931533
match self.sty {
14941534
Bool | Char | Int(_) | Uint(_) | Float(_) => true,

src/librustc_codegen_llvm/debuginfo/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,8 @@ pub fn create_function_debug_context(
279279
}
280280
None => {}
281281
};
282+
283+
// Tell LLVM that functions that return uninhabited types will not return.
282284
if cx.layout_of(sig.output()).abi.is_uninhabited() {
283285
flags = flags | DIFlags::FlagNoReturn;
284286
}

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

+1-2
Original file line numberDiff line numberDiff line change
@@ -1390,8 +1390,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
13901390
}
13911391
}
13921392
None => {
1393-
// FIXME(canndrew): This is_never should probably be an is_uninhabited
1394-
if !sig.output().is_never() {
1393+
if !sig.output().conservative_is_uninhabited(self.tcx()) {
13951394
span_mirbug!(self, term, "call to converging function {:?} w/o dest", sig);
13961395
}
13971396
}

src/librustc_mir/build/expr/into.rs

+1-3
Original file line numberDiff line numberDiff line change
@@ -265,8 +265,6 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
265265
exit_block.unit()
266266
}
267267
ExprKind::Call { ty, fun, args } => {
268-
// FIXME(canndrew): This is_never should probably be an is_uninhabited
269-
let diverges = expr.ty.is_never();
270268
let intrinsic = match ty.sty {
271269
ty::FnDef(def_id, _) => {
272270
let f = ty.fn_sig(this.hir.tcx());
@@ -321,7 +319,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
321319
func: fun,
322320
args,
323321
cleanup: Some(cleanup),
324-
destination: if diverges {
322+
destination: if expr.ty.conservative_is_uninhabited(this.hir.tcx()) {
325323
None
326324
} else {
327325
Some((destination.clone(), success))

src/librustc_mir/hair/pattern/check_match.rs

+1-10
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/libstd/net/tcp.rs

+1
Original file line numberDiff line numberDiff line change
@@ -729,6 +729,7 @@ impl TcpListener {
729729
/// ```
730730
#[stable(feature = "rust1", since = "1.0.0")]
731731
pub fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> {
732+
#[allow(unused_variables)] // `TcpStream` is uninhabited on some platforms.
732733
self.0.accept().map(|(a, b)| (TcpStream(a), b))
733734
}
734735

src/libstd/sys_common/process.rs

+2
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ impl<K: EnvKey> CommandEnv<K> {
6666
pub fn capture(&self) -> BTreeMap<K, OsString> {
6767
let mut result = BTreeMap::<K, OsString>::new();
6868
if !self.clear {
69+
#[allow(unused_mut)] // `Env` is uninhabited on some platforms.
6970
for (k, v) in env::vars_os() {
7071
result.insert(k.into(), v);
7172
}
@@ -83,6 +84,7 @@ impl<K: EnvKey> CommandEnv<K> {
8384
// Apply these changes directly to the current environment
8485
pub fn apply(&self) {
8586
if self.clear {
87+
#[allow(unused_mut)] // `Env` is uninhabited on some platforms.
8688
for (k, _) in env::vars_os() {
8789
env::remove_var(k);
8890
}

src/test/debuginfo/nil-enum.rs

+7-32
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,25 @@
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-
111
// NOTE Instantiating an empty enum is UB. This test may break in the future.
12-
13-
// LLDB can't handle zero-sized values
2+
//
3+
// LLDB can't handle zero-sized values.
144
// ignore-lldb
155

16-
176
// compile-flags:-g
187
// gdb-command:run
198

20-
// gdb-command:print first
9+
// gdb-command:print *first
2110
// gdbg-check:$1 = {<No data fields>}
2211
// gdbr-check:$1 = <error reading variable>
2312

24-
// gdb-command:print second
25-
// gdbg-check:$2 = {<No data fields>}
26-
// gdbr-check:$2 = <error reading variable>
27-
2813
#![allow(unused_variables)]
2914
#![feature(omit_gdb_pretty_printer_section)]
30-
#![feature(maybe_uninit)]
3115
#![omit_gdb_pretty_printer_section]
3216

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

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.
4119
fn main() {
42-
unsafe {
43-
let first: ANilEnum = MaybeUninit::uninitialized().into_inner();
44-
let second: AnotherNilEnum = MaybeUninit::uninitialized().into_inner();
20+
let first: *const Void = 1 as *const _;
4521

46-
zzz(); // #break
47-
}
22+
zzz(); // #break
4823
}
4924

50-
fn zzz() {()}
25+
fn zzz() {}

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

+2-2
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

+1-25
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)