Skip to content

Commit 08dfbfb

Browse files
committed
Auto merge of #70170 - eddyb:wf-early-exit, r=nikomatsakis
wf: handle "livelock" checking before reaching `WfPredicates::compute`. For `wf::obligations`'s "livelock" handling, this PR shouldn't cause any behavioral changes, as the check moved to it should be equivalent to the old one in `WfPredicates::compute`. However, it fixes #70168 by making *other* users of `WfPredicates::compute` (that is, `wf::predicate_obligations` and `compute`'s own upvar handling) correct for `ty::Infer`, in that they now get a `WellFormed(ty::Infer(_))` obligation instead of silently ignoring the type. r? @nikomatsakis
2 parents dae90c1 + 05a872d commit 08dfbfb

File tree

1 file changed

+45
-43
lines changed
  • src/librustc_trait_selection/traits

1 file changed

+45
-43
lines changed

src/librustc_trait_selection/traits/wf.rs

+45-43
Original file line numberDiff line numberDiff line change
@@ -22,16 +22,27 @@ pub fn obligations<'a, 'tcx>(
2222
ty: Ty<'tcx>,
2323
span: Span,
2424
) -> Option<Vec<traits::PredicateObligation<'tcx>>> {
25+
// Handle the "livelock" case (see comment above) by bailing out if necessary.
26+
let ty = match ty.kind {
27+
ty::Infer(ty::TyVar(_)) => {
28+
let resolved_ty = infcx.shallow_resolve(ty);
29+
if resolved_ty == ty {
30+
// No progress, bail out to prevent "livelock".
31+
return None;
32+
}
33+
34+
resolved_ty
35+
}
36+
_ => ty,
37+
};
38+
2539
let mut wf = WfPredicates { infcx, param_env, body_id, span, out: vec![], item: None };
26-
if wf.compute(ty) {
27-
debug!("wf::obligations({:?}, body_id={:?}) = {:?}", ty, body_id, wf.out);
28-
29-
let result = wf.normalize();
30-
debug!("wf::obligations({:?}, body_id={:?}) ~~> {:?}", ty, body_id, result);
31-
Some(result)
32-
} else {
33-
None // no progress made, return None
34-
}
40+
wf.compute(ty);
41+
debug!("wf::obligations({:?}, body_id={:?}) = {:?}", ty, body_id, wf.out);
42+
43+
let result = wf.normalize();
44+
debug!("wf::obligations({:?}, body_id={:?}) ~~> {:?}", ty, body_id, result);
45+
Some(result)
3546
}
3647

3748
/// Returns the obligations that make this trait reference
@@ -311,12 +322,9 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
311322
}
312323
}
313324

314-
/// Pushes new obligations into `out`. Returns `true` if it was able
315-
/// to generate all the predicates needed to validate that `ty0`
316-
/// is WF. Returns false if `ty0` is an unresolved type variable,
317-
/// in which case we are not able to simplify at all.
318-
fn compute(&mut self, ty0: Ty<'tcx>) -> bool {
319-
let mut walker = ty0.walk();
325+
/// Pushes all the predicates needed to validate that `ty` is WF into `out`.
326+
fn compute(&mut self, ty: Ty<'tcx>) {
327+
let mut walker = ty.walk();
320328
let param_env = self.param_env;
321329
while let Some(arg) = walker.next() {
322330
let ty = match arg.unpack() {
@@ -348,6 +356,12 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
348356
// WfScalar, WfParameter, etc
349357
}
350358

359+
// Can only infer to `ty::Int(_) | ty::Uint(_)`.
360+
ty::Infer(ty::IntVar(_)) => {}
361+
362+
// Can only infer to `ty::Float(_)`.
363+
ty::Infer(ty::FloatVar(_)) => {}
364+
351365
ty::Slice(subty) => {
352366
self.require_sized(subty, traits::SliceOrArrayElem);
353367
}
@@ -442,8 +456,9 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
442456
// are not directly inspecting closure types
443457
// anyway, except via auto trait matching (which
444458
// only inspects the upvar types).
445-
walker.skip_current_subtree(); // subtree handled by compute_projection
459+
walker.skip_current_subtree(); // subtree handled below
446460
for upvar_ty in substs.as_closure().upvar_tys() {
461+
// FIXME(eddyb) add the type to `walker` instead of recursing.
447462
self.compute(upvar_ty);
448463
}
449464
}
@@ -496,44 +511,31 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
496511
//
497512
// 1. Check if they have been resolved, and if so proceed with
498513
// THAT type.
499-
// 2. If not, check whether this is the type that we
500-
// started with (ty0). In that case, we've made no
501-
// progress at all, so return false. Otherwise,
502-
// we've at least simplified things (i.e., we went
503-
// from `Vec<$0>: WF` to `$0: WF`, so we can
514+
// 2. If not, we've at least simplified things (e.g., we went
515+
// from `Vec<$0>: WF` to `$0: WF`), so we can
504516
// register a pending obligation and keep
505517
// moving. (Goal is that an "inductive hypothesis"
506518
// is satisfied to ensure termination.)
519+
// See also the comment on `fn obligations`, describing "livelock"
520+
// prevention, which happens before this can be reached.
507521
ty::Infer(_) => {
508522
let ty = self.infcx.shallow_resolve(ty);
509-
if let ty::Infer(_) = ty.kind {
510-
// not yet resolved...
511-
if ty == ty0 {
512-
// ...this is the type we started from! no progress.
513-
return false;
514-
}
515-
523+
if let ty::Infer(ty::TyVar(_)) = ty.kind {
524+
// Not yet resolved, but we've made progress.
516525
let cause = self.cause(traits::MiscObligation);
517-
self.out.push(
518-
// ...not the type we started from, so we made progress.
519-
traits::Obligation::new(
520-
cause,
521-
self.param_env,
522-
ty::Predicate::WellFormed(ty),
523-
),
524-
);
526+
self.out.push(traits::Obligation::new(
527+
cause,
528+
param_env,
529+
ty::Predicate::WellFormed(ty),
530+
));
525531
} else {
526-
// Yes, resolved, proceed with the
527-
// result. Should never return false because
528-
// `ty` is not a Infer.
529-
assert!(self.compute(ty));
532+
// Yes, resolved, proceed with the result.
533+
// FIXME(eddyb) add the type to `walker` instead of recursing.
534+
self.compute(ty);
530535
}
531536
}
532537
}
533538
}
534-
535-
// if we made it through that loop above, we made progress!
536-
true
537539
}
538540

539541
fn nominal_obligations(

0 commit comments

Comments
 (0)