From d1dc2afd05a0f501512d9ed24f2d1785f7228eba Mon Sep 17 00:00:00 2001 From: Eduard-Mihai Burtescu Date: Fri, 20 Mar 2020 01:49:01 +0200 Subject: [PATCH 1/2] wf: handle "livelock" checking before reaching `WfPredicates::compute`. --- src/librustc_trait_selection/traits/wf.rs | 80 +++++++++++------------ 1 file changed, 38 insertions(+), 42 deletions(-) diff --git a/src/librustc_trait_selection/traits/wf.rs b/src/librustc_trait_selection/traits/wf.rs index 7eabdf706ef9f..d11a3e61c6589 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>> { + // 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( From 05a872df2ba4942f86141bfdbefd171eb419a3cc Mon Sep 17 00:00:00 2001 From: Eduard-Mihai Burtescu Date: Fri, 3 Apr 2020 01:01:30 +0300 Subject: [PATCH 2/2] wf: {Int,Float}Var can only infer to always-WF ints/floats. --- src/librustc_trait_selection/traits/wf.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/librustc_trait_selection/traits/wf.rs b/src/librustc_trait_selection/traits/wf.rs index d11a3e61c6589..1d4afeaae42c4 100644 --- a/src/librustc_trait_selection/traits/wf.rs +++ b/src/librustc_trait_selection/traits/wf.rs @@ -24,7 +24,7 @@ pub fn obligations<'a, 'tcx>( ) -> Option>> { // Handle the "livelock" case (see comment above) by bailing out if necessary. let ty = match ty.kind { - ty::Infer(_) => { + ty::Infer(ty::TyVar(_)) => { let resolved_ty = infcx.shallow_resolve(ty); if resolved_ty == ty { // No progress, bail out to prevent "livelock". @@ -356,6 +356,12 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { // WfScalar, WfParameter, etc } + // Can only infer to `ty::Int(_) | ty::Uint(_)`. + ty::Infer(ty::IntVar(_)) => {} + + // Can only infer to `ty::Float(_)`. + ty::Infer(ty::FloatVar(_)) => {} + ty::Slice(subty) => { self.require_sized(subty, traits::SliceOrArrayElem); } @@ -514,7 +520,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { // prevention, which happens before this can be reached. ty::Infer(_) => { let ty = self.infcx.shallow_resolve(ty); - if let ty::Infer(_) = ty.kind { + if let ty::Infer(ty::TyVar(_)) = ty.kind { // Not yet resolved, but we've made progress. let cause = self.cause(traits::MiscObligation); self.out.push(traits::Obligation::new(