From 99f5c8593b1778947830d1da580c55b70680b919 Mon Sep 17 00:00:00 2001 From: Eduard-Mihai Burtescu Date: Sat, 15 Apr 2017 23:51:58 +0300 Subject: [PATCH] rustc: ban registering obligations during InferCtxt snapshots. --- src/librustc/infer/mod.rs | 56 +++++++++++++-------------- src/librustc/traits/fulfill.rs | 2 +- src/librustc/traits/specialize/mod.rs | 2 +- src/librustc_typeck/check/mod.rs | 2 +- 4 files changed, 31 insertions(+), 31 deletions(-) diff --git a/src/librustc/infer/mod.rs b/src/librustc/infer/mod.rs index e98792b120d..a1bafe113e4 100644 --- a/src/librustc/infer/mod.rs +++ b/src/librustc/infer/mod.rs @@ -199,10 +199,8 @@ pub struct InferCtxt<'a, 'gcx: 'a+'tcx, 'tcx: 'a> { // `tained_by_errors`) to avoid reporting certain kinds of errors. err_count_on_creation: usize, - // This flag is used for debugging, and is set to true if there are - // any obligations set during the current snapshot. In that case, the - // snapshot can't be rolled back. - pub obligations_in_snapshot: Cell, + // This flag is true while there is an active snapshot. + in_snapshot: Cell, } /// A map returned by `skolemize_late_bound_regions()` indicating the skolemized @@ -507,7 +505,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'gcx> { projection_mode: Reveal::UserFacing, tainted_by_errors_flag: Cell::new(false), err_count_on_creation: self.sess.err_count(), - obligations_in_snapshot: Cell::new(false), + in_snapshot: Cell::new(false), } } } @@ -545,7 +543,7 @@ impl<'a, 'gcx, 'tcx> InferCtxtBuilder<'a, 'gcx, 'tcx> { projection_mode: projection_mode, tainted_by_errors_flag: Cell::new(false), err_count_on_creation: tcx.sess.err_count(), - obligations_in_snapshot: Cell::new(false), + in_snapshot: Cell::new(false), })) } } @@ -573,7 +571,7 @@ pub struct CombinedSnapshot { int_snapshot: unify::Snapshot, float_snapshot: unify::Snapshot, region_vars_snapshot: RegionSnapshot, - obligations_in_snapshot: bool, + was_in_snapshot: bool, } /// Helper trait for shortening the lifetimes inside a @@ -734,6 +732,10 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { self.projection_mode } + pub fn is_in_snapshot(&self) -> bool { + self.in_snapshot.get() + } + pub fn freshen>(&self, t: T) -> T { t.fold_with(&mut self.freshener()) } @@ -861,38 +863,37 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { result.map(move |t| InferOk { value: t, obligations: fields.obligations }) } - // Clear the "obligations in snapshot" flag, invoke the closure, + // Clear the "currently in a snapshot" flag, invoke the closure, // then restore the flag to its original value. This flag is a // debugging measure designed to detect cases where we start a - // snapshot, create type variables, register obligations involving - // those type variables in the fulfillment cx, and then have to - // unroll the snapshot, leaving "dangling type variables" behind. - // In such cases, the flag will be set by the fulfillment cx, and - // an assertion will fail when rolling the snapshot back. Very - // useful, much better than grovelling through megabytes of - // RUST_LOG output. + // snapshot, create type variables, and register obligations + // which may involve those type variables in the fulfillment cx, + // potentially leaving "dangling type variables" behind. + // In such cases, an assertion will fail when attempting to + // register obligations, within a snapshot. Very useful, much + // better than grovelling through megabytes of RUST_LOG output. // - // HOWEVER, in some cases the flag is wrong. In particular, we + // HOWEVER, in some cases the flag is unhelpful. In particular, we // sometimes create a "mini-fulfilment-cx" in which we enroll // obligations. As long as this fulfillment cx is fully drained // before we return, this is not a problem, as there won't be any // escaping obligations in the main cx. In those cases, you can // use this function. - pub fn save_and_restore_obligations_in_snapshot_flag(&self, func: F) -> R + pub fn save_and_restore_in_snapshot_flag(&self, func: F) -> R where F: FnOnce(&Self) -> R { - let flag = self.obligations_in_snapshot.get(); - self.obligations_in_snapshot.set(false); + let flag = self.in_snapshot.get(); + self.in_snapshot.set(false); let result = func(self); - self.obligations_in_snapshot.set(flag); + self.in_snapshot.set(flag); result } fn start_snapshot(&self) -> CombinedSnapshot { debug!("start_snapshot()"); - let obligations_in_snapshot = self.obligations_in_snapshot.get(); - self.obligations_in_snapshot.set(false); + let in_snapshot = self.in_snapshot.get(); + self.in_snapshot.set(true); CombinedSnapshot { projection_cache_snapshot: self.projection_cache.borrow_mut().snapshot(), @@ -900,7 +901,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { int_snapshot: self.int_unification_table.borrow_mut().snapshot(), float_snapshot: self.float_unification_table.borrow_mut().snapshot(), region_vars_snapshot: self.region_vars.start_snapshot(), - obligations_in_snapshot: obligations_in_snapshot, + was_in_snapshot: in_snapshot, } } @@ -911,10 +912,9 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { int_snapshot, float_snapshot, region_vars_snapshot, - obligations_in_snapshot } = snapshot; + was_in_snapshot } = snapshot; - assert!(!self.obligations_in_snapshot.get()); - self.obligations_in_snapshot.set(obligations_in_snapshot); + self.in_snapshot.set(was_in_snapshot); self.projection_cache .borrow_mut() @@ -939,9 +939,9 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { int_snapshot, float_snapshot, region_vars_snapshot, - obligations_in_snapshot } = snapshot; + was_in_snapshot } = snapshot; - self.obligations_in_snapshot.set(obligations_in_snapshot); + self.in_snapshot.set(was_in_snapshot); self.projection_cache .borrow_mut() diff --git a/src/librustc/traits/fulfill.rs b/src/librustc/traits/fulfill.rs index 64453f2983b..d771be077ae 100644 --- a/src/librustc/traits/fulfill.rs +++ b/src/librustc/traits/fulfill.rs @@ -171,7 +171,7 @@ impl<'a, 'gcx, 'tcx> FulfillmentContext<'tcx> { debug!("register_predicate_obligation(obligation={:?})", obligation); - infcx.obligations_in_snapshot.set(true); + assert!(!infcx.is_in_snapshot()); if infcx.tcx.fulfilled_predicates.borrow().check_duplicate(&obligation.predicate) { debug!("register_predicate_obligation: duplicate"); diff --git a/src/librustc/traits/specialize/mod.rs b/src/librustc/traits/specialize/mod.rs index 50a4d982832..92b7c736d42 100644 --- a/src/librustc/traits/specialize/mod.rs +++ b/src/librustc/traits/specialize/mod.rs @@ -242,7 +242,7 @@ fn fulfill_implication<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>, // attempt to prove all of the predicates for impl2 given those for impl1 // (which are packed up in penv) - infcx.save_and_restore_obligations_in_snapshot_flag(|infcx| { + infcx.save_and_restore_in_snapshot_flag(|infcx| { let mut fulfill_cx = FulfillmentContext::new(); for oblig in obligations.into_iter() { fulfill_cx.register_predicate_obligation(&infcx, oblig); diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 5e7325275b8..61e4b74ae8d 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -2588,7 +2588,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { // we can. We don't care if some things turn // out unconstrained or ambiguous, as we're // just trying to get hints here. - let result = self.save_and_restore_obligations_in_snapshot_flag(|_| { + let result = self.save_and_restore_in_snapshot_flag(|_| { let mut fulfill = FulfillmentContext::new(); let ok = ok; // FIXME(#30046) for obligation in ok.obligations {