Make uninhabitedness checking more intelligent

This commit is contained in:
varkor 2018-09-11 13:04:21 +01:00
parent 62b359094f
commit 88afbf2d99
6 changed files with 35 additions and 9 deletions

View File

@ -415,7 +415,7 @@ impl<'a, 'tcx> CFGBuilder<'a, 'tcx> {
args: I) -> CFGIndex { args: I) -> CFGIndex {
let func_or_rcvr_exit = self.expr(func_or_rcvr, pred); let func_or_rcvr_exit = self.expr(func_or_rcvr, pred);
let ret = self.straightline(call_expr, func_or_rcvr_exit, args); let ret = self.straightline(call_expr, func_or_rcvr_exit, args);
if self.tables.expr_ty(call_expr).conservative_is_uninhabited() { if self.tables.expr_ty(call_expr).conservative_is_uninhabited(self.tcx) {
self.add_unreachable_node() self.add_unreachable_node()
} else { } else {
ret ret

View File

@ -1197,7 +1197,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
} }
hir::ExprKind::Call(ref f, ref args) => { hir::ExprKind::Call(ref f, ref args) => {
let succ = if self.tables.expr_ty(expr).conservative_is_uninhabited() { let succ = if self.tables.expr_ty(expr).conservative_is_uninhabited(self.ir.tcx) {
self.s.exit_ln self.s.exit_ln
} else { } else {
succ succ
@ -1207,7 +1207,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
} }
hir::ExprKind::MethodCall(.., ref args) => { hir::ExprKind::MethodCall(.., ref args) => {
let succ = if self.tables.expr_ty(expr).conservative_is_uninhabited() { let succ = if self.tables.expr_ty(expr).conservative_is_uninhabited(self.ir.tcx) {
self.s.exit_ln self.s.exit_ln
} else { } else {
succ succ

View File

@ -1543,14 +1543,40 @@ impl<'a, 'gcx, 'tcx> TyS<'tcx> {
} }
} }
pub fn conservative_is_uninhabited(&self) -> bool { pub fn conservative_is_uninhabited(&self, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> bool {
// Checks whether a type is definitely uninhabited. This is // Checks whether a type is definitely uninhabited. This is
// conservative: for some types that are uninhabited we return `false`, // conservative: for some types that are uninhabited we return `false`,
// but we only return `true` for types that are definitely uninhabited. // but we only return `true` for types that are definitely uninhabited.
match self.sty { match self.sty {
ty::Never => true, ty::Never => true,
ty::Adt(def, _) => def.variants.is_empty(), ty::Adt(def, _) => {
_ => false // Any ADT is uninhabited if:
// (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(..) => {
// Though references to uninhabited types are trivially uninhabited
// theoretically, null references are permitted in unsafe code (as
// long as the value is not dereferenced), so we treat all references
// as inhabited.
false
}
_ => false,
} }
} }

View File

@ -1546,7 +1546,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
} }
} }
None => { None => {
if !sig.output().conservative_is_uninhabited() { if !sig.output().conservative_is_uninhabited(self.tcx()) {
span_mirbug!(self, term, "call to converging function {:?} w/o dest", sig); span_mirbug!(self, term, "call to converging function {:?} w/o dest", sig);
} }
} }

View File

@ -331,7 +331,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
func: fun, func: fun,
args, args,
cleanup: Some(cleanup), cleanup: Some(cleanup),
destination: if expr.ty.conservative_is_uninhabited() { destination: if expr.ty.conservative_is_uninhabited(this.hir.tcx()) {
None None
} else { } else {
Some((destination.clone(), success)) Some((destination.clone(), success))

View File

@ -230,7 +230,7 @@ impl<'a, 'tcx> MatchVisitor<'a, 'tcx> {
let scrutinee_is_uninhabited = if self.tcx.features().exhaustive_patterns { let scrutinee_is_uninhabited = if self.tcx.features().exhaustive_patterns {
self.tcx.is_ty_uninhabited_from(module, pat_ty) self.tcx.is_ty_uninhabited_from(module, pat_ty)
} else { } else {
pat_ty.conservative_is_uninhabited() pat_ty.conservative_is_uninhabited(self.tcx)
}; };
if !scrutinee_is_uninhabited { if !scrutinee_is_uninhabited {
// We know the type is inhabited, so this must be wrong // We know the type is inhabited, so this must be wrong