wf: handle "livelock" checking before reaching WfPredicates::compute
.
This commit is contained in:
parent
eece58a8e3
commit
d1dc2afd05
1 changed files with 38 additions and 42 deletions
|
@ -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>>> {
|
||||||
let mut wf = WfPredicates { infcx, param_env, body_id, span, out: vec![], item: None };
|
// Handle the "livelock" case (see comment above) by bailing out if necessary.
|
||||||
if wf.compute(ty) {
|
let ty = match ty.kind {
|
||||||
debug!("wf::obligations({:?}, body_id={:?}) = {:?}", ty, body_id, wf.out);
|
ty::Infer(_) => {
|
||||||
|
let resolved_ty = infcx.shallow_resolve(ty);
|
||||||
|
if resolved_ty == ty {
|
||||||
|
// No progress, bail out to prevent "livelock".
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
let result = wf.normalize();
|
resolved_ty
|
||||||
debug!("wf::obligations({:?}, body_id={:?}) ~~> {:?}", ty, body_id, result);
|
}
|
||||||
Some(result)
|
_ => ty,
|
||||||
} else {
|
};
|
||||||
None // no progress made, return None
|
|
||||||
}
|
let mut wf = WfPredicates { infcx, param_env, body_id, span, out: vec![], item: None };
|
||||||
|
wf.compute(ty);
|
||||||
|
debug!("wf::obligations({:?}, body_id={:?}) = {:?}", ty, body_id, wf.out);
|
||||||
|
|
||||||
|
let result = wf.normalize();
|
||||||
|
debug!("wf::obligations({:?}, body_id={:?}) ~~> {:?}", ty, body_id, result);
|
||||||
|
Some(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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.
|
cause,
|
||||||
traits::Obligation::new(
|
param_env,
|
||||||
cause,
|
ty::Predicate::WellFormed(ty),
|
||||||
self.param_env,
|
));
|
||||||
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(
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue