From b7caae30c7389c3b92a8399577e007116c28f075 Mon Sep 17 00:00:00 2001 From: Masood Malekghassemi Date: Mon, 25 Apr 2016 18:47:23 -0700 Subject: [PATCH 1/2] Don't commit when assembling candidates Candidate assembly during selection should not affect the inference context. --- src/librustc/traits/select.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustc/traits/select.rs b/src/librustc/traits/select.rs index d7528fc3130c9..598d786fa5e8b 100644 --- a/src/librustc/traits/select.rs +++ b/src/librustc/traits/select.rs @@ -1394,7 +1394,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { return; } - self.infcx.commit_if_ok(|snapshot| { + self.infcx.probe(|snapshot| { let (self_ty, _) = self.infcx().skolemize_late_bound_regions(&obligation.self_ty(), snapshot); let poly_trait_ref = match self_ty.sty { From bf98976ec559d36f2aac16c1552155f417046afc Mon Sep 17 00:00:00 2001 From: Masood Malekghassemi Date: Mon, 25 Apr 2016 18:16:46 -0700 Subject: [PATCH 2/2] Plumb inference obligations through selection --- src/librustc/traits/fulfill.rs | 6 +- src/librustc/traits/mod.rs | 23 +-- src/librustc/traits/project.rs | 7 +- src/librustc/traits/select.rs | 247 ++++++++++++++++++-------- src/librustc_const_eval/eval.rs | 8 +- src/librustc_lint/builtin.rs | 14 +- src/librustc_trans/common.rs | 8 +- src/librustc_typeck/check/coercion.rs | 6 +- 8 files changed, 209 insertions(+), 110 deletions(-) diff --git a/src/librustc/traits/fulfill.rs b/src/librustc/traits/fulfill.rs index 8946ec2153b03..2a178a596aa6c 100644 --- a/src/librustc/traits/fulfill.rs +++ b/src/librustc/traits/fulfill.rs @@ -28,6 +28,7 @@ use super::PredicateObligation; use super::project; use super::report_overflow_error_cycle; use super::select::SelectionContext; +use super::SelectionOk; use super::Unimplemented; use super::util::predicate_for_builtin_bound; @@ -541,10 +542,11 @@ fn process_predicate1<'a,'tcx>(selcx: &mut SelectionContext<'a,'tcx>, let trait_obligation = obligation.with(data.clone()); match selcx.select(&trait_obligation) { - Ok(Some(vtable)) => { + Ok(Some(SelectionOk{ selection: vtable, mut obligations })) => { debug!("selecting trait `{:?}` at depth {} yielded Ok(Some)", data, obligation.recursion_depth); - Ok(Some(vtable.nested_obligations())) + obligations.extend(vtable.nested_obligations()); + Ok(Some(obligations)) } Ok(None) => { debug!("selecting trait `{:?}` at depth {} yielded Ok(None)", diff --git a/src/librustc/traits/mod.rs b/src/librustc/traits/mod.rs index a160465e2e815..284088d7db3ff 100644 --- a/src/librustc/traits/mod.rs +++ b/src/librustc/traits/mod.rs @@ -10,7 +10,6 @@ //! Trait Resolution. See the Book for more. -pub use self::SelectionError::*; pub use self::FulfillmentErrorCode::*; pub use self::Vtable::*; pub use self::ObligationCauseCode::*; @@ -47,6 +46,8 @@ pub use self::object_safety::is_vtable_safe_method; pub use self::select::{EvaluationCache, SelectionContext, SelectionCache}; pub use self::select::{MethodMatchResult, MethodMatched, MethodAmbiguous, MethodDidNotMatch}; pub use self::select::{MethodMatchedData}; // intentionally don't export variants +pub use self::select::{Selection, SelectionOk, SelectionError, SelectionResult}; +pub use self::select::{Unimplemented, OutputTypeParameterMismatch, TraitNotObjectSafe}; pub use self::specialize::{Overlap, specialization_graph, specializes, translate_substs}; pub use self::util::elaborate_predicates; pub use self::util::get_vtable_index_of_object_method; @@ -162,17 +163,6 @@ pub type Obligations<'tcx, O> = Vec>; pub type PredicateObligations<'tcx> = Vec>; pub type TraitObligations<'tcx> = Vec>; -pub type Selection<'tcx> = Vtable<'tcx, PredicateObligation<'tcx>>; - -#[derive(Clone,Debug)] -pub enum SelectionError<'tcx> { - Unimplemented, - OutputTypeParameterMismatch(ty::PolyTraitRef<'tcx>, - ty::PolyTraitRef<'tcx>, - ty::error::TypeError<'tcx>), - TraitNotObjectSafe(DefId), -} - pub struct FulfillmentError<'tcx> { pub obligation: PredicateObligation<'tcx>, pub code: FulfillmentErrorCode<'tcx> @@ -185,15 +175,6 @@ pub enum FulfillmentErrorCode<'tcx> { CodeAmbiguity, } -/// When performing resolution, it is typically the case that there -/// can be one of three outcomes: -/// -/// - `Ok(Some(r))`: success occurred with result `r` -/// - `Ok(None)`: could not definitely determine anything, usually due -/// to inconclusive type inference. -/// - `Err(e)`: error `e` occurred -pub type SelectionResult<'tcx, T> = Result, SelectionError<'tcx>>; - /// Given the successful resolution of an obligation, the `Vtable` /// indicates where the vtable comes from. Note that while we call this /// a "vtable", it does not necessarily indicate dynamic dispatch at diff --git a/src/librustc/traits/project.rs b/src/librustc/traits/project.rs index a0d6f5f912b2c..d4f68fdbdce3c 100644 --- a/src/librustc/traits/project.rs +++ b/src/librustc/traits/project.rs @@ -19,6 +19,7 @@ use super::ObligationCause; use super::PredicateObligation; use super::SelectionContext; use super::SelectionError; +use super::SelectionOk; use super::VtableClosureData; use super::VtableImplData; use super::util; @@ -899,7 +900,11 @@ fn assemble_candidates_from_impls<'cx,'tcx>( let poly_trait_ref = obligation_trait_ref.to_poly_trait_ref(); let trait_obligation = obligation.with(poly_trait_ref.to_poly_trait_predicate()); let vtable = match selcx.select(&trait_obligation) { - Ok(Some(vtable)) => vtable, + Ok(Some(SelectionOk { selection: vtable, obligations })) => { + // FIXME(#32730) propagate obligations (or... not... this *is* an 'assembly_*') + assert!(obligations.is_empty()); + vtable + }, Ok(None) => { candidate_set.ambiguous = true; return Ok(()); diff --git a/src/librustc/traits/select.rs b/src/librustc/traits/select.rs index 598d786fa5e8b..437f1608eaa5f 100644 --- a/src/librustc/traits/select.rs +++ b/src/librustc/traits/select.rs @@ -12,6 +12,7 @@ pub use self::MethodMatchResult::*; pub use self::MethodMatchedData::*; +pub use self::SelectionError::*; use self::SelectionCandidate::*; use self::BuiltinBoundConditions::*; use self::EvaluationResult::*; @@ -20,15 +21,12 @@ use super::coherence; use super::DerivedObligationCause; use super::project; use super::project::{normalize_with_depth, Normalized}; -use super::{PredicateObligation, TraitObligation, ObligationCause}; +use super::{PredicateObligation, PredicateObligations, TraitObligation, ObligationCause}; use super::report_overflow_error; use super::{ObligationCauseCode, BuiltinDerivedObligation, ImplDerivedObligation}; -use super::{SelectionError, Unimplemented, OutputTypeParameterMismatch}; use super::{ObjectCastObligation, Obligation}; use super::ProjectionMode; -use super::TraitNotObjectSafe; -use super::Selection; -use super::SelectionResult; +use super::Vtable; use super::{VtableBuiltin, VtableImpl, VtableParam, VtableClosure, VtableFnPointer, VtableObject, VtableDefaultImpl}; use super::{VtableImplData, VtableObjectData, VtableBuiltinData, @@ -48,6 +46,7 @@ use ty::relate::TypeRelation; use std::cell::RefCell; use std::fmt; use std::rc::Rc; +use std::mem; use syntax::abi::Abi; use hir; use util::common::ErrorReported; @@ -78,8 +77,40 @@ pub struct SelectionContext<'cx, 'tcx:'cx> { /// there is no type that the user could *actually name* that /// would satisfy it. This avoids crippling inference, basically. intercrate: bool, + + /// Obligations generated during inference. If None, no obligations are to be collected (e.g. + /// this is swapped out to a `None` when probing). + inferred_obligations: Option> +} + +pub type Selection<'tcx> = Vtable<'tcx, PredicateObligation<'tcx>>; + +pub struct SelectionOk<'tcx> { + pub selection: Selection<'tcx>, + pub obligations: PredicateObligations<'tcx>, } +#[derive(Clone,Debug)] +pub enum SelectionError<'tcx> { + Unimplemented, + OutputTypeParameterMismatch(ty::PolyTraitRef<'tcx>, + ty::PolyTraitRef<'tcx>, + ty::error::TypeError<'tcx>), + TraitNotObjectSafe(DefId), +} + +/// When performing resolution, it is typically the case that there +/// can be one of three outcomes: +/// +/// - `Ok(Some(SelectionOk { .. }))`: success occurred with result `r` +/// - `Ok(None)`: could not definitely determine anything, usually due to +/// inconclusive type inference. +/// - `Err(e)`: error `e` occurred +pub type SelectionResult<'tcx> = Result>, SelectionError<'tcx>>; + +type SelectionCandidateResult<'tcx> = + Result>, SelectionError<'tcx>>; + // A stack that walks back up the stack frame. struct TraitObligationStack<'prev, 'tcx: 'prev> { obligation: &'prev TraitObligation<'tcx>, @@ -94,7 +125,7 @@ struct TraitObligationStack<'prev, 'tcx: 'prev> { #[derive(Clone)] pub struct SelectionCache<'tcx> { hashmap: RefCell, - SelectionResult<'tcx, SelectionCandidate<'tcx>>>>, + SelectionCandidateResult<'tcx>>>, } pub enum MethodMatchResult { @@ -264,6 +295,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { infcx: infcx, freshener: infcx.freshener(), intercrate: false, + inferred_obligations: None, } } @@ -272,6 +304,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { infcx: infcx, freshener: infcx.freshener(), intercrate: true, + inferred_obligations: None, } } @@ -295,6 +328,58 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { self.infcx.projection_mode() } + fn maybe_extend_obligations(&mut self, obligations: I) + where I: IntoIterator> + { + if let Some(ref mut inferred_obligations) = self.inferred_obligations { + inferred_obligations.extend(obligations); + } + } + + fn with_inferred_obligations_storage(&mut self, + store: Option>, + f: F) + -> (R, Option>) + where F: FnOnce(&mut Self) -> R + { + let old_obligations = mem::replace(&mut self.inferred_obligations, store); + let result = f(self); + (result, mem::replace(&mut self.inferred_obligations, old_obligations)) + } + + /// Wraps a probe s.t. obligations collected during it are ignored and old obligations are + /// retained. + fn probe(&mut self, f: F) -> R + where F: FnOnce(&mut Self, &infer::CombinedSnapshot) -> R + { + self.with_inferred_obligations_storage(None, |this| { + this.infcx.probe(|snapshot| f(this, snapshot)) + }).0 + } + + /// Wraps a commit_if_ok s.t. obligations collected during it are not returned in selection if + /// the transaction fails and s.t. old obligations are retained. + fn commit_if_ok(&mut self, f: F) -> Result where + F: FnOnce(&mut Self, &infer::CombinedSnapshot) -> Result + { + if self.inferred_obligations.is_some() { + let (result, inferred) = + self.with_inferred_obligations_storage( + Some(PredicateObligations::new()), |this| { + this.infcx.commit_if_ok(|snapshot| f(this, snapshot)) + }); + match result { + Ok(ok) => { + self.inferred_obligations.as_mut().unwrap().extend(inferred.unwrap()); + Ok(ok) + }, + Err(err) => Err(err), + } + } else { + self.infcx.commit_if_ok(|snapshot| f(self, snapshot)) + } + } + /////////////////////////////////////////////////////////////////////////// // Selection // @@ -313,7 +398,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { /// Attempts to satisfy the obligation. If successful, this will affect the surrounding /// type environment by performing unification. pub fn select(&mut self, obligation: &TraitObligation<'tcx>) - -> SelectionResult<'tcx, Selection<'tcx>> { + -> SelectionResult<'tcx> { debug!("select({:?})", obligation); assert!(!obligation.predicate.has_escaping_regions()); @@ -321,9 +406,22 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let _task = self.tcx().dep_graph.in_task(dep_node); let stack = self.push_stack(TraitObligationStackList::empty(), obligation); - match self.candidate_from_obligation(&stack)? { - None => Ok(None), - Some(candidate) => Ok(Some(self.confirm_candidate(obligation, candidate)?)), + match self.with_inferred_obligations_storage( + None, |this| this.candidate_from_obligation(&stack)) + { + (Ok(None), _) => Ok(None), + (Ok(Some(candidate)), _) => { + let (candidate_result, inferred_obligations) = + self.with_inferred_obligations_storage( + Some(PredicateObligations::new()), + |this| this.confirm_candidate(obligation, candidate)); + candidate_result.map(|confirmed_candidate| + Some(SelectionOk { + selection: confirmed_candidate, + obligations: inferred_obligations.unwrap(), + })) + }, + (Err(err), _) => Err(err), } } @@ -345,8 +443,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { debug!("evaluate_obligation({:?})", obligation); - self.infcx.probe(|_| { - self.evaluate_predicate_recursively(TraitObligationStackList::empty(), obligation) + self.probe(|this, _| { + this.evaluate_predicate_recursively(TraitObligationStackList::empty(), obligation) .may_apply() }) } @@ -361,8 +459,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { debug!("evaluate_obligation_conservatively({:?})", obligation); - self.infcx.probe(|_| { - self.evaluate_predicate_recursively(TraitObligationStackList::empty(), obligation) + self.probe(|this, _| { + this.evaluate_predicate_recursively(TraitObligationStackList::empty(), obligation) == EvaluatedToOk }) } @@ -421,9 +519,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ty::Predicate::Equate(ref p) => { // does this code ever run? match self.infcx.equality_predicate(obligation.cause.span, p) { - Ok(InferOk { obligations, .. }) => { - // FIXME(#32730) propagate obligations - assert!(obligations.is_empty()); + Ok(InferOk { .. }) => { + // We don't collect new obligations during evaluation, as we're supposed to + // be in a probe. EvaluatedToOk }, Err(_) => EvaluatedToErr @@ -605,11 +703,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { { debug!("evaluate_candidate: depth={} candidate={:?}", stack.obligation.recursion_depth, candidate); - let result = self.infcx.probe(|_| { + let result = self.probe(|this, _| { let candidate = (*candidate).clone(); - match self.confirm_candidate(stack.obligation, candidate) { + match this.confirm_candidate(stack.obligation, candidate) { Ok(selection) => { - self.evaluate_predicates_recursively( + this.evaluate_predicates_recursively( stack.list(), selection.nested_obligations().iter()) } @@ -669,7 +767,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { fn candidate_from_obligation<'o>(&mut self, stack: &TraitObligationStack<'o, 'tcx>) - -> SelectionResult<'tcx, SelectionCandidate<'tcx>> + -> SelectionCandidateResult<'tcx> { // Watch out for overflow. This intentionally bypasses (and does // not update) the cache. @@ -713,7 +811,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // Treat negative impls as unimplemented fn filter_negative_impls(&self, candidate: SelectionCandidate<'tcx>) - -> SelectionResult<'tcx, SelectionCandidate<'tcx>> { + -> SelectionCandidateResult<'tcx> { if let ImplCandidate(def_id) = candidate { if self.tcx().trait_impl_polarity(def_id) == Some(hir::ImplPolarity::Negative) { return Err(Unimplemented) @@ -724,7 +822,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { fn candidate_from_obligation_no_cache<'o>(&mut self, stack: &TraitObligationStack<'o, 'tcx>) - -> SelectionResult<'tcx, SelectionCandidate<'tcx>> + -> SelectionCandidateResult<'tcx> { if stack.obligation.predicate.references_error() { // If we encounter a `TyError`, we generally prefer the @@ -894,7 +992,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { fn check_candidate_cache(&mut self, cache_fresh_trait_pred: &ty::PolyTraitPredicate<'tcx>) - -> Option>> + -> Option> { let cache = self.pick_candidate_cache(); let hashmap = cache.hashmap.borrow(); @@ -903,7 +1001,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { fn insert_candidate_cache(&mut self, cache_fresh_trait_pred: ty::PolyTraitPredicate<'tcx>, - candidate: SelectionResult<'tcx, SelectionCandidate<'tcx>>) + candidate: SelectionCandidateResult<'tcx>) { let cache = self.pick_candidate_cache(); let mut hashmap = cache.hashmap.borrow_mut(); @@ -912,7 +1010,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { fn should_update_candidate_cache(&mut self, cache_fresh_trait_pred: &ty::PolyTraitPredicate<'tcx>, - candidate: &SelectionResult<'tcx, SelectionCandidate<'tcx>>) + candidate: &SelectionCandidateResult<'tcx>) -> bool { // In general, it's a good idea to cache results, even @@ -1053,8 +1151,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { debug!("assemble_candidates_for_projected_tys: trait_def_id={:?}", trait_def_id); - let result = self.infcx.probe(|snapshot| { - self.match_projection_obligation_against_bounds_from_trait(obligation, + let result = self.probe(|this, snapshot| { + this.match_projection_obligation_against_bounds_from_trait(obligation, snapshot) }); @@ -1102,12 +1200,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { util::elaborate_predicates(self.tcx(), bounds.predicates.into_vec()) .filter_to_traits() .find( - |bound| self.infcx.probe( - |_| self.match_projection(obligation, - bound.clone(), - skol_trait_predicate.trait_ref.clone(), - &skol_map, - snapshot))); + |bound| self.probe( + |this, _| this.match_projection(obligation, + bound.clone(), + skol_trait_predicate.trait_ref.clone(), + &skol_map, + snapshot))); debug!("match_projection_obligation_against_bounds_from_trait: \ matching_bound={:?}", @@ -1142,8 +1240,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { trait_bound.clone(), ty::Binder(skol_trait_ref.clone())) { Ok(InferOk { obligations, .. }) => { - // FIXME(#32730) propagate obligations - assert!(obligations.is_empty()); + self.maybe_extend_obligations(obligations); } Err(_) => { return false; } } @@ -1185,10 +1282,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { where_clause_trait_ref: ty::PolyTraitRef<'tcx>) -> EvaluationResult { - self.infcx().probe(move |_| { - match self.match_where_clause_trait_ref(stack.obligation, where_clause_trait_ref) { + self.probe(move |this, _| { + match this.match_where_clause_trait_ref(stack.obligation, where_clause_trait_ref) { Ok(obligations) => { - self.evaluate_predicates_recursively(stack.list(), obligations.iter()) + this.evaluate_predicates_recursively(stack.list(), obligations.iter()) } Err(()) => EvaluatedToErr } @@ -1307,8 +1404,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { self.tcx(), obligation.predicate.0.trait_ref.self_ty(), |impl_def_id| { - self.infcx.probe(|snapshot| { - if let Ok(_) = self.match_impl(impl_def_id, obligation, snapshot) { + self.probe(|this, snapshot| { + if let Ok(_) = this.match_impl(impl_def_id, obligation, snapshot) { candidates.vec.push(ImplCandidate(impl_def_id)); } }); @@ -1394,12 +1491,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { return; } - self.infcx.probe(|snapshot| { + self.probe(|this, snapshot| { let (self_ty, _) = - self.infcx().skolemize_late_bound_regions(&obligation.self_ty(), snapshot); + this.infcx().skolemize_late_bound_regions(&obligation.self_ty(), snapshot); let poly_trait_ref = match self_ty.sty { ty::TyTrait(ref data) => { - match self.tcx().lang_items.to_builtin_kind(obligation.predicate.def_id()) { + match this.tcx().lang_items.to_builtin_kind(obligation.predicate.def_id()) { Some(bound @ ty::BoundSend) | Some(bound @ ty::BoundSync) => { if data.bounds.builtin_bounds.contains(&bound) { debug!("assemble_candidates_from_object_ty: matched builtin bound, \ @@ -1411,7 +1508,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { _ => {} } - data.principal_trait_ref_with_self_ty(self.tcx(), self_ty) + data.principal_trait_ref_with_self_ty(this.tcx(), self_ty) } ty::TyInfer(ty::TyVar(_)) => { debug!("assemble_candidates_from_object_ty: ambiguous"); @@ -1432,11 +1529,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // For example, we may be trying to upcast `Foo` to `Bar`, // but `Foo` is declared as `trait Foo : Bar`. let upcast_trait_refs = - util::supertraits(self.tcx(), poly_trait_ref) + util::supertraits(this.tcx(), poly_trait_ref) .filter(|upcast_trait_ref| { - self.infcx.probe(|_| { + this.probe(|this, _| { let upcast_trait_ref = upcast_trait_ref.clone(); - self.match_poly_trait_ref(obligation, upcast_trait_ref).is_ok() + this.match_poly_trait_ref(obligation, upcast_trait_ref).is_ok() }) }) .count(); @@ -1953,23 +2050,23 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // For each type, produce a vector of resulting obligations let obligations: Result>, _> = bound_types.iter().map(|nested_ty| { - self.infcx.commit_if_ok(|snapshot| { + self.commit_if_ok(|this, snapshot| { let (skol_ty, skol_map) = - self.infcx().skolemize_late_bound_regions(nested_ty, snapshot); + this.infcx().skolemize_late_bound_regions(nested_ty, snapshot); let Normalized { value: normalized_ty, mut obligations } = - project::normalize_with_depth(self, + project::normalize_with_depth(this, obligation.cause.clone(), obligation.recursion_depth + 1, &skol_ty); let skol_obligation = - util::predicate_for_trait_def(self.tcx(), + util::predicate_for_trait_def(this.tcx(), derived_cause.clone(), trait_def_id, obligation.recursion_depth + 1, normalized_ty, vec![]); obligations.push(skol_obligation); - Ok(self.infcx().plug_leaks(skol_map, snapshot, &obligations)) + Ok(this.infcx().plug_leaks(skol_map, snapshot, &obligations)) }) }).collect(); @@ -2065,9 +2162,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { obligation: &TraitObligation<'tcx>) { let _: Result<(),()> = - self.infcx.commit_if_ok(|snapshot| { + self.commit_if_ok(|this, snapshot| { let result = - self.match_projection_obligation_against_bounds_from_trait(obligation, + this.match_projection_obligation_against_bounds_from_trait(obligation, snapshot); assert!(result); Ok(()) @@ -2211,11 +2308,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { trait_def_id, nested); - let trait_obligations: Result,()> = self.infcx.commit_if_ok(|snapshot| { + let trait_obligations: Result,()> = self.commit_if_ok(|this, snapshot| { let poly_trait_ref = obligation.predicate.to_poly_trait_ref(); let (trait_ref, skol_map) = - self.infcx().skolemize_late_bound_regions(&poly_trait_ref, snapshot); - Ok(self.impl_or_trait_obligations(obligation.cause.clone(), + this.infcx().skolemize_late_bound_regions(&poly_trait_ref, snapshot); + Ok(this.impl_or_trait_obligations(obligation.cause.clone(), obligation.recursion_depth + 1, trait_def_id, &trait_ref.substs, @@ -2246,12 +2343,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // First, create the substitutions by matching the impl again, // this time not in a probe. - self.infcx.commit_if_ok(|snapshot| { + self.commit_if_ok(|this, snapshot| { let (substs, skol_map) = - self.rematch_impl(impl_def_id, obligation, + this.rematch_impl(impl_def_id, obligation, snapshot); debug!("confirm_impl_candidate substs={:?}", substs); - Ok(self.vtable_impl(impl_def_id, substs, obligation.cause.clone(), + Ok(this.vtable_impl(impl_def_id, substs, obligation.cause.clone(), obligation.recursion_depth + 1, skol_map, snapshot)) }) } @@ -2321,6 +2418,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let vtable_base; { + let tcx = self.tcx(); + // We want to find the first supertrait in the list of // supertraits that we can unify with, and do that // unification. We know that there is exactly one in the list @@ -2331,8 +2430,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { util::supertraits(self.tcx(), poly_trait_ref) .take_while(|&t| { match - self.infcx.commit_if_ok( - |_| self.match_poly_trait_ref(obligation, t)) + self.commit_if_ok( + |this, _| this.match_poly_trait_ref(obligation, t)) { Ok(_) => { upcast_trait_ref = Some(t); false } Err(_) => { true } @@ -2344,7 +2443,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // entries, so that we can compute the offset for the selected // trait. vtable_base = - nonmatching.map(|t| util::count_own_vtable_entries(self.tcx(), t)) + nonmatching.map(|t| util::count_own_vtable_entries(tcx, t)) .sum(); } @@ -2455,8 +2554,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { origin, expected_trait_ref.clone(), obligation_trait_ref.clone()) - // FIXME(#32730) propagate obligations - .map(|InferOk { obligations, .. }| assert!(obligations.is_empty())) + .map(|InferOk { obligations, .. }| self.maybe_extend_obligations(obligations)) .map_err(|e| OutputTypeParameterMismatch(expected_trait_ref, obligation_trait_ref, e)) } @@ -2491,8 +2589,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let InferOk { obligations, .. } = self.infcx.sub_types(false, origin, new_trait, target) .map_err(|_| Unimplemented)?; - // FIXME(#32730) propagate obligations - assert!(obligations.is_empty()); + self.maybe_extend_obligations(obligations); // Register one obligation for 'a: 'b. let cause = ObligationCause::new(obligation.cause.span, @@ -2558,8 +2655,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let InferOk { obligations, .. } = self.infcx.sub_types(false, origin, a, b) .map_err(|_| Unimplemented)?; - // FIXME(#32730) propagate obligations - assert!(obligations.is_empty()); + self.maybe_extend_obligations(obligations); } // Struct -> Struct. @@ -2618,8 +2714,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let InferOk { obligations, .. } = self.infcx.sub_types(false, origin, new_struct, target) .map_err(|_| Unimplemented)?; - // FIXME(#32730) propagate obligations - assert!(obligations.is_empty()); + self.maybe_extend_obligations(obligations); // Construct the nested Field: Unsize> predicate. nested.push(util::predicate_for_trait_def(tcx, @@ -2713,8 +2808,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { debug!("match_impl: failed eq_trait_refs due to `{}`", e); () })?; - // FIXME(#32730) propagate obligations - assert!(obligations.is_empty()); + self.maybe_extend_obligations(obligations); if let Err(e) = self.infcx.leak_check(&skol_map, snapshot) { debug!("match_impl: failed leak check due to `{}`", e); @@ -2767,7 +2861,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { /// Returns `Ok` if `poly_trait_ref` being true implies that the /// obligation is satisfied. - fn match_poly_trait_ref(&self, + fn match_poly_trait_ref(&mut self, obligation: &TraitObligation<'tcx>, poly_trait_ref: ty::PolyTraitRef<'tcx>) -> Result<(),()> @@ -2781,8 +2875,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { origin, poly_trait_ref, obligation.predicate.to_poly_trait_ref()) - // FIXME(#32730) propagate obligations - .map(|InferOk { obligations, .. }| assert!(obligations.is_empty())) + .map(|InferOk { obligations, .. }| self.maybe_extend_obligations(obligations)) .map_err(|_| ()) } diff --git a/src/librustc_const_eval/eval.rs b/src/librustc_const_eval/eval.rs index c2ac3d838c8d0..bfb1107d2743b 100644 --- a/src/librustc_const_eval/eval.rs +++ b/src/librustc_const_eval/eval.rs @@ -24,7 +24,7 @@ use rustc::hir::def_id::DefId; use rustc::hir::pat_util::def_to_path; use rustc::ty::{self, Ty, TyCtxt, subst}; use rustc::ty::util::IntTypeExt; -use rustc::traits::ProjectionMode; +use rustc::traits::{SelectionOk, ProjectionMode}; use rustc::middle::astconv_util::ast_ty_to_prim_ty; use rustc::util::nodemap::NodeMap; use rustc::lint; @@ -1026,7 +1026,11 @@ fn resolve_trait_associated_const<'a, 'tcx: 'a>(tcx: &'a TyCtxt<'tcx>, let obligation = traits::Obligation::new(traits::ObligationCause::dummy(), trait_ref.to_poly_trait_predicate()); let selection = match selcx.select(&obligation) { - Ok(Some(vtable)) => vtable, + Ok(Some(SelectionOk { selection: vtable, obligations })) => { + // FIXME(#32730) propagate obligations + assert!(obligations.is_empty()); + vtable + }, // Still ambiguous, so give up and let the caller decide whether this // expression is really needed yet. Some associated constant values // can't be evaluated until monomorphization is done in trans. diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs index 5e3a47701ebbf..b12e313d19dca 100644 --- a/src/librustc_lint/builtin.rs +++ b/src/librustc_lint/builtin.rs @@ -36,7 +36,7 @@ use rustc::{cfg, infer}; use rustc::ty::subst::Substs; use rustc::ty::{self, Ty, TyCtxt}; use rustc::ty::adjustment; -use rustc::traits::{self, ProjectionMode}; +use rustc::traits::{self, SelectionOk, ProjectionMode}; use rustc::hir::map as hir_map; use util::nodemap::{NodeSet}; use lint::{Level, LateContext, LintContext, LintArray, Lint}; @@ -879,7 +879,11 @@ impl LateLintPass for UnconditionalRecursion { // The method comes from a `T: Trait` bound. // If `T` is `Self`, then this call is inside // a default method definition. - Ok(Some(traits::VtableParam(_))) => { + Ok(Some(SelectionOk { selection: traits::VtableParam(_), + obligations })) => { + // FIXME(#32730) propagate obligations (... but... is it really + // necessary here?) + assert!(obligations.is_empty()); let self_ty = callee_substs.self_ty(); let on_self = self_ty.map_or(false, |t| t.is_self()); // We can only be recurring in a default @@ -890,7 +894,11 @@ impl LateLintPass for UnconditionalRecursion { // The `impl` is known, so we check that with a // special case: - Ok(Some(traits::VtableImpl(vtable_impl))) => { + Ok(Some(SelectionOk { selection: traits::VtableImpl(vtable_impl), + obligations })) => { + // FIXME(#32730) propagate obligations (... but... is it really + // necessary here?) + assert!(obligations.is_empty()); let container = ty::ImplContainer(vtable_impl.impl_def_id); // It matches if it comes from the same impl, // and has the same method name. diff --git a/src/librustc_trans/common.rs b/src/librustc_trans/common.rs index 5ce7caf5deb06..2148dc0545323 100644 --- a/src/librustc_trans/common.rs +++ b/src/librustc_trans/common.rs @@ -38,7 +38,7 @@ use monomorphize; use type_::Type; use value::Value; use rustc::ty::{self, Ty, TyCtxt}; -use rustc::traits::{self, SelectionContext, ProjectionMode}; +use rustc::traits::{self, SelectionContext, SelectionOk, ProjectionMode}; use rustc::ty::fold::{TypeFolder, TypeFoldable}; use rustc::hir; use util::nodemap::NodeMap; @@ -1083,7 +1083,11 @@ pub fn fulfill_obligation<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, traits::Obligation::new(traits::ObligationCause::misc(span, ast::DUMMY_NODE_ID), trait_ref.to_poly_trait_predicate()); let selection = match selcx.select(&obligation) { - Ok(Some(selection)) => selection, + Ok(Some(SelectionOk { selection, obligations })) => { + // FIXME(#32730) propagate obligations + assert!(obligations.is_empty()); + selection + }, Ok(None) => { // Ambiguity can happen when monomorphizing during trans // expands to some humongo type that never occurred diff --git a/src/librustc_typeck/check/coercion.rs b/src/librustc_typeck/check/coercion.rs index f1c6868efd210..c8ac5f7e18320 100644 --- a/src/librustc_typeck/check/coercion.rs +++ b/src/librustc_typeck/check/coercion.rs @@ -63,7 +63,7 @@ use check::{autoderef, FnCtxt, UnresolvedTypeAction}; use rustc::infer::{Coercion, InferOk, TypeOrigin, TypeTrace}; -use rustc::traits::{self, ObligationCause}; +use rustc::traits::{self, ObligationCause, SelectionOk}; use rustc::traits::{predicate_for_trait_def, report_selection_error}; use rustc::ty::adjustment::{AutoAdjustment, AutoDerefRef, AdjustDerefRef}; use rustc::ty::adjustment::{AutoPtr, AutoUnsafe, AdjustReifyFnPointer}; @@ -483,7 +483,9 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { // be silent, as it causes a type mismatch later. } - Ok(Some(vtable)) => { + Ok(Some(SelectionOk { selection: vtable, obligations })) => { + // FIXME(#32730) propagate obligations + assert!(obligations.is_empty()); for obligation in vtable.nested_obligations() { queue.push_back(obligation); }