Skip to content

Commit 467aaab

Browse files
committed
Auto merge of #41325 - eddyb:isolate-snapshots-for-good, r=arielb1
Ban registering obligations during InferCtxt snapshots. Back in #33852, a flag was added to `InferCtxt` to prevent rolling back a snapshot if obligations were added to some `FulfillmentContext` during the snapshot, to prevent leaking fresh inference variables (created during that snapshot, so their indices would get reused) in obligations, which could ICE or worse. But that isn't enough in the long run, as type-checking ends up relying on success implying that eager side-effects are fine, and while stray obligations *do* get caught nowadays, those errors prevent, e.g. the speculative coercions from #37658, which *have to* be rolled back *even* if they succeed. We can't just allow those obligations to stay around though, because we end up, again, in ICEs or worse. Instead, this PR modifies `lookup_method_in_trait_adjusted` to return `InferOk` containing the obligations that `Autoderef::finalize_as_infer_ok` can propagate to deref coercions. As there shouldn't be *anything* left that registers obligations during snapshots, it's completely banned. r? @nikomatsakis @arielb1
2 parents 5f22d46 + cd64ff9 commit 467aaab

File tree

13 files changed

+190
-206
lines changed

13 files changed

+190
-206
lines changed

src/librustc/infer/mod.rs

+28-28
Original file line numberDiff line numberDiff line change
@@ -199,10 +199,8 @@ pub struct InferCtxt<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
199199
// `tained_by_errors`) to avoid reporting certain kinds of errors.
200200
err_count_on_creation: usize,
201201

202-
// This flag is used for debugging, and is set to true if there are
203-
// any obligations set during the current snapshot. In that case, the
204-
// snapshot can't be rolled back.
205-
pub obligations_in_snapshot: Cell<bool>,
202+
// This flag is true while there is an active snapshot.
203+
in_snapshot: Cell<bool>,
206204
}
207205

208206
/// A map returned by `skolemize_late_bound_regions()` indicating the skolemized
@@ -507,7 +505,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'gcx> {
507505
projection_mode: Reveal::UserFacing,
508506
tainted_by_errors_flag: Cell::new(false),
509507
err_count_on_creation: self.sess.err_count(),
510-
obligations_in_snapshot: Cell::new(false),
508+
in_snapshot: Cell::new(false),
511509
}
512510
}
513511
}
@@ -545,7 +543,7 @@ impl<'a, 'gcx, 'tcx> InferCtxtBuilder<'a, 'gcx, 'tcx> {
545543
projection_mode: projection_mode,
546544
tainted_by_errors_flag: Cell::new(false),
547545
err_count_on_creation: tcx.sess.err_count(),
548-
obligations_in_snapshot: Cell::new(false),
546+
in_snapshot: Cell::new(false),
549547
}))
550548
}
551549
}
@@ -573,7 +571,7 @@ pub struct CombinedSnapshot {
573571
int_snapshot: unify::Snapshot<ty::IntVid>,
574572
float_snapshot: unify::Snapshot<ty::FloatVid>,
575573
region_vars_snapshot: RegionSnapshot,
576-
obligations_in_snapshot: bool,
574+
was_in_snapshot: bool,
577575
}
578576

579577
/// Helper trait for shortening the lifetimes inside a
@@ -734,6 +732,10 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
734732
self.projection_mode
735733
}
736734

735+
pub fn is_in_snapshot(&self) -> bool {
736+
self.in_snapshot.get()
737+
}
738+
737739
pub fn freshen<T:TypeFoldable<'tcx>>(&self, t: T) -> T {
738740
t.fold_with(&mut self.freshener())
739741
}
@@ -861,46 +863,45 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
861863
result.map(move |t| InferOk { value: t, obligations: fields.obligations })
862864
}
863865

864-
// Clear the "obligations in snapshot" flag, invoke the closure,
866+
// Clear the "currently in a snapshot" flag, invoke the closure,
865867
// then restore the flag to its original value. This flag is a
866868
// debugging measure designed to detect cases where we start a
867-
// snapshot, create type variables, register obligations involving
868-
// those type variables in the fulfillment cx, and then have to
869-
// unroll the snapshot, leaving "dangling type variables" behind.
870-
// In such cases, the flag will be set by the fulfillment cx, and
871-
// an assertion will fail when rolling the snapshot back. Very
872-
// useful, much better than grovelling through megabytes of
873-
// RUST_LOG output.
869+
// snapshot, create type variables, and register obligations
870+
// which may involve those type variables in the fulfillment cx,
871+
// potentially leaving "dangling type variables" behind.
872+
// In such cases, an assertion will fail when attempting to
873+
// register obligations, within a snapshot. Very useful, much
874+
// better than grovelling through megabytes of RUST_LOG output.
874875
//
875-
// HOWEVER, in some cases the flag is wrong. In particular, we
876+
// HOWEVER, in some cases the flag is unhelpful. In particular, we
876877
// sometimes create a "mini-fulfilment-cx" in which we enroll
877878
// obligations. As long as this fulfillment cx is fully drained
878879
// before we return, this is not a problem, as there won't be any
879880
// escaping obligations in the main cx. In those cases, you can
880881
// use this function.
881-
pub fn save_and_restore_obligations_in_snapshot_flag<F, R>(&self, func: F) -> R
882+
pub fn save_and_restore_in_snapshot_flag<F, R>(&self, func: F) -> R
882883
where F: FnOnce(&Self) -> R
883884
{
884-
let flag = self.obligations_in_snapshot.get();
885-
self.obligations_in_snapshot.set(false);
885+
let flag = self.in_snapshot.get();
886+
self.in_snapshot.set(false);
886887
let result = func(self);
887-
self.obligations_in_snapshot.set(flag);
888+
self.in_snapshot.set(flag);
888889
result
889890
}
890891

891892
fn start_snapshot(&self) -> CombinedSnapshot {
892893
debug!("start_snapshot()");
893894

894-
let obligations_in_snapshot = self.obligations_in_snapshot.get();
895-
self.obligations_in_snapshot.set(false);
895+
let in_snapshot = self.in_snapshot.get();
896+
self.in_snapshot.set(true);
896897

897898
CombinedSnapshot {
898899
projection_cache_snapshot: self.projection_cache.borrow_mut().snapshot(),
899900
type_snapshot: self.type_variables.borrow_mut().snapshot(),
900901
int_snapshot: self.int_unification_table.borrow_mut().snapshot(),
901902
float_snapshot: self.float_unification_table.borrow_mut().snapshot(),
902903
region_vars_snapshot: self.region_vars.start_snapshot(),
903-
obligations_in_snapshot: obligations_in_snapshot,
904+
was_in_snapshot: in_snapshot,
904905
}
905906
}
906907

@@ -911,10 +912,9 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
911912
int_snapshot,
912913
float_snapshot,
913914
region_vars_snapshot,
914-
obligations_in_snapshot } = snapshot;
915+
was_in_snapshot } = snapshot;
915916

916-
assert!(!self.obligations_in_snapshot.get());
917-
self.obligations_in_snapshot.set(obligations_in_snapshot);
917+
self.in_snapshot.set(was_in_snapshot);
918918

919919
self.projection_cache
920920
.borrow_mut()
@@ -939,9 +939,9 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
939939
int_snapshot,
940940
float_snapshot,
941941
region_vars_snapshot,
942-
obligations_in_snapshot } = snapshot;
942+
was_in_snapshot } = snapshot;
943943

944-
self.obligations_in_snapshot.set(obligations_in_snapshot);
944+
self.in_snapshot.set(was_in_snapshot);
945945

946946
self.projection_cache
947947
.borrow_mut()

src/librustc/traits/fulfill.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ impl<'a, 'gcx, 'tcx> FulfillmentContext<'tcx> {
171171

172172
debug!("register_predicate_obligation(obligation={:?})", obligation);
173173

174-
infcx.obligations_in_snapshot.set(true);
174+
assert!(!infcx.is_in_snapshot());
175175

176176
if infcx.tcx.fulfilled_predicates.borrow().check_duplicate(&obligation.predicate) {
177177
debug!("register_predicate_obligation: duplicate");

src/librustc/traits/specialize/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,7 @@ fn fulfill_implication<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>,
242242
// attempt to prove all of the predicates for impl2 given those for impl1
243243
// (which are packed up in penv)
244244

245-
infcx.save_and_restore_obligations_in_snapshot_flag(|infcx| {
245+
infcx.save_and_restore_in_snapshot_flag(|infcx| {
246246
let mut fulfill_cx = FulfillmentContext::new();
247247
for oblig in obligations.into_iter() {
248248
fulfill_cx.register_predicate_obligation(&infcx, oblig);

src/librustc_typeck/check/assoc.rs

-39
This file was deleted.

src/librustc_typeck/check/autoderef.rs

+13-10
Original file line numberDiff line numberDiff line change
@@ -149,22 +149,25 @@ impl<'a, 'gcx, 'tcx> Autoderef<'a, 'gcx, 'tcx> {
149149
self.fcx.resolve_type_vars_if_possible(&self.cur_ty)
150150
}
151151

152-
pub fn finalize<E>(self, pref: LvaluePreference, exprs: &[E])
153-
where E: AsCoercionSite
154-
{
152+
pub fn finalize(self, pref: LvaluePreference, expr: &hir::Expr) {
155153
let fcx = self.fcx;
156-
fcx.register_infer_ok_obligations(self.finalize_as_infer_ok(pref, exprs));
154+
fcx.register_infer_ok_obligations(self.finalize_as_infer_ok(pref, &[expr]));
157155
}
158156

159157
pub fn finalize_as_infer_ok<E>(self, pref: LvaluePreference, exprs: &[E])
160158
-> InferOk<'tcx, ()>
161159
where E: AsCoercionSite
162160
{
163-
let methods: Vec<_> = self.steps
161+
let Autoderef { fcx, span, mut obligations, steps, .. } = self;
162+
let methods: Vec<_> = steps
164163
.iter()
165164
.map(|&(ty, kind)| {
166165
if let AutoderefKind::Overloaded = kind {
167-
self.fcx.try_overloaded_deref(self.span, None, ty, pref)
166+
fcx.try_overloaded_deref(span, None, ty, pref)
167+
.map(|InferOk { value, obligations: o }| {
168+
obligations.extend(o);
169+
value
170+
})
168171
} else {
169172
None
170173
}
@@ -174,22 +177,22 @@ impl<'a, 'gcx, 'tcx> Autoderef<'a, 'gcx, 'tcx> {
174177
debug!("finalize({:?}) - {:?},{:?}",
175178
pref,
176179
methods,
177-
self.obligations);
180+
obligations);
178181

179182
for expr in exprs {
180183
let expr = expr.as_coercion_site();
181184
debug!("finalize - finalizing #{} - {:?}", expr.id, expr);
182185
for (n, method) in methods.iter().enumerate() {
183186
if let &Some(method) = method {
184187
let method_call = MethodCall::autoderef(expr.id, n as u32);
185-
self.fcx.tables.borrow_mut().method_map.insert(method_call, method);
188+
fcx.tables.borrow_mut().method_map.insert(method_call, method);
186189
}
187190
}
188191
}
189192

190193
InferOk {
191194
value: (),
192-
obligations: self.obligations
195+
obligations
193196
}
194197
}
195198
}
@@ -211,7 +214,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
211214
base_expr: Option<&hir::Expr>,
212215
base_ty: Ty<'tcx>,
213216
lvalue_pref: LvaluePreference)
214-
-> Option<MethodCallee<'tcx>> {
217+
-> Option<InferOk<'tcx, MethodCallee<'tcx>>> {
215218
debug!("try_overloaded_deref({:?},{:?},{:?},{:?})",
216219
span,
217220
base_expr,

src/librustc_typeck/check/callee.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
5555
})
5656
.next();
5757
let callee_ty = autoderef.unambiguous_final_ty();
58-
autoderef.finalize(LvaluePreference::NoPreference, &[callee_expr]);
58+
autoderef.finalize(LvaluePreference::NoPreference, callee_expr);
5959

6060
let output = match result {
6161
None => {
@@ -173,7 +173,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
173173
adjusted_ty,
174174
None) {
175175
None => continue,
176-
Some(method_callee) => {
176+
Some(ok) => {
177+
let method_callee = self.register_infer_ok_obligations(ok);
177178
return Some(method_callee);
178179
}
179180
}

src/librustc_typeck/check/coercion.rs

+9-10
Original file line numberDiff line numberDiff line change
@@ -711,16 +711,15 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
711711

712712
let cause = self.cause(expr.span, ObligationCauseCode::ExprAssignable);
713713
let coerce = Coerce::new(self, cause);
714-
self.commit_if_ok(|_| {
715-
let ok = coerce.coerce(&[expr], source, target)?;
716-
let adjustment = self.register_infer_ok_obligations(ok);
717-
self.apply_adjustment(expr.id, adjustment);
718-
719-
// We should now have added sufficient adjustments etc to
720-
// ensure that the type of expression, post-adjustment, is
721-
// a subtype of target.
722-
Ok(target)
723-
})
714+
let ok = self.commit_if_ok(|_| coerce.coerce(&[expr], source, target))?;
715+
716+
let adjustment = self.register_infer_ok_obligations(ok);
717+
self.apply_adjustment(expr.id, adjustment);
718+
719+
// We should now have added sufficient adjustments etc to
720+
// ensure that the type of expression, post-adjustment, is
721+
// a subtype of target.
722+
Ok(target)
724723
}
725724

726725
/// Given some expressions, their known unified type and another expression,

0 commit comments

Comments
 (0)