Skip to content

Commit 3f7c718

Browse files
committed
Auto merge of #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 #47291 and #50262. Fixes #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

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

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

+17-5
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

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

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

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

+5-10
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

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

-55
This file was deleted.

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

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

src/test/run-pass/binding/empty-types-in-patterns.rs

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#![feature(slice_patterns)]
1515
#![allow(unreachable_patterns)]
1616
#![allow(unreachable_code)]
17+
#![allow(unused_variables)]
1718

1819
#[allow(dead_code)]
1920
fn foo(z: !) {

src/test/run-pass/issues/issue-41696.rs

+1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
// run-pass
1212
#![allow(dead_code)]
1313
#![allow(unused_variables)]
14+
#![recursion_limit = "128"]
1415
// this used to cause exponential code-size blowup during LLVM passes.
1516

1617
#![feature(test)]
+8-14
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,3 @@
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
#![allow(const_err)] // make sure we cannot allow away the errors tested here
133

@@ -16,14 +6,18 @@ use std::mem;
166
#[derive(Copy, Clone)]
177
enum Bar {}
188

19-
const BAD_BAD_BAD: Bar = unsafe { mem::transmute(()) };
9+
union TransmuteUnion<A: Clone + Copy, B: Clone + Copy> {
10+
a: A,
11+
b: B,
12+
}
13+
14+
const BAD_BAD_BAD: Bar = unsafe { (TransmuteUnion::<(), Bar> { a: () }).b };
2015
//~^ ERROR it is undefined behavior to use this value
2116

2217
const BAD_BAD_REF: &Bar = unsafe { mem::transmute(1usize) };
2318
//~^ ERROR it is undefined behavior to use this value
2419

25-
const BAD_BAD_ARRAY: [Bar; 1] = unsafe { mem::transmute(()) };
20+
const BAD_BAD_ARRAY: [Bar; 1] = unsafe { (TransmuteUnion::<(), [Bar; 1]> { a: () }).b };
2621
//~^ ERROR it is undefined behavior to use this value
2722

28-
fn main() {
29-
}
23+
fn main() {}

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

+7-7
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,24 @@
11
error[E0080]: it is undefined behavior to use this value
2-
--> $DIR/ub-uninhabit.rs:19:1
2+
--> $DIR/ub-uninhabit.rs:14: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]: it is undefined behavior to use this value
10-
--> $DIR/ub-uninhabit.rs:22:1
10+
--> $DIR/ub-uninhabit.rs:17: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]: it is undefined behavior to use this value
18-
--> $DIR/ub-uninhabit.rs:25:1
18+
--> $DIR/ub-uninhabit.rs:20: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

0 commit comments

Comments
 (0)