Skip to content

Commit 7946597

Browse files
committed
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.
1 parent 9482492 commit 7946597

File tree

6 files changed

+294
-87
lines changed

6 files changed

+294
-87
lines changed

src/librustc/ty/context.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ use ty::{BareFnTy, InferTy, ParamTy, ProjectionTy, ExistentialPredicate};
3333
use ty::{TyVar, TyVid, IntVar, IntVid, FloatVar, FloatVid};
3434
use ty::TypeVariants::*;
3535
use ty::layout::{Layout, TargetDataLayout};
36+
use ty::inhabitedness::NodeForrest;
3637
use ty::maps;
3738
use util::common::MemoizationMap;
3839
use util::nodemap::{NodeMap, NodeSet, DefIdMap, DefIdSet};
@@ -459,6 +460,8 @@ pub struct GlobalCtxt<'tcx> {
459460
// FIXME dep tracking -- should be harmless enough
460461
pub normalized_cache: RefCell<FxHashMap<Ty<'tcx>, Ty<'tcx>>>,
461462

463+
pub inhabitedness_cache: RefCell<FxHashMap<Ty<'tcx>, NodeForrest>>,
464+
462465
pub lang_items: middle::lang_items::LanguageItems,
463466

464467
/// Maps from def-id of a type or region parameter to its
@@ -760,6 +763,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
760763
associated_item_def_ids: RefCell::new(DepTrackingMap::new(dep_graph.clone())),
761764
ty_param_defs: RefCell::new(NodeMap()),
762765
normalized_cache: RefCell::new(FxHashMap()),
766+
inhabitedness_cache: RefCell::new(FxHashMap()),
763767
lang_items: lang_items,
764768
inherent_impls: RefCell::new(DepTrackingMap::new(dep_graph.clone())),
765769
used_unsafe: RefCell::new(NodeSet()),

src/librustc/ty/inhabitedness.rs

Lines changed: 261 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,261 @@
1+
// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
use std::mem;
12+
use rustc_data_structures::small_vec::SmallVec;
13+
use syntax::ast::{CRATE_NODE_ID, NodeId};
14+
use util::nodemap::FxHashSet;
15+
use ty::context::TyCtxt;
16+
use ty::{AdtDef, VariantDef, FieldDef, TyS};
17+
use ty::{DefId, Substs};
18+
use ty::{AdtKind, Visibility, NodeIdTree};
19+
use ty::TypeVariants::*;
20+
21+
/// Represents a set of nodes closed under the ancestor relation. That is, if a
22+
/// node is in this set then so are all its descendants.
23+
#[derive(Clone)]
24+
pub struct NodeForrest {
25+
/// The minimal set of nodes required to represent the whole set.
26+
/// If A and B are nodes in the NodeForrest, and A is a desecendant
27+
/// of B, then only B will be in root_nodes.
28+
/// We use a SmallVec here because (for its use in this module) its rare
29+
/// that this will contain more than one or two nodes.
30+
root_nodes: SmallVec<[NodeId; 1]>,
31+
}
32+
33+
impl<'a, 'gcx, 'tcx> NodeForrest {
34+
/// Create an empty set.
35+
pub fn empty() -> NodeForrest {
36+
NodeForrest {
37+
root_nodes: SmallVec::new(),
38+
}
39+
}
40+
41+
/// Create a set containing every node.
42+
#[inline]
43+
pub fn full() -> NodeForrest {
44+
NodeForrest::from_node(CRATE_NODE_ID)
45+
}
46+
47+
/// Create a set containing a node and all its descendants.
48+
pub fn from_node(node: NodeId) -> NodeForrest {
49+
let mut root_nodes = SmallVec::new();
50+
root_nodes.push(node);
51+
NodeForrest {
52+
root_nodes: root_nodes,
53+
}
54+
}
55+
56+
/// Test whether the set is empty.
57+
pub fn is_empty(&self) -> bool {
58+
self.root_nodes.is_empty()
59+
}
60+
61+
/// Test whether the set conains a node.
62+
pub fn contains(&self,
63+
tcx: TyCtxt<'a, 'gcx, 'tcx>,
64+
node: NodeId) -> bool
65+
{
66+
for root_node in self.root_nodes.iter() {
67+
if tcx.map.is_descendant_of(node, *root_node) {
68+
return true;
69+
}
70+
}
71+
false
72+
}
73+
74+
/// Calculate the intersection of a collection of sets.
75+
pub fn intersection<I>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
76+
iter: I) -> NodeForrest
77+
where I: IntoIterator<Item=NodeForrest>
78+
{
79+
let mut ret = NodeForrest::full();
80+
let mut next_ret = SmallVec::new();
81+
let mut old_ret: SmallVec<[NodeId; 1]> = SmallVec::new();
82+
for next_set in iter {
83+
for node in ret.root_nodes.drain(..) {
84+
if next_set.contains(tcx, node) {
85+
next_ret.push(node);
86+
} else {
87+
old_ret.push(node);
88+
}
89+
}
90+
ret.root_nodes.extend(old_ret.drain(..));
91+
92+
for node in next_set.root_nodes {
93+
if ret.contains(tcx, node) {
94+
next_ret.push(node);
95+
}
96+
}
97+
98+
mem::swap(&mut next_ret, &mut ret.root_nodes);
99+
next_ret.drain(..);
100+
}
101+
ret
102+
}
103+
104+
/// Calculate the union of a collection of sets.
105+
pub fn union<I>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
106+
iter: I) -> NodeForrest
107+
where I: IntoIterator<Item=NodeForrest>
108+
{
109+
let mut ret = NodeForrest::empty();
110+
let mut next_ret = SmallVec::new();
111+
for next_set in iter {
112+
for node in ret.root_nodes.drain(..) {
113+
if !next_set.contains(tcx, node) {
114+
next_ret.push(node);
115+
}
116+
}
117+
118+
for node in next_set.root_nodes {
119+
if !next_ret.contains(&node) {
120+
next_ret.push(node);
121+
}
122+
}
123+
124+
mem::swap(&mut next_ret, &mut ret.root_nodes);
125+
next_ret.drain(..);
126+
}
127+
ret
128+
}
129+
}
130+
131+
impl<'a, 'gcx, 'tcx> AdtDef {
132+
/// Calculate the set of nodes from which this adt is visibly uninhabited.
133+
pub fn uninhabited_from(
134+
&self,
135+
visited: &mut FxHashSet<(DefId, &'tcx Substs<'tcx>)>,
136+
tcx: TyCtxt<'a, 'gcx, 'tcx>,
137+
substs: &'tcx Substs<'tcx>) -> NodeForrest
138+
{
139+
if !visited.insert((self.did, substs)) {
140+
return NodeForrest::empty();
141+
}
142+
143+
let ret = NodeForrest::intersection(tcx, self.variants.iter().map(|v| {
144+
v.uninhabited_from(visited, tcx, substs, self.adt_kind())
145+
}));
146+
visited.remove(&(self.did, substs));
147+
ret
148+
}
149+
}
150+
151+
impl<'a, 'gcx, 'tcx> VariantDef {
152+
/// Calculate the set of nodes from which this variant is visibly uninhabited.
153+
pub fn uninhabited_from(
154+
&self,
155+
visited: &mut FxHashSet<(DefId, &'tcx Substs<'tcx>)>,
156+
tcx: TyCtxt<'a, 'gcx, 'tcx>,
157+
substs: &'tcx Substs<'tcx>,
158+
adt_kind: AdtKind) -> NodeForrest
159+
{
160+
match adt_kind {
161+
AdtKind::Union => {
162+
NodeForrest::intersection(tcx, self.fields.iter().map(|f| {
163+
f.uninhabited_from(visited, tcx, substs, false)
164+
}))
165+
},
166+
AdtKind::Struct => {
167+
NodeForrest::union(tcx, self.fields.iter().map(|f| {
168+
f.uninhabited_from(visited, tcx, substs, false)
169+
}))
170+
},
171+
AdtKind::Enum => {
172+
NodeForrest::union(tcx, self.fields.iter().map(|f| {
173+
f.uninhabited_from(visited, tcx, substs, true)
174+
}))
175+
},
176+
}
177+
}
178+
}
179+
180+
impl<'a, 'gcx, 'tcx> FieldDef {
181+
/// Calculate the set of nodes from which this field is visibly uninhabited.
182+
pub fn uninhabited_from(
183+
&self,
184+
visited: &mut FxHashSet<(DefId, &'tcx Substs<'tcx>)>,
185+
tcx: TyCtxt<'a, 'gcx, 'tcx>,
186+
substs: &'tcx Substs<'tcx>,
187+
is_enum: bool) -> NodeForrest
188+
{
189+
if let Visibility::PrivateExternal = self.vis {
190+
return NodeForrest::empty();
191+
}
192+
193+
let data_inhabitedness = self.ty(tcx, substs).uninhabited_from(visited, tcx);
194+
match self.vis {
195+
Visibility::Restricted(from) if !is_enum => {
196+
let node_set = NodeForrest::from_node(from);
197+
let iter = Some(node_set).into_iter().chain(Some(data_inhabitedness));
198+
NodeForrest::intersection(tcx, iter)
199+
},
200+
_ => data_inhabitedness,
201+
}
202+
}
203+
}
204+
205+
impl<'a, 'gcx, 'tcx> TyS<'tcx> {
206+
/// Calculate the set of nodes from which this type is visibly uninhabited.
207+
pub fn uninhabited_from(
208+
&self,
209+
visited: &mut FxHashSet<(DefId, &'tcx Substs<'tcx>)>,
210+
tcx: TyCtxt<'a, 'gcx, 'tcx>) -> NodeForrest
211+
{
212+
match tcx.lift_to_global(&self) {
213+
Some(global_ty) => {
214+
{
215+
let cache = tcx.inhabitedness_cache.borrow();
216+
if let Some(closed_node_set) = cache.get(&global_ty) {
217+
return closed_node_set.clone();
218+
}
219+
}
220+
let node_set = global_ty.uninhabited_from_inner(visited, tcx);
221+
let mut cache = tcx.inhabitedness_cache.borrow_mut();
222+
cache.insert(global_ty, node_set.clone());
223+
node_set
224+
},
225+
None => {
226+
let node_set = self.uninhabited_from_inner(visited, tcx);
227+
node_set
228+
},
229+
}
230+
}
231+
232+
fn uninhabited_from_inner(
233+
&self,
234+
visited: &mut FxHashSet<(DefId, &'tcx Substs<'tcx>)>,
235+
tcx: TyCtxt<'a, 'gcx, 'tcx>) -> NodeForrest
236+
{
237+
match self.sty {
238+
TyAdt(def, substs) => {
239+
def.uninhabited_from(visited, tcx, substs)
240+
},
241+
242+
TyNever => NodeForrest::full(),
243+
TyTuple(ref tys) => {
244+
NodeForrest::union(tcx, tys.iter().map(|ty| {
245+
ty.uninhabited_from(visited, tcx)
246+
}))
247+
},
248+
TyArray(ty, len) => {
249+
if len == 0 {
250+
NodeForrest::empty()
251+
} else {
252+
ty.uninhabited_from(visited, tcx)
253+
}
254+
}
255+
TyRef(_, ref tm) => tm.ty.uninhabited_from(visited, tcx),
256+
257+
_ => NodeForrest::empty(),
258+
}
259+
}
260+
}
261+

src/librustc/ty/mod.rs

Lines changed: 2 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ use ty;
2929
use ty::subst::{Subst, Substs};
3030
use ty::walk::TypeWalker;
3131
use util::common::MemoizationMap;
32-
use util::nodemap::{NodeSet, NodeMap, FxHashMap, FxHashSet};
32+
use util::nodemap::{NodeSet, NodeMap, FxHashMap};
3333

3434
use serialize::{self, Encodable, Encoder};
3535
use std::borrow::Cow;
@@ -78,6 +78,7 @@ pub mod cast;
7878
pub mod error;
7979
pub mod fast_reject;
8080
pub mod fold;
81+
pub mod inhabitedness;
8182
pub mod item_path;
8283
pub mod layout;
8384
pub mod _match;
@@ -1406,20 +1407,6 @@ impl<'a, 'gcx, 'tcx> AdtDef {
14061407
self.flags.set(self.flags.get() | AdtFlags::IS_DTORCK_VALID)
14071408
}
14081409

1409-
#[inline]
1410-
pub fn is_uninhabited_recurse(&self,
1411-
visited: &mut FxHashSet<(DefId, &'tcx Substs<'tcx>)>,
1412-
block: Option<NodeId>,
1413-
tcx: TyCtxt<'a, 'gcx, 'tcx>,
1414-
substs: &'tcx Substs<'tcx>) -> bool {
1415-
if !visited.insert((self.did, substs)) {
1416-
return false;
1417-
};
1418-
self.variants.iter().all(|v| {
1419-
v.is_uninhabited_recurse(visited, block, tcx, substs, self.adt_kind())
1420-
})
1421-
}
1422-
14231410
#[inline]
14241411
pub fn is_struct(&self) -> bool {
14251412
!self.is_union() && !self.is_enum()
@@ -1754,51 +1741,12 @@ impl<'a, 'gcx, 'tcx> VariantDef {
17541741
pub fn field_named(&self, name: ast::Name) -> &FieldDef {
17551742
self.find_field_named(name).unwrap()
17561743
}
1757-
1758-
#[inline]
1759-
pub fn is_uninhabited_recurse(&self,
1760-
visited: &mut FxHashSet<(DefId, &'tcx Substs<'tcx>)>,
1761-
block: Option<NodeId>,
1762-
tcx: TyCtxt<'a, 'gcx, 'tcx>,
1763-
substs: &'tcx Substs<'tcx>,
1764-
adt_kind: AdtKind) -> bool {
1765-
match adt_kind {
1766-
AdtKind::Union => {
1767-
self.fields.iter().all(|f| {
1768-
f.is_uninhabited_recurse(visited, block, tcx, substs, false)
1769-
})
1770-
},
1771-
AdtKind::Struct => {
1772-
self.fields.iter().any(|f| {
1773-
f.is_uninhabited_recurse(visited, block, tcx, substs, false)
1774-
})
1775-
},
1776-
AdtKind::Enum => {
1777-
self.fields.iter().any(|f| {
1778-
f.is_uninhabited_recurse(visited, block, tcx, substs, true)
1779-
})
1780-
},
1781-
}
1782-
}
17831744
}
17841745

17851746
impl<'a, 'gcx, 'tcx> FieldDef {
17861747
pub fn ty(&self, tcx: TyCtxt<'a, 'gcx, 'tcx>, subst: &Substs<'tcx>) -> Ty<'tcx> {
17871748
tcx.item_type(self.did).subst(tcx, subst)
17881749
}
1789-
1790-
#[inline]
1791-
pub fn is_uninhabited_recurse(&self,
1792-
visited: &mut FxHashSet<(DefId, &'tcx Substs<'tcx>)>,
1793-
block: Option<NodeId>,
1794-
tcx: TyCtxt<'a, 'gcx, 'tcx>,
1795-
substs: &'tcx Substs<'tcx>,
1796-
is_enum: bool) -> bool {
1797-
let visible = is_enum || block.map_or(true, |b| {
1798-
tcx.vis_is_accessible_from(self.vis, b)
1799-
});
1800-
visible && self.ty(tcx, substs).is_uninhabited_recurse(visited, block, tcx)
1801-
}
18021750
}
18031751

18041752
/// Records the substitutions used to translate the polytype for an

0 commit comments

Comments
 (0)