Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
canndrew committed Jan 3, 2017
1 parent 9482492 commit 7946597
Show file tree
Hide file tree
Showing 6 changed files with 294 additions and 87 deletions.
4 changes: 4 additions & 0 deletions src/librustc/ty/context.rs
Expand Up @@ -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};
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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()),
Expand Down
261 changes: 261 additions & 0 deletions src/librustc/ty/inhabitedness.rs
@@ -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(),
}
}
}

56 changes: 2 additions & 54 deletions src/librustc/ty/mod.rs
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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
Expand Down

0 comments on commit 7946597

Please sign in to comment.