Skip to content

Commit d515586

Browse files
authored
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?
2 parents 127a83d + 2121118 commit d515586

File tree

3 files changed

+66
-18
lines changed

3 files changed

+66
-18
lines changed

src/librustc/ty/mod.rs

+45-6
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ use ty::subst::{Subst, Substs};
3030
use ty::walk::TypeWalker;
3131
use util::common::MemoizationMap;
3232
use util::nodemap::NodeSet;
33-
use util::nodemap::FxHashMap;
33+
use util::nodemap::{FxHashMap, FxHashSet};
3434

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

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

1393+
impl<'a, 'gcx, 'tcx> AdtDefData<'tcx, 'static> {
1394+
#[inline]
1395+
pub fn is_uninhabited_recurse(&'tcx self,
1396+
visited: &mut FxHashSet<(DefId, &'tcx Substs<'tcx>)>,
1397+
block: Option<NodeId>,
1398+
cx: TyCtxt<'a, 'gcx, 'tcx>,
1399+
substs: &'tcx Substs<'tcx>) -> bool {
1400+
if !visited.insert((self.did, substs)) {
1401+
return false;
1402+
};
1403+
self.variants.iter().all(|v| {
1404+
v.is_uninhabited_recurse(visited, block, cx, substs, self.is_union())
1405+
})
1406+
}
1407+
}
1408+
13931409
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
13941410
pub enum AdtKind { Struct, Union, Enum }
13951411

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

1535-
#[inline]
1536-
pub fn is_empty(&self) -> bool {
1537-
self.variants.is_empty()
1538-
}
1539-
15401551
#[inline]
15411552
pub fn is_univariant(&self) -> bool {
15421553
self.variants.len() == 1
@@ -1796,6 +1807,22 @@ impl<'tcx, 'container> VariantDefData<'tcx, 'container> {
17961807
}
17971808
}
17981809

1810+
impl<'a, 'gcx, 'tcx> VariantDefData<'tcx, 'static> {
1811+
#[inline]
1812+
pub fn is_uninhabited_recurse(&'tcx self,
1813+
visited: &mut FxHashSet<(DefId, &'tcx Substs<'tcx>)>,
1814+
block: Option<NodeId>,
1815+
cx: TyCtxt<'a, 'gcx, 'tcx>,
1816+
substs: &'tcx Substs<'tcx>,
1817+
is_union: bool) -> bool {
1818+
if is_union {
1819+
self.fields.iter().all(|f| f.is_uninhabited_recurse(visited, block, cx, substs))
1820+
} else {
1821+
self.fields.iter().any(|f| f.is_uninhabited_recurse(visited, block, cx, substs))
1822+
}
1823+
}
1824+
}
1825+
17991826
impl<'a, 'gcx, 'tcx, 'container> FieldDefData<'tcx, 'container> {
18001827
pub fn new(did: DefId,
18011828
name: Name,
@@ -1821,6 +1848,18 @@ impl<'a, 'gcx, 'tcx, 'container> FieldDefData<'tcx, 'container> {
18211848
}
18221849
}
18231850

1851+
impl<'a, 'gcx, 'tcx> FieldDefData<'tcx, 'static> {
1852+
#[inline]
1853+
pub fn is_uninhabited_recurse(&'tcx self,
1854+
visited: &mut FxHashSet<(DefId, &'tcx Substs<'tcx>)>,
1855+
block: Option<NodeId>,
1856+
tcx: TyCtxt<'a, 'gcx, 'tcx>,
1857+
substs: &'tcx Substs<'tcx>) -> bool {
1858+
block.map_or(true, |b| self.vis.is_accessible_from(b, &tcx.map)) &&
1859+
self.ty(tcx, substs).is_uninhabited_recurse(visited, block, tcx)
1860+
}
1861+
}
1862+
18241863
/// Records the substitutions used to translate the polytype for an
18251864
/// item into the monotype of an item reference.
18261865
#[derive(Clone, RustcEncodable, RustcDecodable)]

src/librustc/ty/sty.rs

+20-11
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,9 @@ use collections::enum_set::{self, EnumSet, CLike};
2222
use std::fmt;
2323
use std::ops;
2424
use syntax::abi;
25-
use syntax::ast::{self, Name};
25+
use syntax::ast::{self, Name, NodeId};
2626
use syntax::symbol::{keywords, InternedString};
27+
use util::nodemap::FxHashSet;
2728

2829
use serialize;
2930

@@ -929,19 +930,27 @@ impl<'a, 'gcx, 'tcx> TyS<'tcx> {
929930
}
930931
}
931932

932-
pub fn is_uninhabited(&self, _cx: TyCtxt) -> bool {
933-
// FIXME(#24885): be smarter here, the AdtDefData::is_empty method could easily be made
934-
// more complete.
933+
/// Checks whether a type is uninhabited.
934+
/// If `block` is `Some(id)` it also checks that the uninhabited-ness is visible from `id`.
935+
pub fn is_uninhabited(&self, block: Option<NodeId>, cx: TyCtxt<'a, 'gcx, 'tcx>) -> bool {
936+
let mut visited = FxHashSet::default();
937+
self.is_uninhabited_recurse(&mut visited, block, cx)
938+
}
939+
940+
pub fn is_uninhabited_recurse(&self,
941+
visited: &mut FxHashSet<(DefId, &'tcx Substs<'tcx>)>,
942+
block: Option<NodeId>,
943+
cx: TyCtxt<'a, 'gcx, 'tcx>) -> bool {
935944
match self.sty {
936-
TyAdt(def, _) => def.is_empty(),
945+
TyAdt(def, substs) => {
946+
def.is_uninhabited_recurse(visited, block, cx, substs)
947+
},
937948

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

943-
// FIXME(canndrew): this line breaks core::fmt
944-
//TyRef(_, ref tm) => tm.ty.is_uninhabited(cx),
945954
_ => false,
946955
}
947956
}

src/librustc_const_eval/check_match.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,7 @@ impl<'a, 'tcx> MatchVisitor<'a, 'tcx> {
204204
// Check for empty enum, because is_useful only works on inhabited types.
205205
let pat_ty = self.tcx.tables().node_id_to_type(scrut.id);
206206
if inlined_arms.is_empty() {
207-
if !pat_ty.is_uninhabited(self.tcx) {
207+
if !pat_ty.is_uninhabited(Some(scrut.id), self.tcx) {
208208
// We know the type is inhabited, so this must be wrong
209209
let mut err = create_e0004(self.tcx.sess, span,
210210
format!("non-exhaustive patterns: type {} \

0 commit comments

Comments
 (0)