about summary refs log tree commit diff
diff options
context:
space:
mode:
authorEduard-Mihai Burtescu <edy.burt@gmail.com>2020-03-20 01:49:01 +0200
committerEduard-Mihai Burtescu <edy.burt@gmail.com>2020-04-30 23:01:22 +0300
commitd1dc2afd05a0f501512d9ed24f2d1785f7228eba (patch)
tree3e4cff19ea7f6b43f73d3b47d101c035dd5a4c18
parenteece58a8e35c444afba6fa34873bc0244e32cd29 (diff)
downloadrust-d1dc2afd05a0f501512d9ed24f2d1785f7228eba.tar.gz
rust-d1dc2afd05a0f501512d9ed24f2d1785f7228eba.zip
wf: handle "livelock" checking before reaching `WfPredicates::compute`.
-rw-r--r--src/librustc_trait_selection/traits/wf.rs80
1 files changed, 38 insertions, 42 deletions
diff --git a/src/librustc_trait_selection/traits/wf.rs b/src/librustc_trait_selection/traits/wf.rs
index 7eabdf706ef..d11a3e61c65 100644
--- a/src/librustc_trait_selection/traits/wf.rs
+++ b/src/librustc_trait_selection/traits/wf.rs
@@ -22,16 +22,27 @@ pub fn obligations<'a, 'tcx>(
     ty: Ty<'tcx>,
     span: Span,
 ) -> 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 };
-    if 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)
-    } else {
-        None // no progress made, return 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
@@ -311,12 +322,9 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
         }
     }
 
-    /// Pushes new obligations into `out`. Returns `true` if it was able
-    /// to generate all the predicates needed to validate that `ty0`
-    /// is WF. Returns false if `ty0` is an unresolved type variable,
-    /// in which case we are not able to simplify at all.
-    fn compute(&mut self, ty0: Ty<'tcx>) -> bool {
-        let mut walker = ty0.walk();
+    /// Pushes all the predicates needed to validate that `ty` is WF into `out`.
+    fn compute(&mut self, ty: Ty<'tcx>) {
+        let mut walker = ty.walk();
         let param_env = self.param_env;
         while let Some(arg) = walker.next() {
             let ty = match arg.unpack() {
@@ -442,8 +450,9 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
                     // are not directly inspecting closure types
                     // anyway, except via auto trait matching (which
                     // 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() {
+                        // FIXME(eddyb) add the type to `walker` instead of recursing.
                         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
                 //    THAT type.
-                // 2. If not, check whether this is the type that we
-                //    started with (ty0). In that case, we've made no
-                //    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
+                // 2. If not, we've at least simplified things (e.g., we went
+                //    from `Vec<$0>: WF` to `$0: WF`), so we can
                 //    register a pending obligation and keep
                 //    moving. (Goal is that an "inductive hypothesis"
                 //    is satisfied to ensure termination.)
+                // See also the comment on `fn obligations`, describing "livelock"
+                // prevention, which happens before this can be reached.
                 ty::Infer(_) => {
                     let ty = self.infcx.shallow_resolve(ty);
                     if let ty::Infer(_) = ty.kind {
-                        // not yet resolved...
-                        if ty == ty0 {
-                            // ...this is the type we started from! no progress.
-                            return false;
-                        }
-
+                        // Not yet resolved, but we've made progress.
                         let cause = self.cause(traits::MiscObligation);
-                        self.out.push(
-                            // ...not the type we started from, so we made progress.
-                            traits::Obligation::new(
-                                cause,
-                                self.param_env,
-                                ty::Predicate::WellFormed(ty),
-                            ),
-                        );
+                        self.out.push(traits::Obligation::new(
+                            cause,
+                            param_env,
+                            ty::Predicate::WellFormed(ty),
+                        ));
                     } else {
-                        // Yes, resolved, proceed with the
-                        // result. Should never return false because
-                        // `ty` is not a Infer.
-                        assert!(self.compute(ty));
+                        // Yes, resolved, proceed with the result.
+                        // FIXME(eddyb) add the type to `walker` instead of recursing.
+                        self.compute(ty);
                     }
                 }
             }
         }
-
-        // if we made it through that loop above, we made progress!
-        true
     }
 
     fn nominal_obligations(