1
Fork 0

wf: handle "livelock" checking before reaching WfPredicates::compute.

This commit is contained in:
Eduard-Mihai Burtescu 2020-03-20 01:49:01 +02:00
parent eece58a8e3
commit d1dc2afd05

View file

@ -22,16 +22,27 @@ pub fn obligations<'a, 'tcx>(
ty: Ty<'tcx>, ty: Ty<'tcx>,
span: Span, span: Span,
) -> Option<Vec<traits::PredicateObligation<'tcx>>> { ) -> Option<Vec<traits::PredicateObligation<'tcx>>> {
// Handle the "livelock" case (see comment above) by bailing out if necessary.
let ty = match ty.kind {
ty::Infer(_) => {
let resolved_ty = infcx.shallow_resolve(ty);
if resolved_ty == ty {
// No progress, bail out to prevent "livelock".
return None;
}
resolved_ty
}
_ => ty,
};
let mut wf = WfPredicates { infcx, param_env, body_id, span, out: vec![], item: None }; let mut wf = WfPredicates { infcx, param_env, body_id, span, out: vec![], item: None };
if wf.compute(ty) { wf.compute(ty);
debug!("wf::obligations({:?}, body_id={:?}) = {:?}", ty, body_id, wf.out); debug!("wf::obligations({:?}, body_id={:?}) = {:?}", ty, body_id, wf.out);
let result = wf.normalize(); let result = wf.normalize();
debug!("wf::obligations({:?}, body_id={:?}) ~~> {:?}", ty, body_id, result); debug!("wf::obligations({:?}, body_id={:?}) ~~> {:?}", ty, body_id, result);
Some(result) Some(result)
} else {
None // no progress made, return None
}
} }
/// Returns the obligations that make this trait reference /// Returns the obligations that make this trait reference
@ -311,12 +322,9 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
} }
} }
/// Pushes new obligations into `out`. Returns `true` if it was able /// Pushes all the predicates needed to validate that `ty` is WF into `out`.
/// to generate all the predicates needed to validate that `ty0` fn compute(&mut self, ty: Ty<'tcx>) {
/// is WF. Returns false if `ty0` is an unresolved type variable, let mut walker = ty.walk();
/// in which case we are not able to simplify at all.
fn compute(&mut self, ty0: Ty<'tcx>) -> bool {
let mut walker = ty0.walk();
let param_env = self.param_env; let param_env = self.param_env;
while let Some(arg) = walker.next() { while let Some(arg) = walker.next() {
let ty = match arg.unpack() { let ty = match arg.unpack() {
@ -442,8 +450,9 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
// are not directly inspecting closure types // are not directly inspecting closure types
// anyway, except via auto trait matching (which // anyway, except via auto trait matching (which
// only inspects the upvar types). // only inspects the upvar types).
walker.skip_current_subtree(); // subtree handled by compute_projection walker.skip_current_subtree(); // subtree handled below
for upvar_ty in substs.as_closure().upvar_tys() { for upvar_ty in substs.as_closure().upvar_tys() {
// FIXME(eddyb) add the type to `walker` instead of recursing.
self.compute(upvar_ty); self.compute(upvar_ty);
} }
} }
@ -496,44 +505,31 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
// //
// 1. Check if they have been resolved, and if so proceed with // 1. Check if they have been resolved, and if so proceed with
// THAT type. // THAT type.
// 2. If not, check whether this is the type that we // 2. If not, we've at least simplified things (e.g., we went
// started with (ty0). In that case, we've made no // from `Vec<$0>: WF` to `$0: WF`), so we can
// progress at all, so return false. Otherwise,
// we've at least simplified things (i.e., we went
// from `Vec<$0>: WF` to `$0: WF`, so we can
// register a pending obligation and keep // register a pending obligation and keep
// moving. (Goal is that an "inductive hypothesis" // moving. (Goal is that an "inductive hypothesis"
// is satisfied to ensure termination.) // is satisfied to ensure termination.)
// See also the comment on `fn obligations`, describing "livelock"
// prevention, which happens before this can be reached.
ty::Infer(_) => { ty::Infer(_) => {
let ty = self.infcx.shallow_resolve(ty); let ty = self.infcx.shallow_resolve(ty);
if let ty::Infer(_) = ty.kind { if let ty::Infer(_) = ty.kind {
// not yet resolved... // Not yet resolved, but we've made progress.
if ty == ty0 {
// ...this is the type we started from! no progress.
return false;
}
let cause = self.cause(traits::MiscObligation); let cause = self.cause(traits::MiscObligation);
self.out.push( self.out.push(traits::Obligation::new(
// ...not the type we started from, so we made progress.
traits::Obligation::new(
cause, cause,
self.param_env, param_env,
ty::Predicate::WellFormed(ty), ty::Predicate::WellFormed(ty),
), ));
);
} else { } else {
// Yes, resolved, proceed with the // Yes, resolved, proceed with the result.
// result. Should never return false because // FIXME(eddyb) add the type to `walker` instead of recursing.
// `ty` is not a Infer. self.compute(ty);
assert!(self.compute(ty));
} }
} }
} }
} }
// if we made it through that loop above, we made progress!
true
} }
fn nominal_obligations( fn nominal_obligations(