Refactor is_uninhabited
We now cache the inhabitedness of types in the GlobalCtxt. Rather than calculating whether a type is visibly uninhabited from a given NodeId we calculate the full set of NodeIds from which a type is visibly uninhabited then cache that set. We can then use that to answer queries about the inhabitedness of a type relative to any given node.
This commit is contained in:
parent
9482492ab6
commit
7946597f75
@ -33,6 +33,7 @@ use ty::{BareFnTy, InferTy, ParamTy, ProjectionTy, ExistentialPredicate};
|
||||
use ty::{TyVar, TyVid, IntVar, IntVid, FloatVar, FloatVid};
|
||||
use ty::TypeVariants::*;
|
||||
use ty::layout::{Layout, TargetDataLayout};
|
||||
use ty::inhabitedness::NodeForrest;
|
||||
use ty::maps;
|
||||
use util::common::MemoizationMap;
|
||||
use util::nodemap::{NodeMap, NodeSet, DefIdMap, DefIdSet};
|
||||
@ -459,6 +460,8 @@ pub struct GlobalCtxt<'tcx> {
|
||||
// FIXME dep tracking -- should be harmless enough
|
||||
pub normalized_cache: RefCell<FxHashMap<Ty<'tcx>, Ty<'tcx>>>,
|
||||
|
||||
pub inhabitedness_cache: RefCell<FxHashMap<Ty<'tcx>, NodeForrest>>,
|
||||
|
||||
pub lang_items: middle::lang_items::LanguageItems,
|
||||
|
||||
/// Maps from def-id of a type or region parameter to its
|
||||
@ -760,6 +763,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
|
||||
associated_item_def_ids: RefCell::new(DepTrackingMap::new(dep_graph.clone())),
|
||||
ty_param_defs: RefCell::new(NodeMap()),
|
||||
normalized_cache: RefCell::new(FxHashMap()),
|
||||
inhabitedness_cache: RefCell::new(FxHashMap()),
|
||||
lang_items: lang_items,
|
||||
inherent_impls: RefCell::new(DepTrackingMap::new(dep_graph.clone())),
|
||||
used_unsafe: RefCell::new(NodeSet()),
|
||||
|
261
src/librustc/ty/inhabitedness.rs
Normal file
261
src/librustc/ty/inhabitedness.rs
Normal file
@ -0,0 +1,261 @@
|
||||
// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use std::mem;
|
||||
use rustc_data_structures::small_vec::SmallVec;
|
||||
use syntax::ast::{CRATE_NODE_ID, NodeId};
|
||||
use util::nodemap::FxHashSet;
|
||||
use ty::context::TyCtxt;
|
||||
use ty::{AdtDef, VariantDef, FieldDef, TyS};
|
||||
use ty::{DefId, Substs};
|
||||
use ty::{AdtKind, Visibility, NodeIdTree};
|
||||
use ty::TypeVariants::*;
|
||||
|
||||
/// Represents a set of nodes closed under the ancestor relation. That is, if a
|
||||
/// node is in this set then so are all its descendants.
|
||||
#[derive(Clone)]
|
||||
pub struct NodeForrest {
|
||||
/// The minimal set of nodes required to represent the whole set.
|
||||
/// If A and B are nodes in the NodeForrest, and A is a desecendant
|
||||
/// of B, then only B will be in root_nodes.
|
||||
/// We use a SmallVec here because (for its use in this module) its rare
|
||||
/// that this will contain more than one or two nodes.
|
||||
root_nodes: SmallVec<[NodeId; 1]>,
|
||||
}
|
||||
|
||||
impl<'a, 'gcx, 'tcx> NodeForrest {
|
||||
/// Create an empty set.
|
||||
pub fn empty() -> NodeForrest {
|
||||
NodeForrest {
|
||||
root_nodes: SmallVec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a set containing every node.
|
||||
#[inline]
|
||||
pub fn full() -> NodeForrest {
|
||||
NodeForrest::from_node(CRATE_NODE_ID)
|
||||
}
|
||||
|
||||
/// Create a set containing a node and all its descendants.
|
||||
pub fn from_node(node: NodeId) -> NodeForrest {
|
||||
let mut root_nodes = SmallVec::new();
|
||||
root_nodes.push(node);
|
||||
NodeForrest {
|
||||
root_nodes: root_nodes,
|
||||
}
|
||||
}
|
||||
|
||||
/// Test whether the set is empty.
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.root_nodes.is_empty()
|
||||
}
|
||||
|
||||
/// Test whether the set conains a node.
|
||||
pub fn contains(&self,
|
||||
tcx: TyCtxt<'a, 'gcx, 'tcx>,
|
||||
node: NodeId) -> bool
|
||||
{
|
||||
for root_node in self.root_nodes.iter() {
|
||||
if tcx.map.is_descendant_of(node, *root_node) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
/// Calculate the intersection of a collection of sets.
|
||||
pub fn intersection<I>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
|
||||
iter: I) -> NodeForrest
|
||||
where I: IntoIterator<Item=NodeForrest>
|
||||
{
|
||||
let mut ret = NodeForrest::full();
|
||||
let mut next_ret = SmallVec::new();
|
||||
let mut old_ret: SmallVec<[NodeId; 1]> = SmallVec::new();
|
||||
for next_set in iter {
|
||||
for node in ret.root_nodes.drain(..) {
|
||||
if next_set.contains(tcx, node) {
|
||||
next_ret.push(node);
|
||||
} else {
|
||||
old_ret.push(node);
|
||||
}
|
||||
}
|
||||
ret.root_nodes.extend(old_ret.drain(..));
|
||||
|
||||
for node in next_set.root_nodes {
|
||||
if ret.contains(tcx, node) {
|
||||
next_ret.push(node);
|
||||
}
|
||||
}
|
||||
|
||||
mem::swap(&mut next_ret, &mut ret.root_nodes);
|
||||
next_ret.drain(..);
|
||||
}
|
||||
ret
|
||||
}
|
||||
|
||||
/// Calculate the union of a collection of sets.
|
||||
pub fn union<I>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
|
||||
iter: I) -> NodeForrest
|
||||
where I: IntoIterator<Item=NodeForrest>
|
||||
{
|
||||
let mut ret = NodeForrest::empty();
|
||||
let mut next_ret = SmallVec::new();
|
||||
for next_set in iter {
|
||||
for node in ret.root_nodes.drain(..) {
|
||||
if !next_set.contains(tcx, node) {
|
||||
next_ret.push(node);
|
||||
}
|
||||
}
|
||||
|
||||
for node in next_set.root_nodes {
|
||||
if !next_ret.contains(&node) {
|
||||
next_ret.push(node);
|
||||
}
|
||||
}
|
||||
|
||||
mem::swap(&mut next_ret, &mut ret.root_nodes);
|
||||
next_ret.drain(..);
|
||||
}
|
||||
ret
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'gcx, 'tcx> AdtDef {
|
||||
/// Calculate the set of nodes from which this adt is visibly uninhabited.
|
||||
pub fn uninhabited_from(
|
||||
&self,
|
||||
visited: &mut FxHashSet<(DefId, &'tcx Substs<'tcx>)>,
|
||||
tcx: TyCtxt<'a, 'gcx, 'tcx>,
|
||||
substs: &'tcx Substs<'tcx>) -> NodeForrest
|
||||
{
|
||||
if !visited.insert((self.did, substs)) {
|
||||
return NodeForrest::empty();
|
||||
}
|
||||
|
||||
let ret = NodeForrest::intersection(tcx, self.variants.iter().map(|v| {
|
||||
v.uninhabited_from(visited, tcx, substs, self.adt_kind())
|
||||
}));
|
||||
visited.remove(&(self.did, substs));
|
||||
ret
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'gcx, 'tcx> VariantDef {
|
||||
/// Calculate the set of nodes from which this variant is visibly uninhabited.
|
||||
pub fn uninhabited_from(
|
||||
&self,
|
||||
visited: &mut FxHashSet<(DefId, &'tcx Substs<'tcx>)>,
|
||||
tcx: TyCtxt<'a, 'gcx, 'tcx>,
|
||||
substs: &'tcx Substs<'tcx>,
|
||||
adt_kind: AdtKind) -> NodeForrest
|
||||
{
|
||||
match adt_kind {
|
||||
AdtKind::Union => {
|
||||
NodeForrest::intersection(tcx, self.fields.iter().map(|f| {
|
||||
f.uninhabited_from(visited, tcx, substs, false)
|
||||
}))
|
||||
},
|
||||
AdtKind::Struct => {
|
||||
NodeForrest::union(tcx, self.fields.iter().map(|f| {
|
||||
f.uninhabited_from(visited, tcx, substs, false)
|
||||
}))
|
||||
},
|
||||
AdtKind::Enum => {
|
||||
NodeForrest::union(tcx, self.fields.iter().map(|f| {
|
||||
f.uninhabited_from(visited, tcx, substs, true)
|
||||
}))
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'gcx, 'tcx> FieldDef {
|
||||
/// Calculate the set of nodes from which this field is visibly uninhabited.
|
||||
pub fn uninhabited_from(
|
||||
&self,
|
||||
visited: &mut FxHashSet<(DefId, &'tcx Substs<'tcx>)>,
|
||||
tcx: TyCtxt<'a, 'gcx, 'tcx>,
|
||||
substs: &'tcx Substs<'tcx>,
|
||||
is_enum: bool) -> NodeForrest
|
||||
{
|
||||
if let Visibility::PrivateExternal = self.vis {
|
||||
return NodeForrest::empty();
|
||||
}
|
||||
|
||||
let data_inhabitedness = self.ty(tcx, substs).uninhabited_from(visited, tcx);
|
||||
match self.vis {
|
||||
Visibility::Restricted(from) if !is_enum => {
|
||||
let node_set = NodeForrest::from_node(from);
|
||||
let iter = Some(node_set).into_iter().chain(Some(data_inhabitedness));
|
||||
NodeForrest::intersection(tcx, iter)
|
||||
},
|
||||
_ => data_inhabitedness,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'gcx, 'tcx> TyS<'tcx> {
|
||||
/// Calculate the set of nodes from which this type is visibly uninhabited.
|
||||
pub fn uninhabited_from(
|
||||
&self,
|
||||
visited: &mut FxHashSet<(DefId, &'tcx Substs<'tcx>)>,
|
||||
tcx: TyCtxt<'a, 'gcx, 'tcx>) -> NodeForrest
|
||||
{
|
||||
match tcx.lift_to_global(&self) {
|
||||
Some(global_ty) => {
|
||||
{
|
||||
let cache = tcx.inhabitedness_cache.borrow();
|
||||
if let Some(closed_node_set) = cache.get(&global_ty) {
|
||||
return closed_node_set.clone();
|
||||
}
|
||||
}
|
||||
let node_set = global_ty.uninhabited_from_inner(visited, tcx);
|
||||
let mut cache = tcx.inhabitedness_cache.borrow_mut();
|
||||
cache.insert(global_ty, node_set.clone());
|
||||
node_set
|
||||
},
|
||||
None => {
|
||||
let node_set = self.uninhabited_from_inner(visited, tcx);
|
||||
node_set
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn uninhabited_from_inner(
|
||||
&self,
|
||||
visited: &mut FxHashSet<(DefId, &'tcx Substs<'tcx>)>,
|
||||
tcx: TyCtxt<'a, 'gcx, 'tcx>) -> NodeForrest
|
||||
{
|
||||
match self.sty {
|
||||
TyAdt(def, substs) => {
|
||||
def.uninhabited_from(visited, tcx, substs)
|
||||
},
|
||||
|
||||
TyNever => NodeForrest::full(),
|
||||
TyTuple(ref tys) => {
|
||||
NodeForrest::union(tcx, tys.iter().map(|ty| {
|
||||
ty.uninhabited_from(visited, tcx)
|
||||
}))
|
||||
},
|
||||
TyArray(ty, len) => {
|
||||
if len == 0 {
|
||||
NodeForrest::empty()
|
||||
} else {
|
||||
ty.uninhabited_from(visited, tcx)
|
||||
}
|
||||
}
|
||||
TyRef(_, ref tm) => tm.ty.uninhabited_from(visited, tcx),
|
||||
|
||||
_ => NodeForrest::empty(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ use ty;
|
||||
use ty::subst::{Subst, Substs};
|
||||
use ty::walk::TypeWalker;
|
||||
use util::common::MemoizationMap;
|
||||
use util::nodemap::{NodeSet, NodeMap, FxHashMap, FxHashSet};
|
||||
use util::nodemap::{NodeSet, NodeMap, FxHashMap};
|
||||
|
||||
use serialize::{self, Encodable, Encoder};
|
||||
use std::borrow::Cow;
|
||||
@ -78,6 +78,7 @@ pub mod cast;
|
||||
pub mod error;
|
||||
pub mod fast_reject;
|
||||
pub mod fold;
|
||||
pub mod inhabitedness;
|
||||
pub mod item_path;
|
||||
pub mod layout;
|
||||
pub mod _match;
|
||||
@ -1406,20 +1407,6 @@ impl<'a, 'gcx, 'tcx> AdtDef {
|
||||
self.flags.set(self.flags.get() | AdtFlags::IS_DTORCK_VALID)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_uninhabited_recurse(&self,
|
||||
visited: &mut FxHashSet<(DefId, &'tcx Substs<'tcx>)>,
|
||||
block: Option<NodeId>,
|
||||
tcx: 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, tcx, substs, self.adt_kind())
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_struct(&self) -> bool {
|
||||
!self.is_union() && !self.is_enum()
|
||||
@ -1754,51 +1741,12 @@ impl<'a, 'gcx, 'tcx> VariantDef {
|
||||
pub fn field_named(&self, name: ast::Name) -> &FieldDef {
|
||||
self.find_field_named(name).unwrap()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_uninhabited_recurse(&self,
|
||||
visited: &mut FxHashSet<(DefId, &'tcx Substs<'tcx>)>,
|
||||
block: Option<NodeId>,
|
||||
tcx: TyCtxt<'a, 'gcx, 'tcx>,
|
||||
substs: &'tcx Substs<'tcx>,
|
||||
adt_kind: AdtKind) -> bool {
|
||||
match adt_kind {
|
||||
AdtKind::Union => {
|
||||
self.fields.iter().all(|f| {
|
||||
f.is_uninhabited_recurse(visited, block, tcx, substs, false)
|
||||
})
|
||||
},
|
||||
AdtKind::Struct => {
|
||||
self.fields.iter().any(|f| {
|
||||
f.is_uninhabited_recurse(visited, block, tcx, substs, false)
|
||||
})
|
||||
},
|
||||
AdtKind::Enum => {
|
||||
self.fields.iter().any(|f| {
|
||||
f.is_uninhabited_recurse(visited, block, tcx, substs, true)
|
||||
})
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'gcx, 'tcx> FieldDef {
|
||||
pub fn ty(&self, tcx: TyCtxt<'a, 'gcx, 'tcx>, subst: &Substs<'tcx>) -> Ty<'tcx> {
|
||||
tcx.item_type(self.did).subst(tcx, subst)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_uninhabited_recurse(&self,
|
||||
visited: &mut FxHashSet<(DefId, &'tcx Substs<'tcx>)>,
|
||||
block: Option<NodeId>,
|
||||
tcx: TyCtxt<'a, 'gcx, 'tcx>,
|
||||
substs: &'tcx Substs<'tcx>,
|
||||
is_enum: bool) -> bool {
|
||||
let visible = is_enum || block.map_or(true, |b| {
|
||||
tcx.vis_is_accessible_from(self.vis, b)
|
||||
});
|
||||
visible && self.ty(tcx, substs).is_uninhabited_recurse(visited, block, tcx)
|
||||
}
|
||||
}
|
||||
|
||||
/// Records the substitutions used to translate the polytype for an
|
||||
|
@ -979,29 +979,21 @@ impl<'a, 'gcx, 'tcx> TyS<'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
/// 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 {
|
||||
/// Checks whether a type is visibly uninhabited from a particular node.
|
||||
pub fn is_uninhabited_from(&self, block: NodeId, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> bool {
|
||||
let mut visited = FxHashSet::default();
|
||||
self.is_uninhabited_recurse(&mut visited, block, cx)
|
||||
let node_set = self.uninhabited_from(&mut visited, tcx);
|
||||
node_set.contains(tcx, block)
|
||||
}
|
||||
|
||||
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, substs) => {
|
||||
def.is_uninhabited_recurse(visited, block, cx, substs)
|
||||
},
|
||||
|
||||
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),
|
||||
|
||||
_ => false,
|
||||
}
|
||||
/// Checks whether a type is uninhabited.
|
||||
/// Note: just because a type is uninhabited, that doesn't mean that it's
|
||||
/// *visibly* uninhabited outside its module. You sometimes may want
|
||||
/// `is_uninhabited_from` instead.
|
||||
pub fn is_uninhabited_anywhere(&self, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> bool {
|
||||
let mut visited = FxHashSet::default();
|
||||
let node_set = self.uninhabited_from(&mut visited, tcx);
|
||||
!node_set.is_empty()
|
||||
}
|
||||
|
||||
pub fn is_primitive(&self) -> bool {
|
||||
|
@ -379,14 +379,14 @@ fn all_constructors<'a, 'tcx: 'a>(cx: &mut MatchCheckCtxt<'a, 'tcx>,
|
||||
ty::TyBool =>
|
||||
[true, false].iter().map(|b| ConstantValue(ConstVal::Bool(*b))).collect(),
|
||||
ty::TySlice(ref sub_ty) => {
|
||||
if sub_ty.is_uninhabited(Some(cx.node), cx.tcx) {
|
||||
if sub_ty.is_uninhabited_from(cx.node, cx.tcx) {
|
||||
vec![Slice(0)]
|
||||
} else {
|
||||
(0..pcx.max_slice_length+1).map(|length| Slice(length)).collect()
|
||||
}
|
||||
}
|
||||
ty::TyArray(ref sub_ty, length) => {
|
||||
if length == 0 || !sub_ty.is_uninhabited(Some(cx.node), cx.tcx) {
|
||||
if length == 0 || !sub_ty.is_uninhabited_from(cx.node, cx.tcx) {
|
||||
vec![Slice(length)]
|
||||
} else {
|
||||
vec![]
|
||||
@ -395,10 +395,10 @@ fn all_constructors<'a, 'tcx: 'a>(cx: &mut MatchCheckCtxt<'a, 'tcx>,
|
||||
ty::TyAdt(def, substs) if def.is_enum() && def.variants.len() != 1 => {
|
||||
def.variants.iter().filter_map(|v| {
|
||||
let mut visited = FxHashSet::default();
|
||||
if v.is_uninhabited_recurse(&mut visited,
|
||||
Some(cx.node),
|
||||
cx.tcx, substs,
|
||||
AdtKind::Enum) {
|
||||
let node_set = v.uninhabited_from(&mut visited,
|
||||
cx.tcx, substs,
|
||||
AdtKind::Enum);
|
||||
if node_set.contains(cx.tcx, cx.node) {
|
||||
None
|
||||
} else {
|
||||
Some(Variant(v.did))
|
||||
@ -406,7 +406,7 @@ fn all_constructors<'a, 'tcx: 'a>(cx: &mut MatchCheckCtxt<'a, 'tcx>,
|
||||
}).collect()
|
||||
}
|
||||
_ => {
|
||||
if pcx.ty.is_uninhabited(Some(cx.node), cx.tcx) {
|
||||
if pcx.ty.is_uninhabited_from(cx.node, cx.tcx) {
|
||||
vec![]
|
||||
} else {
|
||||
vec![Single]
|
||||
|
@ -100,12 +100,14 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
|
||||
|
||||
PatternKind::Variant { adt_def, substs, variant_index, ref subpatterns } => {
|
||||
let irrefutable = adt_def.variants.iter().enumerate().all(|(i, v)| {
|
||||
let mut visited = FxHashSet::default();
|
||||
i == variant_index || v.is_uninhabited_recurse(&mut visited,
|
||||
None,
|
||||
self.hir.tcx(),
|
||||
substs,
|
||||
adt_def.adt_kind())
|
||||
i == variant_index || {
|
||||
let mut visited = FxHashSet::default();
|
||||
let node_set = v.uninhabited_from(&mut visited,
|
||||
self.hir.tcx(),
|
||||
substs,
|
||||
adt_def.adt_kind());
|
||||
!node_set.is_empty()
|
||||
}
|
||||
});
|
||||
if irrefutable {
|
||||
let lvalue = match_pair.lvalue.downcast(adt_def, variant_index);
|
||||
|
Loading…
Reference in New Issue
Block a user