Skip to content

Commit 040fc94

Browse files
committed
catch attempts to leak obligations out of snapshots
1 parent c209d44 commit 040fc94

File tree

7 files changed

+127
-116
lines changed

7 files changed

+127
-116
lines changed

src/librustc/infer/mod.rs

+28-5
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,11 @@ pub struct InferCtxt<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
163163
// If the number of errors increases, that's also a sign (line
164164
// `tained_by_errors`) to avoid reporting certain kinds of errors.
165165
err_count_on_creation: usize,
166+
167+
// This flag is used for debugging, and is set to true if there are
168+
// any obligations set during the current snapshot. In that case, the
169+
// snapshot can't be rolled back.
170+
pub obligations_in_snapshot: Cell<bool>,
166171
}
167172

168173
/// A map returned by `skolemize_late_bound_regions()` indicating the skolemized
@@ -476,7 +481,8 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'gcx> {
476481
normalize: false,
477482
projection_mode: ProjectionMode::AnyFinal,
478483
tainted_by_errors_flag: Cell::new(false),
479-
err_count_on_creation: self.sess.err_count()
484+
err_count_on_creation: self.sess.err_count(),
485+
obligations_in_snapshot: Cell::new(false),
480486
}
481487
}
482488
}
@@ -515,7 +521,8 @@ impl<'a, 'gcx, 'tcx> InferCtxtBuilder<'a, 'gcx, 'tcx> {
515521
normalize: normalize,
516522
projection_mode: projection_mode,
517523
tainted_by_errors_flag: Cell::new(false),
518-
err_count_on_creation: tcx.sess.err_count()
524+
err_count_on_creation: tcx.sess.err_count(),
525+
obligations_in_snapshot: Cell::new(false),
519526
}))
520527
}
521528
}
@@ -542,6 +549,7 @@ pub struct CombinedSnapshot {
542549
int_snapshot: unify::Snapshot<ty::IntVid>,
543550
float_snapshot: unify::Snapshot<ty::FloatVid>,
544551
region_vars_snapshot: RegionSnapshot,
552+
obligations_in_snapshot: bool,
545553
}
546554

547555
/// Helper trait for shortening the lifetimes inside a
@@ -809,11 +817,15 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
809817
}
810818

811819
fn start_snapshot(&self) -> CombinedSnapshot {
820+
let obligations_in_snapshot = self.obligations_in_snapshot.get();
821+
self.obligations_in_snapshot.set(false);
822+
812823
CombinedSnapshot {
813824
type_snapshot: self.type_variables.borrow_mut().snapshot(),
814825
int_snapshot: self.int_unification_table.borrow_mut().snapshot(),
815826
float_snapshot: self.float_unification_table.borrow_mut().snapshot(),
816827
region_vars_snapshot: self.region_vars.start_snapshot(),
828+
obligations_in_snapshot: obligations_in_snapshot,
817829
}
818830
}
819831

@@ -822,7 +834,11 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
822834
let CombinedSnapshot { type_snapshot,
823835
int_snapshot,
824836
float_snapshot,
825-
region_vars_snapshot } = snapshot;
837+
region_vars_snapshot,
838+
obligations_in_snapshot } = snapshot;
839+
840+
assert!(!self.obligations_in_snapshot.get());
841+
self.obligations_in_snapshot.set(obligations_in_snapshot);
826842

827843
self.type_variables
828844
.borrow_mut()
@@ -842,7 +858,10 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
842858
let CombinedSnapshot { type_snapshot,
843859
int_snapshot,
844860
float_snapshot,
845-
region_vars_snapshot } = snapshot;
861+
region_vars_snapshot,
862+
obligations_in_snapshot } = snapshot;
863+
864+
self.obligations_in_snapshot.set(obligations_in_snapshot);
846865

847866
self.type_variables
848867
.borrow_mut()
@@ -904,12 +923,16 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
904923
let CombinedSnapshot { type_snapshot,
905924
int_snapshot,
906925
float_snapshot,
907-
region_vars_snapshot } = self.start_snapshot();
926+
region_vars_snapshot,
927+
obligations_in_snapshot } = self.start_snapshot();
908928

909929
let r = self.commit_if_ok(|_| f());
910930

911931
debug!("commit_regions_if_ok: rolling back everything but regions");
912932

933+
assert!(!self.obligations_in_snapshot.get());
934+
self.obligations_in_snapshot.set(obligations_in_snapshot);
935+
913936
// Roll back any non-region bindings - they should be resolved
914937
// inside `f`, with, e.g. `resolve_type_vars_if_possible`.
915938
self.type_variables

src/librustc/traits/fulfill.rs

+2
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,8 @@ impl<'a, 'gcx, 'tcx> FulfillmentContext<'tcx> {
171171
// debug output much nicer to read and so on.
172172
let obligation = infcx.resolve_type_vars_if_possible(&obligation);
173173

174+
infcx.obligations_in_snapshot.set(true);
175+
174176
if infcx.tcx.fulfilled_predicates.borrow().check_duplicate(&obligation.predicate)
175177
{
176178
return

src/librustc/traits/specialize/mod.rs

+40-42
Original file line numberDiff line numberDiff line change
@@ -187,51 +187,49 @@ fn fulfill_implication<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>,
187187
source_trait_ref: ty::TraitRef<'tcx>,
188188
target_impl: DefId)
189189
-> Result<&'tcx Substs<'tcx>, ()> {
190-
infcx.commit_if_ok(|_| {
191-
let selcx = &mut SelectionContext::new(&infcx);
192-
let target_substs = fresh_type_vars_for_impl(&infcx, DUMMY_SP, target_impl);
193-
let (target_trait_ref, obligations) = impl_trait_ref_and_oblig(selcx,
194-
target_impl,
195-
&target_substs);
196-
197-
// do the impls unify? If not, no specialization.
198-
if let Err(_) = infcx.eq_trait_refs(true,
199-
TypeOrigin::Misc(DUMMY_SP),
200-
source_trait_ref,
201-
target_trait_ref) {
202-
debug!("fulfill_implication: {:?} does not unify with {:?}",
203-
source_trait_ref,
204-
target_trait_ref);
205-
return Err(());
206-
}
190+
let selcx = &mut SelectionContext::new(&infcx);
191+
let target_substs = fresh_type_vars_for_impl(&infcx, DUMMY_SP, target_impl);
192+
let (target_trait_ref, obligations) = impl_trait_ref_and_oblig(selcx,
193+
target_impl,
194+
&target_substs);
195+
196+
// do the impls unify? If not, no specialization.
197+
if let Err(_) = infcx.eq_trait_refs(true,
198+
TypeOrigin::Misc(DUMMY_SP),
199+
source_trait_ref,
200+
target_trait_ref) {
201+
debug!("fulfill_implication: {:?} does not unify with {:?}",
202+
source_trait_ref,
203+
target_trait_ref);
204+
return Err(());
205+
}
207206

208-
// attempt to prove all of the predicates for impl2 given those for impl1
209-
// (which are packed up in penv)
207+
// attempt to prove all of the predicates for impl2 given those for impl1
208+
// (which are packed up in penv)
210209

211-
let mut fulfill_cx = FulfillmentContext::new();
212-
for oblig in obligations.into_iter() {
213-
fulfill_cx.register_predicate_obligation(&infcx, oblig);
214-
}
210+
let mut fulfill_cx = FulfillmentContext::new();
211+
for oblig in obligations.into_iter() {
212+
fulfill_cx.register_predicate_obligation(&infcx, oblig);
213+
}
215214

216-
if let Err(errors) = infcx.drain_fulfillment_cx(&mut fulfill_cx, &()) {
217-
// no dice!
218-
debug!("fulfill_implication: for impls on {:?} and {:?}, could not fulfill: {:?} given \
219-
{:?}",
220-
source_trait_ref,
221-
target_trait_ref,
222-
errors,
223-
infcx.parameter_environment.caller_bounds);
224-
Err(())
225-
} else {
226-
debug!("fulfill_implication: an impl for {:?} specializes {:?}",
227-
source_trait_ref,
228-
target_trait_ref);
229-
230-
// Now resolve the *substitution* we built for the target earlier, replacing
231-
// the inference variables inside with whatever we got from fulfillment.
232-
Ok(infcx.resolve_type_vars_if_possible(&target_substs))
233-
}
234-
})
215+
if let Err(errors) = infcx.drain_fulfillment_cx(&mut fulfill_cx, &()) {
216+
// no dice!
217+
debug!("fulfill_implication: for impls on {:?} and {:?}, could not fulfill: {:?} given \
218+
{:?}",
219+
source_trait_ref,
220+
target_trait_ref,
221+
errors,
222+
infcx.parameter_environment.caller_bounds);
223+
Err(())
224+
} else {
225+
debug!("fulfill_implication: an impl for {:?} specializes {:?}",
226+
source_trait_ref,
227+
target_trait_ref);
228+
229+
// Now resolve the *substitution* we built for the target earlier, replacing
230+
// the inference variables inside with whatever we got from fulfillment.
231+
Ok(infcx.resolve_type_vars_if_possible(&target_substs))
232+
}
235233
}
236234

237235
pub struct SpecializesCache {

src/librustc_typeck/check/coercion.rs

+2
Original file line numberDiff line numberDiff line change
@@ -363,6 +363,8 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> {
363363
}
364364
};
365365

366+
// This commits the obligations to the fulfillcx. After this succeeds,
367+
// this snapshot can't be rolled back.
366368
autoderef.finalize(LvaluePreference::from_mutbl(mt_b.mutbl), exprs());
367369

368370
// Now apply the autoref. We have to extract the region out of

src/librustc_typeck/check/compare_method.rs

+52-67
Original file line numberDiff line numberDiff line change
@@ -279,78 +279,63 @@ pub fn compare_impl_method<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
279279
// type.
280280

281281
// Compute skolemized form of impl and trait method tys.
282-
let impl_fty = tcx.mk_fn_ptr(impl_m.fty);
283-
let impl_fty = impl_fty.subst(tcx, impl_to_skol_substs);
284-
let trait_fty = tcx.mk_fn_ptr(trait_m.fty);
285-
let trait_fty = trait_fty.subst(tcx, &trait_to_skol_substs);
286-
287-
let err = infcx.commit_if_ok(|snapshot| {
288-
let tcx = infcx.tcx;
289-
let origin = TypeOrigin::MethodCompatCheck(impl_m_span);
290-
291-
let (impl_sig, _) =
292-
infcx.replace_late_bound_regions_with_fresh_var(impl_m_span,
293-
infer::HigherRankedType,
294-
&impl_m.fty.sig);
295-
let impl_sig =
296-
impl_sig.subst(tcx, impl_to_skol_substs);
297-
let impl_sig =
298-
assoc::normalize_associated_types_in(&infcx,
299-
&mut fulfillment_cx,
300-
impl_m_span,
301-
impl_m_body_id,
302-
&impl_sig);
303-
let impl_fty = tcx.mk_fn_ptr(tcx.mk_bare_fn(ty::BareFnTy {
304-
unsafety: impl_m.fty.unsafety,
305-
abi: impl_m.fty.abi,
306-
sig: ty::Binder(impl_sig)
307-
}));
308-
debug!("compare_impl_method: impl_fty={:?}",
309-
impl_fty);
310-
311-
let (trait_sig, skol_map) =
312-
infcx.skolemize_late_bound_regions(&trait_m.fty.sig, snapshot);
313-
let trait_sig =
314-
trait_sig.subst(tcx, &trait_to_skol_substs);
315-
let trait_sig =
316-
assoc::normalize_associated_types_in(&infcx,
317-
&mut fulfillment_cx,
318-
impl_m_span,
319-
impl_m_body_id,
320-
&trait_sig);
321-
let trait_fty = tcx.mk_fn_ptr(tcx.mk_bare_fn(ty::BareFnTy {
322-
unsafety: trait_m.fty.unsafety,
323-
abi: trait_m.fty.abi,
324-
sig: ty::Binder(trait_sig)
325-
}));
326-
327-
debug!("compare_impl_method: trait_fty={:?}",
282+
let tcx = infcx.tcx;
283+
let origin = TypeOrigin::MethodCompatCheck(impl_m_span);
284+
285+
let (impl_sig, _) =
286+
infcx.replace_late_bound_regions_with_fresh_var(impl_m_span,
287+
infer::HigherRankedType,
288+
&impl_m.fty.sig);
289+
let impl_sig =
290+
impl_sig.subst(tcx, impl_to_skol_substs);
291+
let impl_sig =
292+
assoc::normalize_associated_types_in(&infcx,
293+
&mut fulfillment_cx,
294+
impl_m_span,
295+
impl_m_body_id,
296+
&impl_sig);
297+
let impl_fty = tcx.mk_fn_ptr(tcx.mk_bare_fn(ty::BareFnTy {
298+
unsafety: impl_m.fty.unsafety,
299+
abi: impl_m.fty.abi,
300+
sig: ty::Binder(impl_sig)
301+
}));
302+
debug!("compare_impl_method: impl_fty={:?}", impl_fty);
303+
304+
let trait_sig = tcx.liberate_late_bound_regions(
305+
infcx.parameter_environment.free_id_outlive,
306+
&trait_m.fty.sig);
307+
let trait_sig =
308+
trait_sig.subst(tcx, &trait_to_skol_substs);
309+
let trait_sig =
310+
assoc::normalize_associated_types_in(&infcx,
311+
&mut fulfillment_cx,
312+
impl_m_span,
313+
impl_m_body_id,
314+
&trait_sig);
315+
let trait_fty = tcx.mk_fn_ptr(tcx.mk_bare_fn(ty::BareFnTy {
316+
unsafety: trait_m.fty.unsafety,
317+
abi: trait_m.fty.abi,
318+
sig: ty::Binder(trait_sig)
319+
}));
320+
321+
debug!("compare_impl_method: trait_fty={:?}", trait_fty);
322+
323+
if let Err(terr) = infcx.sub_types(false, origin, impl_fty, trait_fty) {
324+
debug!("sub_types failed: impl ty {:?}, trait ty {:?}",
325+
impl_fty,
328326
trait_fty);
329-
330-
infcx.sub_types(false, origin, impl_fty, trait_fty)?;
331-
332-
infcx.leak_check(false, &skol_map, snapshot)
333-
});
334-
335-
match err {
336-
Ok(()) => { }
337-
Err(terr) => {
338-
debug!("checking trait method for compatibility: impl ty {:?}, trait ty {:?}",
339-
impl_fty,
340-
trait_fty);
341-
span_err!(tcx.sess, impl_m_span, E0053,
342-
"method `{}` has an incompatible type for trait: {}",
343-
trait_m.name,
344-
terr);
345-
return;
346-
}
327+
span_err!(tcx.sess, impl_m_span, E0053,
328+
"method `{}` has an incompatible type for trait: {}",
329+
trait_m.name,
330+
terr);
331+
return
347332
}
348333

349334
// Check that all obligations are satisfied by the implementation's
350335
// version.
351-
match fulfillment_cx.select_all_or_error(&infcx) {
352-
Err(ref errors) => { infcx.report_fulfillment_errors(errors) }
353-
Ok(_) => {}
336+
if let Err(ref errors) = fulfillment_cx.select_all_or_error(&infcx) {
337+
infcx.report_fulfillment_errors(errors);
338+
return
354339
}
355340

356341
// Finally, resolve all regions. This catches wily misuses of

src/test/compile-fail/regions-bound-missing-bound-in-impl.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,8 @@ impl<'a, 't> Foo<'a, 't> for &'a isize {
3434
}
3535

3636
fn wrong_bound1<'b,'c,'d:'a+'c>(self, b: Inv<'b>, c: Inv<'c>, d: Inv<'d>) {
37-
//~^ ERROR method `wrong_bound1` has an incompatible type for trait
37+
//~^ ERROR method not compatible with trait
38+
//~^^ ERROR method not compatible with trait
3839
//
3940
// Note: This is a terrible error message. It is caused
4041
// because, in the trait, 'b is early bound, and in the impl,

src/test/compile-fail/regions-trait-1.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ impl<'a> get_ctxt for has_ctxt<'a> {
2323

2424
// Here an error occurs because we used `&self` but
2525
// the definition used `&`:
26-
fn get_ctxt(&self) -> &'a ctxt { //~ ERROR method `get_ctxt` has an incompatible type
26+
fn get_ctxt(&self) -> &'a ctxt { //~ ERROR method not compatible with trait
2727
self.c
2828
}
2929

0 commit comments

Comments
 (0)