Skip to content

Commit 7f3ec5e

Browse files
Make lookup of associated item by name O(log n)
1 parent 3bb7da2 commit 7f3ec5e

File tree

28 files changed

+216
-135
lines changed

28 files changed

+216
-135
lines changed

src/librustc/arena.rs

+1
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ macro_rules! arena_types {
4848
[] item_local_set: rustc_hir::ItemLocalSet,
4949
[decode] mir_const_qualif: rustc_index::bit_set::BitSet<rustc::mir::Local>,
5050
[] trait_impls_of: rustc::ty::trait_def::TraitImpls,
51+
[] associated_items: rustc::ty::AssociatedItems,
5152
[] dropck_outlives:
5253
rustc::infer::canonical::Canonical<'tcx,
5354
rustc::infer::canonical::QueryResponse<'tcx,

src/librustc/query/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -325,7 +325,7 @@ rustc_queries! {
325325
query associated_item(_: DefId) -> ty::AssocItem {}
326326

327327
/// Collects the associated items defined on a trait or impl.
328-
query associated_items(key: DefId) -> &'tcx [ty::AssocItem] {
328+
query associated_items(key: DefId) -> &'tcx ty::AssociatedItems {
329329
desc { |tcx| "collecting associated items of {}", tcx.def_path_str(key) }
330330
}
331331

src/librustc/traits/specialization_graph.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -81,8 +81,8 @@ impl<'tcx> Node {
8181
}
8282

8383
/// Iterate over the items defined directly by the given (impl or trait) node.
84-
pub fn items(&self, tcx: TyCtxt<'tcx>) -> &'tcx [ty::AssocItem] {
85-
tcx.associated_items(self.def_id())
84+
pub fn items(&self, tcx: TyCtxt<'tcx>) -> impl 'tcx + Iterator<Item = &'tcx ty::AssocItem> {
85+
tcx.associated_items(self.def_id()).in_definition_order()
8686
}
8787

8888
/// Finds an associated item defined in this node.
@@ -99,7 +99,7 @@ impl<'tcx> Node {
9999
use crate::ty::AssocKind::*;
100100

101101
tcx.associated_items(self.def_id())
102-
.iter()
102+
.filter_by_name_unhygienic(trait_item_name.name)
103103
.find(move |impl_item| {
104104
match (trait_item_kind, impl_item.kind) {
105105
| (Const, Const)

src/librustc/ty/adjustment.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ impl<'tcx> OverloadedDeref<'tcx> {
122122
};
123123
let method_def_id = tcx
124124
.associated_items(trait_def_id.unwrap())
125-
.iter()
125+
.in_definition_order()
126126
.find(|m| m.kind == ty::AssocKind::Method)
127127
.unwrap()
128128
.def_id;

src/librustc/ty/instance.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -337,7 +337,7 @@ impl<'tcx> Instance<'tcx> {
337337
let fn_once = tcx.lang_items().fn_once_trait().unwrap();
338338
let call_once = tcx
339339
.associated_items(fn_once)
340-
.iter()
340+
.in_definition_order()
341341
.find(|it| it.kind == ty::AssocKind::Method)
342342
.unwrap()
343343
.def_id;

src/librustc/ty/mod.rs

+79-3
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ use rustc_attr as attr;
3030
use rustc_data_structures::captures::Captures;
3131
use rustc_data_structures::fx::FxHashMap;
3232
use rustc_data_structures::fx::FxIndexMap;
33+
use rustc_data_structures::sorted_map::SortedIndexMultiMap;
3334
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
3435
use rustc_data_structures::sync::{self, par_iter, Lrc, ParallelIterator};
3536
use rustc_hir as hir;
@@ -264,6 +265,81 @@ impl AssocItem {
264265
}
265266
}
266267

268+
/// A list of `ty::AssocItem`s in definition order that allows for efficient lookup by name.
269+
///
270+
/// When doing lookup by name, we try to postpone hygienic comparison for as long as possible since
271+
/// it is relatively expensive. Instead, items are indexed by `Symbol` and hygienic comparison is
272+
/// done only on items with the same name.
273+
#[derive(Debug, Clone, PartialEq, HashStable)]
274+
pub struct AssociatedItems {
275+
items: SortedIndexMultiMap<u32, Symbol, ty::AssocItem>,
276+
}
277+
278+
impl AssociatedItems {
279+
/// Constructs an `AssociatedItems` map from a series of `ty::AssocItem`s in definition order.
280+
pub fn new(items_in_def_order: Vec<ty::AssocItem>) -> Self {
281+
let items = items_in_def_order.into_iter().map(|item| (item.ident.name, item)).collect();
282+
AssociatedItems { items }
283+
}
284+
285+
/// Returns a slice of associated items in the order they were defined.
286+
///
287+
/// New code should avoid relying on definition order. If you need a particular associated item
288+
/// for a known trait, make that trait a lang item instead of indexing this array.
289+
pub fn in_definition_order(&self) -> impl '_ + Iterator<Item = &ty::AssocItem> {
290+
self.items.iter().map(|(_, v)| v)
291+
}
292+
293+
/// Returns an iterator over all associated items with the given name, ignoring hygiene.
294+
pub fn filter_by_name_unhygienic(
295+
&self,
296+
name: Symbol,
297+
) -> impl '_ + Iterator<Item = &ty::AssocItem> {
298+
self.items.get_by_key(&name)
299+
}
300+
301+
/// Returns an iterator over all associated items with the given name.
302+
///
303+
/// Multiple items may have the same name if they are in different `Namespace`s. For example,
304+
/// an associated type can have the same name as a method. Use one of the `find_by_name_and_*`
305+
/// methods below if you know which item you are looking for.
306+
pub fn filter_by_name(
307+
&'a self,
308+
tcx: TyCtxt<'a>,
309+
ident: Ident,
310+
parent_def_id: DefId,
311+
) -> impl 'a + Iterator<Item = &'a ty::AssocItem> {
312+
self.filter_by_name_unhygienic(ident.name)
313+
.filter(move |item| tcx.hygienic_eq(ident, item.ident, parent_def_id))
314+
}
315+
316+
/// Returns the associated item with the given name and `AssocKind`, if one exists.
317+
pub fn find_by_name_and_kind(
318+
&self,
319+
tcx: TyCtxt<'_>,
320+
ident: Ident,
321+
kind: AssocKind,
322+
parent_def_id: DefId,
323+
) -> Option<&ty::AssocItem> {
324+
self.filter_by_name_unhygienic(ident.name)
325+
.filter(|item| item.kind == kind)
326+
.find(|item| tcx.hygienic_eq(ident, item.ident, parent_def_id))
327+
}
328+
329+
/// Returns the associated item with the given name in the given `Namespace`, if one exists.
330+
pub fn find_by_name_and_namespace(
331+
&self,
332+
tcx: TyCtxt<'_>,
333+
ident: Ident,
334+
ns: Namespace,
335+
parent_def_id: DefId,
336+
) -> Option<&ty::AssocItem> {
337+
self.filter_by_name_unhygienic(ident.name)
338+
.filter(|item| item.kind.namespace() == ns)
339+
.find(|item| tcx.hygienic_eq(ident, item.ident, parent_def_id))
340+
}
341+
}
342+
267343
#[derive(Clone, Debug, PartialEq, Eq, Copy, RustcEncodable, RustcDecodable, HashStable)]
268344
pub enum Visibility {
269345
/// Visible everywhere (including in other crates).
@@ -2738,14 +2814,14 @@ impl<'tcx> TyCtxt<'tcx> {
27382814
.for_each(|&body_id| f(self.hir().body_owner_def_id(body_id)));
27392815
}
27402816

2741-
pub fn provided_trait_methods(self, id: DefId) -> impl Iterator<Item = &'tcx AssocItem> {
2817+
pub fn provided_trait_methods(self, id: DefId) -> impl 'tcx + Iterator<Item = &'tcx AssocItem> {
27422818
self.associated_items(id)
2743-
.iter()
2819+
.in_definition_order()
27442820
.filter(|item| item.kind == AssocKind::Method && item.defaultness.has_value())
27452821
}
27462822

27472823
pub fn trait_relevant_for_never(self, did: DefId) -> bool {
2748-
self.associated_items(did).iter().any(|item| item.relevant_for_never())
2824+
self.associated_items(did).in_definition_order().any(|item| item.relevant_for_never())
27492825
}
27502826

27512827
pub fn opt_item_name(self, def_id: DefId) -> Option<Ident> {

src/librustc/ty/sty.rs

+1-5
Original file line numberDiff line numberDiff line change
@@ -1066,11 +1066,7 @@ impl<'tcx> ProjectionTy<'tcx> {
10661066
) -> ProjectionTy<'tcx> {
10671067
let item_def_id = tcx
10681068
.associated_items(trait_ref.def_id)
1069-
.iter()
1070-
.find(|item| {
1071-
item.kind == ty::AssocKind::Type
1072-
&& tcx.hygienic_eq(item_name, item.ident, trait_ref.def_id)
1073-
})
1069+
.find_by_name_and_kind(tcx, item_name, ty::AssocKind::Type, trait_ref.def_id)
10741070
.unwrap()
10751071
.def_id;
10761072

src/librustc/ty/util.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -357,7 +357,7 @@ impl<'tcx> TyCtxt<'tcx> {
357357
let mut dtor_did = None;
358358
let ty = self.type_of(adt_did);
359359
self.for_each_relevant_impl(drop_trait, ty, |impl_did| {
360-
if let Some(item) = self.associated_items(impl_did).first() {
360+
if let Some(item) = self.associated_items(impl_did).in_definition_order().nth(0) {
361361
if validate(self, impl_did).is_ok() {
362362
dtor_did = Some(item.def_id);
363363
}

src/librustc_infer/traits/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -537,7 +537,7 @@ fn vtable_methods<'tcx>(
537537
tcx.arena.alloc_from_iter(supertraits(tcx, trait_ref).flat_map(move |trait_ref| {
538538
let trait_methods = tcx
539539
.associated_items(trait_ref.def_id())
540-
.iter()
540+
.in_definition_order()
541541
.filter(|item| item.kind == ty::AssocKind::Method);
542542

543543
// Now list each method's DefId and InternalSubsts (for within its trait).

src/librustc_infer/traits/object_safety.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,7 @@ fn object_safety_violations_for_trait(
213213
// Check methods for violations.
214214
let mut violations: Vec<_> = tcx
215215
.associated_items(trait_def_id)
216-
.iter()
216+
.in_definition_order()
217217
.filter(|item| item.kind == ty::AssocKind::Method)
218218
.filter_map(|item| {
219219
object_safety_violation_for_method(tcx, trait_def_id, &item)
@@ -289,7 +289,7 @@ fn object_safety_violations_for_trait(
289289

290290
violations.extend(
291291
tcx.associated_items(trait_def_id)
292-
.iter()
292+
.in_definition_order()
293293
.filter(|item| item.kind == ty::AssocKind::Const)
294294
.map(|item| ObjectSafetyViolation::AssocConst(item.ident.name, item.ident.span)),
295295
);
@@ -646,7 +646,7 @@ fn object_ty_for_trait<'tcx>(
646646
let mut associated_types = traits::supertraits(tcx, ty::Binder::dummy(trait_ref))
647647
.flat_map(|super_trait_ref| {
648648
tcx.associated_items(super_trait_ref.def_id())
649-
.iter()
649+
.in_definition_order()
650650
.map(move |item| (super_trait_ref, item))
651651
})
652652
.filter(|(_, item)| item.kind == ty::AssocKind::Type)

src/librustc_infer/traits/util.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -586,7 +586,7 @@ pub fn count_own_vtable_entries(tcx: TyCtxt<'tcx>, trait_ref: ty::PolyTraitRef<'
586586
let mut entries = 0;
587587
// Count number of methods and add them to the total offset.
588588
// Skip over associated types and constants.
589-
for trait_item in tcx.associated_items(trait_ref.def_id()) {
589+
for trait_item in tcx.associated_items(trait_ref.def_id()).in_definition_order() {
590590
if trait_item.kind == ty::AssocKind::Method {
591591
entries += 1;
592592
}
@@ -606,7 +606,7 @@ pub fn get_vtable_index_of_object_method<N>(
606606
// add them to the total offset.
607607
// Skip over associated types and constants.
608608
let mut entries = object.vtable_base;
609-
for trait_item in tcx.associated_items(object.upcast_trait_ref.def_id()) {
609+
for trait_item in tcx.associated_items(object.upcast_trait_ref.def_id()).in_definition_order() {
610610
if trait_item.def_id == method_def_id {
611611
// The item with the ID we were given really ought to be a method.
612612
assert_eq!(trait_item.kind, ty::AssocKind::Method);

src/librustc_infer/traits/wf.rs

+5-2
Original file line numberDiff line numberDiff line change
@@ -318,7 +318,10 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
318318
};
319319

320320
if let Elaborate::All = elaborate {
321-
let trait_assoc_items = tcx.associated_items(trait_ref.def_id);
321+
// FIXME: Make `extend_cause_with_original_assoc_item_obligation` take an iterator
322+
// instead of a slice.
323+
let trait_assoc_items: Vec<_> =
324+
tcx.associated_items(trait_ref.def_id).in_definition_order().copied().collect();
322325

323326
let predicates = obligations.iter().map(|obligation| obligation.predicate).collect();
324327
let implied_obligations = traits::elaborate_predicates(tcx, predicates);
@@ -327,7 +330,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
327330
extend_cause_with_original_assoc_item_obligation(
328331
&mut cause,
329332
&pred,
330-
trait_assoc_items,
333+
&*trait_assoc_items,
331334
);
332335
traits::Obligation::new(cause, param_env, pred)
333336
});

src/librustc_mir/shim.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceDef<'tcx>) -> &'tcx
6868
let fn_mut = tcx.lang_items().fn_mut_trait().unwrap();
6969
let call_mut = tcx
7070
.associated_items(fn_mut)
71-
.iter()
71+
.in_definition_order()
7272
.find(|it| it.kind == ty::AssocKind::Method)
7373
.unwrap()
7474
.def_id;

src/librustc_mir/util/elaborate_drops.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -539,7 +539,7 @@ where
539539
debug!("destructor_call_block({:?}, {:?})", self, succ);
540540
let tcx = self.tcx();
541541
let drop_trait = tcx.lang_items().drop_trait().unwrap();
542-
let drop_fn = tcx.associated_items(drop_trait)[0];
542+
let drop_fn = tcx.associated_items(drop_trait).in_definition_order().nth(0).unwrap();
543543
let ty = self.place_ty(self.place);
544544
let substs = tcx.mk_substs_trait(ty, &[]);
545545

src/librustc_mir_build/hair/cx/mod.rs

+12-10
Original file line numberDiff line numberDiff line change
@@ -167,17 +167,19 @@ impl<'a, 'tcx> Cx<'a, 'tcx> {
167167
params: &[GenericArg<'tcx>],
168168
) -> &'tcx ty::Const<'tcx> {
169169
let substs = self.tcx.mk_substs_trait(self_ty, params);
170-
for item in self.tcx.associated_items(trait_def_id) {
171-
// The unhygienic comparison here is acceptable because this is only
172-
// used on known traits.
173-
if item.kind == ty::AssocKind::Method && item.ident.name == method_name {
174-
let method_ty = self.tcx.type_of(item.def_id);
175-
let method_ty = method_ty.subst(self.tcx, substs);
176-
return ty::Const::zero_sized(self.tcx, method_ty);
177-
}
178-
}
179170

180-
bug!("found no method `{}` in `{:?}`", method_name, trait_def_id);
171+
// The unhygienic comparison here is acceptable because this is only
172+
// used on known traits.
173+
let item = self
174+
.tcx
175+
.associated_items(trait_def_id)
176+
.filter_by_name_unhygienic(method_name)
177+
.find(|item| item.kind == ty::AssocKind::Method)
178+
.expect("trait method not found");
179+
180+
let method_ty = self.tcx.type_of(item.def_id);
181+
let method_ty = method_ty.subst(self.tcx, substs);
182+
ty::Const::zero_sized(self.tcx, method_ty)
181183
}
182184

183185
crate fn all_fields(&mut self, adt_def: &ty::AdtDef, variant_index: VariantIdx) -> Vec<Field> {

src/librustc_passes/stability.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -468,8 +468,8 @@ impl Visitor<'tcx> for Checker<'tcx> {
468468
let trait_item_def_id = self
469469
.tcx
470470
.associated_items(trait_did)
471-
.iter()
472-
.find(|item| item.ident.name == impl_item.ident.name)
471+
.filter_by_name_unhygienic(impl_item.ident.name)
472+
.next()
473473
.map(|item| item.def_id);
474474
if let Some(def_id) = trait_item_def_id {
475475
// Pass `None` to skip deprecation warnings.

src/librustc_save_analysis/lib.rs

+10-9
Original file line numberDiff line numberDiff line change
@@ -408,7 +408,6 @@ impl<'l, 'tcx> SaveContext<'l, 'tcx> {
408408
qualname.push_str(&self.tcx.hir().hir_to_pretty_string(self_ty.hir_id));
409409

410410
let trait_id = self.tcx.trait_id_of_impl(impl_id);
411-
let mut decl_id = None;
412411
let mut docs = String::new();
413412
let mut attrs = vec![];
414413
let hir_id = self.tcx.hir().node_to_hir_id(id);
@@ -417,15 +416,18 @@ impl<'l, 'tcx> SaveContext<'l, 'tcx> {
417416
attrs = item.attrs.to_vec();
418417
}
419418

419+
let mut decl_id = None;
420420
if let Some(def_id) = trait_id {
421421
// A method in a trait impl.
422422
qualname.push_str(" as ");
423423
qualname.push_str(&self.tcx.def_path_str(def_id));
424-
self.tcx
424+
425+
decl_id = self
426+
.tcx
425427
.associated_items(def_id)
426-
.iter()
427-
.find(|item| item.ident.name == ident.name)
428-
.map(|item| decl_id = Some(item.def_id));
428+
.filter_by_name_unhygienic(ident.name)
429+
.next()
430+
.map(|item| item.def_id);
429431
}
430432
qualname.push_str(">");
431433

@@ -716,12 +718,11 @@ impl<'l, 'tcx> SaveContext<'l, 'tcx> {
716718
Res::Def(HirDefKind::Method, decl_id) => {
717719
let def_id = if decl_id.is_local() {
718720
let ti = self.tcx.associated_item(decl_id);
721+
719722
self.tcx
720723
.associated_items(ti.container.id())
721-
.iter()
722-
.find(|item| {
723-
item.ident.name == ti.ident.name && item.defaultness.has_value()
724-
})
724+
.filter_by_name_unhygienic(ti.ident.name)
725+
.find(|item| item.defaultness.has_value())
725726
.map(|item| item.def_id)
726727
} else {
727728
None

src/librustc_ty/ty.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -210,10 +210,10 @@ fn associated_item_def_ids(tcx: TyCtxt<'_>, def_id: DefId) -> &[DefId] {
210210
}
211211
}
212212

213-
fn associated_items<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> &'tcx [ty::AssocItem] {
214-
tcx.arena.alloc_from_iter(
215-
tcx.associated_item_def_ids(def_id).iter().map(|did| tcx.associated_item(*did)),
216-
)
213+
fn associated_items<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> &'tcx ty::AssociatedItems {
214+
let items =
215+
tcx.associated_item_def_ids(def_id).iter().map(|did| tcx.associated_item(*did)).collect();
216+
tcx.arena.alloc(ty::AssociatedItems::new(items))
217217
}
218218

219219
fn def_span(tcx: TyCtxt<'_>, def_id: DefId) -> Span {

0 commit comments

Comments
 (0)