Skip to content

Commit 08bffdd

Browse files
committed
fix dropck performance regression
Turns out that calling `resolve_type_variables_if_possible` in a O(n^2) loop is a bad idea. Now we just resolve each copy of the region variable to its lowest name each time (we resolve the region variable to its lowest name, rather than to its unify-table name to avoid the risk of the unify-table name changing infinitely many times. That may be not a problem in practice, but I am not sure of it).
1 parent e583ab6 commit 08bffdd

File tree

4 files changed

+61
-25
lines changed

4 files changed

+61
-25
lines changed

src/librustc/middle/infer/region_inference/mod.rs

+8-4
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ pub use self::RegionResolutionError::*;
1818
pub use self::VarValue::*;
1919

2020
use super::{RegionVariableOrigin, SubregionOrigin, TypeTrace, MiscVariable};
21+
use super::unify_key;
2122

2223
use rustc_data_structures::graph::{self, Direction, NodeIndex};
2324
use rustc_data_structures::unify::{self, UnificationTable};
@@ -345,10 +346,13 @@ impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> {
345346
}
346347

347348
pub fn new_region_var(&self, origin: RegionVariableOrigin) -> RegionVid {
348-
let id = self.num_vars();
349+
let vid = RegionVid { index: self.num_vars() };
349350
self.var_origins.borrow_mut().push(origin.clone());
350-
let vid = self.unification_table.borrow_mut().new_key(());
351-
assert_eq!(vid.index, id);
351+
352+
let u_vid = self.unification_table.borrow_mut().new_key(
353+
unify_key::RegionVidKey { min_vid: vid }
354+
);
355+
assert_eq!(vid, u_vid);
352356
if self.in_snapshot() {
353357
self.undo_log.borrow_mut().push(AddVar(vid));
354358
}
@@ -581,7 +585,7 @@ impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> {
581585
}
582586

583587
pub fn opportunistic_resolve_var(&self, rid: RegionVid) -> ty::Region {
584-
ty::ReVar(self.unification_table.borrow_mut().find(rid))
588+
ty::ReVar(self.unification_table.borrow_mut().find_value(rid).min_vid)
585589
}
586590

587591
fn combine_map(&self, t: CombineMapType) -> &RefCell<CombineMap> {

src/librustc/middle/infer/unify_key.rs

+22-2
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
use syntax::ast;
1212
use middle::ty::{self, IntVarValue, Ty};
13-
use rustc_data_structures::unify::UnifyKey;
13+
use rustc_data_structures::unify::{Combine, UnifyKey};
1414

1515
pub trait ToType<'tcx> {
1616
fn to_type(&self, tcx: &ty::ctxt<'tcx>) -> Ty<'tcx>;
@@ -23,8 +23,28 @@ impl UnifyKey for ty::IntVid {
2323
fn tag(_: Option<ty::IntVid>) -> &'static str { "IntVid" }
2424
}
2525

26+
#[derive(PartialEq, Copy, Clone, Debug)]
27+
pub struct RegionVidKey {
28+
/// The minimum region vid in the unification set. This is needed
29+
/// to have a canonical name for a type to prevent infinite
30+
/// recursion.
31+
pub min_vid: ty::RegionVid
32+
}
33+
34+
impl Combine for RegionVidKey {
35+
fn combine(&self, other: &RegionVidKey) -> RegionVidKey {
36+
let min_vid = if self.min_vid.index < other.min_vid.index {
37+
self.min_vid
38+
} else {
39+
other.min_vid
40+
};
41+
42+
RegionVidKey { min_vid: min_vid }
43+
}
44+
}
45+
2646
impl UnifyKey for ty::RegionVid {
27-
type Value = ();
47+
type Value = RegionVidKey;
2848
fn index(&self) -> u32 { self.index }
2949
fn from_index(i: u32) -> ty::RegionVid { ty::RegionVid { index: i } }
3050
fn tag(_: Option<ty::RegionVid>) -> &'static str { "RegionVid" }

src/librustc_data_structures/unify/mod.rs

+18-3
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,16 @@ pub trait UnifyKey : Copy + Clone + Debug + PartialEq {
3737
fn tag(k: Option<Self>) -> &'static str;
3838
}
3939

40+
/// This trait is implemented for unify values that can be
41+
/// combined. This relation should be a monoid.
42+
pub trait Combine {
43+
fn combine(&self, other: &Self) -> Self;
44+
}
45+
46+
impl Combine for () {
47+
fn combine(&self, _other: &()) {}
48+
}
49+
4050
/// Value of a unification key. We implement Tarjan's union-find
4151
/// algorithm: when two keys are unified, one of them is converted
4252
/// into a "redirect" pointing at the other. These redirects form a
@@ -243,23 +253,28 @@ impl<K:UnifyKey> sv::SnapshotVecDelegate for Delegate<K> {
243253
///////////////////////////////////////////////////////////////////////////
244254
// Base union-find algorithm, where we are just making sets
245255

246-
impl<'tcx,K> UnificationTable<K>
247-
where K : UnifyKey<Value=()>,
256+
impl<'tcx,K:UnifyKey> UnificationTable<K>
257+
where K::Value: Combine
248258
{
249259
pub fn union(&mut self, a_id: K, b_id: K) {
250260
let node_a = self.get(a_id);
251261
let node_b = self.get(b_id);
252262
let a_id = node_a.key();
253263
let b_id = node_b.key();
254264
if a_id != b_id {
255-
self.unify(node_a, node_b, ());
265+
let new_value = node_a.value.combine(&node_b.value);
266+
self.unify(node_a, node_b, new_value);
256267
}
257268
}
258269

259270
pub fn find(&mut self, id: K) -> K {
260271
self.get(id).key()
261272
}
262273

274+
pub fn find_value(&mut self, id: K) -> K::Value {
275+
self.get(id).value
276+
}
277+
263278
pub fn unioned(&mut self, a_id: K, b_id: K) -> bool {
264279
self.find(a_id) == self.find(b_id)
265280
}

src/librustc_typeck/check/dropck.rs

+13-16
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ use middle::region;
1717
use middle::subst::{self, Subst};
1818
use middle::traits;
1919
use middle::ty::{self, Ty};
20+
use util::nodemap::FnvHashSet;
2021

2122
use syntax::ast;
2223
use syntax::codemap::{self, Span};
@@ -279,7 +280,7 @@ pub fn check_safety_of_destructor_if_necessary<'a, 'tcx>(rcx: &mut Rcx<'a, 'tcx>
279280
rcx: rcx,
280281
span: span,
281282
parent_scope: parent_scope,
282-
breadcrumbs: Vec::new(),
283+
breadcrumbs: FnvHashSet()
283284
},
284285
TypeContext::Root,
285286
typ,
@@ -340,7 +341,7 @@ enum TypeContext {
340341
struct DropckContext<'a, 'b: 'a, 'tcx: 'b> {
341342
rcx: &'a mut Rcx<'b, 'tcx>,
342343
/// types that have already been traversed
343-
breadcrumbs: Vec<Ty<'tcx>>,
344+
breadcrumbs: FnvHashSet<Ty<'tcx>>,
344345
/// span for error reporting
345346
span: Span,
346347
/// the scope reachable dtorck types must outlive
@@ -355,8 +356,6 @@ fn iterate_over_potentially_unsafe_regions_in_type<'a, 'b, 'tcx>(
355356
depth: usize) -> Result<(), Error<'tcx>>
356357
{
357358
let tcx = cx.rcx.tcx();
358-
let ty = cx.rcx.infcx().resolve_type_and_region_vars_if_possible(&ty);
359-
360359
// Issue #22443: Watch out for overflow. While we are careful to
361360
// handle regular types properly, non-regular ones cause problems.
362361
let recursion_limit = tcx.sess.recursion_limit.get();
@@ -367,19 +366,17 @@ fn iterate_over_potentially_unsafe_regions_in_type<'a, 'b, 'tcx>(
367366
return Err(Error::Overflow(context, ty))
368367
}
369368

370-
for breadcrumb in &mut cx.breadcrumbs {
371-
*breadcrumb =
372-
cx.rcx.infcx().resolve_type_and_region_vars_if_possible(breadcrumb);
373-
if *breadcrumb == ty {
374-
debug!("iterate_over_potentially_unsafe_regions_in_type \
375-
{}ty: {} scope: {:?} - cached",
376-
(0..depth).map(|_| ' ').collect::<String>(),
377-
ty, cx.parent_scope);
378-
return Ok(()); // we already visited this type
379-
}
380-
}
381-
cx.breadcrumbs.push(ty);
369+
// canoncialize the regions in `ty` before inserting - infinitely many
370+
// region variables can refer to the same region.
371+
let ty = cx.rcx.infcx().resolve_type_and_region_vars_if_possible(&ty);
382372

373+
if !cx.breadcrumbs.insert(ty) {
374+
debug!("iterate_over_potentially_unsafe_regions_in_type \
375+
{}ty: {} scope: {:?} - cached",
376+
(0..depth).map(|_| ' ').collect::<String>(),
377+
ty, cx.parent_scope);
378+
return Ok(()); // we already visited this type
379+
}
383380
debug!("iterate_over_potentially_unsafe_regions_in_type \
384381
{}ty: {} scope: {:?}",
385382
(0..depth).map(|_| ' ').collect::<String>(),

0 commit comments

Comments
 (0)