From 7946597f7556dc8e1ad05e02d7e82b4ff800a5ac Mon Sep 17 00:00:00 2001 From: Andrew Cann Date: Sun, 11 Dec 2016 22:30:14 +0800 Subject: [PATCH] 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. --- src/librustc/ty/context.rs | 4 + src/librustc/ty/inhabitedness.rs | 261 +++++++++++++++++++++ src/librustc/ty/mod.rs | 56 +---- src/librustc/ty/sty.rs | 32 +-- src/librustc_const_eval/_match.rs | 14 +- src/librustc_mir/build/matches/simplify.rs | 14 +- 6 files changed, 294 insertions(+), 87 deletions(-) create mode 100644 src/librustc/ty/inhabitedness.rs diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs index f58d7dcb45f..6c7946a528e 100644 --- a/src/librustc/ty/context.rs +++ b/src/librustc/ty/context.rs @@ -33,6 +33,7 @@ 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, Ty<'tcx>>>, + pub inhabitedness_cache: RefCell, NodeForrest>>, + pub lang_items: middle::lang_items::LanguageItems, /// Maps from def-id of a type or region parameter to its @@ -760,6 +763,7 @@ pub fn create_and_enter(s: &'tcx Session, 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()), diff --git a/src/librustc/ty/inhabitedness.rs b/src/librustc/ty/inhabitedness.rs new file mode 100644 index 00000000000..54fdbe1a34a --- /dev/null +++ b/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 or the MIT license +// , 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(tcx: TyCtxt<'a, 'gcx, 'tcx>, + iter: I) -> NodeForrest + where I: IntoIterator + { + 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(tcx: TyCtxt<'a, 'gcx, 'tcx>, + iter: I) -> NodeForrest + where I: IntoIterator + { + 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(), + } + } +} + diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index ba389b98b8c..7cfc0f74214 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -29,7 +29,7 @@ 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 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 @@ fn calculate_dtorck(&'gcx self, tcx: TyCtxt) { 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, - 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 @@ pub fn index_of_field_named(&self, 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, - 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, - 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 diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs index 638345608c2..340b7415f5c 100644 --- a/src/librustc/ty/sty.rs +++ b/src/librustc/ty/sty.rs @@ -979,29 +979,21 @@ pub fn is_never(&self) -> bool { } } - /// 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, 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, - 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 { diff --git a/src/librustc_const_eval/_match.rs b/src/librustc_const_eval/_match.rs index f5408aa2ce2..ea01857745e 100644 --- a/src/librustc_const_eval/_match.rs +++ b/src/librustc_const_eval/_match.rs @@ -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] diff --git a/src/librustc_mir/build/matches/simplify.rs b/src/librustc_mir/build/matches/simplify.rs index c3414c591ab..b0718341223 100644 --- a/src/librustc_mir/build/matches/simplify.rs +++ b/src/librustc_mir/build/matches/simplify.rs @@ -100,12 +100,14 @@ fn simplify_match_pair<'pat>(&mut self, 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); -- GitLab