Auto merge of #36449 - canndrew:expand_is_uninhabited, r=eddyb
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?
This commit is contained in:
commit
d515586465
@ -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;
|
||||
@ -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 }
|
||||
|
||||
@ -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
|
||||
@ -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,
|
||||
@ -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)]
|
||||
|
@ -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;
|
||||
|
||||
@ -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,
|
||||
}
|
||||
}
|
||||
|
@ -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 {} \
|
||||
|
Loading…
x
Reference in New Issue
Block a user