Skip to content

Commit 66b961b

Browse files
committed
correct subtle bug in the type variable code
1 parent 2b7f276 commit 66b961b

File tree

1 file changed

+63
-108
lines changed

1 file changed

+63
-108
lines changed

src/librustc/infer/type_variable.rs

Lines changed: 63 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,18 @@ use syntax::ast;
1212
use syntax_pos::Span;
1313
use ty::{self, Ty};
1414

15-
use std::cmp::min;
1615
use std::marker::PhantomData;
1716
use std::u32;
1817
use rustc_data_structures::fx::FxHashMap;
19-
use rustc_data_structures::snapshot_vec as sv;
2018
use rustc_data_structures::unify as ut;
2119

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

2528
/// Two variables are unified in `eq_relations` when we have a
2629
/// constraint `?X == ?Y`. This table also stores, for each key,
@@ -79,21 +82,20 @@ enum TypeVariableValue<'tcx> {
7982
}
8083

8184
pub struct Snapshot<'tcx> {
82-
snapshot: sv::Snapshot,
85+
/// number of variables at the time of the snapshot
86+
num_vars: usize,
87+
88+
/// snapshot from the `eq_relations` table
8389
eq_snapshot: ut::Snapshot<ut::InPlace<TyVidEqKey<'tcx>>>,
84-
sub_snapshot: ut::Snapshot<ut::InPlace<ty::TyVid>>,
85-
}
8690

87-
struct Instantiate {
88-
vid: ty::TyVid,
91+
/// snapshot from the `sub_relations` table
92+
sub_snapshot: ut::Snapshot<ut::InPlace<ty::TyVid>>,
8993
}
9094

91-
struct Delegate;
92-
9395
impl<'tcx> TypeVariableTable<'tcx> {
9496
pub fn new() -> TypeVariableTable<'tcx> {
9597
TypeVariableTable {
96-
values: sv::SnapshotVec::new(),
98+
var_data: Vec::new(),
9799
eq_relations: ut::UnificationTable::new(),
98100
sub_relations: ut::UnificationTable::new(),
99101
}
@@ -104,15 +106,15 @@ impl<'tcx> TypeVariableTable<'tcx> {
104106
/// Note that this function does not return care whether
105107
/// `vid` has been unified with something else or not.
106108
pub fn var_diverges<'a>(&'a self, vid: ty::TyVid) -> bool {
107-
self.values.get(vid.index as usize).diverging
109+
self.var_data[vid.index as usize].diverging
108110
}
109111

110112
/// Returns the origin that was given when `vid` was created.
111113
///
112114
/// Note that this function does not return care whether
113115
/// `vid` has been unified with something else or not.
114116
pub fn var_origin(&self, vid: ty::TyVid) -> &TypeVariableOrigin {
115-
&self.values.get(vid.index as usize).origin
117+
&self.var_data[vid.index as usize].origin
116118
}
117119

118120
/// Records that `a == b`, depending on `dir`.
@@ -144,11 +146,6 @@ impl<'tcx> TypeVariableTable<'tcx> {
144146
"instantiating type variable `{:?}` twice: new-value = {:?}, old-value={:?}",
145147
vid, ty, self.eq_relations.probe_value(vid));
146148
self.eq_relations.union_value(vid, TypeVariableValue::Known { value: ty });
147-
148-
// Hack: we only need this so that `types_escaping_snapshot`
149-
// can see what has been unified; see the Delegate impl for
150-
// more details.
151-
self.values.record(Instantiate { vid: vid });
152149
}
153150

154151
/// Creates a new type variable.
@@ -170,11 +167,8 @@ impl<'tcx> TypeVariableTable<'tcx> {
170167
let sub_key = self.sub_relations.new_key(());
171168
assert_eq!(eq_key.vid, sub_key);
172169

173-
let index = self.values.push(TypeVariableData {
174-
origin,
175-
diverging,
176-
});
177-
assert_eq!(eq_key.vid.index, index as u32);
170+
assert_eq!(self.var_data.len(), sub_key.index as usize);
171+
self.var_data.push(TypeVariableData { origin, diverging });
178172

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

@@ -183,7 +177,7 @@ impl<'tcx> TypeVariableTable<'tcx> {
183177

184178
/// Returns the number of type variables created thus far.
185179
pub fn num_vars(&self) -> usize {
186-
self.values.len()
180+
self.var_data.len()
187181
}
188182

189183
/// Returns the "root" variable of `vid` in the `eq_relations`
@@ -243,7 +237,7 @@ impl<'tcx> TypeVariableTable<'tcx> {
243237
/// be processed in a stack-like fashion.
244238
pub fn snapshot(&mut self) -> Snapshot<'tcx> {
245239
Snapshot {
246-
snapshot: self.values.start_snapshot(),
240+
num_vars: self.var_data.len(),
247241
eq_snapshot: self.eq_relations.snapshot(),
248242
sub_snapshot: self.sub_relations.snapshot(),
249243
}
@@ -253,30 +247,21 @@ impl<'tcx> TypeVariableTable<'tcx> {
253247
/// snapshots created since that point must already have been
254248
/// committed or rolled back.
255249
pub fn rollback_to(&mut self, s: Snapshot<'tcx>) {
256-
debug!("rollback_to{:?}", {
257-
for action in self.values.actions_since_snapshot(&s.snapshot) {
258-
match *action {
259-
sv::UndoLog::NewElem(index) => {
260-
debug!("inference variable _#{}t popped", index)
261-
}
262-
_ => { }
263-
}
264-
}
265-
});
266-
267-
let Snapshot { snapshot, eq_snapshot, sub_snapshot } = s;
268-
self.values.rollback_to(snapshot);
250+
let Snapshot { num_vars, eq_snapshot, sub_snapshot } = s;
251+
debug!("type_variables::rollback_to(num_vars = {})", num_vars);
252+
assert!(self.var_data.len() >= num_vars);
269253
self.eq_relations.rollback_to(eq_snapshot);
270254
self.sub_relations.rollback_to(sub_snapshot);
255+
self.var_data.truncate(num_vars);
271256
}
272257

273258
/// Commits all changes since the snapshot was created, making
274259
/// them permanent (unless this snapshot was created within
275260
/// another snapshot). Any snapshots created since that point
276261
/// must already have been committed or rolled back.
277262
pub fn commit(&mut self, s: Snapshot<'tcx>) {
278-
let Snapshot { snapshot, eq_snapshot, sub_snapshot } = s;
279-
self.values.commit(snapshot);
263+
let Snapshot { num_vars, eq_snapshot, sub_snapshot } = s;
264+
debug!("type_variables::commit(num_vars = {})", num_vars);
280265
self.eq_relations.commit(eq_snapshot);
281266
self.sub_relations.commit(sub_snapshot);
282267
}
@@ -285,19 +270,12 @@ impl<'tcx> TypeVariableTable<'tcx> {
285270
/// ty-variables created during the snapshot, and the values
286271
/// `{V2}` are the root variables that they were unified with,
287272
/// along with their origin.
288-
pub fn types_created_since_snapshot(&mut self, s: &Snapshot<'tcx>) -> TypeVariableMap {
289-
let actions_since_snapshot = self.values.actions_since_snapshot(&s.snapshot);
290-
291-
actions_since_snapshot
273+
pub fn types_created_since_snapshot(&mut self, snapshot: &Snapshot<'tcx>) -> TypeVariableMap {
274+
self.var_data
292275
.iter()
293-
.filter_map(|action| match action {
294-
&sv::UndoLog::NewElem(index) => Some(ty::TyVid { index: index as u32 }),
295-
_ => None,
296-
})
297-
.map(|vid| {
298-
let origin = self.values.get(vid.index as usize).origin.clone();
299-
(vid, origin)
300-
})
276+
.enumerate()
277+
.skip(snapshot.num_vars) // skip those that existed when snapshot was taken
278+
.map(|(index, data)| (ty::TyVid { index: index as u32 }, data.origin))
301279
.collect()
302280
}
303281

@@ -307,47 +285,45 @@ impl<'tcx> TypeVariableTable<'tcx> {
307285
/// a type variable `V0`, then we started the snapshot, then we
308286
/// created a type variable `V1`, unifed `V0` with `T0`, and
309287
/// unified `V1` with `T1`, this function would return `{T0}`.
310-
pub fn types_escaping_snapshot(&mut self, s: &Snapshot<'tcx>) -> Vec<Ty<'tcx>> {
311-
let mut new_elem_threshold = u32::MAX;
312-
let mut escaping_types = Vec::new();
313-
let actions_since_snapshot = self.values.actions_since_snapshot(&s.snapshot);
314-
debug!("actions_since_snapshot.len() = {}", actions_since_snapshot.len());
315-
for action in actions_since_snapshot {
316-
match *action {
317-
sv::UndoLog::NewElem(index) => {
318-
// if any new variables were created during the
319-
// snapshot, remember the lower index (which will
320-
// always be the first one we see). Note that this
321-
// action must precede those variables being
322-
// specified.
323-
new_elem_threshold = min(new_elem_threshold, index as u32);
324-
debug!("NewElem({}) new_elem_threshold={}", index, new_elem_threshold);
325-
}
326-
327-
sv::UndoLog::Other(Instantiate { vid, .. }) => {
328-
if vid.index < new_elem_threshold {
329-
// quick check to see if this variable was
330-
// created since the snapshot started or not.
331-
let escaping_type = match self.eq_relations.probe_value(vid) {
332-
TypeVariableValue::Unknown => bug!(),
333-
TypeVariableValue::Known { value } => value,
334-
};
335-
escaping_types.push(escaping_type);
336-
}
337-
debug!("SpecifyVar({:?}) new_elem_threshold={}", vid, new_elem_threshold);
338-
}
339-
340-
_ => { }
341-
}
342-
}
343-
288+
pub fn types_escaping_snapshot(&mut self, snapshot: &Snapshot<'tcx>) -> Vec<Ty<'tcx>> {
289+
// We want to select only those instantiations that have
290+
// occurred since the snapshot *and* which affect some
291+
// variable that existed prior to the snapshot. This code just
292+
// affects all instantiatons that ever occurred which affect
293+
// variables prior to the snapshot.
294+
//
295+
// It's hard to do better than this, though, without changing
296+
// the unification table to prefer "lower" vids -- the problem
297+
// is that we may have a variable X (from before the snapshot)
298+
// and Y (from after the snapshot) which get unified, with Y
299+
// chosen as the new root. Now we are "instantiating" Y with a
300+
// value, but it escapes into X, but we wouldn't readily see
301+
// that. (In fact, earlier revisions of this code had this
302+
// bug; it was introduced when we added the `eq_relations`
303+
// table, but it's hard to create rust code that triggers it.)
304+
//
305+
// We could tell the table to prefer lower vids, and then we would
306+
// see the case above, but we would get less-well-balanced trees.
307+
//
308+
// Since I hope to kill the leak-check in this branch, and
309+
// that's the code which uses this logic anyway, I'm going to
310+
// use the less efficient algorithm for now.
311+
let mut escaping_types = Vec::with_capacity(snapshot.num_vars);
312+
escaping_types.extend(
313+
(0..snapshot.num_vars) // for all variables that pre-exist the snapshot...
314+
.map(|i| ty::TyVid { index: i as u32 })
315+
.filter_map(|vid| match self.eq_relations.probe_value(vid) {
316+
TypeVariableValue::Unknown => None,
317+
TypeVariableValue::Known { value } => Some(value),
318+
})); // ...collect what types they've been instantiated with.
319+
debug!("types_escaping_snapshot = {:?}", escaping_types);
344320
escaping_types
345321
}
346322

347323
/// Returns indices of all variables that are not yet
348324
/// instantiated.
349325
pub fn unsolved_variables(&mut self) -> Vec<ty::TyVid> {
350-
(0..self.values.len())
326+
(0..self.var_data.len())
351327
.filter_map(|i| {
352328
let vid = ty::TyVid { index: i as u32 };
353329
if self.probe(vid).is_some() {
@@ -359,27 +335,6 @@ impl<'tcx> TypeVariableTable<'tcx> {
359335
.collect()
360336
}
361337
}
362-
363-
impl sv::SnapshotVecDelegate for Delegate {
364-
type Value = TypeVariableData;
365-
type Undo = Instantiate;
366-
367-
fn reverse(_values: &mut Vec<TypeVariableData>, _action: Instantiate) {
368-
// We don't actually have to *do* anything to reverse an
369-
// instanation; the value for a variable is stored in the
370-
// `eq_relations` and hence its rollback code will handle
371-
// it. In fact, we could *almost* just remove the
372-
// `SnapshotVec` entirely, except that we would have to
373-
// reproduce *some* of its logic, since we want to know which
374-
// type variables have been instantiated since the snapshot
375-
// was started, so we can implement `types_escaping_snapshot`.
376-
//
377-
// (If we extended the `UnificationTable` to let us see which
378-
// values have been unified and so forth, that might also
379-
// suffice.)
380-
}
381-
}
382-
383338
///////////////////////////////////////////////////////////////////////////
384339

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

0 commit comments

Comments
 (0)