diff --git a/src/librustc/traits/fulfill.rs b/src/librustc/traits/fulfill.rs index dff89f3c888a7..78ebd2d139096 100644 --- a/src/librustc/traits/fulfill.rs +++ b/src/librustc/traits/fulfill.rs @@ -12,8 +12,8 @@ use infer::{RegionObligation, InferCtxt}; use mir::interpret::GlobalId; use ty::{self, Ty, TypeFoldable, ToPolyTraitRef, ToPredicate}; use ty::error::ExpectedFound; -use rustc_data_structures::obligation_forest::{ObligationForest, Error}; -use rustc_data_structures::obligation_forest::{ForestObligation, ObligationProcessor}; +use rustc_data_structures::obligation_forest::{Error, ForestObligation, ObligationForest}; +use rustc_data_structures::obligation_forest::{ObligationProcessor, ProcessResult}; use std::marker::PhantomData; use hir::def_id::DefId; use middle::const_val::{ConstEvalErr, ErrKind}; @@ -251,302 +251,306 @@ struct FulfillProcessor<'a, 'b: 'a, 'gcx: 'tcx, 'tcx: 'b> { register_region_obligations: bool } +fn mk_pending(os: Vec>) -> Vec> { + os.into_iter().map(|o| PendingPredicateObligation { + obligation: o, + stalled_on: vec![] + }).collect() +} + impl<'a, 'b, 'gcx, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'b, 'gcx, 'tcx> { type Obligation = PendingPredicateObligation<'tcx>; type Error = FulfillmentErrorCode<'tcx>; + /// Processes a predicate obligation and returns either: + /// - `Changed(v)` if the predicate is true, presuming that `v` are also true + /// - `Unchanged` if we don't have enough info to be sure + /// - `Error(e)` if the predicate does not hold + /// + /// This is always inlined, despite its size, because it has a single + /// callsite and it is called *very* frequently. + #[inline(always)] fn process_obligation(&mut self, - obligation: &mut Self::Obligation) - -> Result>, Self::Error> - { - process_predicate(self.selcx, obligation, self.register_region_obligations) - .map(|os| os.map(|os| os.into_iter().map(|o| PendingPredicateObligation { - obligation: o, - stalled_on: vec![] - }).collect())) - } - - fn process_backedge<'c, I>(&mut self, cycle: I, - _marker: PhantomData<&'c PendingPredicateObligation<'tcx>>) - where I: Clone + Iterator>, + pending_obligation: &mut Self::Obligation) + -> ProcessResult { - if self.selcx.coinductive_match(cycle.clone().map(|s| s.obligation.predicate)) { - debug!("process_child_obligations: coinductive match"); - } else { - let cycle : Vec<_> = cycle.map(|c| c.obligation.clone()).collect(); - self.selcx.infcx().report_overflow_error_cycle(&cycle); + // if we were stalled on some unresolved variables, first check + // whether any of them have been resolved; if not, don't bother + // doing more work yet + if !pending_obligation.stalled_on.is_empty() { + if pending_obligation.stalled_on.iter().all(|&ty| { + let resolved_ty = self.selcx.infcx().shallow_resolve(&ty); + resolved_ty == ty // nothing changed here + }) { + debug!("process_predicate: pending obligation {:?} still stalled on {:?}", + self.selcx.infcx() + .resolve_type_vars_if_possible(&pending_obligation.obligation), + pending_obligation.stalled_on); + return ProcessResult::Unchanged; + } + pending_obligation.stalled_on = vec![]; } - } -} -/// Return the set of type variables contained in a trait ref -fn trait_ref_type_vars<'a, 'gcx, 'tcx>(selcx: &mut SelectionContext<'a, 'gcx, 'tcx>, - t: ty::PolyTraitRef<'tcx>) -> Vec> -{ - t.skip_binder() // ok b/c this check doesn't care about regions - .input_types() - .map(|t| selcx.infcx().resolve_type_vars_if_possible(&t)) - .filter(|t| t.has_infer_types()) - .flat_map(|t| t.walk()) - .filter(|t| match t.sty { ty::TyInfer(_) => true, _ => false }) - .collect() -} + let obligation = &mut pending_obligation.obligation; -/// Processes a predicate obligation and returns either: -/// - `Ok(Some(v))` if the predicate is true, presuming that `v` are also true -/// - `Ok(None)` if we don't have enough info to be sure -/// - `Err` if the predicate does not hold -fn process_predicate<'a, 'gcx, 'tcx>( - selcx: &mut SelectionContext<'a, 'gcx, 'tcx>, - pending_obligation: &mut PendingPredicateObligation<'tcx>, - register_region_obligations: bool) - -> Result>>, - FulfillmentErrorCode<'tcx>> -{ - // if we were stalled on some unresolved variables, first check - // whether any of them have been resolved; if not, don't bother - // doing more work yet - if !pending_obligation.stalled_on.is_empty() { - if pending_obligation.stalled_on.iter().all(|&ty| { - let resolved_ty = selcx.infcx().shallow_resolve(&ty); - resolved_ty == ty // nothing changed here - }) { - debug!("process_predicate: pending obligation {:?} still stalled on {:?}", - selcx.infcx().resolve_type_vars_if_possible(&pending_obligation.obligation), - pending_obligation.stalled_on); - return Ok(None); + if obligation.predicate.has_infer_types() { + obligation.predicate = + self.selcx.infcx().resolve_type_vars_if_possible(&obligation.predicate); } - pending_obligation.stalled_on = vec![]; - } - - let obligation = &mut pending_obligation.obligation; - - if obligation.predicate.has_infer_types() { - obligation.predicate = selcx.infcx().resolve_type_vars_if_possible(&obligation.predicate); - } - match obligation.predicate { - ty::Predicate::Trait(ref data) => { - let trait_obligation = obligation.with(data.clone()); - - if data.is_global() && !data.has_late_bound_regions() { - // no type variables present, can use evaluation for better caching. - // FIXME: consider caching errors too. - if selcx.infcx().predicate_must_hold(&obligation) { - debug!("selecting trait `{:?}` at depth {} evaluated to holds", - data, obligation.recursion_depth); - return Ok(Some(vec![])) + match obligation.predicate { + ty::Predicate::Trait(ref data) => { + let trait_obligation = obligation.with(data.clone()); + + if data.is_global() && !data.has_late_bound_regions() { + // no type variables present, can use evaluation for better caching. + // FIXME: consider caching errors too. + if self.selcx.infcx().predicate_must_hold(&obligation) { + debug!("selecting trait `{:?}` at depth {} evaluated to holds", + data, obligation.recursion_depth); + return ProcessResult::Changed(vec![]) + } } - } - match selcx.select(&trait_obligation) { - Ok(Some(vtable)) => { - debug!("selecting trait `{:?}` at depth {} yielded Ok(Some)", - data, obligation.recursion_depth); - Ok(Some(vtable.nested_obligations())) - } - Ok(None) => { - debug!("selecting trait `{:?}` at depth {} yielded Ok(None)", - data, obligation.recursion_depth); - - // This is a bit subtle: for the most part, the - // only reason we can fail to make progress on - // trait selection is because we don't have enough - // information about the types in the trait. One - // exception is that we sometimes haven't decided - // what kind of closure a closure is. *But*, in - // that case, it turns out, the type of the - // closure will also change, because the closure - // also includes references to its upvars as part - // of its type, and those types are resolved at - // the same time. - // - // FIXME(#32286) logic seems false if no upvars - pending_obligation.stalled_on = - trait_ref_type_vars(selcx, data.to_poly_trait_ref()); - - debug!("process_predicate: pending obligation {:?} now stalled on {:?}", - selcx.infcx().resolve_type_vars_if_possible(obligation), - pending_obligation.stalled_on); - - Ok(None) - } - Err(selection_err) => { - info!("selecting trait `{:?}` at depth {} yielded Err", - data, obligation.recursion_depth); + match self.selcx.select(&trait_obligation) { + Ok(Some(vtable)) => { + debug!("selecting trait `{:?}` at depth {} yielded Ok(Some)", + data, obligation.recursion_depth); + ProcessResult::Changed(mk_pending(vtable.nested_obligations())) + } + Ok(None) => { + debug!("selecting trait `{:?}` at depth {} yielded Ok(None)", + data, obligation.recursion_depth); + + // This is a bit subtle: for the most part, the + // only reason we can fail to make progress on + // trait selection is because we don't have enough + // information about the types in the trait. One + // exception is that we sometimes haven't decided + // what kind of closure a closure is. *But*, in + // that case, it turns out, the type of the + // closure will also change, because the closure + // also includes references to its upvars as part + // of its type, and those types are resolved at + // the same time. + // + // FIXME(#32286) logic seems false if no upvars + pending_obligation.stalled_on = + trait_ref_type_vars(self.selcx, data.to_poly_trait_ref()); + + debug!("process_predicate: pending obligation {:?} now stalled on {:?}", + self.selcx.infcx().resolve_type_vars_if_possible(obligation), + pending_obligation.stalled_on); + + ProcessResult::Unchanged + } + Err(selection_err) => { + info!("selecting trait `{:?}` at depth {} yielded Err", + data, obligation.recursion_depth); - Err(CodeSelectionError(selection_err)) + ProcessResult::Error(CodeSelectionError(selection_err)) + } } } - } - ty::Predicate::RegionOutlives(ref binder) => { - match selcx.infcx().region_outlives_predicate(&obligation.cause, binder) { - Ok(()) => Ok(Some(Vec::new())), - Err(_) => Err(CodeSelectionError(Unimplemented)), + ty::Predicate::RegionOutlives(ref binder) => { + match self.selcx.infcx().region_outlives_predicate(&obligation.cause, binder) { + Ok(()) => ProcessResult::Changed(vec![]), + Err(_) => ProcessResult::Error(CodeSelectionError(Unimplemented)), + } } - } - ty::Predicate::TypeOutlives(ref binder) => { - // Check if there are higher-ranked regions. - match binder.no_late_bound_regions() { - // If there are, inspect the underlying type further. - None => { - // Convert from `Binder>` to `Binder`. - let binder = binder.map_bound_ref(|pred| pred.0); - - // Check if the type has any bound regions. - match binder.no_late_bound_regions() { - // If so, this obligation is an error (for now). Eventually we should be - // able to support additional cases here, like `for<'a> &'a str: 'a`. - None => { - Err(CodeSelectionError(Unimplemented)) - } - // Otherwise, we have something of the form - // `for<'a> T: 'a where 'a not in T`, which we can treat as `T: 'static`. - Some(t_a) => { - let r_static = selcx.tcx().types.re_static; - if register_region_obligations { - selcx.infcx().register_region_obligation( - obligation.cause.body_id, - RegionObligation { - sup_type: t_a, - sub_region: r_static, - cause: obligation.cause.clone(), - }); + ty::Predicate::TypeOutlives(ref binder) => { + // Check if there are higher-ranked regions. + match binder.no_late_bound_regions() { + // If there are, inspect the underlying type further. + None => { + // Convert from `Binder>` to `Binder`. + let binder = binder.map_bound_ref(|pred| pred.0); + + // Check if the type has any bound regions. + match binder.no_late_bound_regions() { + // If so, this obligation is an error (for now). Eventually we should be + // able to support additional cases here, like `for<'a> &'a str: 'a`. + None => { + ProcessResult::Error(CodeSelectionError(Unimplemented)) + } + // Otherwise, we have something of the form + // `for<'a> T: 'a where 'a not in T`, which we can treat as + // `T: 'static`. + Some(t_a) => { + let r_static = self.selcx.tcx().types.re_static; + if self.register_region_obligations { + self.selcx.infcx().register_region_obligation( + obligation.cause.body_id, + RegionObligation { + sup_type: t_a, + sub_region: r_static, + cause: obligation.cause.clone(), + }); + } + ProcessResult::Changed(vec![]) } - Ok(Some(vec![])) } } - } - // If there aren't, register the obligation. - Some(ty::OutlivesPredicate(t_a, r_b)) => { - if register_region_obligations { - selcx.infcx().register_region_obligation( - obligation.cause.body_id, - RegionObligation { - sup_type: t_a, - sub_region: r_b, - cause: obligation.cause.clone() - }); + // If there aren't, register the obligation. + Some(ty::OutlivesPredicate(t_a, r_b)) => { + if self.register_region_obligations { + self.selcx.infcx().register_region_obligation( + obligation.cause.body_id, + RegionObligation { + sup_type: t_a, + sub_region: r_b, + cause: obligation.cause.clone() + }); + } + ProcessResult::Changed(vec![]) } - Ok(Some(vec![])) } } - } - ty::Predicate::Projection(ref data) => { - let project_obligation = obligation.with(data.clone()); - match project::poly_project_and_unify_type(selcx, &project_obligation) { - Ok(None) => { - let tcx = selcx.tcx(); - pending_obligation.stalled_on = - trait_ref_type_vars(selcx, data.to_poly_trait_ref(tcx)); - Ok(None) + ty::Predicate::Projection(ref data) => { + let project_obligation = obligation.with(data.clone()); + match project::poly_project_and_unify_type(self.selcx, &project_obligation) { + Ok(None) => { + let tcx = self.selcx.tcx(); + pending_obligation.stalled_on = + trait_ref_type_vars(self.selcx, data.to_poly_trait_ref(tcx)); + ProcessResult::Unchanged + } + Ok(Some(os)) => ProcessResult::Changed(mk_pending(os)), + Err(e) => ProcessResult::Error(CodeProjectionError(e)) } - Ok(v) => Ok(v), - Err(e) => Err(CodeProjectionError(e)) } - } - ty::Predicate::ObjectSafe(trait_def_id) => { - if !selcx.tcx().is_object_safe(trait_def_id) { - Err(CodeSelectionError(Unimplemented)) - } else { - Ok(Some(Vec::new())) + ty::Predicate::ObjectSafe(trait_def_id) => { + if !self.selcx.tcx().is_object_safe(trait_def_id) { + ProcessResult::Error(CodeSelectionError(Unimplemented)) + } else { + ProcessResult::Changed(vec![]) + } } - } - ty::Predicate::ClosureKind(closure_def_id, closure_substs, kind) => { - match selcx.infcx().closure_kind(closure_def_id, closure_substs) { - Some(closure_kind) => { - if closure_kind.extends(kind) { - Ok(Some(vec![])) - } else { - Err(CodeSelectionError(Unimplemented)) + ty::Predicate::ClosureKind(closure_def_id, closure_substs, kind) => { + match self.selcx.infcx().closure_kind(closure_def_id, closure_substs) { + Some(closure_kind) => { + if closure_kind.extends(kind) { + ProcessResult::Changed(vec![]) + } else { + ProcessResult::Error(CodeSelectionError(Unimplemented)) + } + } + None => { + ProcessResult::Unchanged } - } - None => { - Ok(None) } } - } - ty::Predicate::WellFormed(ty) => { - match ty::wf::obligations(selcx.infcx(), - obligation.param_env, - obligation.cause.body_id, - ty, obligation.cause.span) { - None => { - pending_obligation.stalled_on = vec![ty]; - Ok(None) + ty::Predicate::WellFormed(ty) => { + match ty::wf::obligations(self.selcx.infcx(), + obligation.param_env, + obligation.cause.body_id, + ty, obligation.cause.span) { + None => { + pending_obligation.stalled_on = vec![ty]; + ProcessResult::Unchanged + } + Some(os) => ProcessResult::Changed(mk_pending(os)) } - s => Ok(s) } - } - ty::Predicate::Subtype(ref subtype) => { - match selcx.infcx().subtype_predicate(&obligation.cause, - obligation.param_env, - subtype) { - None => { - // none means that both are unresolved - pending_obligation.stalled_on = vec![subtype.skip_binder().a, - subtype.skip_binder().b]; - Ok(None) - } - Some(Ok(ok)) => { - Ok(Some(ok.obligations)) - } - Some(Err(err)) => { - let expected_found = ExpectedFound::new(subtype.skip_binder().a_is_expected, - subtype.skip_binder().a, - subtype.skip_binder().b); - Err(FulfillmentErrorCode::CodeSubtypeError(expected_found, err)) + ty::Predicate::Subtype(ref subtype) => { + match self.selcx.infcx().subtype_predicate(&obligation.cause, + obligation.param_env, + subtype) { + None => { + // None means that both are unresolved. + pending_obligation.stalled_on = vec![subtype.skip_binder().a, + subtype.skip_binder().b]; + ProcessResult::Unchanged + } + Some(Ok(ok)) => { + ProcessResult::Changed(mk_pending(ok.obligations)) + } + Some(Err(err)) => { + let expected_found = ExpectedFound::new(subtype.skip_binder().a_is_expected, + subtype.skip_binder().a, + subtype.skip_binder().b); + ProcessResult::Error( + FulfillmentErrorCode::CodeSubtypeError(expected_found, err)) + } } } - } - ty::Predicate::ConstEvaluatable(def_id, substs) => { - match selcx.tcx().lift_to_global(&obligation.param_env) { - None => { - Ok(None) - } - Some(param_env) => { - match selcx.tcx().lift_to_global(&substs) { - Some(substs) => { - let instance = ty::Instance::resolve( - selcx.tcx().global_tcx(), - param_env, - def_id, - substs, - ); - if let Some(instance) = instance { - let cid = GlobalId { - instance, - promoted: None, - }; - match selcx.tcx().at(obligation.cause.span) - .const_eval(param_env.and(cid)) { - Ok(_) => Ok(Some(vec![])), - Err(err) => Err(CodeSelectionError(ConstEvalFailure(err))) + ty::Predicate::ConstEvaluatable(def_id, substs) => { + match self.selcx.tcx().lift_to_global(&obligation.param_env) { + None => { + ProcessResult::Unchanged + } + Some(param_env) => { + match self.selcx.tcx().lift_to_global(&substs) { + Some(substs) => { + let instance = ty::Instance::resolve( + self.selcx.tcx().global_tcx(), + param_env, + def_id, + substs, + ); + if let Some(instance) = instance { + let cid = GlobalId { + instance, + promoted: None, + }; + match self.selcx.tcx().at(obligation.cause.span) + .const_eval(param_env.and(cid)) { + Ok(_) => ProcessResult::Changed(vec![]), + Err(err) => ProcessResult::Error( + CodeSelectionError(ConstEvalFailure(err))) + } + } else { + ProcessResult::Error( + CodeSelectionError(ConstEvalFailure(ConstEvalErr { + span: obligation.cause.span, + kind: ErrKind::CouldNotResolve.into(), + })) + ) } - } else { - Err(CodeSelectionError(ConstEvalFailure(ConstEvalErr { - span: obligation.cause.span, - kind: ErrKind::CouldNotResolve.into(), - }))) + }, + None => { + pending_obligation.stalled_on = substs.types().collect(); + ProcessResult::Unchanged } - }, - None => { - pending_obligation.stalled_on = substs.types().collect(); - Ok(None) } } } } } } + + fn process_backedge<'c, I>(&mut self, cycle: I, + _marker: PhantomData<&'c PendingPredicateObligation<'tcx>>) + where I: Clone + Iterator>, + { + if self.selcx.coinductive_match(cycle.clone().map(|s| s.obligation.predicate)) { + debug!("process_child_obligations: coinductive match"); + } else { + let cycle : Vec<_> = cycle.map(|c| c.obligation.clone()).collect(); + self.selcx.infcx().report_overflow_error_cycle(&cycle); + } + } +} + +/// Return the set of type variables contained in a trait ref +fn trait_ref_type_vars<'a, 'gcx, 'tcx>(selcx: &mut SelectionContext<'a, 'gcx, 'tcx>, + t: ty::PolyTraitRef<'tcx>) -> Vec> +{ + t.skip_binder() // ok b/c this check doesn't care about regions + .input_types() + .map(|t| selcx.infcx().resolve_type_vars_if_possible(&t)) + .filter(|t| t.has_infer_types()) + .flat_map(|t| t.walk()) + .filter(|t| match t.sty { ty::TyInfer(_) => true, _ => false }) + .collect() } fn to_fulfillment_error<'tcx>( diff --git a/src/librustc_data_structures/obligation_forest/mod.rs b/src/librustc_data_structures/obligation_forest/mod.rs index 612f44f09cf65..1378f46b64cac 100644 --- a/src/librustc_data_structures/obligation_forest/mod.rs +++ b/src/librustc_data_structures/obligation_forest/mod.rs @@ -41,7 +41,7 @@ pub trait ObligationProcessor { fn process_obligation(&mut self, obligation: &mut Self::Obligation) - -> Result>, Self::Error>; + -> ProcessResult; /// As we do the cycle check, we invoke this callback when we /// encounter an actual cycle. `cycle` is an iterator that starts @@ -57,6 +57,14 @@ pub trait ObligationProcessor { where I: Clone + Iterator; } +/// The result type used by `process_obligation`. +#[derive(Debug)] +pub enum ProcessResult { + Unchanged, + Changed(Vec), + Error(E), +} + pub struct ObligationForest { /// The list of obligations. In between calls to /// `process_obligations`, this list only contains nodes in the @@ -136,8 +144,8 @@ pub struct Outcome { /// If true, then we saw no successful obligations, which means /// there is no point in further iteration. This is based on the - /// assumption that when trait matching returns `Err` or - /// `Ok(None)`, those results do not affect environmental + /// assumption that when trait matching returns `Error` or + /// `Unchanged`, those results do not affect environmental /// inference state. (Note that if we invoke `process_obligations` /// with no pending obligations, stalled will be true.) pub stalled: bool, @@ -270,11 +278,11 @@ impl ObligationForest { result); match result { - Ok(None) => { - // no change in state + ProcessResult::Unchanged => { + // No change in state. } - Ok(Some(children)) => { - // if we saw a Some(_) result, we are not (yet) stalled + ProcessResult::Changed(children) => { + // We are not (yet) stalled. stalled = false; self.nodes[index].state.set(NodeState::Success); @@ -290,7 +298,7 @@ impl ObligationForest { } } } - Err(err) => { + ProcessResult::Error(err) => { stalled = false; let backtrace = self.error_at(index); errors.push(Error { diff --git a/src/librustc_data_structures/obligation_forest/test.rs b/src/librustc_data_structures/obligation_forest/test.rs index a95b2b84b34c8..527a1ef0ec441 100644 --- a/src/librustc_data_structures/obligation_forest/test.rs +++ b/src/librustc_data_structures/obligation_forest/test.rs @@ -10,7 +10,7 @@ #![cfg(test)] -use super::{ObligationForest, ObligationProcessor, Outcome, Error}; +use super::{Error, ObligationForest, ObligationProcessor, Outcome, ProcessResult}; use std::fmt; use std::marker::PhantomData; @@ -31,7 +31,7 @@ struct ClosureObligationProcessor { #[allow(non_snake_case)] fn C(of: OF, bf: BF) -> ClosureObligationProcessor - where OF: FnMut(&mut O) -> Result>, &'static str>, + where OF: FnMut(&mut O) -> ProcessResult, BF: FnMut(&[O]) { ClosureObligationProcessor { @@ -44,7 +44,7 @@ fn C(of: OF, bf: BF) -> ClosureObligationProcessor ObligationProcessor for ClosureObligationProcessor where O: super::ForestObligation + fmt::Debug, E: fmt::Debug, - OF: FnMut(&mut O) -> Result>, E>, + OF: FnMut(&mut O) -> ProcessResult, BF: FnMut(&[O]) { type Obligation = O; @@ -52,7 +52,7 @@ impl ObligationProcessor for ClosureObligationProcessor Result>, Self::Error> + -> ProcessResult { (self.process_obligation)(obligation) } @@ -78,9 +78,9 @@ fn push_pop() { let Outcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(|obligation| { match *obligation { - "A" => Ok(Some(vec!["A.1", "A.2", "A.3"])), - "B" => Err("B is for broken"), - "C" => Ok(Some(vec![])), + "A" => ProcessResult::Changed(vec!["A.1", "A.2", "A.3"]), + "B" => ProcessResult::Error("B is for broken"), + "C" => ProcessResult::Changed(vec![]), _ => unreachable!(), } }, |_| {})); @@ -101,10 +101,10 @@ fn push_pop() { let Outcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(|obligation| { match *obligation { - "A.1" => Ok(None), - "A.2" => Ok(None), - "A.3" => Ok(Some(vec!["A.3.i"])), - "D" => Ok(Some(vec!["D.1", "D.2"])), + "A.1" => ProcessResult::Unchanged, + "A.2" => ProcessResult::Unchanged, + "A.3" => ProcessResult::Changed(vec!["A.3.i"]), + "D" => ProcessResult::Changed(vec!["D.1", "D.2"]), _ => unreachable!(), } }, |_| {})); @@ -119,11 +119,11 @@ fn push_pop() { let Outcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(|obligation| { match *obligation { - "A.1" => Ok(Some(vec![])), - "A.2" => Err("A is for apple"), - "A.3.i" => Ok(Some(vec![])), - "D.1" => Ok(Some(vec!["D.1.i"])), - "D.2" => Ok(Some(vec!["D.2.i"])), + "A.1" => ProcessResult::Changed(vec![]), + "A.2" => ProcessResult::Error("A is for apple"), + "A.3.i" => ProcessResult::Changed(vec![]), + "D.1" => ProcessResult::Changed(vec!["D.1.i"]), + "D.2" => ProcessResult::Changed(vec!["D.2.i"]), _ => unreachable!(), } }, |_| {})); @@ -138,8 +138,8 @@ fn push_pop() { let Outcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(|obligation| { match *obligation { - "D.1.i" => Err("D is for dumb"), - "D.2.i" => Ok(Some(vec![])), + "D.1.i" => ProcessResult::Error("D is for dumb"), + "D.2.i" => ProcessResult::Changed(vec![]), _ => panic!("unexpected obligation {:?}", obligation), } }, |_| {})); @@ -167,7 +167,7 @@ fn success_in_grandchildren() { let Outcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(|obligation| { match *obligation { - "A" => Ok(Some(vec!["A.1", "A.2", "A.3"])), + "A" => ProcessResult::Changed(vec!["A.1", "A.2", "A.3"]), _ => unreachable!(), } }, |_| {})); @@ -177,9 +177,9 @@ fn success_in_grandchildren() { let Outcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(|obligation| { match *obligation { - "A.1" => Ok(Some(vec![])), - "A.2" => Ok(Some(vec!["A.2.i", "A.2.ii"])), - "A.3" => Ok(Some(vec![])), + "A.1" => ProcessResult::Changed(vec![]), + "A.2" => ProcessResult::Changed(vec!["A.2.i", "A.2.ii"]), + "A.3" => ProcessResult::Changed(vec![]), _ => unreachable!(), } }, |_| {})); @@ -189,8 +189,8 @@ fn success_in_grandchildren() { let Outcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(|obligation| { match *obligation { - "A.2.i" => Ok(Some(vec!["A.2.i.a"])), - "A.2.ii" => Ok(Some(vec![])), + "A.2.i" => ProcessResult::Changed(vec!["A.2.i.a"]), + "A.2.ii" => ProcessResult::Changed(vec![]), _ => unreachable!(), } }, |_| {})); @@ -200,7 +200,7 @@ fn success_in_grandchildren() { let Outcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(|obligation| { match *obligation { - "A.2.i.a" => Ok(Some(vec![])), + "A.2.i.a" => ProcessResult::Changed(vec![]), _ => unreachable!(), } }, |_| {})); @@ -223,7 +223,7 @@ fn to_errors_no_throw() { let Outcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(|obligation| { match *obligation { - "A" => Ok(Some(vec!["A.1", "A.2", "A.3"])), + "A" => ProcessResult::Changed(vec!["A.1", "A.2", "A.3"]), _ => unreachable!(), } }, |_|{})); @@ -244,7 +244,7 @@ fn diamond() { let Outcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(|obligation| { match *obligation { - "A" => Ok(Some(vec!["A.1", "A.2"])), + "A" => ProcessResult::Changed(vec!["A.1", "A.2"]), _ => unreachable!(), } }, |_|{})); @@ -254,8 +254,8 @@ fn diamond() { let Outcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(|obligation| { match *obligation { - "A.1" => Ok(Some(vec!["D"])), - "A.2" => Ok(Some(vec!["D"])), + "A.1" => ProcessResult::Changed(vec!["D"]), + "A.2" => ProcessResult::Changed(vec!["D"]), _ => unreachable!(), } }, |_|{})); @@ -266,7 +266,7 @@ fn diamond() { let Outcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(|obligation| { match *obligation { - "D" => { d_count += 1; Ok(Some(vec![])) }, + "D" => { d_count += 1; ProcessResult::Changed(vec![]) }, _ => unreachable!(), } }, |_|{})); @@ -281,7 +281,7 @@ fn diamond() { let Outcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(|obligation| { match *obligation { - "A'" => Ok(Some(vec!["A'.1", "A'.2"])), + "A'" => ProcessResult::Changed(vec!["A'.1", "A'.2"]), _ => unreachable!(), } }, |_|{})); @@ -291,8 +291,8 @@ fn diamond() { let Outcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(|obligation| { match *obligation { - "A'.1" => Ok(Some(vec!["D'", "A'"])), - "A'.2" => Ok(Some(vec!["D'"])), + "A'.1" => ProcessResult::Changed(vec!["D'", "A'"]), + "A'.2" => ProcessResult::Changed(vec!["D'"]), _ => unreachable!(), } }, |_|{})); @@ -303,7 +303,7 @@ fn diamond() { let Outcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(|obligation| { match *obligation { - "D'" => { d_count += 1; Err("operation failed") }, + "D'" => { d_count += 1; ProcessResult::Error("operation failed") }, _ => unreachable!(), } }, |_|{})); @@ -329,7 +329,7 @@ fn done_dependency() { let Outcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(|obligation| { match *obligation { - "A: Sized" | "B: Sized" | "C: Sized" => Ok(Some(vec![])), + "A: Sized" | "B: Sized" | "C: Sized" => ProcessResult::Changed(vec![]), _ => unreachable!(), } }, |_|{})); @@ -340,11 +340,11 @@ fn done_dependency() { let Outcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(|obligation| { match *obligation { - "(A,B,C): Sized" => Ok(Some(vec![ + "(A,B,C): Sized" => ProcessResult::Changed(vec![ "A: Sized", "B: Sized", "C: Sized" - ])), + ]), _ => unreachable!(), } }, |_|{})); @@ -367,10 +367,10 @@ fn orphan() { let Outcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(|obligation| { match *obligation { - "A" => Ok(Some(vec!["D", "E"])), - "B" => Ok(None), - "C1" => Ok(Some(vec![])), - "C2" => Ok(Some(vec![])), + "A" => ProcessResult::Changed(vec!["D", "E"]), + "B" => ProcessResult::Unchanged, + "C1" => ProcessResult::Changed(vec![]), + "C2" => ProcessResult::Changed(vec![]), _ => unreachable!(), } }, |_|{})); @@ -380,8 +380,8 @@ fn orphan() { let Outcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(|obligation| { match *obligation { - "D" | "E" => Ok(None), - "B" => Ok(Some(vec!["D"])), + "D" | "E" => ProcessResult::Unchanged, + "B" => ProcessResult::Changed(vec!["D"]), _ => unreachable!(), } }, |_|{})); @@ -391,8 +391,8 @@ fn orphan() { let Outcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(|obligation| { match *obligation { - "D" => Ok(None), - "E" => Err("E is for error"), + "D" => ProcessResult::Unchanged, + "E" => ProcessResult::Error("E is for error"), _ => unreachable!(), } }, |_|{})); @@ -405,7 +405,7 @@ fn orphan() { let Outcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(|obligation| { match *obligation { - "D" => Err("D is dead"), + "D" => ProcessResult::Error("D is dead"), _ => unreachable!(), } }, |_|{})); @@ -429,8 +429,8 @@ fn simultaneous_register_and_error() { let Outcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(|obligation| { match *obligation { - "A" => Err("An error"), - "B" => Ok(Some(vec!["A"])), + "A" => ProcessResult::Error("An error"), + "B" => ProcessResult::Changed(vec!["A"]), _ => unreachable!(), } }, |_|{})); @@ -447,8 +447,8 @@ fn simultaneous_register_and_error() { let Outcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(|obligation| { match *obligation { - "A" => Err("An error"), - "B" => Ok(Some(vec!["A"])), + "A" => ProcessResult::Error("An error"), + "B" => ProcessResult::Changed(vec!["A"]), _ => unreachable!(), } }, |_|{}));