Skip to content

Commit

Permalink
Make the inherent impl overlap check linear-time
Browse files Browse the repository at this point in the history
  • Loading branch information
jonas-schievink committed Feb 9, 2020
1 parent 71c7e14 commit 11c7e82
Showing 1 changed file with 73 additions and 83 deletions.
156 changes: 73 additions & 83 deletions src/librustc_typeck/coherence/inherent_impls_overlap.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
use crate::namespace::Namespace;
use rustc::traits::{self, IntercrateMode, SkipLeakCheck};
use rustc::ty::{AssocItem, TyCtxt};
use rustc_data_structures::fx::FxHashMap;
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 smallvec::SmallVec;
use std::collections::hash_map::Entry;

pub fn crate_inherent_impls_overlap_check(tcx: TyCtxt<'_>, crate_num: CrateNum) {
assert_eq!(crate_num, LOCAL_CRATE);
Expand All @@ -17,78 +20,12 @@ 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);

for item1 in &impl_items1[..] {
for item2 in &impl_items2[..] {
// Avoid costly `.modern()` calls as much as possible by doing them as late as we
// can. Compare raw symbols first.
if item1.ident.name == item2.ident.name
&& Namespace::from(item1.kind) == Namespace::from(item2.kind)
{
// Symbols and namespace match, compare hygienically.
if item1.ident.modern() == item2.ident.modern() {
return true;
}
}
}
}

false
}

fn check_for_common_items_in_impls(
&self,
impl1: DefId,
impl2: DefId,
overlap: traits::OverlapResult<'_>,
) {
let name_and_namespace =
|assoc: &AssocItem| (assoc.ident.modern(), Namespace::from(assoc.kind));

let impl_items1 = self.tcx.associated_items(impl1);
let impl_items2 = self.tcx.associated_items(impl2);

for item1 in &impl_items1[..] {
let (name, namespace) = name_and_namespace(item1);

for item2 in &impl_items2[..] {
if (name, namespace) == name_and_namespace(item2) {
let mut err = struct_span_err!(
self.tcx.sess,
self.tcx.span_of_impl(item1.def_id).unwrap(),
E0592,
"duplicate definitions with name `{}`",
name
);
err.span_label(
self.tcx.span_of_impl(item1.def_id).unwrap(),
format!("duplicate definitions for `{}`", name),
);
err.span_label(
self.tcx.span_of_impl(item2.def_id).unwrap(),
format!("other definition for `{}`", name),
);

for cause in &overlap.intercrate_ambiguity_causes {
cause.add_intercrate_ambiguity_hint(&mut err);
}

if overlap.involves_placeholder {
traits::add_placeholder_note(&mut err);
}
/// Emits an error if the impls defining `item1` and `item2` overlap.
fn forbid_overlap(&self, item1: &AssocItem, item2: &AssocItem) {
let impl1_def_id = item1.container.id();
let impl2_def_id = item2.container.id();
let name = item1.ident;

err.emit();
}
}
}
}

fn check_for_overlapping_inherent_impls(&self, impl1_def_id: DefId, impl2_def_id: DefId) {
traits::overlapping_impls(
self.tcx,
impl1_def_id,
Expand All @@ -98,12 +35,73 @@ impl InherentOverlapChecker<'tcx> {
// inherent impls without warning.
SkipLeakCheck::Yes,
|overlap| {
self.check_for_common_items_in_impls(impl1_def_id, impl2_def_id, overlap);
false
let mut err = struct_span_err!(
self.tcx.sess,
self.tcx.span_of_impl(item1.def_id).unwrap(),
E0592,
"duplicate definitions with name `{}`",
name
);
err.span_label(
self.tcx.span_of_impl(item1.def_id).unwrap(),
format!("duplicate definitions for `{}`", name),
);
err.span_label(
self.tcx.span_of_impl(item2.def_id).unwrap(),
format!("other definition for `{}`", name),
);

for cause in &overlap.intercrate_ambiguity_causes {
cause.add_intercrate_ambiguity_hint(&mut err);
}

if overlap.involves_placeholder {
traits::add_placeholder_note(&mut err);
}

err.emit();
},
|| true,
|| {},
);
}

fn process_local_ty(&self, ty_def_id: DefId) {
let impls = self.tcx.inherent_impls(ty_def_id);

// Create a map from `Symbol`s to the list of `&'tcx AssocItem`s with that name, across all
// inherent impls.
let mut item_map: FxHashMap<_, SmallVec<[&AssocItem; 1]>> = FxHashMap::default();
for impl_def_id in impls {
let impl_items = self.tcx.associated_items(*impl_def_id);

for impl_item in impl_items {
match item_map.entry(impl_item.ident.name) {
Entry::Occupied(mut occupied) => {
// Do a proper name check respecting namespaces and hygiene against all
// items in this map entry.
let (ident1, ns1) =
(impl_item.ident.modern(), Namespace::from(impl_item.kind));

for impl_item2 in occupied.get() {
let (ident2, ns2) =
(impl_item2.ident.modern(), Namespace::from(impl_item2.kind));

if ns1 == ns2 && ident1 == ident2 {
// Items with same name. Their containing impls must not overlap.

self.forbid_overlap(impl_item2, impl_item);
}
}

occupied.get_mut().push(impl_item);
}
Entry::Vacant(vacant) => {
vacant.insert(SmallVec::from([impl_item]));
}
}
}
}
}
}

impl ItemLikeVisitor<'v> for InherentOverlapChecker<'tcx> {
Expand All @@ -114,15 +112,7 @@ impl ItemLikeVisitor<'v> for InherentOverlapChecker<'tcx> {
| hir::ItemKind::Trait(..)
| hir::ItemKind::Union(..) => {
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) {
self.check_for_overlapping_inherent_impls(impl1_def_id, impl2_def_id);
}
}
}
self.process_local_ty(ty_def_id);
}
_ => {}
}
Expand Down

0 comments on commit 11c7e82

Please sign in to comment.