Skip to content

Commit 5f1f628

Browse files
committed
Auto merge of #48666 - sgrif:sg-revert-perf-regression, r=nikomatsakis
Revert "correct subtle bug in the type variable code" This reverts commit ccd92c2. This commit is the source of a major perf regression, and was not intended to be included in #47861. At some point I must have accidentally re-added the commit. Fixes #48660. r? @nikomatsakis
2 parents e8af0f4 + f5f53e9 commit 5f1f628

File tree

1 file changed

+107
-60
lines changed

1 file changed

+107
-60
lines changed

src/librustc/infer/type_variable.rs

+107-60
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,11 @@ use std::cmp;
1616
use std::marker::PhantomData;
1717
use std::u32;
1818
use rustc_data_structures::fx::FxHashMap;
19+
use rustc_data_structures::snapshot_vec as sv;
1920
use rustc_data_structures::unify as ut;
2021

2122
pub struct TypeVariableTable<'tcx> {
22-
/// Extra data for each type variable, such as the origin. This is
23-
/// not stored in the unification table since, when we inquire
24-
/// after the origin of a variable X, we want the origin of **that
25-
/// variable X**, not the origin of some other variable Y with
26-
/// which X has been unified.
27-
var_data: Vec<TypeVariableData>,
23+
values: sv::SnapshotVec<Delegate>,
2824

2925
/// Two variables are unified in `eq_relations` when we have a
3026
/// constraint `?X == ?Y`. This table also stores, for each key,
@@ -118,20 +114,21 @@ impl<'tcx> TypeVariableValue<'tcx> {
118114
}
119115

120116
pub struct Snapshot<'tcx> {
121-
/// number of variables at the time of the snapshot
122-
num_vars: usize,
123-
124-
/// snapshot from the `eq_relations` table
117+
snapshot: sv::Snapshot,
125118
eq_snapshot: ut::Snapshot<ut::InPlace<TyVidEqKey<'tcx>>>,
126-
127-
/// snapshot from the `sub_relations` table
128119
sub_snapshot: ut::Snapshot<ut::InPlace<ty::TyVid>>,
129120
}
130121

122+
struct Instantiate {
123+
vid: ty::TyVid,
124+
}
125+
126+
struct Delegate;
127+
131128
impl<'tcx> TypeVariableTable<'tcx> {
132129
pub fn new() -> TypeVariableTable<'tcx> {
133130
TypeVariableTable {
134-
var_data: Vec::new(),
131+
values: sv::SnapshotVec::new(),
135132
eq_relations: ut::UnificationTable::new(),
136133
sub_relations: ut::UnificationTable::new(),
137134
}
@@ -142,15 +139,15 @@ impl<'tcx> TypeVariableTable<'tcx> {
142139
/// Note that this function does not return care whether
143140
/// `vid` has been unified with something else or not.
144141
pub fn var_diverges<'a>(&'a self, vid: ty::TyVid) -> bool {
145-
self.var_data[vid.index as usize].diverging
142+
self.values.get(vid.index as usize).diverging
146143
}
147144

148145
/// Returns the origin that was given when `vid` was created.
149146
///
150147
/// Note that this function does not return care whether
151148
/// `vid` has been unified with something else or not.
152149
pub fn var_origin(&self, vid: ty::TyVid) -> &TypeVariableOrigin {
153-
&self.var_data[vid.index as usize].origin
150+
&self.values.get(vid.index as usize).origin
154151
}
155152

156153
/// Records that `a == b`, depending on `dir`.
@@ -182,6 +179,11 @@ impl<'tcx> TypeVariableTable<'tcx> {
182179
"instantiating type variable `{:?}` twice: new-value = {:?}, old-value={:?}",
183180
vid, ty, self.eq_relations.probe_value(vid));
184181
self.eq_relations.union_value(vid, TypeVariableValue::Known { value: ty });
182+
183+
// Hack: we only need this so that `types_escaping_snapshot`
184+
// can see what has been unified; see the Delegate impl for
185+
// more details.
186+
self.values.record(Instantiate { vid: vid });
185187
}
186188

187189
/// Creates a new type variable.
@@ -204,8 +206,11 @@ impl<'tcx> TypeVariableTable<'tcx> {
204206
let sub_key = self.sub_relations.new_key(());
205207
assert_eq!(eq_key.vid, sub_key);
206208

207-
assert_eq!(self.var_data.len(), sub_key.index as usize);
208-
self.var_data.push(TypeVariableData { origin, diverging });
209+
let index = self.values.push(TypeVariableData {
210+
origin,
211+
diverging,
212+
});
213+
assert_eq!(eq_key.vid.index, index as u32);
209214

210215
debug!("new_var(index={:?}, diverging={:?}, origin={:?}", eq_key.vid, diverging, origin);
211216

@@ -214,7 +219,7 @@ impl<'tcx> TypeVariableTable<'tcx> {
214219

215220
/// Returns the number of type variables created thus far.
216221
pub fn num_vars(&self) -> usize {
217-
self.var_data.len()
222+
self.values.len()
218223
}
219224

220225
/// Returns the "root" variable of `vid` in the `eq_relations`
@@ -270,7 +275,7 @@ impl<'tcx> TypeVariableTable<'tcx> {
270275
/// be processed in a stack-like fashion.
271276
pub fn snapshot(&mut self) -> Snapshot<'tcx> {
272277
Snapshot {
273-
num_vars: self.var_data.len(),
278+
snapshot: self.values.start_snapshot(),
274279
eq_snapshot: self.eq_relations.snapshot(),
275280
sub_snapshot: self.sub_relations.snapshot(),
276281
}
@@ -280,21 +285,30 @@ impl<'tcx> TypeVariableTable<'tcx> {
280285
/// snapshots created since that point must already have been
281286
/// committed or rolled back.
282287
pub fn rollback_to(&mut self, s: Snapshot<'tcx>) {
283-
let Snapshot { num_vars, eq_snapshot, sub_snapshot } = s;
284-
debug!("type_variables::rollback_to(num_vars = {})", num_vars);
285-
assert!(self.var_data.len() >= num_vars);
288+
debug!("rollback_to{:?}", {
289+
for action in self.values.actions_since_snapshot(&s.snapshot) {
290+
match *action {
291+
sv::UndoLog::NewElem(index) => {
292+
debug!("inference variable _#{}t popped", index)
293+
}
294+
_ => { }
295+
}
296+
}
297+
});
298+
299+
let Snapshot { snapshot, eq_snapshot, sub_snapshot } = s;
300+
self.values.rollback_to(snapshot);
286301
self.eq_relations.rollback_to(eq_snapshot);
287302
self.sub_relations.rollback_to(sub_snapshot);
288-
self.var_data.truncate(num_vars);
289303
}
290304

291305
/// Commits all changes since the snapshot was created, making
292306
/// them permanent (unless this snapshot was created within
293307
/// another snapshot). Any snapshots created since that point
294308
/// must already have been committed or rolled back.
295309
pub fn commit(&mut self, s: Snapshot<'tcx>) {
296-
let Snapshot { num_vars, eq_snapshot, sub_snapshot } = s;
297-
debug!("type_variables::commit(num_vars = {})", num_vars);
310+
let Snapshot { snapshot, eq_snapshot, sub_snapshot } = s;
311+
self.values.commit(snapshot);
298312
self.eq_relations.commit(eq_snapshot);
299313
self.sub_relations.commit(sub_snapshot);
300314
}
@@ -303,12 +317,19 @@ impl<'tcx> TypeVariableTable<'tcx> {
303317
/// ty-variables created during the snapshot, and the values
304318
/// `{V2}` are the root variables that they were unified with,
305319
/// along with their origin.
306-
pub fn types_created_since_snapshot(&mut self, snapshot: &Snapshot<'tcx>) -> TypeVariableMap {
307-
self.var_data
320+
pub fn types_created_since_snapshot(&mut self, s: &Snapshot<'tcx>) -> TypeVariableMap {
321+
let actions_since_snapshot = self.values.actions_since_snapshot(&s.snapshot);
322+
323+
actions_since_snapshot
308324
.iter()
309-
.enumerate()
310-
.skip(snapshot.num_vars) // skip those that existed when snapshot was taken
311-
.map(|(index, data)| (ty::TyVid { index: index as u32 }, data.origin))
325+
.filter_map(|action| match action {
326+
&sv::UndoLog::NewElem(index) => Some(ty::TyVid { index: index as u32 }),
327+
_ => None,
328+
})
329+
.map(|vid| {
330+
let origin = self.values.get(vid.index as usize).origin.clone();
331+
(vid, origin)
332+
})
312333
.collect()
313334
}
314335

@@ -318,42 +339,47 @@ impl<'tcx> TypeVariableTable<'tcx> {
318339
/// a type variable `V0`, then we started the snapshot, then we
319340
/// created a type variable `V1`, unifed `V0` with `T0`, and
320341
/// unified `V1` with `T1`, this function would return `{T0}`.
321-
pub fn types_escaping_snapshot(&mut self, snapshot: &Snapshot<'tcx>) -> Vec<Ty<'tcx>> {
322-
// We want to select only those instantiations that have
323-
// occurred since the snapshot *and* which affect some
324-
// variable that existed prior to the snapshot. This code just
325-
// affects all instantiatons that ever occurred which affect
326-
// variables prior to the snapshot.
327-
//
328-
// It's hard to do better than this, though, without changing
329-
// the unification table to prefer "lower" vids -- the problem
330-
// is that we may have a variable X (from before the snapshot)
331-
// and Y (from after the snapshot) which get unified, with Y
332-
// chosen as the new root. Now we are "instantiating" Y with a
333-
// value, but it escapes into X, but we wouldn't readily see
334-
// that. (In fact, earlier revisions of this code had this
335-
// bug; it was introduced when we added the `eq_relations`
336-
// table, but it's hard to create rust code that triggers it.)
337-
//
338-
// We could tell the table to prefer lower vids, and then we would
339-
// see the case above, but we would get less-well-balanced trees.
340-
//
341-
// Since I hope to kill the leak-check in this branch, and
342-
// that's the code which uses this logic anyway, I'm going to
343-
// use the less efficient algorithm for now.
344-
let mut escaping_types = Vec::with_capacity(snapshot.num_vars);
345-
escaping_types.extend(
346-
(0..snapshot.num_vars) // for all variables that pre-exist the snapshot, collect..
347-
.map(|i| ty::TyVid { index: i as u32 })
348-
.filter_map(|vid| self.probe(vid).known())); // ..types they are instantiated with.
349-
debug!("types_escaping_snapshot = {:?}", escaping_types);
342+
pub fn types_escaping_snapshot(&mut self, s: &Snapshot<'tcx>) -> Vec<Ty<'tcx>> {
343+
let mut new_elem_threshold = u32::MAX;
344+
let mut escaping_types = Vec::new();
345+
let actions_since_snapshot = self.values.actions_since_snapshot(&s.snapshot);
346+
debug!("actions_since_snapshot.len() = {}", actions_since_snapshot.len());
347+
for action in actions_since_snapshot {
348+
match *action {
349+
sv::UndoLog::NewElem(index) => {
350+
// if any new variables were created during the
351+
// snapshot, remember the lower index (which will
352+
// always be the first one we see). Note that this
353+
// action must precede those variables being
354+
// specified.
355+
new_elem_threshold = cmp::min(new_elem_threshold, index as u32);
356+
debug!("NewElem({}) new_elem_threshold={}", index, new_elem_threshold);
357+
}
358+
359+
sv::UndoLog::Other(Instantiate { vid, .. }) => {
360+
if vid.index < new_elem_threshold {
361+
// quick check to see if this variable was
362+
// created since the snapshot started or not.
363+
let escaping_type = match self.eq_relations.probe_value(vid) {
364+
TypeVariableValue::Unknown { .. } => bug!(),
365+
TypeVariableValue::Known { value } => value,
366+
};
367+
escaping_types.push(escaping_type);
368+
}
369+
debug!("SpecifyVar({:?}) new_elem_threshold={}", vid, new_elem_threshold);
370+
}
371+
372+
_ => { }
373+
}
374+
}
375+
350376
escaping_types
351377
}
352378

353379
/// Returns indices of all variables that are not yet
354380
/// instantiated.
355381
pub fn unsolved_variables(&mut self) -> Vec<ty::TyVid> {
356-
(0..self.var_data.len())
382+
(0..self.values.len())
357383
.filter_map(|i| {
358384
let vid = ty::TyVid { index: i as u32 };
359385
match self.probe(vid) {
@@ -364,6 +390,27 @@ impl<'tcx> TypeVariableTable<'tcx> {
364390
.collect()
365391
}
366392
}
393+
394+
impl sv::SnapshotVecDelegate for Delegate {
395+
type Value = TypeVariableData;
396+
type Undo = Instantiate;
397+
398+
fn reverse(_values: &mut Vec<TypeVariableData>, _action: Instantiate) {
399+
// We don't actually have to *do* anything to reverse an
400+
// instanation; the value for a variable is stored in the
401+
// `eq_relations` and hence its rollback code will handle
402+
// it. In fact, we could *almost* just remove the
403+
// `SnapshotVec` entirely, except that we would have to
404+
// reproduce *some* of its logic, since we want to know which
405+
// type variables have been instantiated since the snapshot
406+
// was started, so we can implement `types_escaping_snapshot`.
407+
//
408+
// (If we extended the `UnificationTable` to let us see which
409+
// values have been unified and so forth, that might also
410+
// suffice.)
411+
}
412+
}
413+
367414
///////////////////////////////////////////////////////////////////////////
368415

369416
/// These structs (a newtyped TyVid) are used as the unification key

0 commit comments

Comments
 (0)