Skip to content

Commit

Permalink
Auto merge of #78323 - est31:smaller_list_overlap, r=varkor
Browse files Browse the repository at this point in the history
Iterate over the smaller list

If there are two lists of different sizes,
iterating over the smaller list and then
looking up in the larger list is cheaper
than vice versa, because lookups scale
sublinearly.
  • Loading branch information
bors committed Oct 28, 2020
2 parents db241bb + 6c9b8ad commit 2eb4fc8
Show file tree
Hide file tree
Showing 2 changed files with 32 additions and 7 deletions.
4 changes: 4 additions & 0 deletions compiler/rustc_middle/src/ty/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,10 @@ impl<'tcx> AssociatedItems<'tcx> {
self.items.iter().map(|(_, v)| *v)
}

pub fn len(&self) -> usize {
self.items.len()
}

/// Returns an iterator over all associated items with the given name, ignoring hygiene.
pub fn filter_by_name_unhygienic(
&self,
Expand Down
35 changes: 28 additions & 7 deletions compiler/rustc_typeck/src/coherence/inherent_impls_overlap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ use rustc_errors::struct_span_err;
use rustc_hir as hir;
use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE};
use rustc_hir::itemlikevisit::ItemLikeVisitor;
use rustc_middle::ty::TyCtxt;
use rustc_middle::ty::{self, TyCtxt};
use rustc_trait_selection::traits::{self, SkipLeakCheck};
use smallvec::SmallVec;

pub fn crate_inherent_impls_overlap_check(tcx: TyCtxt<'_>, crate_num: CrateNum) {
assert_eq!(crate_num, LOCAL_CRATE);
Expand All @@ -18,9 +19,18 @@ struct InherentOverlapChecker<'tcx> {
impl InherentOverlapChecker<'tcx> {
/// Checks whether any associated items in impls 1 and 2 share the same identifier and
/// namespace.
fn impls_have_common_items(&self, impl1: DefId, impl2: DefId) -> bool {
let impl_items1 = self.tcx.associated_items(impl1);
let impl_items2 = self.tcx.associated_items(impl2);
fn impls_have_common_items(
&self,
impl_items1: &ty::AssociatedItems<'_>,
impl_items2: &ty::AssociatedItems<'_>,
) -> bool {
let mut impl_items1 = &impl_items1;
let mut impl_items2 = &impl_items2;

// Performance optimization: iterate over the smaller list
if impl_items1.len() > impl_items2.len() {
std::mem::swap(&mut impl_items1, &mut impl_items2);
}

for item1 in impl_items1.in_definition_order() {
let collision = impl_items2.filter_by_name_unhygienic(item1.ident.name).any(|item2| {
Expand Down Expand Up @@ -113,9 +123,20 @@ impl ItemLikeVisitor<'v> for InherentOverlapChecker<'tcx> {
let ty_def_id = self.tcx.hir().local_def_id(item.hir_id);
let impls = self.tcx.inherent_impls(ty_def_id);

for (i, &impl1_def_id) in impls.iter().enumerate() {
for &impl2_def_id in &impls[(i + 1)..] {
if self.impls_have_common_items(impl1_def_id, impl2_def_id) {
// If there is only one inherent impl block,
// there is nothing to overlap check it with
if impls.len() <= 1 {
return;
}

let impls_items = impls
.iter()
.map(|impl_def_id| (impl_def_id, self.tcx.associated_items(*impl_def_id)))
.collect::<SmallVec<[_; 8]>>();

for (i, &(&impl1_def_id, impl_items1)) in impls_items.iter().enumerate() {
for &(&impl2_def_id, impl_items2) in &impls_items[(i + 1)..] {
if self.impls_have_common_items(impl_items1, impl_items2) {
self.check_for_overlapping_inherent_impls(impl1_def_id, impl2_def_id);
}
}
Expand Down

0 comments on commit 2eb4fc8

Please sign in to comment.