Skip to content

Commit 3f7c718

Browse files
committed
Auto 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 09d6ab9 + 0a8b696 commit 3f7c718

File tree

16 files changed

+157
-105
lines changed

16 files changed

+157
-105
lines changed

src/librustc/cfg/construct.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -415,8 +415,8 @@ 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+
let m = self.tcx.hir().get_module_parent(call_expr.id);
419+
if self.tcx.is_ty_uninhabited_from(m, self.tables.expr_ty(call_expr)) {
420420
self.add_unreachable_node()
421421
} else {
422422
ret

src/librustc/middle/liveness.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1197,8 +1197,8 @@ 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 m = self.ir.tcx.hir().get_module_parent(expr.id);
1201+
let succ = if self.ir.tcx.is_ty_uninhabited_from(m, self.tables.expr_ty(expr)) {
12021202
self.s.exit_ln
12031203
} else {
12041204
succ
@@ -1208,8 +1208,8 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
12081208
}
12091209

12101210
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() {
1211+
let m = self.ir.tcx.hir().get_module_parent(expr.id);
1212+
let succ = if self.ir.tcx.is_ty_uninhabited_from(m, self.tables.expr_ty(expr)) {
12131213
self.s.exit_ln
12141214
} else {
12151215
succ

src/librustc/ty/layout.rs

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

192192
ty::tls::enter_context(&icx, |_| {
193193
let cx = LayoutCx { tcx, param_env };
194-
cx.layout_raw_uncached(ty)
194+
let layout = cx.layout_raw_uncached(ty);
195+
// Type-level uninhabitedness should always imply ABI uninhabitedness.
196+
if let Ok(layout) = layout {
197+
if ty.conservative_is_privately_uninhabited(tcx) {
198+
assert!(layout.abi.is_uninhabited());
199+
}
200+
}
201+
layout
195202
})
196203
})
197204
}
@@ -205,12 +212,11 @@ pub fn provide(providers: &mut ty::query::Providers<'_>) {
205212

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();
@@ -551,13 +557,19 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> {
551557
let size = element.size.checked_mul(count, dl)
552558
.ok_or(LayoutError::SizeOverflow(ty))?;
553559

560+
let abi = if count != 0 && ty.conservative_is_privately_uninhabited(tcx) {
561+
Abi::Uninhabited
562+
} else {
563+
Abi::Aggregate { sized: true }
564+
};
565+
554566
tcx.intern_layout(LayoutDetails {
555567
variants: Variants::Single { index: VariantIdx::new(0) },
556568
fields: FieldPlacement::Array {
557569
stride: element.size,
558570
count
559571
},
560-
abi: Abi::Aggregate { sized: true },
572+
abi,
561573
align: element.align,
562574
size
563575
})

src/librustc/ty/sty.rs

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1543,6 +1543,51 @@ impl<'a, 'gcx, 'tcx> TyS<'tcx> {
15431543
}
15441544
}
15451545

1546+
/// Checks whether a type is definitely uninhabited. This is
1547+
/// conservative: for some types that are uninhabited we return `false`,
1548+
/// but we only return `true` for types that are definitely uninhabited.
1549+
/// `ty.conservative_is_privately_uninhabited` implies that any value of type `ty`
1550+
/// will be `Abi::Uninhabited`. (Note that uninhabited types may have nonzero
1551+
/// size, to account for partial initialisation. See #49298 for details.)
1552+
pub fn conservative_is_privately_uninhabited(&self, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> bool {
1553+
// FIXME(varkor): we can make this less conversative by substituting concrete
1554+
// type arguments.
1555+
match self.sty {
1556+
ty::Never => true,
1557+
ty::Adt(def, _) if def.is_union() => {
1558+
// For now, `union`s are never considered uninhabited.
1559+
false
1560+
}
1561+
ty::Adt(def, _) => {
1562+
// Any ADT is uninhabited if either:
1563+
// (a) It has no variants (i.e. an empty `enum`);
1564+
// (b) Each of its variants (a single one in the case of a `struct`) has at least
1565+
// one uninhabited field.
1566+
def.variants.iter().all(|var| {
1567+
var.fields.iter().any(|field| {
1568+
tcx.type_of(field.did).conservative_is_privately_uninhabited(tcx)
1569+
})
1570+
})
1571+
}
1572+
ty::Tuple(tys) => tys.iter().any(|ty| ty.conservative_is_privately_uninhabited(tcx)),
1573+
ty::Array(ty, len) => {
1574+
match len.assert_usize(tcx) {
1575+
// If the array is definitely non-empty, it's uninhabited if
1576+
// the type of its elements is uninhabited.
1577+
Some(n) if n != 0 => ty.conservative_is_privately_uninhabited(tcx),
1578+
_ => false
1579+
}
1580+
}
1581+
ty::Ref(..) => {
1582+
// References to uninitialised memory is valid for any type, including
1583+
// uninhabited types, in unsafe code, so we treat all references as
1584+
// inhabited.
1585+
false
1586+
}
1587+
_ => false,
1588+
}
1589+
}
1590+
15461591
pub fn is_primitive(&self) -> bool {
15471592
match self.sty {
15481593
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
@@ -1545,8 +1545,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
15451545
}
15461546
}
15471547
None => {
1548-
// FIXME(canndrew): This is_never should probably be an is_uninhabited
1549-
if !sig.output().is_never() {
1548+
if !sig.output().conservative_is_privately_uninhabited(self.tcx()) {
15501549
span_mirbug!(self, term, "call to converging function {:?} w/o dest", sig);
15511550
}
15521551
}

src/librustc_mir/build/expr/into.rs

Lines changed: 5 additions & 5 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,10 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
332330
func: fun,
333331
args,
334332
cleanup: Some(cleanup),
335-
destination: if diverges {
333+
// FIXME(varkor): replace this with an uninhabitedness-based check.
334+
// This requires getting access to the current module to call
335+
// `tcx.is_ty_uninhabited_from`, which is currently tricky to do.
336+
destination: if expr.ty.is_never() {
336337
None
337338
} else {
338339
Some((destination.clone(), success))
@@ -421,8 +422,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
421422
});
422423

423424
let rvalue = unpack!(block = this.as_local_rvalue(block, expr));
424-
this.cfg
425-
.push_assign(block, source_info, destination, rvalue);
425+
this.cfg.push_assign(block, source_info, destination, rvalue);
426426
block.unit()
427427
}
428428
};

src/librustc_mir/hair/pattern/check_match.rs

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,11 @@ impl<'a, 'tcx> MatchVisitor<'a, 'tcx> {
230230
let scrutinee_is_uninhabited = if self.tcx.features().exhaustive_patterns {
231231
self.tcx.is_ty_uninhabited_from(module, pat_ty)
232232
} else {
233-
self.conservative_is_uninhabited(pat_ty)
233+
match pat_ty.sty {
234+
ty::Never => true,
235+
ty::Adt(def, _) => def.variants.is_empty(),
236+
_ => false
237+
}
234238
};
235239
if !scrutinee_is_uninhabited {
236240
// We know the type is inhabited, so this must be wrong
@@ -258,15 +262,6 @@ impl<'a, 'tcx> MatchVisitor<'a, 'tcx> {
258262
})
259263
}
260264

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

src/libstd/net/tcp.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -729,6 +729,9 @@ impl TcpListener {
729729
/// ```
730730
#[stable(feature = "rust1", since = "1.0.0")]
731731
pub fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> {
732+
// On WASM, `TcpStream` is uninhabited (as it's unsupported) and so
733+
// the `a` variable here is technically unused.
734+
#[cfg_attr(target_arch = "wasm32", allow(unused_variables))]
732735
self.0.accept().map(|(a, b)| (TcpStream(a), b))
733736
}
734737

src/test/debuginfo/nil-enum.rs

Lines changed: 0 additions & 55 deletions
This file was deleted.

src/test/mir-opt/uninhabited-enum.rs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
#![feature(never_type)]
2+
3+
pub enum Void {}
4+
5+
#[no_mangle]
6+
pub fn process_never(input: *const !) {
7+
let _input = unsafe { &*input };
8+
}
9+
10+
#[no_mangle]
11+
pub fn process_void(input: *const Void) {
12+
let _input = unsafe { &*input };
13+
// In the future, this should end with `unreachable`, but we currently only do
14+
// unreachability analysis for `!`.
15+
}
16+
17+
fn main() {}
18+
19+
// END RUST SOURCE
20+
//
21+
// START rustc.process_never.SimplifyLocals.after.mir
22+
// bb0: {
23+
// StorageLive(_2);
24+
// _2 = &(*_1);
25+
// StorageDead(_2);
26+
// unreachable;
27+
// }
28+
// END rustc.process_never.SimplifyLocals.after.mir
29+
//
30+
// START rustc.process_void.SimplifyLocals.after.mir
31+
// bb0: {
32+
// StorageLive(_2);
33+
// _2 = &(*_1);
34+
// StorageDead(_2);
35+
// return;
36+
// }
37+
// END rustc.process_void.SimplifyLocals.after.mir

0 commit comments

Comments
 (0)