Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expand is_uninhabited #36449

Merged
merged 9 commits into from
Nov 23, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -1389,6 +1389,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 @@ -1531,11 +1547,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 @@ -1795,6 +1806,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))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This assumes an union is always one of its members. Did we ever settle this? cc @petrochenkov

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@eddyb
This is certainly a desirable property, but it can be violated right now, see example in #32836 (comment). So it's not settled yet.
However, the logic "all fields are uninhabitable" -> "union is uninhabitable" seems to be correct - a union needs to be initialized before use (move checker ensures this) and it can't be initialized if it only has uninhabited fields.

} 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 @@ -1820,6 +1847,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