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 116d82881d22df0e320b858a0df4144748295872 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/mod.rs | 55 +++-- src/librustc/traits/project.rs | 2 +- src/librustc/traits/select.rs | 281 ++++++++++++++++-------- src/librustc/traits/structural_impls.rs | 26 ++- src/librustc/traits/util.rs | 6 +- src/librustc_trans/callee.rs | 2 +- src/librustc_trans/meth.rs | 2 +- 7 files changed, 234 insertions(+), 140 deletions(-) diff --git a/src/librustc/traits/mod.rs b/src/librustc/traits/mod.rs index a160465e2e815..cce1dc329e3c8 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, 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 @@ -250,7 +231,7 @@ pub enum Vtable<'tcx, N> { VtableParam(Vec), /// Virtual calls through an object - VtableObject(VtableObjectData<'tcx>), + VtableObject(VtableObjectData<'tcx, N>), /// Successful resolution for a builtin trait. VtableBuiltin(VtableBuiltinData), @@ -261,7 +242,7 @@ pub enum Vtable<'tcx, N> { VtableClosure(VtableClosureData<'tcx, N>), /// Same as above, but for a fn pointer type with the given signature. - VtableFnPointer(ty::Ty<'tcx>), + VtableFnPointer(ty::Ty<'tcx>, Vec), } /// Identifies a particular impl in the source, along with a set of @@ -304,14 +285,15 @@ pub struct VtableBuiltinData { /// A vtable for some object-safe trait `Foo` automatically derived /// for the object type `Foo`. #[derive(PartialEq,Eq,Clone)] -pub struct VtableObjectData<'tcx> { +pub struct VtableObjectData<'tcx, N> { /// `Foo` upcast to the obligation trait. This will be some supertrait of `Foo`. pub upcast_trait_ref: ty::PolyTraitRef<'tcx>, /// The vtable is formed by concatenating together the method lists of /// the base object trait and all supertraits; this is the start of /// `upcast_trait_ref`'s methods in that vtable. - pub vtable_base: usize + pub vtable_base: usize, + pub nested: Vec } /// Creates predicate obligations from the generic bounds. @@ -577,7 +559,20 @@ impl<'tcx, N> Vtable<'tcx, N> { VtableBuiltin(i) => i.nested, VtableDefaultImpl(d) => d.nested, VtableClosure(c) => c.nested, - VtableObject(_) | VtableFnPointer(..) => vec![] + VtableObject(i) => i.nested, + VtableFnPointer(_, n) => n + } + } + + fn nested_obligations_mut(&mut self) -> &mut Vec { + match self { + &mut VtableImpl(ref mut i) => &mut i.nested, + &mut VtableParam(ref mut n) => n, + &mut VtableBuiltin(ref mut i) => &mut i.nested, + &mut VtableDefaultImpl(ref mut d) => &mut d.nested, + &mut VtableClosure(ref mut c) => &mut c.nested, + &mut VtableObject(ref mut i) => &mut i.nested, + &mut VtableFnPointer(_, ref mut n) => n } } @@ -592,12 +587,16 @@ impl<'tcx, N> Vtable<'tcx, N> { VtableBuiltin(i) => VtableBuiltin(VtableBuiltinData { nested: i.nested.into_iter().map(f).collect() }), - VtableObject(o) => VtableObject(o), + VtableObject(o) => VtableObject(VtableObjectData { + upcast_trait_ref: o.upcast_trait_ref, + vtable_base: o.vtable_base, + nested: o.nested.into_iter().map(f).collect(), + }), VtableDefaultImpl(d) => VtableDefaultImpl(VtableDefaultImplData { trait_def_id: d.trait_def_id, nested: d.nested.into_iter().map(f).collect() }), - VtableFnPointer(f) => VtableFnPointer(f), + VtableFnPointer(r, n) => VtableFnPointer(r, n.into_iter().map(f).collect()), VtableClosure(c) => VtableClosure(VtableClosureData { closure_def_id: c.closure_def_id, substs: c.substs, diff --git a/src/librustc/traits/project.rs b/src/librustc/traits/project.rs index a0d6f5f912b2c..13d1d881ab7b1 100644 --- a/src/librustc/traits/project.rs +++ b/src/librustc/traits/project.rs @@ -927,7 +927,7 @@ fn assemble_candidates_from_impls<'cx,'tcx>( candidate_set.vec.push( ProjectionTyCandidate::Closure(data)); } - super::VtableFnPointer(fn_type) => { + super::VtableFnPointer(fn_type, _) => { candidate_set.vec.push( ProjectionTyCandidate::FnPointer(fn_type)); } diff --git a/src/librustc/traits/select.rs b/src/librustc/traits/select.rs index 598d786fa5e8b..52ad8216b3e7c 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,35 @@ 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>>; + +#[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(Selection))`: 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 +120,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 +290,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { infcx: infcx, freshener: infcx.freshener(), intercrate: false, + inferred_obligations: None, } } @@ -272,6 +299,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { infcx: infcx, freshener: infcx.freshener(), intercrate: true, + inferred_obligations: None, } } @@ -295,6 +323,60 @@ 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); + } + } + + /// Uses the specified inferred obligations storage for the operation `f`. Restores the old + /// inferred obligations upon finishing `f`. + 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 +395,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 +403,21 @@ 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(|mut candidate| { + candidate.nested_obligations_mut().extend(inferred_obligations.unwrap()); + Some(candidate) + }) + }, + (Err(err), _) => Err(err), } } @@ -345,8 +439,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 +455,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 +515,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 +699,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 +763,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 +807,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 +818,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 +988,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 +997,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 +1006,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 +1147,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 +1196,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 +1236,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 +1278,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 +1400,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 +1487,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 +1504,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 +1525,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 +2046,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(); @@ -1987,6 +2080,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // with the values found in the obligation, possibly yielding a // type error. See `README.md` for more details. + /// Given a selection candidate, performs mutation of the inference context and returns the + /// selection result with all inferred obligations. Clears `inferred_obligations`. fn confirm_candidate(&mut self, obligation: &TraitObligation<'tcx>, candidate: SelectionCandidate<'tcx>) @@ -1998,8 +2093,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { match candidate { BuiltinCandidate(builtin_bound) => { - Ok(VtableBuiltin( - self.confirm_builtin_candidate(obligation, builtin_bound)?)) + let vtable_builtin = self.confirm_builtin_candidate(obligation, builtin_bound)?; + Ok(VtableBuiltin(vtable_builtin)) } ParamCandidate(param) => { @@ -2008,18 +2103,18 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } DefaultImplCandidate(trait_def_id) => { - let data = self.confirm_default_impl_candidate(obligation, trait_def_id); - Ok(VtableDefaultImpl(data)) + let vtable_default = self.confirm_default_impl_candidate(obligation, trait_def_id); + Ok(VtableDefaultImpl(vtable_default)) } DefaultImplObjectCandidate(trait_def_id) => { - let data = self.confirm_default_impl_object_candidate(obligation, trait_def_id); - Ok(VtableDefaultImpl(data)) + let vtable_impl_object = + self.confirm_default_impl_object_candidate(obligation, trait_def_id); + Ok(VtableDefaultImpl(vtable_impl_object)) } ImplCandidate(impl_def_id) => { - let vtable_impl = - self.confirm_impl_candidate(obligation, impl_def_id)?; + let vtable_impl = self.confirm_impl_candidate(obligation, impl_def_id)?; Ok(VtableImpl(vtable_impl)) } @@ -2035,28 +2130,27 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // because that's what the object type is telling us, // and there's really no additional obligations to // prove and no types in particular to unify etc. - Ok(VtableParam(Vec::new())) + Ok(VtableParam(vec![])) } ObjectCandidate => { - let data = self.confirm_object_candidate(obligation); - Ok(VtableObject(data)) + let vtable_object = self.confirm_object_candidate(obligation); + Ok(VtableObject(vtable_object)) } FnPointerCandidate => { - let fn_type = - self.confirm_fn_pointer_candidate(obligation)?; - Ok(VtableFnPointer(fn_type)) + let fn_type = self.confirm_fn_pointer_candidate(obligation)?; + Ok(VtableFnPointer(fn_type, vec![])) } ProjectionCandidate => { self.confirm_projection_candidate(obligation); - Ok(VtableParam(Vec::new())) + Ok(VtableParam(vec![])) } BuiltinUnsizeCandidate => { - let data = self.confirm_builtin_unsize_candidate(obligation)?; - Ok(VtableBuiltin(data)) + let vtable_builtin_unsize = self.confirm_builtin_unsize_candidate(obligation)?; + Ok(VtableBuiltin(vtable_builtin_unsize)) } } } @@ -2065,9 +2159,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 +2305,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 +2340,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)) }) } @@ -2297,7 +2391,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { fn confirm_object_candidate(&mut self, obligation: &TraitObligation<'tcx>) - -> VtableObjectData<'tcx> + -> VtableObjectData<'tcx, PredicateObligation<'tcx>> { debug!("confirm_object_candidate({:?})", obligation); @@ -2321,6 +2415,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 +2427,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 +2440,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(); } @@ -2352,6 +2448,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { VtableObjectData { upcast_trait_ref: upcast_trait_ref.unwrap(), vtable_base: vtable_base, + nested: vec![], } } @@ -2455,8 +2552,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 +2587,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 +2653,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 +2712,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 +2806,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 +2859,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 +2873,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/traits/structural_impls.rs b/src/librustc/traits/structural_impls.rs index 367e5f32ba3ec..496622c894d3a 100644 --- a/src/librustc/traits/structural_impls.rs +++ b/src/librustc/traits/structural_impls.rs @@ -51,8 +51,8 @@ impl<'tcx, N: fmt::Debug> fmt::Debug for traits::Vtable<'tcx, N> { super::VtableClosure(ref d) => write!(f, "{:?}", d), - super::VtableFnPointer(ref d) => - write!(f, "VtableFnPointer({:?})", d), + super::VtableFnPointer(ref d, ref n) => + write!(f, "VtableFnPointer({:?}, {:?})", d, n), super::VtableObject(ref d) => write!(f, "{:?}", d), @@ -98,11 +98,12 @@ impl<'tcx, N: fmt::Debug> fmt::Debug for traits::VtableDefaultImplData { } } -impl<'tcx> fmt::Debug for traits::VtableObjectData<'tcx> { +impl<'tcx, N: fmt::Debug> fmt::Debug for traits::VtableObjectData<'tcx, N> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "VtableObject(upcast={:?}, vtable_base={})", + write!(f, "VtableObject(upcast={:?}, vtable_base={}, nested={:?})", self.upcast_trait_ref, - self.vtable_base) + self.vtable_base, + self.nested) } } @@ -199,16 +200,17 @@ impl<'tcx, N: TypeFoldable<'tcx>> TypeFoldable<'tcx> for traits::VtableBuiltinDa } } -impl<'tcx> TypeFoldable<'tcx> for traits::VtableObjectData<'tcx> { +impl<'tcx, N: TypeFoldable<'tcx>> TypeFoldable<'tcx> for traits::VtableObjectData<'tcx, N> { fn super_fold_with>(&self, folder: &mut F) -> Self { traits::VtableObjectData { upcast_trait_ref: self.upcast_trait_ref.fold_with(folder), - vtable_base: self.vtable_base + vtable_base: self.vtable_base, + nested: self.nested.fold_with(folder), } } fn super_visit_with>(&self, visitor: &mut V) -> bool { - self.upcast_trait_ref.visit_with(visitor) + self.upcast_trait_ref.visit_with(visitor) || self.nested.visit_with(visitor) } } @@ -220,8 +222,8 @@ impl<'tcx, N: TypeFoldable<'tcx>> TypeFoldable<'tcx> for traits::Vtable<'tcx, N> traits::VtableClosure(ref d) => { traits::VtableClosure(d.fold_with(folder)) } - traits::VtableFnPointer(ref d) => { - traits::VtableFnPointer(d.fold_with(folder)) + traits::VtableFnPointer(ref d, ref n) => { + traits::VtableFnPointer(d.fold_with(folder), n.fold_with(folder)) } traits::VtableParam(ref n) => traits::VtableParam(n.fold_with(folder)), traits::VtableBuiltin(ref d) => traits::VtableBuiltin(d.fold_with(folder)), @@ -234,7 +236,9 @@ impl<'tcx, N: TypeFoldable<'tcx>> TypeFoldable<'tcx> for traits::Vtable<'tcx, N> traits::VtableImpl(ref v) => v.visit_with(visitor), traits::VtableDefaultImpl(ref t) => t.visit_with(visitor), traits::VtableClosure(ref d) => d.visit_with(visitor), - traits::VtableFnPointer(ref d) => d.visit_with(visitor), + traits::VtableFnPointer(ref d, ref n) => { + d.visit_with(visitor) || n.visit_with(visitor) + }, traits::VtableParam(ref n) => n.visit_with(visitor), traits::VtableBuiltin(ref d) => d.visit_with(visitor), traits::VtableObject(ref d) => d.visit_with(visitor), diff --git a/src/librustc/traits/util.rs b/src/librustc/traits/util.rs index 90def00be0730..bb3e1123fca09 100644 --- a/src/librustc/traits/util.rs +++ b/src/librustc/traits/util.rs @@ -465,9 +465,9 @@ pub fn count_own_vtable_entries<'tcx>(tcx: &TyCtxt<'tcx>, /// Given an upcast trait object described by `object`, returns the /// index of the method `method_def_id` (which should be part of /// `object.upcast_trait_ref`) within the vtable for `object`. -pub fn get_vtable_index_of_object_method<'tcx>(tcx: &TyCtxt<'tcx>, - object: &super::VtableObjectData<'tcx>, - method_def_id: DefId) -> usize { +pub fn get_vtable_index_of_object_method<'tcx, N>(tcx: &TyCtxt<'tcx>, + object: &super::VtableObjectData<'tcx, N>, + method_def_id: DefId) -> usize { // Count number of methods preceding the one we are selecting and // add them to the total offset. // Skip over associated types and constants. diff --git a/src/librustc_trans/callee.rs b/src/librustc_trans/callee.rs index 8c22ddbb462c0..1f7139adbaed0 100644 --- a/src/librustc_trans/callee.rs +++ b/src/librustc_trans/callee.rs @@ -188,7 +188,7 @@ impl<'tcx> Callee<'tcx> { }; Callee::ptr(immediate_rvalue(llfn, fn_ptr_ty)) } - traits::VtableFnPointer(fn_ty) => { + traits::VtableFnPointer(fn_ty, _) => { let trait_closure_kind = tcx.lang_items.fn_trait_kind(trait_id).unwrap(); let llfn = trans_fn_pointer_shim(ccx, trait_closure_kind, fn_ty); diff --git a/src/librustc_trans/meth.rs b/src/librustc_trans/meth.rs index 9e5476ae80d47..8aefa229f47a1 100644 --- a/src/librustc_trans/meth.rs +++ b/src/librustc_trans/meth.rs @@ -177,7 +177,7 @@ pub fn get_vtable<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, trait_closure_kind); vec![llfn].into_iter() } - traits::VtableFnPointer(bare_fn_ty) => { + traits::VtableFnPointer(bare_fn_ty, _) => { let trait_closure_kind = tcx.lang_items.fn_trait_kind(trait_ref.def_id()).unwrap(); vec![trans_fn_pointer_shim(ccx, trait_closure_kind, bare_fn_ty)].into_iter() }