1 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
2 use rustc_errors::struct_span_err;
4 use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE};
5 use rustc_hir::itemlikevisit::ItemLikeVisitor;
6 use rustc_middle::ty::{self, TyCtxt};
7 use rustc_span::Symbol;
8 use rustc_trait_selection::traits::{self, SkipLeakCheck};
9 use smallvec::SmallVec;
10 use std::collections::hash_map::Entry;
12 pub fn crate_inherent_impls_overlap_check(tcx: TyCtxt<'_>, crate_num: CrateNum) {
13 assert_eq!(crate_num, LOCAL_CRATE);
14 let krate = tcx.hir().krate();
15 krate.visit_all_item_likes(&mut InherentOverlapChecker { tcx });
18 struct InherentOverlapChecker<'tcx> {
22 impl InherentOverlapChecker<'tcx> {
23 /// Checks whether any associated items in impls 1 and 2 share the same identifier and
25 fn impls_have_common_items(
27 impl_items1: &ty::AssocItems<'_>,
28 impl_items2: &ty::AssocItems<'_>,
30 let mut impl_items1 = &impl_items1;
31 let mut impl_items2 = &impl_items2;
33 // Performance optimization: iterate over the smaller list
34 if impl_items1.len() > impl_items2.len() {
35 std::mem::swap(&mut impl_items1, &mut impl_items2);
38 for item1 in impl_items1.in_definition_order() {
39 let collision = impl_items2
40 .filter_by_name_unhygienic(item1.ident.name)
41 .any(|item2| self.compare_hygienically(item1, item2));
51 fn compare_hygienically(&self, item1: &ty::AssocItem, item2: &ty::AssocItem) -> bool {
52 // Symbols and namespace match, compare hygienically.
53 item1.kind.namespace() == item2.kind.namespace()
54 && item1.ident.normalize_to_macros_2_0() == item2.ident.normalize_to_macros_2_0()
57 fn check_for_common_items_in_impls(
61 overlap: traits::OverlapResult<'_>,
63 let impl_items1 = self.tcx.associated_items(impl1);
64 let impl_items2 = self.tcx.associated_items(impl2);
66 for item1 in impl_items1.in_definition_order() {
67 let collision = impl_items2
68 .filter_by_name_unhygienic(item1.ident.name)
69 .find(|item2| self.compare_hygienically(item1, item2));
71 if let Some(item2) = collision {
72 let name = item1.ident.normalize_to_macros_2_0();
73 let mut err = struct_span_err!(
75 self.tcx.span_of_impl(item1.def_id).unwrap(),
77 "duplicate definitions with name `{}`",
81 self.tcx.span_of_impl(item1.def_id).unwrap(),
82 format!("duplicate definitions for `{}`", name),
85 self.tcx.span_of_impl(item2.def_id).unwrap(),
86 format!("other definition for `{}`", name),
89 for cause in &overlap.intercrate_ambiguity_causes {
90 cause.add_intercrate_ambiguity_hint(&mut err);
93 if overlap.involves_placeholder {
94 traits::add_placeholder_note(&mut err);
102 fn check_for_overlapping_inherent_impls(&self, impl1_def_id: DefId, impl2_def_id: DefId) {
103 traits::overlapping_impls(
107 // We go ahead and just skip the leak check for
108 // inherent impls without warning.
111 self.check_for_common_items_in_impls(impl1_def_id, impl2_def_id, overlap);
119 impl ItemLikeVisitor<'v> for InherentOverlapChecker<'tcx> {
120 fn visit_item(&mut self, item: &'v hir::Item<'v>) {
122 hir::ItemKind::Enum(..)
123 | hir::ItemKind::Struct(..)
124 | hir::ItemKind::Trait(..)
125 | hir::ItemKind::Union(..) => {
126 let impls = self.tcx.inherent_impls(item.def_id);
128 // If there is only one inherent impl block,
129 // there is nothing to overlap check it with
130 if impls.len() <= 1 {
134 let impls_items = impls
136 .map(|impl_def_id| (impl_def_id, self.tcx.associated_items(*impl_def_id)))
137 .collect::<SmallVec<[_; 8]>>();
139 // Perform a O(n^2) algorithm for small n,
140 // otherwise switch to an allocating algorithm with
141 // faster asymptotic runtime.
142 const ALLOCATING_ALGO_THRESHOLD: usize = 500;
143 if impls.len() < ALLOCATING_ALGO_THRESHOLD {
144 for (i, &(&impl1_def_id, impl_items1)) in impls_items.iter().enumerate() {
145 for &(&impl2_def_id, impl_items2) in &impls_items[(i + 1)..] {
146 if self.impls_have_common_items(impl_items1, impl_items2) {
147 self.check_for_overlapping_inherent_impls(
155 // Build a set of connected regions of impl blocks.
156 // Two impl blocks are regarded as connected if they share
157 // an item with the same unhygienic identifier.
158 // After we have assembled the connected regions,
159 // run the O(n^2) algorithm on each connected region.
160 // This is advantageous to running the algorithm over the
161 // entire graph when there are many connected regions.
163 struct ConnectedRegion {
164 idents: SmallVec<[Symbol; 8]>,
165 impl_blocks: FxHashSet<usize>,
167 // Highest connected region id
168 let mut highest_region_id = 0;
169 let mut connected_region_ids = FxHashMap::default();
170 let mut connected_regions = FxHashMap::default();
172 for (i, &(&_impl_def_id, impl_items)) in impls_items.iter().enumerate() {
173 if impl_items.len() == 0 {
176 // First obtain a list of existing connected region ids
177 let mut idents_to_add = SmallVec::<[Symbol; 8]>::new();
179 .in_definition_order()
181 let entry = connected_region_ids.entry(item.ident.name);
182 if let Entry::Occupied(e) = &entry {
185 idents_to_add.push(item.ident.name);
189 .collect::<FxHashSet<usize>>();
192 let id_to_set = if ids.len() == 0 {
193 // Create a new connected region
194 let region = ConnectedRegion {
195 idents: idents_to_add,
196 impl_blocks: std::iter::once(i).collect(),
198 connected_regions.insert(highest_region_id, region);
199 (highest_region_id, highest_region_id += 1).0
201 // Take the only id inside the list
202 let id_to_set = *ids.iter().next().unwrap();
203 let region = connected_regions.get_mut(&id_to_set).unwrap();
204 region.impl_blocks.insert(i);
205 region.idents.extend_from_slice(&idents_to_add);
208 let (_id, region) = connected_regions.iter().next().unwrap();
209 // Update the connected region ids
210 for ident in region.idents.iter() {
211 connected_region_ids.insert(*ident, id_to_set);
215 // We have multiple connected regions to merge.
216 // In the worst case this might add impl blocks
217 // one by one and can thus be O(n^2) in the size
218 // of the resulting final connected region, but
219 // this is no issue as the final step to check
220 // for overlaps runs in O(n^2) as well.
222 // Take the smallest id from the list
223 let id_to_set = *ids.iter().min().unwrap();
225 // Sort the id list so that the algorithm is deterministic
226 let mut ids = ids.into_iter().collect::<SmallVec<[_; 8]>>();
229 let mut region = connected_regions.remove(&id_to_set).unwrap();
230 region.idents.extend_from_slice(&idents_to_add);
231 region.impl_blocks.insert(i);
233 for &id in ids.iter() {
237 let r = connected_regions.remove(&id).unwrap();
238 // Update the connected region ids
239 for ident in r.idents.iter() {
240 connected_region_ids.insert(*ident, id_to_set);
242 region.idents.extend_from_slice(&r.idents);
243 region.impl_blocks.extend(r.impl_blocks);
245 connected_regions.insert(id_to_set, region);
251 "churning through {} components (sum={}, avg={}, var={}, max={})",
252 connected_regions.len(),
254 impls.len() / connected_regions.len(),
256 let avg = impls.len() / connected_regions.len();
257 let s = connected_regions
259 .map(|r| r.1.impl_blocks.len() as isize - avg as isize)
260 .map(|v| v.abs() as usize)
262 s / connected_regions.len()
264 connected_regions.iter().map(|r| r.1.impl_blocks.len()).max().unwrap()
266 // List of connected regions is built. Now, run the overlap check
267 // for each pair of impl blocks in the same connected region.
268 for (_id, region) in connected_regions.into_iter() {
269 let mut impl_blocks =
270 region.impl_blocks.into_iter().collect::<SmallVec<[_; 8]>>();
272 for (i, &impl1_items_idx) in impl_blocks.iter().enumerate() {
273 let &(&impl1_def_id, impl_items1) = &impls_items[impl1_items_idx];
274 for &impl2_items_idx in impl_blocks[(i + 1)..].iter() {
275 let &(&impl2_def_id, impl_items2) = &impls_items[impl2_items_idx];
276 if self.impls_have_common_items(impl_items1, impl_items2) {
277 self.check_for_overlapping_inherent_impls(
291 fn visit_trait_item(&mut self, _trait_item: &hir::TraitItem<'v>) {}
293 fn visit_impl_item(&mut self, _impl_item: &hir::ImplItem<'v>) {}
295 fn visit_foreign_item(&mut self, _foreign_item: &hir::ForeignItem<'v>) {}