1
+ use rustc_data_structures:: fx:: { FxHashMap , FxHashSet } ;
1
2
use rustc_errors:: struct_span_err;
2
3
use rustc_hir as hir;
3
4
use rustc_hir:: def_id:: { CrateNum , DefId , LOCAL_CRATE } ;
4
5
use rustc_hir:: itemlikevisit:: ItemLikeVisitor ;
5
6
use rustc_middle:: ty:: { self , TyCtxt } ;
7
+ use rustc_span:: Symbol ;
6
8
use rustc_trait_selection:: traits:: { self , SkipLeakCheck } ;
7
9
use smallvec:: SmallVec ;
10
+ use std:: collections:: hash_map:: Entry ;
8
11
9
12
pub fn crate_inherent_impls_overlap_check ( tcx : TyCtxt < ' _ > , crate_num : CrateNum ) {
10
13
assert_eq ! ( crate_num, LOCAL_CRATE ) ;
@@ -33,12 +36,9 @@ impl InherentOverlapChecker<'tcx> {
33
36
}
34
37
35
38
for item1 in impl_items1. in_definition_order ( ) {
36
- let collision = impl_items2. filter_by_name_unhygienic ( item1. ident . name ) . any ( |item2| {
37
- // Symbols and namespace match, compare hygienically.
38
- item1. kind . namespace ( ) == item2. kind . namespace ( )
39
- && item1. ident . normalize_to_macros_2_0 ( )
40
- == item2. ident . normalize_to_macros_2_0 ( )
41
- } ) ;
39
+ let collision = impl_items2
40
+ . filter_by_name_unhygienic ( item1. ident . name )
41
+ . any ( |item2| self . compare_hygienically ( item1, item2) ) ;
42
42
43
43
if collision {
44
44
return true ;
@@ -48,6 +48,12 @@ impl InherentOverlapChecker<'tcx> {
48
48
false
49
49
}
50
50
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 ( )
55
+ }
56
+
51
57
fn check_for_common_items_in_impls (
52
58
& self ,
53
59
impl1 : DefId ,
@@ -58,12 +64,9 @@ impl InherentOverlapChecker<'tcx> {
58
64
let impl_items2 = self . tcx . associated_items ( impl2) ;
59
65
60
66
for item1 in impl_items1. in_definition_order ( ) {
61
- let collision = impl_items2. filter_by_name_unhygienic ( item1. ident . name ) . find ( |item2| {
62
- // Symbols and namespace match, compare hygienically.
63
- item1. kind . namespace ( ) == item2. kind . namespace ( )
64
- && item1. ident . normalize_to_macros_2_0 ( )
65
- == item2. ident . normalize_to_macros_2_0 ( )
66
- } ) ;
67
+ let collision = impl_items2
68
+ . filter_by_name_unhygienic ( item1. ident . name )
69
+ . find ( |item2| self . compare_hygienically ( item1, item2) ) ;
67
70
68
71
if let Some ( item2) = collision {
69
72
let name = item1. ident . normalize_to_macros_2_0 ( ) ;
@@ -134,10 +137,150 @@ impl ItemLikeVisitor<'v> for InherentOverlapChecker<'tcx> {
134
137
. map ( |impl_def_id| ( impl_def_id, self . tcx . associated_items ( * impl_def_id) ) )
135
138
. collect :: < SmallVec < [ _ ; 8 ] > > ( ) ;
136
139
137
- for ( i, & ( & impl1_def_id, impl_items1) ) in impls_items. iter ( ) . enumerate ( ) {
138
- for & ( & impl2_def_id, impl_items2) in & impls_items[ ( i + 1 ) ..] {
139
- if self . impls_have_common_items ( impl_items1, impl_items2) {
140
- self . check_for_overlapping_inherent_impls ( impl1_def_id, impl2_def_id) ;
140
+ // Perform a O(n^2) algorithm for small n,
141
+ // otherwise switch to an allocating algorithm with
142
+ // faster asymptotic runtime.
143
+ const ALLOCATING_ALGO_THRESHOLD : usize = 500 ;
144
+ if impls. len ( ) < ALLOCATING_ALGO_THRESHOLD {
145
+ for ( i, & ( & impl1_def_id, impl_items1) ) in impls_items. iter ( ) . enumerate ( ) {
146
+ for & ( & impl2_def_id, impl_items2) in & impls_items[ ( i + 1 ) ..] {
147
+ if self . impls_have_common_items ( impl_items1, impl_items2) {
148
+ self . check_for_overlapping_inherent_impls (
149
+ impl1_def_id,
150
+ impl2_def_id,
151
+ ) ;
152
+ }
153
+ }
154
+ }
155
+ } else {
156
+ // Build a set of connected regions of impl blocks.
157
+ // Two impl blocks are regarded as connected if they share
158
+ // an item with the same unhygienic identifier.
159
+ // After we have assembled the connected regions,
160
+ // run the O(n^2) algorithm on each connected region.
161
+ // This is advantageous to running the algorithm over the
162
+ // entire graph when there are many connected regions.
163
+
164
+ struct ConnectedRegion {
165
+ idents : SmallVec < [ Symbol ; 8 ] > ,
166
+ impl_blocks : FxHashSet < usize > ,
167
+ }
168
+ // Highest connected region id
169
+ let mut highest_region_id = 0 ;
170
+ let mut connected_region_ids = FxHashMap :: default ( ) ;
171
+ let mut connected_regions = FxHashMap :: default ( ) ;
172
+
173
+ for ( i, & ( & _impl_def_id, impl_items) ) in impls_items. iter ( ) . enumerate ( ) {
174
+ if impl_items. len ( ) == 0 {
175
+ continue ;
176
+ }
177
+ // First obtain a list of existing connected region ids
178
+ let mut idents_to_add = SmallVec :: < [ Symbol ; 8 ] > :: new ( ) ;
179
+ let ids = impl_items
180
+ . in_definition_order ( )
181
+ . filter_map ( |item| {
182
+ let entry = connected_region_ids. entry ( item. ident . name ) ;
183
+ if let Entry :: Occupied ( e) = & entry {
184
+ Some ( * e. get ( ) )
185
+ } else {
186
+ idents_to_add. push ( item. ident . name ) ;
187
+ None
188
+ }
189
+ } )
190
+ . collect :: < FxHashSet < usize > > ( ) ;
191
+ match ids. len ( ) {
192
+ 0 | 1 => {
193
+ let id_to_set = if ids. len ( ) == 0 {
194
+ // Create a new connected region
195
+ let region = ConnectedRegion {
196
+ idents : idents_to_add,
197
+ impl_blocks : std:: iter:: once ( i) . collect ( ) ,
198
+ } ;
199
+ connected_regions. insert ( highest_region_id, region) ;
200
+ ( highest_region_id, highest_region_id += 1 ) . 0
201
+ } else {
202
+ // Take the only id inside the list
203
+ let id_to_set = * ids. iter ( ) . next ( ) . unwrap ( ) ;
204
+ let region = connected_regions. get_mut ( & id_to_set) . unwrap ( ) ;
205
+ region. impl_blocks . insert ( i) ;
206
+ region. idents . extend_from_slice ( & idents_to_add) ;
207
+ id_to_set
208
+ } ;
209
+ let ( _id, region) = connected_regions. iter ( ) . next ( ) . unwrap ( ) ;
210
+ // Update the connected region ids
211
+ for ident in region. idents . iter ( ) {
212
+ connected_region_ids. insert ( * ident, id_to_set) ;
213
+ }
214
+ }
215
+ _ => {
216
+ // We have multiple connected regions to merge.
217
+ // In the worst case this might add impl blocks
218
+ // one by one and can thus be O(n^2) in the size
219
+ // of the resulting final connected region, but
220
+ // this is no issue as the final step to check
221
+ // for overlaps runs in O(n^2) as well.
222
+
223
+ // Take the smallest id from the list
224
+ let id_to_set = * ids. iter ( ) . min ( ) . unwrap ( ) ;
225
+
226
+ // Sort the id list so that the algorithm is deterministic
227
+ let mut ids = ids. into_iter ( ) . collect :: < SmallVec < [ _ ; 8 ] > > ( ) ;
228
+ ids. sort ( ) ;
229
+
230
+ let mut region = connected_regions. remove ( & id_to_set) . unwrap ( ) ;
231
+ region. idents . extend_from_slice ( & idents_to_add) ;
232
+ region. impl_blocks . insert ( i) ;
233
+
234
+ for & id in ids. iter ( ) {
235
+ if id == id_to_set {
236
+ continue ;
237
+ }
238
+ let r = connected_regions. remove ( & id) . unwrap ( ) ;
239
+ // Update the connected region ids
240
+ for ident in r. idents . iter ( ) {
241
+ connected_region_ids. insert ( * ident, id_to_set) ;
242
+ }
243
+ region. idents . extend_from_slice ( & r. idents ) ;
244
+ region. impl_blocks . extend ( r. impl_blocks ) ;
245
+ }
246
+ connected_regions. insert ( id_to_set, region) ;
247
+ }
248
+ }
249
+ }
250
+
251
+ debug ! (
252
+ "churning through {} components (sum={}, avg={}, var={}, max={})" ,
253
+ connected_regions. len( ) ,
254
+ impls. len( ) ,
255
+ impls. len( ) / connected_regions. len( ) ,
256
+ {
257
+ let avg = impls. len( ) / connected_regions. len( ) ;
258
+ let s = connected_regions
259
+ . iter( )
260
+ . map( |r| r. 1 . impl_blocks. len( ) as isize - avg as isize )
261
+ . map( |v| v. abs( ) as usize )
262
+ . sum:: <usize >( ) ;
263
+ s / connected_regions. len( )
264
+ } ,
265
+ connected_regions. iter( ) . map( |r| r. 1 . impl_blocks. len( ) ) . max( ) . unwrap( )
266
+ ) ;
267
+ // List of connected regions is built. Now, run the overlap check
268
+ // for each pair of impl blocks in the same connected region.
269
+ for ( _id, region) in connected_regions. into_iter ( ) {
270
+ let mut impl_blocks =
271
+ region. impl_blocks . into_iter ( ) . collect :: < SmallVec < [ _ ; 8 ] > > ( ) ;
272
+ impl_blocks. sort ( ) ;
273
+ for ( i, & impl1_items_idx) in impl_blocks. iter ( ) . enumerate ( ) {
274
+ let & ( & impl1_def_id, impl_items1) = & impls_items[ impl1_items_idx] ;
275
+ for & impl2_items_idx in impl_blocks[ ( i + 1 ) ..] . iter ( ) {
276
+ let & ( & impl2_def_id, impl_items2) = & impls_items[ impl2_items_idx] ;
277
+ if self . impls_have_common_items ( impl_items1, impl_items2) {
278
+ self . check_for_overlapping_inherent_impls (
279
+ impl1_def_id,
280
+ impl2_def_id,
281
+ ) ;
282
+ }
283
+ }
141
284
}
142
285
}
143
286
}
0 commit comments