Skip to content

Commit

Permalink
Auto merge of #36449 - canndrew:expand_is_uninhabited, r=eddyb
Browse files Browse the repository at this point in the history
Expand is_uninhabited

This allows code such as this to compile:

``` rust
let x: ! = ...;
match x {};

let y: (u32, !) = ...;
match y {};
```

@eddyb You were worried about making this change. Do you have any idea about what could break? Are there any special tests that need to be written for it?
  • Loading branch information
bors authored Nov 23, 2016
2 parents 127a83d + 2121118 commit d515586
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 18 deletions.
51 changes: 45 additions & 6 deletions src/librustc/ty/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ use ty::subst::{Subst, Substs};
use ty::walk::TypeWalker;
use util::common::MemoizationMap;
use util::nodemap::NodeSet;
use util::nodemap::FxHashMap;
use util::nodemap::{FxHashMap, FxHashSet};

use serialize::{self, Encodable, Encoder};
use std::borrow::Cow;
Expand Down Expand Up @@ -1390,6 +1390,22 @@ impl<'tcx> serialize::UseSpecializedEncodable for AdtDef<'tcx> {

impl<'tcx> serialize::UseSpecializedDecodable for AdtDef<'tcx> {}

impl<'a, 'gcx, 'tcx> AdtDefData<'tcx, 'static> {
#[inline]
pub fn is_uninhabited_recurse(&'tcx self,
visited: &mut FxHashSet<(DefId, &'tcx Substs<'tcx>)>,
block: Option<NodeId>,
cx: TyCtxt<'a, 'gcx, 'tcx>,
substs: &'tcx Substs<'tcx>) -> bool {
if !visited.insert((self.did, substs)) {
return false;
};
self.variants.iter().all(|v| {
v.is_uninhabited_recurse(visited, block, cx, substs, self.is_union())
})
}
}

#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum AdtKind { Struct, Union, Enum }

Expand Down Expand Up @@ -1532,11 +1548,6 @@ impl<'a, 'gcx, 'tcx, 'container> AdtDefData<'gcx, 'container> {
self.variants.iter().flat_map(VariantDefData::fields_iter)
}

#[inline]
pub fn is_empty(&self) -> bool {
self.variants.is_empty()
}

#[inline]
pub fn is_univariant(&self) -> bool {
self.variants.len() == 1
Expand Down Expand Up @@ -1796,6 +1807,22 @@ impl<'tcx, 'container> VariantDefData<'tcx, 'container> {
}
}

impl<'a, 'gcx, 'tcx> VariantDefData<'tcx, 'static> {
#[inline]
pub fn is_uninhabited_recurse(&'tcx self,
visited: &mut FxHashSet<(DefId, &'tcx Substs<'tcx>)>,
block: Option<NodeId>,
cx: TyCtxt<'a, 'gcx, 'tcx>,
substs: &'tcx Substs<'tcx>,
is_union: bool) -> bool {
if is_union {
self.fields.iter().all(|f| f.is_uninhabited_recurse(visited, block, cx, substs))
} else {
self.fields.iter().any(|f| f.is_uninhabited_recurse(visited, block, cx, substs))
}
}
}

impl<'a, 'gcx, 'tcx, 'container> FieldDefData<'tcx, 'container> {
pub fn new(did: DefId,
name: Name,
Expand All @@ -1821,6 +1848,18 @@ impl<'a, 'gcx, 'tcx, 'container> FieldDefData<'tcx, 'container> {
}
}

impl<'a, 'gcx, 'tcx> FieldDefData<'tcx, 'static> {
#[inline]
pub fn is_uninhabited_recurse(&'tcx self,
visited: &mut FxHashSet<(DefId, &'tcx Substs<'tcx>)>,
block: Option<NodeId>,
tcx: TyCtxt<'a, 'gcx, 'tcx>,
substs: &'tcx Substs<'tcx>) -> bool {
block.map_or(true, |b| self.vis.is_accessible_from(b, &tcx.map)) &&
self.ty(tcx, substs).is_uninhabited_recurse(visited, block, tcx)
}
}

/// Records the substitutions used to translate the polytype for an
/// item into the monotype of an item reference.
#[derive(Clone, RustcEncodable, RustcDecodable)]
Expand Down
31 changes: 20 additions & 11 deletions src/librustc/ty/sty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,9 @@ use collections::enum_set::{self, EnumSet, CLike};
use std::fmt;
use std::ops;
use syntax::abi;
use syntax::ast::{self, Name};
use syntax::ast::{self, Name, NodeId};
use syntax::symbol::{keywords, InternedString};
use util::nodemap::FxHashSet;

use serialize;

Expand Down Expand Up @@ -929,19 +930,27 @@ impl<'a, 'gcx, 'tcx> TyS<'tcx> {
}
}

pub fn is_uninhabited(&self, _cx: TyCtxt) -> bool {
// FIXME(#24885): be smarter here, the AdtDefData::is_empty method could easily be made
// more complete.
/// Checks whether a type is uninhabited.
/// If `block` is `Some(id)` it also checks that the uninhabited-ness is visible from `id`.
pub fn is_uninhabited(&self, block: Option<NodeId>, cx: TyCtxt<'a, 'gcx, 'tcx>) -> bool {
let mut visited = FxHashSet::default();
self.is_uninhabited_recurse(&mut visited, block, cx)
}

pub fn is_uninhabited_recurse(&self,
visited: &mut FxHashSet<(DefId, &'tcx Substs<'tcx>)>,
block: Option<NodeId>,
cx: TyCtxt<'a, 'gcx, 'tcx>) -> bool {
match self.sty {
TyAdt(def, _) => def.is_empty(),
TyAdt(def, substs) => {
def.is_uninhabited_recurse(visited, block, cx, substs)
},

// FIXME(canndrew): There's no reason why these can't be uncommented, they're tested
// and they don't break anything. But I'm keeping my changes small for now.
//TyNever => true,
//TyTuple(ref tys) => tys.iter().any(|ty| ty.is_uninhabited(cx)),
TyNever => true,
TyTuple(ref tys) => tys.iter().any(|ty| ty.is_uninhabited_recurse(visited, block, cx)),
TyArray(ty, len) => len > 0 && ty.is_uninhabited_recurse(visited, block, cx),
TyRef(_, ref tm) => tm.ty.is_uninhabited_recurse(visited, block, cx),

// FIXME(canndrew): this line breaks core::fmt
//TyRef(_, ref tm) => tm.ty.is_uninhabited(cx),
_ => false,
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/librustc_const_eval/check_match.rs
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ impl<'a, 'tcx> MatchVisitor<'a, 'tcx> {
// Check for empty enum, because is_useful only works on inhabited types.
let pat_ty = self.tcx.tables().node_id_to_type(scrut.id);
if inlined_arms.is_empty() {
if !pat_ty.is_uninhabited(self.tcx) {
if !pat_ty.is_uninhabited(Some(scrut.id), self.tcx) {
// We know the type is inhabited, so this must be wrong
let mut err = create_e0004(self.tcx.sess, span,
format!("non-exhaustive patterns: type {} \
Expand Down

0 comments on commit d515586

Please sign in to comment.