Skip to content

Commit

Permalink
Auto merge of #54125 - varkor:less-conservative-uninhabitedness-check…
Browse files Browse the repository at this point in the history
…, 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
  • Loading branch information
bors committed Sep 26, 2018
2 parents c3a1a0d + 1abd0d8 commit 5cad9f0
Show file tree
Hide file tree
Showing 12 changed files with 61 additions and 80 deletions.
3 changes: 1 addition & 2 deletions src/librustc/cfg/construct.rs
Original file line number Diff line number Diff line change
Expand Up @@ -415,8 +415,7 @@ impl<'a, 'tcx> CFGBuilder<'a, 'tcx> {
args: I) -> CFGIndex {
let func_or_rcvr_exit = self.expr(func_or_rcvr, pred);
let ret = self.straightline(call_expr, func_or_rcvr_exit, args);
// FIXME(canndrew): This is_never should probably be an is_uninhabited.
if self.tables.expr_ty(call_expr).is_never() {
if self.tables.expr_ty(call_expr).conservative_is_uninhabited(self.tcx) {
self.add_unreachable_node()
} else {
ret
Expand Down
6 changes: 2 additions & 4 deletions src/librustc/middle/liveness.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1209,8 +1209,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
}

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

hir::ExprKind::MethodCall(.., ref args) => {
// FIXME(canndrew): This is_never should really be an is_uninhabited
let succ = if self.tables.expr_ty(expr).is_never() {
let succ = if self.tables.expr_ty(expr).conservative_is_uninhabited(self.ir.tcx) {
self.s.exit_ln
} else {
succ
Expand Down
40 changes: 40 additions & 0 deletions src/librustc/ty/sty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1489,6 +1489,46 @@ impl<'a, 'gcx, 'tcx> TyS<'tcx> {
}
}

pub fn conservative_is_uninhabited(&self, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> bool {
// Checks whether a type is definitely uninhabited. This is
// conservative: for some types that are uninhabited we return `false`,
// but we only return `true` for types that are definitely uninhabited.
match self.sty {
ty::Never => true,
ty::Adt(def, _) if def.is_union() => {
// For now, `union`s are never considered uninhabited.
false
}
ty::Adt(def, _) => {
// Any ADT is uninhabited if either:
// (a) It has no variants (i.e. an empty `enum`);
// (b) Each of its variants (a single one in the case of a `struct`) has at least
// one uninhabited field.
def.variants.iter().all(|var| {
var.fields.iter().any(|field| {
tcx.type_of(field.did).conservative_is_uninhabited(tcx)
})
})
}
ty::Tuple(tys) => tys.iter().any(|ty| ty.conservative_is_uninhabited(tcx)),
ty::Array(ty, len) => {
match len.val.try_to_scalar() {
// If the array is definitely non-empty, it's uninhabited if
// the type of its elements is uninhabited.
Some(n) if !n.is_null() => ty.conservative_is_uninhabited(tcx),
_ => false
}
}
ty::Ref(..) => {
// References to uninitialised memory is valid for any type, including
// uninhabited types, in unsafe code, so we treat all references as
// inhabited.
false
}
_ => false,
}
}

pub fn is_primitive(&self) -> bool {
match self.sty {
Bool | Char | Int(_) | Uint(_) | Float(_) => true,
Expand Down
2 changes: 2 additions & 0 deletions src/librustc_codegen_llvm/debuginfo/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,8 @@ pub fn create_function_debug_context(
}
None => {}
};

// Tell LLVM that functions that return uninhabited types will not return.
if cx.layout_of(sig.output()).abi.is_uninhabited() {
flags = flags | DIFlags::FlagNoReturn;
}
Expand Down
3 changes: 1 addition & 2 deletions src/librustc_mir/borrow_check/nll/type_check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1390,8 +1390,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
}
}
None => {
// FIXME(canndrew): This is_never should probably be an is_uninhabited
if !sig.output().is_never() {
if !sig.output().conservative_is_uninhabited(self.tcx()) {
span_mirbug!(self, term, "call to converging function {:?} w/o dest", sig);
}
}
Expand Down
4 changes: 1 addition & 3 deletions src/librustc_mir/build/expr/into.rs
Original file line number Diff line number Diff line change
Expand Up @@ -265,8 +265,6 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
exit_block.unit()
}
ExprKind::Call { ty, fun, args } => {
// FIXME(canndrew): This is_never should probably be an is_uninhabited
let diverges = expr.ty.is_never();
let intrinsic = match ty.sty {
ty::FnDef(def_id, _) => {
let f = ty.fn_sig(this.hir.tcx());
Expand Down Expand Up @@ -321,7 +319,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
func: fun,
args,
cleanup: Some(cleanup),
destination: if diverges {
destination: if expr.ty.conservative_is_uninhabited(this.hir.tcx()) {
None
} else {
Some((destination.clone(), success))
Expand Down
11 changes: 1 addition & 10 deletions src/librustc_mir/hair/pattern/check_match.rs
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,7 @@ impl<'a, 'tcx> MatchVisitor<'a, 'tcx> {
let scrutinee_is_uninhabited = if self.tcx.features().exhaustive_patterns {
self.tcx.is_ty_uninhabited_from(module, pat_ty)
} else {
self.conservative_is_uninhabited(pat_ty)
pat_ty.conservative_is_uninhabited(self.tcx)
};
if !scrutinee_is_uninhabited {
// We know the type is inhabited, so this must be wrong
Expand Down Expand Up @@ -257,15 +257,6 @@ impl<'a, 'tcx> MatchVisitor<'a, 'tcx> {
})
}

fn conservative_is_uninhabited(&self, scrutinee_ty: Ty<'tcx>) -> bool {
// "rustc-1.0-style" uncontentious uninhabitableness check
match scrutinee_ty.sty {
ty::Never => true,
ty::Adt(def, _) => def.variants.is_empty(),
_ => false
}
}

fn check_irrefutable(&self, pat: &'tcx Pat, origin: &str) {
let module = self.tcx.hir.get_module_parent(pat.id);
MatchCheckCtxt::create_and_enter(self.tcx, module, |ref mut cx| {
Expand Down
1 change: 1 addition & 0 deletions src/libstd/net/tcp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -729,6 +729,7 @@ impl TcpListener {
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
pub fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> {
#[allow(unused_variables)] // `TcpStream` is uninhabited on some platforms.
self.0.accept().map(|(a, b)| (TcpStream(a), b))
}

Expand Down
2 changes: 2 additions & 0 deletions src/libstd/sys_common/process.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ impl<K: EnvKey> CommandEnv<K> {
pub fn capture(&self) -> BTreeMap<K, OsString> {
let mut result = BTreeMap::<K, OsString>::new();
if !self.clear {
#[allow(unused_mut)] // `Env` is uninhabited on some platforms.
for (k, v) in env::vars_os() {
result.insert(k.into(), v);
}
Expand All @@ -83,6 +84,7 @@ impl<K: EnvKey> CommandEnv<K> {
// Apply these changes directly to the current environment
pub fn apply(&self) {
if self.clear {
#[allow(unused_mut)] // `Env` is uninhabited on some platforms.
for (k, _) in env::vars_os() {
env::remove_var(k);
}
Expand Down
39 changes: 7 additions & 32 deletions src/test/debuginfo/nil-enum.rs
Original file line number Diff line number Diff line change
@@ -1,50 +1,25 @@
// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// NOTE Instantiating an empty enum is UB. This test may break in the future.

// LLDB can't handle zero-sized values
//
// LLDB can't handle zero-sized values.
// ignore-lldb


// compile-flags:-g
// gdb-command:run

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

// gdb-command:print second
// gdbg-check:$2 = {<No data fields>}
// gdbr-check:$2 = <error reading variable>

#![allow(unused_variables)]
#![feature(omit_gdb_pretty_printer_section)]
#![feature(maybe_uninit)]
#![omit_gdb_pretty_printer_section]

use std::mem::MaybeUninit;

enum ANilEnum {}
enum AnotherNilEnum {}
enum Void {}

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

zzz(); // #break
}
zzz(); // #break
}

fn zzz() {()}
fn zzz() {}
4 changes: 2 additions & 2 deletions src/test/ui/uninhabited/uninhabited-matches-feature-gated.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@ fn main() {
let _ = match x {}; //~ ERROR non-exhaustive

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

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

let x: &[Void] = unsafe { std::mem::uninitialized() };
let _ = match x { //~ ERROR non-exhaustive
Expand Down
26 changes: 1 addition & 25 deletions src/test/ui/uninhabited/uninhabited-matches-feature-gated.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -16,30 +16,6 @@ help: Please ensure that all possible cases are being handled; possibly adding w
LL | let _ = match x {}; //~ ERROR non-exhaustive
| ^

error[E0004]: non-exhaustive patterns: type (Void,) is non-empty
--> $DIR/uninhabited-matches-feature-gated.rs:23:19
|
LL | let _ = match x {}; //~ ERROR non-exhaustive
| ^
|
help: Please ensure that all possible cases are being handled; possibly adding wildcards or more match arms.
--> $DIR/uninhabited-matches-feature-gated.rs:23:19
|
LL | let _ = match x {}; //~ ERROR non-exhaustive
| ^

error[E0004]: non-exhaustive patterns: type [Void; 1] is non-empty
--> $DIR/uninhabited-matches-feature-gated.rs:26:19
|
LL | let _ = match x {}; //~ ERROR non-exhaustive
| ^
|
help: Please ensure that all possible cases are being handled; possibly adding wildcards or more match arms.
--> $DIR/uninhabited-matches-feature-gated.rs:26:19
|
LL | let _ = match x {}; //~ ERROR non-exhaustive
| ^

error[E0004]: non-exhaustive patterns: `&[_]` not covered
--> $DIR/uninhabited-matches-feature-gated.rs:29:19
|
Expand All @@ -58,7 +34,7 @@ error[E0005]: refutable pattern in local binding: `Err(_)` not covered
LL | let Ok(x) = x;
| ^^^^^ pattern `Err(_)` not covered

error: aborting due to 7 previous errors
error: aborting due to 5 previous errors

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

0 comments on commit 5cad9f0

Please sign in to comment.