1+ use rustc_data_structures:: fx:: { FxHashMap , FxHashSet } ;
12use rustc_errors:: struct_span_err;
23use rustc_hir as hir;
34use rustc_hir:: def_id:: { CrateNum , DefId , LOCAL_CRATE } ;
45use rustc_hir:: itemlikevisit:: ItemLikeVisitor ;
56use rustc_middle:: ty:: { self , TyCtxt } ;
7+ use rustc_span:: Symbol ;
68use rustc_trait_selection:: traits:: { self , SkipLeakCheck } ;
79use smallvec:: SmallVec ;
10+ use std:: collections:: hash_map:: Entry ;
811
912pub fn crate_inherent_impls_overlap_check ( tcx : TyCtxt < ' _ > , crate_num : CrateNum ) {
1013 assert_eq ! ( crate_num, LOCAL_CRATE ) ;
@@ -33,12 +36,9 @@ impl InherentOverlapChecker<'tcx> {
3336 }
3437
3538 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) ) ;
4242
4343 if collision {
4444 return true ;
@@ -48,6 +48,12 @@ impl InherentOverlapChecker<'tcx> {
4848 false
4949 }
5050
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+
5157 fn check_for_common_items_in_impls (
5258 & self ,
5359 impl1 : DefId ,
@@ -58,12 +64,9 @@ impl InherentOverlapChecker<'tcx> {
5864 let impl_items2 = self . tcx . associated_items ( impl2) ;
5965
6066 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) ) ;
6770
6871 if let Some ( item2) = collision {
6972 let name = item1. ident . normalize_to_macros_2_0 ( ) ;
@@ -134,10 +137,150 @@ impl ItemLikeVisitor<'v> for InherentOverlapChecker<'tcx> {
134137 . map ( |impl_def_id| ( impl_def_id, self . tcx . associated_items ( * impl_def_id) ) )
135138 . collect :: < SmallVec < [ _ ; 8 ] > > ( ) ;
136139
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+ }
141284 }
142285 }
143286 }
0 commit comments