Skip to content

Commit 11c7e82

Browse files
Make the inherent impl overlap check linear-time
1 parent 71c7e14 commit 11c7e82

File tree

1 file changed

+73
-83
lines changed

1 file changed

+73
-83
lines changed

src/librustc_typeck/coherence/inherent_impls_overlap.rs

+73-83
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
use crate::namespace::Namespace;
22
use rustc::traits::{self, IntercrateMode, SkipLeakCheck};
33
use rustc::ty::{AssocItem, TyCtxt};
4+
use rustc_data_structures::fx::FxHashMap;
45
use rustc_errors::struct_span_err;
56
use rustc_hir as hir;
67
use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE};
78
use rustc_hir::itemlikevisit::ItemLikeVisitor;
9+
use smallvec::SmallVec;
10+
use std::collections::hash_map::Entry;
811

912
pub fn crate_inherent_impls_overlap_check(tcx: TyCtxt<'_>, crate_num: CrateNum) {
1013
assert_eq!(crate_num, LOCAL_CRATE);
@@ -17,78 +20,12 @@ struct InherentOverlapChecker<'tcx> {
1720
}
1821

1922
impl InherentOverlapChecker<'tcx> {
20-
/// Checks whether any associated items in impls 1 and 2 share the same identifier and
21-
/// namespace.
22-
fn impls_have_common_items(&self, impl1: DefId, impl2: DefId) -> bool {
23-
let impl_items1 = self.tcx.associated_items(impl1);
24-
let impl_items2 = self.tcx.associated_items(impl2);
25-
26-
for item1 in &impl_items1[..] {
27-
for item2 in &impl_items2[..] {
28-
// Avoid costly `.modern()` calls as much as possible by doing them as late as we
29-
// can. Compare raw symbols first.
30-
if item1.ident.name == item2.ident.name
31-
&& Namespace::from(item1.kind) == Namespace::from(item2.kind)
32-
{
33-
// Symbols and namespace match, compare hygienically.
34-
if item1.ident.modern() == item2.ident.modern() {
35-
return true;
36-
}
37-
}
38-
}
39-
}
40-
41-
false
42-
}
43-
44-
fn check_for_common_items_in_impls(
45-
&self,
46-
impl1: DefId,
47-
impl2: DefId,
48-
overlap: traits::OverlapResult<'_>,
49-
) {
50-
let name_and_namespace =
51-
|assoc: &AssocItem| (assoc.ident.modern(), Namespace::from(assoc.kind));
52-
53-
let impl_items1 = self.tcx.associated_items(impl1);
54-
let impl_items2 = self.tcx.associated_items(impl2);
55-
56-
for item1 in &impl_items1[..] {
57-
let (name, namespace) = name_and_namespace(item1);
58-
59-
for item2 in &impl_items2[..] {
60-
if (name, namespace) == name_and_namespace(item2) {
61-
let mut err = struct_span_err!(
62-
self.tcx.sess,
63-
self.tcx.span_of_impl(item1.def_id).unwrap(),
64-
E0592,
65-
"duplicate definitions with name `{}`",
66-
name
67-
);
68-
err.span_label(
69-
self.tcx.span_of_impl(item1.def_id).unwrap(),
70-
format!("duplicate definitions for `{}`", name),
71-
);
72-
err.span_label(
73-
self.tcx.span_of_impl(item2.def_id).unwrap(),
74-
format!("other definition for `{}`", name),
75-
);
76-
77-
for cause in &overlap.intercrate_ambiguity_causes {
78-
cause.add_intercrate_ambiguity_hint(&mut err);
79-
}
80-
81-
if overlap.involves_placeholder {
82-
traits::add_placeholder_note(&mut err);
83-
}
23+
/// Emits an error if the impls defining `item1` and `item2` overlap.
24+
fn forbid_overlap(&self, item1: &AssocItem, item2: &AssocItem) {
25+
let impl1_def_id = item1.container.id();
26+
let impl2_def_id = item2.container.id();
27+
let name = item1.ident;
8428

85-
err.emit();
86-
}
87-
}
88-
}
89-
}
90-
91-
fn check_for_overlapping_inherent_impls(&self, impl1_def_id: DefId, impl2_def_id: DefId) {
9229
traits::overlapping_impls(
9330
self.tcx,
9431
impl1_def_id,
@@ -98,12 +35,73 @@ impl InherentOverlapChecker<'tcx> {
9835
// inherent impls without warning.
9936
SkipLeakCheck::Yes,
10037
|overlap| {
101-
self.check_for_common_items_in_impls(impl1_def_id, impl2_def_id, overlap);
102-
false
38+
let mut err = struct_span_err!(
39+
self.tcx.sess,
40+
self.tcx.span_of_impl(item1.def_id).unwrap(),
41+
E0592,
42+
"duplicate definitions with name `{}`",
43+
name
44+
);
45+
err.span_label(
46+
self.tcx.span_of_impl(item1.def_id).unwrap(),
47+
format!("duplicate definitions for `{}`", name),
48+
);
49+
err.span_label(
50+
self.tcx.span_of_impl(item2.def_id).unwrap(),
51+
format!("other definition for `{}`", name),
52+
);
53+
54+
for cause in &overlap.intercrate_ambiguity_causes {
55+
cause.add_intercrate_ambiguity_hint(&mut err);
56+
}
57+
58+
if overlap.involves_placeholder {
59+
traits::add_placeholder_note(&mut err);
60+
}
61+
62+
err.emit();
10363
},
104-
|| true,
64+
|| {},
10565
);
10666
}
67+
68+
fn process_local_ty(&self, ty_def_id: DefId) {
69+
let impls = self.tcx.inherent_impls(ty_def_id);
70+
71+
// Create a map from `Symbol`s to the list of `&'tcx AssocItem`s with that name, across all
72+
// inherent impls.
73+
let mut item_map: FxHashMap<_, SmallVec<[&AssocItem; 1]>> = FxHashMap::default();
74+
for impl_def_id in impls {
75+
let impl_items = self.tcx.associated_items(*impl_def_id);
76+
77+
for impl_item in impl_items {
78+
match item_map.entry(impl_item.ident.name) {
79+
Entry::Occupied(mut occupied) => {
80+
// Do a proper name check respecting namespaces and hygiene against all
81+
// items in this map entry.
82+
let (ident1, ns1) =
83+
(impl_item.ident.modern(), Namespace::from(impl_item.kind));
84+
85+
for impl_item2 in occupied.get() {
86+
let (ident2, ns2) =
87+
(impl_item2.ident.modern(), Namespace::from(impl_item2.kind));
88+
89+
if ns1 == ns2 && ident1 == ident2 {
90+
// Items with same name. Their containing impls must not overlap.
91+
92+
self.forbid_overlap(impl_item2, impl_item);
93+
}
94+
}
95+
96+
occupied.get_mut().push(impl_item);
97+
}
98+
Entry::Vacant(vacant) => {
99+
vacant.insert(SmallVec::from([impl_item]));
100+
}
101+
}
102+
}
103+
}
104+
}
107105
}
108106

109107
impl ItemLikeVisitor<'v> for InherentOverlapChecker<'tcx> {
@@ -114,15 +112,7 @@ impl ItemLikeVisitor<'v> for InherentOverlapChecker<'tcx> {
114112
| hir::ItemKind::Trait(..)
115113
| hir::ItemKind::Union(..) => {
116114
let ty_def_id = self.tcx.hir().local_def_id(item.hir_id);
117-
let impls = self.tcx.inherent_impls(ty_def_id);
118-
119-
for (i, &impl1_def_id) in impls.iter().enumerate() {
120-
for &impl2_def_id in &impls[(i + 1)..] {
121-
if self.impls_have_common_items(impl1_def_id, impl2_def_id) {
122-
self.check_for_overlapping_inherent_impls(impl1_def_id, impl2_def_id);
123-
}
124-
}
125-
}
115+
self.process_local_ty(ty_def_id);
126116
}
127117
_ => {}
128118
}

0 commit comments

Comments
 (0)