mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-25 16:24:46 +00:00
Rollup merge of #89643 - cjgillot:overlap, r=matthewjasper
Fix inherent impl overlap check. The current implementation of the overlap check was slightly buggy, and unified the wrong connected component in the `ids.len() <= 1` case. This became visible in another PR which changed the iteration order of items. r? ``@matthewjasper`` since you reviewed the other PR.
This commit is contained in:
commit
b80dd9e445
@ -738,6 +738,12 @@ impl<I: Idx, T> IndexVec<I, Option<T>> {
|
|||||||
self.ensure_contains_elem(index, || None);
|
self.ensure_contains_elem(index, || None);
|
||||||
self[index].get_or_insert_with(value)
|
self[index].get_or_insert_with(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn remove(&mut self, index: I) -> Option<T> {
|
||||||
|
self.ensure_contains_elem(index, || None);
|
||||||
|
self[index].take()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<I: Idx, T: Clone> IndexVec<I, T> {
|
impl<I: Idx, T: Clone> IndexVec<I, T> {
|
||||||
|
@ -3,6 +3,7 @@ use rustc_errors::struct_span_err;
|
|||||||
use rustc_hir as hir;
|
use rustc_hir as hir;
|
||||||
use rustc_hir::def_id::DefId;
|
use rustc_hir::def_id::DefId;
|
||||||
use rustc_hir::itemlikevisit::ItemLikeVisitor;
|
use rustc_hir::itemlikevisit::ItemLikeVisitor;
|
||||||
|
use rustc_index::vec::IndexVec;
|
||||||
use rustc_middle::ty::{self, TyCtxt};
|
use rustc_middle::ty::{self, TyCtxt};
|
||||||
use rustc_span::Symbol;
|
use rustc_span::Symbol;
|
||||||
use rustc_trait_selection::traits::{self, SkipLeakCheck};
|
use rustc_trait_selection::traits::{self, SkipLeakCheck};
|
||||||
@ -158,14 +159,18 @@ impl ItemLikeVisitor<'v> for InherentOverlapChecker<'tcx> {
|
|||||||
// This is advantageous to running the algorithm over the
|
// This is advantageous to running the algorithm over the
|
||||||
// entire graph when there are many connected regions.
|
// entire graph when there are many connected regions.
|
||||||
|
|
||||||
|
rustc_index::newtype_index! {
|
||||||
|
pub struct RegionId {
|
||||||
|
ENCODABLE = custom
|
||||||
|
}
|
||||||
|
}
|
||||||
struct ConnectedRegion {
|
struct ConnectedRegion {
|
||||||
idents: SmallVec<[Symbol; 8]>,
|
idents: SmallVec<[Symbol; 8]>,
|
||||||
impl_blocks: FxHashSet<usize>,
|
impl_blocks: FxHashSet<usize>,
|
||||||
}
|
}
|
||||||
// Highest connected region id
|
let mut connected_regions: IndexVec<RegionId, _> = Default::default();
|
||||||
let mut highest_region_id = 0;
|
// Reverse map from the Symbol to the connected region id.
|
||||||
let mut connected_region_ids = FxHashMap::default();
|
let mut connected_region_ids = FxHashMap::default();
|
||||||
let mut connected_regions = FxHashMap::default();
|
|
||||||
|
|
||||||
for (i, &(&_impl_def_id, impl_items)) in impls_items.iter().enumerate() {
|
for (i, &(&_impl_def_id, impl_items)) in impls_items.iter().enumerate() {
|
||||||
if impl_items.len() == 0 {
|
if impl_items.len() == 0 {
|
||||||
@ -173,7 +178,7 @@ impl ItemLikeVisitor<'v> for InherentOverlapChecker<'tcx> {
|
|||||||
}
|
}
|
||||||
// First obtain a list of existing connected region ids
|
// First obtain a list of existing connected region ids
|
||||||
let mut idents_to_add = SmallVec::<[Symbol; 8]>::new();
|
let mut idents_to_add = SmallVec::<[Symbol; 8]>::new();
|
||||||
let ids = impl_items
|
let mut ids = impl_items
|
||||||
.in_definition_order()
|
.in_definition_order()
|
||||||
.filter_map(|item| {
|
.filter_map(|item| {
|
||||||
let entry = connected_region_ids.entry(item.ident.name);
|
let entry = connected_region_ids.entry(item.ident.name);
|
||||||
@ -184,62 +189,64 @@ impl ItemLikeVisitor<'v> for InherentOverlapChecker<'tcx> {
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect::<FxHashSet<usize>>();
|
.collect::<SmallVec<[RegionId; 8]>>();
|
||||||
match ids.len() {
|
// Sort the id list so that the algorithm is deterministic
|
||||||
0 | 1 => {
|
ids.sort_unstable();
|
||||||
let id_to_set = if ids.is_empty() {
|
let ids = ids;
|
||||||
// Create a new connected region
|
match &ids[..] {
|
||||||
let region = ConnectedRegion {
|
// Create a new connected region
|
||||||
|
[] => {
|
||||||
|
let id_to_set = connected_regions.next_index();
|
||||||
|
// Update the connected region ids
|
||||||
|
for ident in &idents_to_add {
|
||||||
|
connected_region_ids.insert(*ident, id_to_set);
|
||||||
|
}
|
||||||
|
connected_regions.insert(
|
||||||
|
id_to_set,
|
||||||
|
ConnectedRegion {
|
||||||
idents: idents_to_add,
|
idents: idents_to_add,
|
||||||
impl_blocks: std::iter::once(i).collect(),
|
impl_blocks: std::iter::once(i).collect(),
|
||||||
};
|
},
|
||||||
connected_regions.insert(highest_region_id, region);
|
);
|
||||||
(highest_region_id, highest_region_id += 1).0
|
}
|
||||||
} else {
|
// Take the only id inside the list
|
||||||
// Take the only id inside the list
|
&[id_to_set] => {
|
||||||
let id_to_set = *ids.iter().next().unwrap();
|
let region = connected_regions[id_to_set].as_mut().unwrap();
|
||||||
let region = connected_regions.get_mut(&id_to_set).unwrap();
|
region.impl_blocks.insert(i);
|
||||||
region.impl_blocks.insert(i);
|
region.idents.extend_from_slice(&idents_to_add);
|
||||||
region.idents.extend_from_slice(&idents_to_add);
|
|
||||||
id_to_set
|
|
||||||
};
|
|
||||||
let (_id, region) = connected_regions.iter().next().unwrap();
|
|
||||||
// Update the connected region ids
|
// Update the connected region ids
|
||||||
for ident in region.idents.iter() {
|
for ident in &idents_to_add {
|
||||||
connected_region_ids.insert(*ident, id_to_set);
|
connected_region_ids.insert(*ident, id_to_set);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {
|
// We have multiple connected regions to merge.
|
||||||
// We have multiple connected regions to merge.
|
// In the worst case this might add impl blocks
|
||||||
// In the worst case this might add impl blocks
|
// one by one and can thus be O(n^2) in the size
|
||||||
// one by one and can thus be O(n^2) in the size
|
// of the resulting final connected region, but
|
||||||
// of the resulting final connected region, but
|
// this is no issue as the final step to check
|
||||||
// this is no issue as the final step to check
|
// for overlaps runs in O(n^2) as well.
|
||||||
// for overlaps runs in O(n^2) as well.
|
&[id_to_set, ..] => {
|
||||||
|
let mut region = connected_regions.remove(id_to_set).unwrap();
|
||||||
// Take the smallest id from the list
|
|
||||||
let id_to_set = *ids.iter().min().unwrap();
|
|
||||||
|
|
||||||
// Sort the id list so that the algorithm is deterministic
|
|
||||||
let mut ids = ids.into_iter().collect::<SmallVec<[usize; 8]>>();
|
|
||||||
ids.sort_unstable();
|
|
||||||
|
|
||||||
let mut region = connected_regions.remove(&id_to_set).unwrap();
|
|
||||||
region.idents.extend_from_slice(&idents_to_add);
|
|
||||||
region.impl_blocks.insert(i);
|
region.impl_blocks.insert(i);
|
||||||
|
region.idents.extend_from_slice(&idents_to_add);
|
||||||
|
// Update the connected region ids
|
||||||
|
for ident in &idents_to_add {
|
||||||
|
connected_region_ids.insert(*ident, id_to_set);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove other regions from ids.
|
||||||
for &id in ids.iter() {
|
for &id in ids.iter() {
|
||||||
if id == id_to_set {
|
if id == id_to_set {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let r = connected_regions.remove(&id).unwrap();
|
let r = connected_regions.remove(id).unwrap();
|
||||||
// Update the connected region ids
|
|
||||||
for ident in r.idents.iter() {
|
for ident in r.idents.iter() {
|
||||||
connected_region_ids.insert(*ident, id_to_set);
|
connected_region_ids.insert(*ident, id_to_set);
|
||||||
}
|
}
|
||||||
region.idents.extend_from_slice(&r.idents);
|
region.idents.extend_from_slice(&r.idents);
|
||||||
region.impl_blocks.extend(r.impl_blocks);
|
region.impl_blocks.extend(r.impl_blocks);
|
||||||
}
|
}
|
||||||
|
|
||||||
connected_regions.insert(id_to_set, region);
|
connected_regions.insert(id_to_set, region);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -254,16 +261,22 @@ impl ItemLikeVisitor<'v> for InherentOverlapChecker<'tcx> {
|
|||||||
let avg = impls.len() / connected_regions.len();
|
let avg = impls.len() / connected_regions.len();
|
||||||
let s = connected_regions
|
let s = connected_regions
|
||||||
.iter()
|
.iter()
|
||||||
.map(|r| r.1.impl_blocks.len() as isize - avg as isize)
|
.flatten()
|
||||||
|
.map(|r| r.impl_blocks.len() as isize - avg as isize)
|
||||||
.map(|v| v.abs() as usize)
|
.map(|v| v.abs() as usize)
|
||||||
.sum::<usize>();
|
.sum::<usize>();
|
||||||
s / connected_regions.len()
|
s / connected_regions.len()
|
||||||
},
|
},
|
||||||
connected_regions.iter().map(|r| r.1.impl_blocks.len()).max().unwrap()
|
connected_regions
|
||||||
|
.iter()
|
||||||
|
.flatten()
|
||||||
|
.map(|r| r.impl_blocks.len())
|
||||||
|
.max()
|
||||||
|
.unwrap()
|
||||||
);
|
);
|
||||||
// List of connected regions is built. Now, run the overlap check
|
// List of connected regions is built. Now, run the overlap check
|
||||||
// for each pair of impl blocks in the same connected region.
|
// for each pair of impl blocks in the same connected region.
|
||||||
for (_id, region) in connected_regions.into_iter() {
|
for region in connected_regions.into_iter().flatten() {
|
||||||
let mut impl_blocks =
|
let mut impl_blocks =
|
||||||
region.impl_blocks.into_iter().collect::<SmallVec<[usize; 8]>>();
|
region.impl_blocks.into_iter().collect::<SmallVec<[usize; 8]>>();
|
||||||
impl_blocks.sort_unstable();
|
impl_blocks.sort_unstable();
|
||||||
|
@ -63,6 +63,7 @@ This API is completely unstable and subject to change.
|
|||||||
#![feature(in_band_lifetimes)]
|
#![feature(in_band_lifetimes)]
|
||||||
#![feature(is_sorted)]
|
#![feature(is_sorted)]
|
||||||
#![feature(iter_zip)]
|
#![feature(iter_zip)]
|
||||||
|
#![feature(min_specialization)]
|
||||||
#![feature(nll)]
|
#![feature(nll)]
|
||||||
#![feature(try_blocks)]
|
#![feature(try_blocks)]
|
||||||
#![feature(never_type)]
|
#![feature(never_type)]
|
||||||
|
Loading…
Reference in New Issue
Block a user