Skip to content

Commit 6d84a35

Browse files
committed
Auto merge of #43723 - arielb1:nonincremental-fast-reject, r=eddyb
make `for_all_relevant_impls` O(1) again A change in #41911 had made `for_all_relevant_impls` do a linear scan over all impls, instead of using an HashMap. Use an HashMap again to avoid quadratic blowup when there is a large number of structs with impls. I think this fixes #43141 completely, but I want better measurements in order to be sure. As a perf patch, please don't roll this up. r? @eddyb beta-nominating because regression
2 parents bcd75d6 + 453ad81 commit 6d84a35

File tree

10 files changed

+77
-138
lines changed

10 files changed

+77
-138
lines changed

src/librustc/traits/error_reporting.rs

+4-5
Original file line numberDiff line numberDiff line change
@@ -278,8 +278,8 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
278278
let mut self_match_impls = vec![];
279279
let mut fuzzy_match_impls = vec![];
280280

281-
self.tcx.trait_def(trait_ref.def_id)
282-
.for_each_relevant_impl(self.tcx, trait_self_ty, |def_id| {
281+
self.tcx.for_each_relevant_impl(
282+
trait_ref.def_id, trait_self_ty, |def_id| {
283283
let impl_substs = self.fresh_substs_for_item(obligation.cause.span, def_id);
284284
let impl_trait_ref = tcx
285285
.impl_trait_ref(def_id)
@@ -396,10 +396,9 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
396396
trait_ref.skip_binder().self_ty(),
397397
true);
398398
let mut impl_candidates = Vec::new();
399-
let trait_def = self.tcx.trait_def(trait_ref.def_id());
400399

401400
match simp {
402-
Some(simp) => trait_def.for_each_impl(self.tcx, |def_id| {
401+
Some(simp) => self.tcx.for_each_impl(trait_ref.def_id(), |def_id| {
403402
let imp = self.tcx.impl_trait_ref(def_id).unwrap();
404403
let imp_simp = fast_reject::simplify_type(self.tcx,
405404
imp.self_ty(),
@@ -411,7 +410,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
411410
}
412411
impl_candidates.push(imp);
413412
}),
414-
None => trait_def.for_each_impl(self.tcx, |def_id| {
413+
None => self.tcx.for_each_impl(trait_ref.def_id(), |def_id| {
415414
impl_candidates.push(
416415
self.tcx.impl_trait_ref(def_id).unwrap());
417416
})

src/librustc/traits/select.rs

+2-4
Original file line numberDiff line numberDiff line change
@@ -1576,10 +1576,8 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
15761576
{
15771577
debug!("assemble_candidates_from_impls(obligation={:?})", obligation);
15781578

1579-
let def = self.tcx().trait_def(obligation.predicate.def_id());
1580-
1581-
def.for_each_relevant_impl(
1582-
self.tcx(),
1579+
self.tcx().for_each_relevant_impl(
1580+
obligation.predicate.def_id(),
15831581
obligation.predicate.0.trait_ref.self_ty(),
15841582
|impl_def_id| {
15851583
self.probe(|this, snapshot| { /* [1] */

src/librustc/traits/specialize/mod.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -300,7 +300,8 @@ pub(super) fn specialization_graph_provider<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx
300300
-> Rc<specialization_graph::Graph> {
301301
let mut sg = specialization_graph::Graph::new();
302302

303-
let mut trait_impls: Vec<DefId> = tcx.trait_impls_of(trait_id).iter().collect();
303+
let mut trait_impls = Vec::new();
304+
tcx.for_each_impl(trait_id, |impl_did| trait_impls.push(impl_did));
304305

305306
// The coherence checking implementation seems to rely on impls being
306307
// iterated over (roughly) in definition order, so we are sorting by

src/librustc/ty/maps.rs

+1-14
Original file line numberDiff line numberDiff line change
@@ -470,12 +470,6 @@ impl<'tcx> QueryDescription for queries::trait_impls_of<'tcx> {
470470
}
471471
}
472472

473-
impl<'tcx> QueryDescription for queries::relevant_trait_impls_for<'tcx> {
474-
fn describe(tcx: TyCtxt, (def_id, ty): (DefId, SimplifiedType)) -> String {
475-
format!("relevant impls for: `({}, {:?})`", tcx.item_path_str(def_id), ty)
476-
}
477-
}
478-
479473
impl<'tcx> QueryDescription for queries::is_object_safe<'tcx> {
480474
fn describe(tcx: TyCtxt, def_id: DefId) -> String {
481475
format!("determine object safety of trait `{}`", tcx.item_path_str(def_id))
@@ -966,10 +960,7 @@ define_maps! { <'tcx>
966960
[] const_is_rvalue_promotable_to_static: ConstIsRvaluePromotableToStatic(DefId) -> bool,
967961
[] is_mir_available: IsMirAvailable(DefId) -> bool,
968962

969-
[] trait_impls_of: TraitImpls(DefId) -> ty::trait_def::TraitImpls,
970-
// Note that TraitDef::for_each_relevant_impl() will do type simplication for you.
971-
[] relevant_trait_impls_for: relevant_trait_impls_for((DefId, SimplifiedType))
972-
-> ty::trait_def::TraitImpls,
963+
[] trait_impls_of: TraitImpls(DefId) -> Rc<ty::trait_def::TraitImpls>,
973964
[] specialization_graph_of: SpecializationGraph(DefId) -> Rc<specialization_graph::Graph>,
974965
[] is_object_safe: ObjectSafety(DefId) -> bool,
975966

@@ -1049,10 +1040,6 @@ fn crate_variances<'tcx>(_: CrateNum) -> DepConstructor<'tcx> {
10491040
DepConstructor::CrateVariances
10501041
}
10511042

1052-
fn relevant_trait_impls_for<'tcx>((def_id, t): (DefId, SimplifiedType)) -> DepConstructor<'tcx> {
1053-
DepConstructor::RelevantTraitImpls(def_id, t)
1054-
}
1055-
10561043
fn is_copy_dep_node<'tcx>(_: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> DepConstructor<'tcx> {
10571044
DepConstructor::IsCopy
10581045
}

src/librustc/ty/mod.rs

-2
Original file line numberDiff line numberDiff line change
@@ -2507,7 +2507,6 @@ pub fn provide(providers: &mut ty::maps::Providers) {
25072507
param_env,
25082508
trait_of_item,
25092509
trait_impls_of: trait_def::trait_impls_of_provider,
2510-
relevant_trait_impls_for: trait_def::relevant_trait_impls_provider,
25112510
..*providers
25122511
};
25132512
}
@@ -2517,7 +2516,6 @@ pub fn provide_extern(providers: &mut ty::maps::Providers) {
25172516
adt_sized_constraint,
25182517
adt_dtorck_constraint,
25192518
trait_impls_of: trait_def::trait_impls_of_provider,
2520-
relevant_trait_impls_for: trait_def::relevant_trait_impls_provider,
25212519
param_env,
25222520
..*providers
25232521
};

src/librustc/ty/trait_def.rs

+64-104
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,17 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11+
use hir;
1112
use hir::def_id::DefId;
1213
use hir::map::DefPathHash;
1314
use traits::specialization_graph;
1415
use ty::fast_reject;
1516
use ty::fold::TypeFoldable;
1617
use ty::{Ty, TyCtxt};
18+
19+
use rustc_data_structures::fx::FxHashMap;
20+
1721
use std::rc::Rc;
18-
use hir;
1922

2023
/// A trait's definition with type information.
2124
pub struct TraitDef {
@@ -36,60 +39,12 @@ pub struct TraitDef {
3639
pub def_path_hash: DefPathHash,
3740
}
3841

39-
// We don't store the list of impls in a flat list because each cached list of
40-
// `relevant_impls_for` we would then duplicate all blanket impls. By keeping
41-
// blanket and non-blanket impls separate, we can share the list of blanket
42-
// impls.
43-
#[derive(Clone)]
4442
pub struct TraitImpls {
45-
blanket_impls: Rc<Vec<DefId>>,
46-
non_blanket_impls: Rc<Vec<DefId>>,
43+
blanket_impls: Vec<DefId>,
44+
/// Impls indexed by their simplified self-type, for fast lookup.
45+
non_blanket_impls: FxHashMap<fast_reject::SimplifiedType, Vec<DefId>>,
4746
}
4847

49-
impl TraitImpls {
50-
pub fn iter(&self) -> TraitImplsIter {
51-
TraitImplsIter {
52-
blanket_impls: self.blanket_impls.clone(),
53-
non_blanket_impls: self.non_blanket_impls.clone(),
54-
index: 0
55-
}
56-
}
57-
}
58-
59-
#[derive(Clone)]
60-
pub struct TraitImplsIter {
61-
blanket_impls: Rc<Vec<DefId>>,
62-
non_blanket_impls: Rc<Vec<DefId>>,
63-
index: usize,
64-
}
65-
66-
impl Iterator for TraitImplsIter {
67-
type Item = DefId;
68-
69-
fn next(&mut self) -> Option<DefId> {
70-
if self.index < self.blanket_impls.len() {
71-
let bi_index = self.index;
72-
self.index += 1;
73-
Some(self.blanket_impls[bi_index])
74-
} else {
75-
let nbi_index = self.index - self.blanket_impls.len();
76-
if nbi_index < self.non_blanket_impls.len() {
77-
self.index += 1;
78-
Some(self.non_blanket_impls[nbi_index])
79-
} else {
80-
None
81-
}
82-
}
83-
}
84-
85-
fn size_hint(&self) -> (usize, Option<usize>) {
86-
let items_left = (self.blanket_impls.len() + self.non_blanket_impls.len()) - self.index;
87-
(items_left, Some(items_left))
88-
}
89-
}
90-
91-
impl ExactSizeIterator for TraitImplsIter {}
92-
9348
impl<'a, 'gcx, 'tcx> TraitDef {
9449
pub fn new(def_id: DefId,
9550
unsafety: hir::Unsafety,
@@ -111,20 +66,36 @@ impl<'a, 'gcx, 'tcx> TraitDef {
11166
-> specialization_graph::Ancestors {
11267
specialization_graph::ancestors(tcx, self.def_id, of_impl)
11368
}
69+
}
11470

115-
pub fn for_each_impl<F: FnMut(DefId)>(&self, tcx: TyCtxt<'a, 'gcx, 'tcx>, mut f: F) {
116-
for impl_def_id in tcx.trait_impls_of(self.def_id).iter() {
71+
impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
72+
pub fn for_each_impl<F: FnMut(DefId)>(self, def_id: DefId, mut f: F) {
73+
let impls = self.trait_impls_of(def_id);
74+
75+
for &impl_def_id in impls.blanket_impls.iter() {
11776
f(impl_def_id);
11877
}
78+
79+
for v in impls.non_blanket_impls.values() {
80+
for &impl_def_id in v {
81+
f(impl_def_id);
82+
}
83+
}
11984
}
12085

12186
/// Iterate over every impl that could possibly match the
12287
/// self-type `self_ty`.
123-
pub fn for_each_relevant_impl<F: FnMut(DefId)>(&self,
124-
tcx: TyCtxt<'a, 'gcx, 'tcx>,
88+
pub fn for_each_relevant_impl<F: FnMut(DefId)>(self,
89+
def_id: DefId,
12590
self_ty: Ty<'tcx>,
12691
mut f: F)
12792
{
93+
let impls = self.trait_impls_of(def_id);
94+
95+
for &impl_def_id in impls.blanket_impls.iter() {
96+
f(impl_def_id);
97+
}
98+
12899
// simplify_type(.., false) basically replaces type parameters and
129100
// projections with infer-variables. This is, of course, done on
130101
// the impl trait-ref when it is instantiated, but not on the
@@ -137,23 +108,39 @@ impl<'a, 'gcx, 'tcx> TraitDef {
137108
// replace `S` with anything - this impl of course can't be
138109
// selected, and as there are hundreds of similar impls,
139110
// considering them would significantly harm performance.
140-
let relevant_impls = if let Some(simplified_self_ty) =
141-
fast_reject::simplify_type(tcx, self_ty, true) {
142-
tcx.relevant_trait_impls_for((self.def_id, simplified_self_ty))
143-
} else {
144-
tcx.trait_impls_of(self.def_id)
145-
};
146111

147-
for impl_def_id in relevant_impls.iter() {
148-
f(impl_def_id);
112+
// This depends on the set of all impls for the trait. That is
113+
// unfortunate. When we get red-green recompilation, we would like
114+
// to have a way of knowing whether the set of relevant impls
115+
// changed. The most naive
116+
// way would be to compute the Vec of relevant impls and see whether
117+
// it differs between compilations. That shouldn't be too slow by
118+
// itself - we do quite a bit of work for each relevant impl anyway.
119+
//
120+
// If we want to be faster, we could have separate queries for
121+
// blanket and non-blanket impls, and compare them separately.
122+
//
123+
// I think we'll cross that bridge when we get to it.
124+
if let Some(simp) = fast_reject::simplify_type(self, self_ty, true) {
125+
if let Some(impls) = impls.non_blanket_impls.get(&simp) {
126+
for &impl_def_id in impls {
127+
f(impl_def_id);
128+
}
129+
}
130+
} else {
131+
for v in impls.non_blanket_impls.values() {
132+
for &impl_def_id in v {
133+
f(impl_def_id);
134+
}
135+
}
149136
}
150137
}
151138
}
152139

153140
// Query provider for `trait_impls_of`.
154141
pub(super) fn trait_impls_of_provider<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
155142
trait_id: DefId)
156-
-> TraitImpls {
143+
-> Rc<TraitImpls> {
157144
let remote_impls = if trait_id.is_local() {
158145
// Traits defined in the current crate can't have impls in upstream
159146
// crates, so we don't bother querying the cstore.
@@ -163,7 +150,7 @@ pub(super) fn trait_impls_of_provider<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
163150
};
164151

165152
let mut blanket_impls = Vec::new();
166-
let mut non_blanket_impls = Vec::new();
153+
let mut non_blanket_impls = FxHashMap();
167154

168155
let local_impls = tcx.hir
169156
.trait_impls(trait_id)
@@ -176,47 +163,20 @@ pub(super) fn trait_impls_of_provider<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
176163
continue
177164
}
178165

179-
if fast_reject::simplify_type(tcx, impl_self_ty, false).is_some() {
180-
non_blanket_impls.push(impl_def_id);
166+
if let Some(simplified_self_ty) =
167+
fast_reject::simplify_type(tcx, impl_self_ty, false)
168+
{
169+
non_blanket_impls
170+
.entry(simplified_self_ty)
171+
.or_insert(vec![])
172+
.push(impl_def_id);
181173
} else {
182174
blanket_impls.push(impl_def_id);
183175
}
184176
}
185177

186-
TraitImpls {
187-
blanket_impls: Rc::new(blanket_impls),
188-
non_blanket_impls: Rc::new(non_blanket_impls),
189-
}
190-
}
191-
192-
// Query provider for `relevant_trait_impls_for`.
193-
pub(super) fn relevant_trait_impls_provider<'a, 'tcx>(
194-
tcx: TyCtxt<'a, 'tcx, 'tcx>,
195-
(trait_id, self_ty): (DefId, fast_reject::SimplifiedType))
196-
-> TraitImpls
197-
{
198-
let all_trait_impls = tcx.trait_impls_of(trait_id);
199-
200-
let relevant: Vec<DefId> = all_trait_impls
201-
.non_blanket_impls
202-
.iter()
203-
.cloned()
204-
.filter(|&impl_def_id| {
205-
let impl_self_ty = tcx.type_of(impl_def_id);
206-
let impl_simple_self_ty = fast_reject::simplify_type(tcx,
207-
impl_self_ty,
208-
false).unwrap();
209-
impl_simple_self_ty == self_ty
210-
})
211-
.collect();
212-
213-
if all_trait_impls.non_blanket_impls.len() == relevant.len() {
214-
// If we didn't filter anything out, re-use the existing vec.
215-
all_trait_impls
216-
} else {
217-
TraitImpls {
218-
blanket_impls: all_trait_impls.blanket_impls.clone(),
219-
non_blanket_impls: Rc::new(relevant),
220-
}
221-
}
178+
Rc::new(TraitImpls {
179+
blanket_impls: blanket_impls,
180+
non_blanket_impls: non_blanket_impls,
181+
})
222182
}

src/librustc/ty/util.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -422,7 +422,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
422422

423423
let mut dtor_did = None;
424424
let ty = self.type_of(adt_did);
425-
self.trait_def(drop_trait).for_each_relevant_impl(self, ty, |impl_did| {
425+
self.for_each_relevant_impl(drop_trait, ty, |impl_did| {
426426
if let Some(item) = self.associated_items(impl_did).next() {
427427
if let Ok(()) = validate(self, impl_did) {
428428
dtor_did = Some(item.def_id);

src/librustc_lint/builtin.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -542,9 +542,8 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MissingDebugImplementations {
542542
};
543543

544544
if self.impling_types.is_none() {
545-
let debug_def = cx.tcx.trait_def(debug);
546545
let mut impls = NodeSet();
547-
debug_def.for_each_impl(cx.tcx, |d| {
546+
cx.tcx.for_each_impl(debug, |d| {
548547
if let Some(ty_def) = cx.tcx.type_of(d).ty_to_def_id() {
549548
if let Some(node_id) = cx.tcx.hir.as_local_node_id(ty_def) {
550549
impls.insert(node_id);

src/librustc_mir/transform/qualify_consts.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -244,8 +244,7 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> {
244244
let mut span = None;
245245

246246
self.tcx
247-
.trait_def(drop_trait_id)
248-
.for_each_relevant_impl(self.tcx, self.mir.return_ty, |impl_did| {
247+
.for_each_relevant_impl(drop_trait_id, self.mir.return_ty, |impl_did| {
249248
self.tcx.hir
250249
.as_local_node_id(impl_did)
251250
.and_then(|impl_node_id| self.tcx.hir.find(impl_node_id))

src/librustc_typeck/check/method/probe.rs

+1-3
Original file line numberDiff line numberDiff line change
@@ -736,10 +736,8 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> {
736736
import_id: Option<ast::NodeId>,
737737
trait_def_id: DefId,
738738
item: ty::AssociatedItem) {
739-
let trait_def = self.tcx.trait_def(trait_def_id);
740-
741739
// FIXME(arielb1): can we use for_each_relevant_impl here?
742-
trait_def.for_each_impl(self.tcx, |impl_def_id| {
740+
self.tcx.for_each_impl(trait_def_id, |impl_def_id| {
743741
debug!("assemble_extension_candidates_for_trait_impl: trait_def_id={:?} \
744742
impl_def_id={:?}",
745743
trait_def_id,

0 commit comments

Comments
 (0)