Skip to content

Commit 9ba3d5e

Browse files
committed
Reinstate fast_reject for overlap checking
The initial implementation of specialization did not use the `fast_reject` mechanism when checking for overlap, which caused a serious performance regression in some cases. This commit modifies the specialization graph to use simplified types for fast rejection when possible, and along the way refactors the logic for building the specialization graph. Closes #32499
1 parent b0d3170 commit 9ba3d5e

File tree

2 files changed

+162
-70
lines changed

2 files changed

+162
-70
lines changed

src/librustc/traits/specialize/specialization_graph.rs

+149-56
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,9 @@ use middle::def_id::DefId;
1818
use infer;
1919
use traits::{self, ProjectionMode};
2020
use ty::{self, TyCtxt, ImplOrTraitItem, TraitDef, TypeFoldable};
21+
use ty::fast_reject::{self, SimplifiedType};
2122
use syntax::ast::Name;
22-
use util::nodemap::DefIdMap;
23+
use util::nodemap::{DefIdMap, FnvHashMap};
2324

2425
/// A per-trait graph of impls in specialization order. At the moment, this
2526
/// graph forms a tree rooted with the trait itself, with all other nodes
@@ -42,7 +43,124 @@ pub struct Graph {
4243
parent: DefIdMap<DefId>,
4344

4445
// the "root" impls are found by looking up the trait's def_id.
45-
children: DefIdMap<Vec<DefId>>,
46+
children: DefIdMap<Children>,
47+
}
48+
49+
/// Children of a given impl, grouped into blanket/non-blanket varieties as is
50+
/// done in `TraitDef`.
51+
struct Children {
52+
// Impls of a trait (or specializations of a given impl). To allow for
53+
// quicker lookup, the impls are indexed by a simplified version of their
54+
// `Self` type: impls with a simplifiable `Self` are stored in
55+
// `nonblanket_impls` keyed by it, while all other impls are stored in
56+
// `blanket_impls`.
57+
//
58+
// A similar division is used within `TraitDef`, but the lists there collect
59+
// together *all* the impls for a trait, and are populated prior to building
60+
// the specialization graph.
61+
62+
/// Impls of the trait.
63+
nonblanket_impls: FnvHashMap<fast_reject::SimplifiedType, Vec<DefId>>,
64+
65+
/// Blanket impls associated with the trait.
66+
blanket_impls: Vec<DefId>,
67+
}
68+
69+
/// The result of attempting to insert an impl into a group of children.
70+
enum InsertResult<'a, 'tcx: 'a> {
71+
/// The impl was inserted as a new child in this group of children.
72+
BecameNewSibling,
73+
74+
/// The impl replaced an existing impl that specializes it.
75+
Replaced(DefId),
76+
77+
/// The impl is a specialization of an existing child.
78+
ShouldRecurseOn(DefId),
79+
80+
/// The impl has an unresolvable overlap with an existing child (neither
81+
/// specializes the other).
82+
Overlapped(Overlap<'a, 'tcx>),
83+
}
84+
85+
impl Children {
86+
fn new() -> Children {
87+
Children {
88+
nonblanket_impls: FnvHashMap(),
89+
blanket_impls: vec![],
90+
}
91+
}
92+
93+
/// Insert an impl into this set of children without comparing to any existing impls
94+
fn insert_blindly(&mut self, tcx: &TyCtxt, impl_def_id: DefId) {
95+
let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap();
96+
if let Some(sty) = fast_reject::simplify_type(tcx, trait_ref.self_ty(), false) {
97+
self.nonblanket_impls.entry(sty).or_insert(vec![]).push(impl_def_id)
98+
} else {
99+
self.blanket_impls.push(impl_def_id)
100+
}
101+
}
102+
103+
/// Attempt to insert an impl into this set of children, while comparing for
104+
/// specialiation relationships.
105+
fn insert<'a, 'tcx>(&mut self,
106+
tcx: &'a TyCtxt<'tcx>,
107+
impl_def_id: DefId,
108+
simplified_self: Option<SimplifiedType>)
109+
-> InsertResult<'a, 'tcx>
110+
{
111+
for slot in match simplified_self {
112+
Some(sty) => self.filtered_mut(sty),
113+
None => self.iter_mut(),
114+
} {
115+
let possible_sibling = *slot;
116+
117+
let infcx = infer::new_infer_ctxt(tcx, &tcx.tables, None, ProjectionMode::Topmost);
118+
let overlap = traits::overlapping_impls(&infcx, possible_sibling, impl_def_id);
119+
120+
if let Some(impl_header) = overlap {
121+
let le = specializes(tcx, impl_def_id, possible_sibling);
122+
let ge = specializes(tcx, possible_sibling, impl_def_id);
123+
124+
if le && !ge {
125+
debug!("descending as child of TraitRef {:?}",
126+
tcx.impl_trait_ref(possible_sibling).unwrap());
127+
128+
// the impl specializes possible_sibling
129+
return InsertResult::ShouldRecurseOn(possible_sibling);
130+
} else if ge && !le {
131+
debug!("placing as parent of TraitRef {:?}",
132+
tcx.impl_trait_ref(possible_sibling).unwrap());
133+
134+
// possible_sibling specializes the impl
135+
*slot = impl_def_id;
136+
return InsertResult::Replaced(possible_sibling);
137+
} else {
138+
// overlap, but no specialization; error out
139+
return InsertResult::Overlapped(Overlap {
140+
with_impl: possible_sibling,
141+
on_trait_ref: impl_header.trait_ref.unwrap(),
142+
in_context: infcx,
143+
});
144+
}
145+
}
146+
}
147+
148+
// no overlap with any potential siblings, so add as a new sibling
149+
debug!("placing as new sibling");
150+
self.insert_blindly(tcx, impl_def_id);
151+
InsertResult::BecameNewSibling
152+
}
153+
154+
fn iter_mut<'a>(&'a mut self) -> Box<Iterator<Item = &'a mut DefId> + 'a> {
155+
let nonblanket = self.nonblanket_impls.iter_mut().flat_map(|(_, v)| v.iter_mut());
156+
Box::new(self.blanket_impls.iter_mut().chain(nonblanket))
157+
}
158+
159+
fn filtered_mut<'a>(&'a mut self, sty: SimplifiedType)
160+
-> Box<Iterator<Item = &'a mut DefId> + 'a> {
161+
let nonblanket = self.nonblanket_impls.entry(sty).or_insert(vec![]).iter_mut();
162+
Box::new(self.blanket_impls.iter_mut().chain(nonblanket))
163+
}
46164
}
47165

48166
impl Graph {
@@ -78,78 +196,53 @@ impl Graph {
78196
trait_ref, impl_def_id, trait_def_id);
79197

80198
self.parent.insert(impl_def_id, trait_def_id);
81-
self.children.entry(trait_def_id).or_insert(vec![]).push(impl_def_id);
199+
self.children.entry(trait_def_id).or_insert(Children::new())
200+
.insert_blindly(tcx, impl_def_id);
82201
return Ok(());
83202
}
84203

85204
let mut parent = trait_def_id;
205+
let simplified = fast_reject::simplify_type(tcx, trait_ref.self_ty(), false);
86206

87-
// Ugly hack around borrowck limitations. Assigned only in the case
88-
// where we bump downward an existing node in the graph.
89-
let child_to_insert;
90-
91-
'descend: loop {
92-
let mut possible_siblings = self.children.entry(parent).or_insert(vec![]);
93-
94-
for slot in possible_siblings.iter_mut() {
95-
let possible_sibling = *slot;
96-
97-
let infcx = infer::new_infer_ctxt(tcx, &tcx.tables, None, ProjectionMode::Topmost);
98-
let overlap = traits::overlapping_impls(&infcx, possible_sibling, impl_def_id);
99-
100-
if let Some(impl_header) = overlap {
101-
let le = specializes(tcx, impl_def_id, possible_sibling);
102-
let ge = specializes(tcx, possible_sibling, impl_def_id);
103-
104-
if le && !ge {
105-
debug!("descending as child of TraitRef {:?}",
106-
tcx.impl_trait_ref(possible_sibling).unwrap());
107-
108-
// the impl specializes possible_sibling
109-
parent = possible_sibling;
110-
continue 'descend;
111-
} else if ge && !le {
112-
debug!("placing as parent of TraitRef {:?}",
113-
tcx.impl_trait_ref(possible_sibling).unwrap());
114-
115-
// possible_sibling specializes the impl
116-
*slot = impl_def_id;
117-
self.parent.insert(impl_def_id, parent);
118-
self.parent.insert(possible_sibling, impl_def_id);
119-
// we have to defer the insertion, because we can't
120-
// relinquish the borrow of `self.children`
121-
child_to_insert = possible_sibling;
122-
break 'descend;
123-
} else {
124-
// overlap, but no specialization; error out
125-
return Err(Overlap {
126-
with_impl: possible_sibling,
127-
on_trait_ref: impl_header.trait_ref.unwrap(),
128-
in_context: infcx,
129-
});
130-
}
207+
// Descend the specialization tree, where `parent` is the current parent node
208+
loop {
209+
use self::InsertResult::*;
210+
211+
let insert_result = self.children.entry(parent).or_insert(Children::new())
212+
.insert(tcx, impl_def_id, simplified);
213+
214+
match insert_result {
215+
BecameNewSibling => {
216+
break;
217+
}
218+
Replaced(new_child) => {
219+
self.parent.insert(new_child, impl_def_id);
220+
let mut new_children = Children::new();
221+
new_children.insert_blindly(tcx, new_child);
222+
self.children.insert(impl_def_id, new_children);
223+
break;
224+
}
225+
ShouldRecurseOn(new_parent) => {
226+
parent = new_parent;
227+
}
228+
Overlapped(error) => {
229+
return Err(error);
131230
}
132231
}
133-
134-
// no overlap with any potential siblings, so add as a new sibling
135-
debug!("placing as new sibling");
136-
self.parent.insert(impl_def_id, parent);
137-
possible_siblings.push(impl_def_id);
138-
return Ok(());
139232
}
140233

141-
self.children.insert(impl_def_id, vec![child_to_insert]);
234+
self.parent.insert(impl_def_id, parent);
142235
Ok(())
143236
}
144237

145238
/// Insert cached metadata mapping from a child impl back to its parent.
146-
pub fn record_impl_from_cstore(&mut self, parent: DefId, child: DefId) {
239+
pub fn record_impl_from_cstore(&mut self, tcx: &TyCtxt, parent: DefId, child: DefId) {
147240
if self.parent.insert(child, parent).is_some() {
148241
panic!("When recording an impl from the crate store, information about its parent \
149242
was already present.");
150243
}
151244

152-
self.children.entry(parent).or_insert(vec![]).push(child);
245+
self.children.entry(parent).or_insert(Children::new()).insert_blindly(tcx, child);
153246
}
154247

155248
/// The parent of a given impl, which is the def id of the trait when the

src/librustc/ty/trait_def.rs

+13-14
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use ty;
1515
use ty::fast_reject;
1616
use ty::{Ty, TyCtxt, TraitRef};
1717
use std::borrow::{Borrow};
18-
use std::cell::{Cell, Ref, RefCell};
18+
use std::cell::{Cell, RefCell};
1919
use syntax::ast::Name;
2020
use rustc_front::hir;
2121
use util::nodemap::FnvHashMap;
@@ -43,10 +43,17 @@ pub struct TraitDef<'tcx> {
4343
/// for resolving `X::Foo` type markers.
4444
pub associated_type_names: Vec<Name>,
4545

46-
// Impls of this trait. To allow for quicker lookup, the impls are indexed
47-
// by a simplified version of their Self type: impls with a simplifiable
48-
// Self are stored in nonblanket_impls keyed by it, while all other impls
49-
// are stored in blanket_impls.
46+
// Impls of a trait. To allow for quicker lookup, the impls are indexed by a
47+
// simplified version of their `Self` type: impls with a simplifiable `Self`
48+
// are stored in `nonblanket_impls` keyed by it, while all other impls are
49+
// stored in `blanket_impls`.
50+
//
51+
// A similar division is used within `specialization_graph`, but the ones
52+
// here are (1) stored as a flat list for the trait and (2) populated prior
53+
// to -- and used while -- determining specialization order.
54+
//
55+
// FIXME: solve the reentrancy issues and remove these lists in favor of the
56+
// ones in `specialization_graph`.
5057
//
5158
// These lists are tracked by `DepNode::TraitImpls`; we don't use
5259
// a DepTrackingMap but instead have the `TraitDef` insert the
@@ -184,7 +191,7 @@ impl<'tcx> TraitDef<'tcx> {
184191
// if the impl is non-local, it's placed directly into the
185192
// specialization graph using parent information drawn from metadata.
186193
self.specialization_graph.borrow_mut()
187-
.record_impl_from_cstore(parent_impl, impl_def_id)
194+
.record_impl_from_cstore(tcx, parent_impl, impl_def_id)
188195
}
189196
}
190197

@@ -261,14 +268,6 @@ impl<'tcx> TraitDef<'tcx> {
261268
}
262269
}
263270
}
264-
265-
pub fn borrow_impl_lists<'s>(&'s self, tcx: &TyCtxt<'tcx>)
266-
-> (Ref<'s, Vec<DefId>>,
267-
Ref<'s, FnvHashMap<fast_reject::SimplifiedType, Vec<DefId>>>) {
268-
self.read_trait_impls(tcx);
269-
(self.blanket_impls.borrow(), self.nonblanket_impls.borrow())
270-
}
271-
272271
}
273272

274273
bitflags! {

0 commit comments

Comments
 (0)