]> git.proxmox.com Git - rustc.git/blame - compiler/rustc_typeck/src/coherence/inherent_impls_overlap.rs
New upstream version 1.57.0+dfsg1
[rustc.git] / compiler / rustc_typeck / src / coherence / inherent_impls_overlap.rs
CommitLineData
fc512014 1use rustc_data_structures::fx::{FxHashMap, FxHashSet};
dfeec247
XL
2use rustc_errors::struct_span_err;
3use rustc_hir as hir;
17df50a5 4use rustc_hir::def_id::DefId;
dfeec247 5use rustc_hir::itemlikevisit::ItemLikeVisitor;
c295e0f8 6use rustc_index::vec::IndexVec;
29967ef6 7use rustc_middle::ty::{self, TyCtxt};
fc512014 8use rustc_span::Symbol;
ba9703b0 9use rustc_trait_selection::traits::{self, SkipLeakCheck};
29967ef6 10use smallvec::SmallVec;
fc512014 11use std::collections::hash_map::Entry;
60c5eb7d 12
17df50a5 13pub fn crate_inherent_impls_overlap_check(tcx: TyCtxt<'_>, (): ()) {
c295e0f8 14 tcx.hir().visit_all_item_likes(&mut InherentOverlapChecker { tcx });
cc61c64b
XL
15}
16
dc9dc135
XL
17struct InherentOverlapChecker<'tcx> {
18 tcx: TyCtxt<'tcx>,
cc61c64b
XL
19}
20
dc9dc135 21impl InherentOverlapChecker<'tcx> {
74b04a01
XL
22 /// Checks whether any associated items in impls 1 and 2 share the same identifier and
23 /// namespace.
29967ef6
XL
24 fn impls_have_common_items(
25 &self,
cdc7bbd5
XL
26 impl_items1: &ty::AssocItems<'_>,
27 impl_items2: &ty::AssocItems<'_>,
29967ef6
XL
28 ) -> bool {
29 let mut impl_items1 = &impl_items1;
30 let mut impl_items2 = &impl_items2;
31
32 // Performance optimization: iterate over the smaller list
33 if impl_items1.len() > impl_items2.len() {
34 std::mem::swap(&mut impl_items1, &mut impl_items2);
35 }
74b04a01
XL
36
37 for item1 in impl_items1.in_definition_order() {
fc512014
XL
38 let collision = impl_items2
39 .filter_by_name_unhygienic(item1.ident.name)
40 .any(|item2| self.compare_hygienically(item1, item2));
74b04a01
XL
41
42 if collision {
43 return true;
44 }
45 }
46
47 false
48 }
49
fc512014
XL
50 fn compare_hygienically(&self, item1: &ty::AssocItem, item2: &ty::AssocItem) -> bool {
51 // Symbols and namespace match, compare hygienically.
52 item1.kind.namespace() == item2.kind.namespace()
53 && item1.ident.normalize_to_macros_2_0() == item2.ident.normalize_to_macros_2_0()
54 }
55
dfeec247
XL
56 fn check_for_common_items_in_impls(
57 &self,
58 impl1: DefId,
59 impl2: DefId,
60 overlap: traits::OverlapResult<'_>,
61 ) {
74b04a01
XL
62 let impl_items1 = self.tcx.associated_items(impl1);
63 let impl_items2 = self.tcx.associated_items(impl2);
ea8adc8c 64
74b04a01 65 for item1 in impl_items1.in_definition_order() {
fc512014
XL
66 let collision = impl_items2
67 .filter_by_name_unhygienic(item1.ident.name)
68 .find(|item2| self.compare_hygienically(item1, item2));
74b04a01
XL
69
70 if let Some(item2) = collision {
ba9703b0 71 let name = item1.ident.normalize_to_macros_2_0();
74b04a01
XL
72 let mut err = struct_span_err!(
73 self.tcx.sess,
74 self.tcx.span_of_impl(item1.def_id).unwrap(),
75 E0592,
76 "duplicate definitions with name `{}`",
77 name
78 );
79 err.span_label(
80 self.tcx.span_of_impl(item1.def_id).unwrap(),
81 format!("duplicate definitions for `{}`", name),
82 );
83 err.span_label(
84 self.tcx.span_of_impl(item2.def_id).unwrap(),
85 format!("other definition for `{}`", name),
86 );
87
88 for cause in &overlap.intercrate_ambiguity_causes {
89 cause.add_intercrate_ambiguity_hint(&mut err);
90 }
0731742a 91
74b04a01
XL
92 if overlap.involves_placeholder {
93 traits::add_placeholder_note(&mut err);
cc61c64b 94 }
74b04a01
XL
95
96 err.emit();
cc61c64b
XL
97 }
98 }
99 }
100
74b04a01
XL
101 fn check_for_overlapping_inherent_impls(&self, impl1_def_id: DefId, impl2_def_id: DefId) {
102 traits::overlapping_impls(
103 self.tcx,
104 impl1_def_id,
105 impl2_def_id,
106 // We go ahead and just skip the leak check for
107 // inherent impls without warning.
108 SkipLeakCheck::Yes,
109 |overlap| {
110 self.check_for_common_items_in_impls(impl1_def_id, impl2_def_id, overlap);
111 false
112 },
113 || true,
114 );
cc61c64b
XL
115 }
116}
117
dc9dc135 118impl ItemLikeVisitor<'v> for InherentOverlapChecker<'tcx> {
dfeec247 119 fn visit_item(&mut self, item: &'v hir::Item<'v>) {
e74abb32 120 match item.kind {
dfeec247
XL
121 hir::ItemKind::Enum(..)
122 | hir::ItemKind::Struct(..)
123 | hir::ItemKind::Trait(..)
124 | hir::ItemKind::Union(..) => {
6a06907d 125 let impls = self.tcx.inherent_impls(item.def_id);
74b04a01 126
29967ef6
XL
127 // If there is only one inherent impl block,
128 // there is nothing to overlap check it with
129 if impls.len() <= 1 {
130 return;
131 }
132
133 let impls_items = impls
134 .iter()
135 .map(|impl_def_id| (impl_def_id, self.tcx.associated_items(*impl_def_id)))
136 .collect::<SmallVec<[_; 8]>>();
137
fc512014
XL
138 // Perform a O(n^2) algorithm for small n,
139 // otherwise switch to an allocating algorithm with
140 // faster asymptotic runtime.
141 const ALLOCATING_ALGO_THRESHOLD: usize = 500;
142 if impls.len() < ALLOCATING_ALGO_THRESHOLD {
143 for (i, &(&impl1_def_id, impl_items1)) in impls_items.iter().enumerate() {
144 for &(&impl2_def_id, impl_items2) in &impls_items[(i + 1)..] {
145 if self.impls_have_common_items(impl_items1, impl_items2) {
146 self.check_for_overlapping_inherent_impls(
147 impl1_def_id,
148 impl2_def_id,
149 );
150 }
151 }
152 }
153 } else {
154 // Build a set of connected regions of impl blocks.
155 // Two impl blocks are regarded as connected if they share
156 // an item with the same unhygienic identifier.
157 // After we have assembled the connected regions,
158 // run the O(n^2) algorithm on each connected region.
159 // This is advantageous to running the algorithm over the
160 // entire graph when there are many connected regions.
161
c295e0f8
XL
162 rustc_index::newtype_index! {
163 pub struct RegionId {
164 ENCODABLE = custom
165 }
166 }
fc512014
XL
167 struct ConnectedRegion {
168 idents: SmallVec<[Symbol; 8]>,
169 impl_blocks: FxHashSet<usize>,
170 }
c295e0f8
XL
171 let mut connected_regions: IndexVec<RegionId, _> = Default::default();
172 // Reverse map from the Symbol to the connected region id.
fc512014 173 let mut connected_region_ids = FxHashMap::default();
fc512014
XL
174
175 for (i, &(&_impl_def_id, impl_items)) in impls_items.iter().enumerate() {
176 if impl_items.len() == 0 {
177 continue;
178 }
179 // First obtain a list of existing connected region ids
180 let mut idents_to_add = SmallVec::<[Symbol; 8]>::new();
c295e0f8 181 let mut ids = impl_items
fc512014
XL
182 .in_definition_order()
183 .filter_map(|item| {
184 let entry = connected_region_ids.entry(item.ident.name);
185 if let Entry::Occupied(e) = &entry {
186 Some(*e.get())
187 } else {
188 idents_to_add.push(item.ident.name);
189 None
190 }
191 })
c295e0f8
XL
192 .collect::<SmallVec<[RegionId; 8]>>();
193 // Sort the id list so that the algorithm is deterministic
194 ids.sort_unstable();
195 ids.dedup();
196 let ids = ids;
197 match &ids[..] {
198 // Create a new connected region
199 [] => {
200 let id_to_set = connected_regions.next_index();
201 // Update the connected region ids
202 for ident in &idents_to_add {
203 connected_region_ids.insert(*ident, id_to_set);
204 }
205 connected_regions.insert(
206 id_to_set,
207 ConnectedRegion {
fc512014
XL
208 idents: idents_to_add,
209 impl_blocks: std::iter::once(i).collect(),
c295e0f8
XL
210 },
211 );
212 }
213 // Take the only id inside the list
214 &[id_to_set] => {
215 let region = connected_regions[id_to_set].as_mut().unwrap();
216 region.impl_blocks.insert(i);
217 region.idents.extend_from_slice(&idents_to_add);
fc512014 218 // Update the connected region ids
c295e0f8 219 for ident in &idents_to_add {
fc512014
XL
220 connected_region_ids.insert(*ident, id_to_set);
221 }
222 }
c295e0f8
XL
223 // We have multiple connected regions to merge.
224 // In the worst case this might add impl blocks
225 // one by one and can thus be O(n^2) in the size
226 // of the resulting final connected region, but
227 // this is no issue as the final step to check
228 // for overlaps runs in O(n^2) as well.
229 &[id_to_set, ..] => {
230 let mut region = connected_regions.remove(id_to_set).unwrap();
fc512014 231 region.impl_blocks.insert(i);
c295e0f8
XL
232 region.idents.extend_from_slice(&idents_to_add);
233 // Update the connected region ids
234 for ident in &idents_to_add {
235 connected_region_ids.insert(*ident, id_to_set);
236 }
fc512014 237
c295e0f8 238 // Remove other regions from ids.
fc512014
XL
239 for &id in ids.iter() {
240 if id == id_to_set {
241 continue;
242 }
c295e0f8 243 let r = connected_regions.remove(id).unwrap();
fc512014
XL
244 for ident in r.idents.iter() {
245 connected_region_ids.insert(*ident, id_to_set);
246 }
247 region.idents.extend_from_slice(&r.idents);
248 region.impl_blocks.extend(r.impl_blocks);
249 }
c295e0f8 250
fc512014
XL
251 connected_regions.insert(id_to_set, region);
252 }
253 }
254 }
255
256 debug!(
257 "churning through {} components (sum={}, avg={}, var={}, max={})",
258 connected_regions.len(),
259 impls.len(),
260 impls.len() / connected_regions.len(),
261 {
262 let avg = impls.len() / connected_regions.len();
263 let s = connected_regions
264 .iter()
c295e0f8
XL
265 .flatten()
266 .map(|r| r.impl_blocks.len() as isize - avg as isize)
fc512014
XL
267 .map(|v| v.abs() as usize)
268 .sum::<usize>();
269 s / connected_regions.len()
270 },
c295e0f8
XL
271 connected_regions
272 .iter()
273 .flatten()
274 .map(|r| r.impl_blocks.len())
275 .max()
276 .unwrap()
fc512014
XL
277 );
278 // List of connected regions is built. Now, run the overlap check
279 // for each pair of impl blocks in the same connected region.
c295e0f8 280 for region in connected_regions.into_iter().flatten() {
fc512014 281 let mut impl_blocks =
94222f64
XL
282 region.impl_blocks.into_iter().collect::<SmallVec<[usize; 8]>>();
283 impl_blocks.sort_unstable();
fc512014
XL
284 for (i, &impl1_items_idx) in impl_blocks.iter().enumerate() {
285 let &(&impl1_def_id, impl_items1) = &impls_items[impl1_items_idx];
286 for &impl2_items_idx in impl_blocks[(i + 1)..].iter() {
287 let &(&impl2_def_id, impl_items2) = &impls_items[impl2_items_idx];
288 if self.impls_have_common_items(impl_items1, impl_items2) {
289 self.check_for_overlapping_inherent_impls(
290 impl1_def_id,
291 impl2_def_id,
292 );
293 }
294 }
74b04a01
XL
295 }
296 }
297 }
cc61c64b
XL
298 }
299 _ => {}
300 }
301 }
302
dfeec247 303 fn visit_trait_item(&mut self, _trait_item: &hir::TraitItem<'v>) {}
cc61c64b 304
dfeec247 305 fn visit_impl_item(&mut self, _impl_item: &hir::ImplItem<'v>) {}
fc512014
XL
306
307 fn visit_foreign_item(&mut self, _foreign_item: &hir::ForeignItem<'v>) {}
cc61c64b 308}