about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/librustc_typeck/check/autoderef.rs14
-rw-r--r--src/librustc_typeck/check/coercion.rs194
2 files changed, 99 insertions, 109 deletions
diff --git a/src/librustc_typeck/check/autoderef.rs b/src/librustc_typeck/check/autoderef.rs
index ca0ab8f1e8c..1aab4853a4f 100644
--- a/src/librustc_typeck/check/autoderef.rs
+++ b/src/librustc_typeck/check/autoderef.rs
@@ -12,6 +12,7 @@ use astconv::AstConv;
 
 use super::FnCtxt;
 
+use rustc::infer::InferOk;
 use rustc::traits;
 use rustc::ty::{self, Ty, TraitRef};
 use rustc::ty::{ToPredicate, TypeFoldable};
@@ -150,6 +151,14 @@ impl<'a, 'gcx, 'tcx> Autoderef<'a, 'gcx, 'tcx> {
     pub fn finalize<'b, I>(self, pref: LvaluePreference, exprs: I)
         where I: IntoIterator<Item = &'b hir::Expr>
     {
+        let fcx = self.fcx;
+        fcx.register_infer_ok_obligations(self.finalize_as_infer_ok(pref, exprs));
+    }
+
+    pub fn finalize_as_infer_ok<'b, I>(self, pref: LvaluePreference, exprs: I)
+                                       -> InferOk<'tcx, ()>
+        where I: IntoIterator<Item = &'b hir::Expr>
+    {
         let methods: Vec<_> = self.steps
             .iter()
             .map(|&(ty, kind)| {
@@ -176,8 +185,9 @@ impl<'a, 'gcx, 'tcx> Autoderef<'a, 'gcx, 'tcx> {
             }
         }
 
-        for obligation in self.obligations {
-            self.fcx.register_predicate(obligation);
+        InferOk {
+            value: (),
+            obligations: self.obligations
         }
     }
 }
diff --git a/src/librustc_typeck/check/coercion.rs b/src/librustc_typeck/check/coercion.rs
index 53759cc115d..65105872881 100644
--- a/src/librustc_typeck/check/coercion.rs
+++ b/src/librustc_typeck/check/coercion.rs
@@ -64,7 +64,8 @@ use check::FnCtxt;
 
 use rustc::hir;
 use rustc::hir::def_id::DefId;
-use rustc::infer::{Coercion, InferOk, TypeTrace};
+use rustc::infer::{Coercion, InferResult, InferOk, TypeTrace};
+use rustc::infer::type_variable::TypeVariableOrigin;
 use rustc::traits::{self, ObligationCause, ObligationCauseCode};
 use rustc::ty::adjustment::{Adjustment, Adjust, AutoBorrow};
 use rustc::ty::{self, LvaluePreference, TypeAndMut,
@@ -75,9 +76,7 @@ use rustc::ty::relate::RelateResult;
 use rustc::ty::subst::Subst;
 use syntax::abi;
 use syntax::feature_gate;
-use util::common::indent;
 
-use std::cell::RefCell;
 use std::collections::VecDeque;
 use std::ops::Deref;
 
@@ -85,7 +84,6 @@ struct Coerce<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> {
     fcx: &'a FnCtxt<'a, 'gcx, 'tcx>,
     cause: ObligationCause<'tcx>,
     use_lub: bool,
-    unsizing_obligations: RefCell<Vec<traits::PredicateObligation<'tcx>>>,
 }
 
 impl<'a, 'gcx, 'tcx> Deref for Coerce<'a, 'gcx, 'tcx> {
@@ -95,7 +93,7 @@ impl<'a, 'gcx, 'tcx> Deref for Coerce<'a, 'gcx, 'tcx> {
     }
 }
 
-type CoerceResult<'tcx> = RelateResult<'tcx, (Ty<'tcx>, Adjust<'tcx>)>;
+type CoerceResult<'tcx> = InferResult<'tcx, Adjustment<'tcx>>;
 
 fn coerce_mutbls<'tcx>(from_mutbl: hir::Mutability,
                        to_mutbl: hir::Mutability)
@@ -108,44 +106,53 @@ fn coerce_mutbls<'tcx>(from_mutbl: hir::Mutability,
     }
 }
 
+fn identity<'tcx>() -> Adjust<'tcx> {
+    Adjust::DerefRef {
+        autoderefs: 0,
+        autoref: None,
+        unsize: false,
+    }
+}
+
+fn success<'tcx>(kind: Adjust<'tcx>,
+                 target: Ty<'tcx>,
+                 obligations: traits::PredicateObligations<'tcx>)
+                 -> CoerceResult<'tcx> {
+    Ok(InferOk {
+        value: Adjustment {
+            kind,
+            target
+        },
+        obligations
+    })
+}
+
 impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> {
     fn new(fcx: &'f FnCtxt<'f, 'gcx, 'tcx>, cause: ObligationCause<'tcx>) -> Self {
         Coerce {
             fcx: fcx,
             cause: cause,
             use_lub: false,
-            unsizing_obligations: RefCell::new(vec![]),
         }
     }
 
-    fn unify(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> {
+    fn unify(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> InferResult<'tcx, Ty<'tcx>> {
         self.commit_if_ok(|_| {
             let trace = TypeTrace::types(&self.cause, false, a, b);
             if self.use_lub {
                 self.lub(false, trace, &a, &b)
-                    .map(|ok| self.register_infer_ok_obligations(ok))
             } else {
                 self.sub(false, trace, &a, &b)
-                    .map(|InferOk { value, obligations }| {
-                        self.fcx.register_predicates(obligations);
-                        value
-                    })
             }
         })
     }
 
-    /// Unify two types (using sub or lub) and produce a noop coercion.
-    fn unify_and_identity(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceResult<'tcx> {
-        self.unify(&a, &b).and_then(|ty| self.identity(ty))
-    }
-
-    /// Synthesize an identity adjustment.
-    fn identity(&self, ty: Ty<'tcx>) -> CoerceResult<'tcx> {
-        Ok((ty, Adjust::DerefRef {
-            autoderefs: 0,
-            autoref: None,
-            unsize: false,
-        }))
+    /// Unify two types (using sub or lub) and produce a specific coercion.
+    fn unify_and(&self, a: Ty<'tcx>, b: Ty<'tcx>, kind: Adjust<'tcx>)
+                 -> CoerceResult<'tcx> {
+        self.unify(&a, &b).and_then(|InferOk { value: ty, obligations }| {
+            success(kind, ty, obligations)
+        })
     }
 
     fn coerce<'a, E, I>(&self, exprs: &E, a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceResult<'tcx>
@@ -158,11 +165,11 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> {
 
         // Just ignore error types.
         if a.references_error() || b.references_error() {
-            return self.identity(b);
+            return success(identity(), b, vec![]);
         }
 
         if a.is_never() {
-            return Ok((b, Adjust::NeverToAny));
+            return success(Adjust::NeverToAny, b, vec![]);
         }
 
         // Consider coercing the subtype to a DST
@@ -208,7 +215,7 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> {
             }
             _ => {
                 // Otherwise, just use unification rules.
-                self.unify_and_identity(a, b)
+                self.unify_and(a, b, identity())
             }
         }
     }
@@ -240,7 +247,7 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> {
                 coerce_mutbls(mt_a.mutbl, mt_b.mutbl)?;
                 (r_a, mt_a)
             }
-            _ => return self.unify_and_identity(a, b),
+            _ => return self.unify_and(a, b, identity()),
         };
 
         let span = self.cause.span;
@@ -248,7 +255,7 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> {
         let mut first_error = None;
         let mut r_borrow_var = None;
         let mut autoderef = self.autoderef(span, a);
-        let mut success = None;
+        let mut found = None;
 
         for (referent_ty, autoderefs) in autoderef.by_ref() {
             if autoderefs == 0 {
@@ -346,8 +353,8 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> {
                                                   mutbl: mt_b.mutbl, // [1] above
                                               });
             match self.unify(derefd_ty_a, b) {
-                Ok(ty) => {
-                    success = Some((ty, autoderefs));
+                Ok(ok) => {
+                    found = Some((ok, autoderefs));
                     break;
                 }
                 Err(err) => {
@@ -363,7 +370,7 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> {
         // (e.g., in example above, the failure from relating `Vec<T>`
         // to the target type), since that should be the least
         // confusing.
-        let (ty, autoderefs) = match success {
+        let (InferOk { value: ty, mut obligations }, autoderefs) = match found {
             Some(d) => d,
             None => {
                 let err = first_error.expect("coerce_borrowed_pointer had no error");
@@ -372,12 +379,6 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> {
             }
         };
 
-        // This commits the obligations to the fulfillcx. After this succeeds,
-        // this snapshot can't be rolled back.
-        autoderef.finalize(LvaluePreference::from_mutbl(mt_b.mutbl), exprs());
-
-        // Now apply the autoref. We have to extract the region out of
-        // the final ref type we got.
         if ty == a && mt_a.mutbl == hir::MutImmutable && autoderefs == 1 {
             // As a special case, if we would produce `&'a *x`, that's
             // a total no-op. We end up with the type `&'a T` just as
@@ -391,8 +392,11 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> {
             // `self.x`, but we auto-coerce it to `foo(&mut *self.x)`,
             // which is a borrow.
             assert_eq!(mt_b.mutbl, hir::MutImmutable); // can only coerce &T -> &U
-            return self.identity(ty);
+            return success(identity(), ty, obligations);
         }
+
+        // Now apply the autoref. We have to extract the region out of
+        // the final ref type we got.
         let r_borrow = match ty.sty {
             ty::TyRef(r_borrow, _) => r_borrow,
             _ => span_bug!(span, "expected a ref type, got {:?}", ty),
@@ -402,11 +406,15 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> {
                ty,
                autoderefs,
                autoref);
-        Ok((ty, Adjust::DerefRef {
+
+        let pref = LvaluePreference::from_mutbl(mt_b.mutbl);
+        obligations.extend(autoderef.finalize_as_infer_ok(pref, exprs()).obligations);
+
+        success(Adjust::DerefRef {
             autoderefs: autoderefs,
             autoref: autoref,
             unsize: false,
-        }))
+        }, ty, obligations)
     }
 
 
@@ -451,7 +459,7 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> {
 
         // Use a FIFO queue for this custom fulfillment procedure.
         let mut queue = VecDeque::new();
-        let mut leftover_predicates = vec![];
+        let mut obligations = vec![];
 
         // Create an obligation for `Source: CoerceUnsized<Target>`.
         let cause = ObligationCause::misc(self.cause.span, self.body_id);
@@ -467,7 +475,7 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> {
             let trait_ref = match obligation.predicate {
                 ty::Predicate::Trait(ref tr) if traits.contains(&tr.def_id()) => tr.clone(),
                 _ => {
-                    leftover_predicates.push(obligation);
+                    obligations.push(obligation);
                     continue;
                 }
             };
@@ -495,33 +503,30 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> {
             }
         }
 
-        *self.unsizing_obligations.borrow_mut() = leftover_predicates;
-
-        let adjustment = Adjust::DerefRef {
+        success(Adjust::DerefRef {
             autoderefs: if reborrow.is_some() { 1 } else { 0 },
             autoref: reborrow,
             unsize: true,
-        };
-        debug!("Success, coerced with {:?}", adjustment);
-        Ok((target, adjustment))
+        }, target, obligations)
     }
 
     fn coerce_from_safe_fn(&self,
                            a: Ty<'tcx>,
                            fn_ty_a: ty::PolyFnSig<'tcx>,
-                           b: Ty<'tcx>)
+                           b: Ty<'tcx>,
+                           to_unsafe: Adjust<'tcx>,
+                           normal: Adjust<'tcx>)
                            -> CoerceResult<'tcx> {
         if let ty::TyFnPtr(fn_ty_b) = b.sty {
             match (fn_ty_a.unsafety(), fn_ty_b.unsafety()) {
                 (hir::Unsafety::Normal, hir::Unsafety::Unsafe) => {
                     let unsafe_a = self.tcx.safe_to_unsafe_fn_ty(fn_ty_a);
-                    return self.unify_and_identity(unsafe_a, b)
-                        .map(|(ty, _)| (ty, Adjust::UnsafeFnPointer));
+                    return self.unify_and(unsafe_a, b, to_unsafe);
                 }
                 _ => {}
             }
         }
-        self.unify_and_identity(a, b)
+        self.unify_and(a, b, normal)
     }
 
     fn coerce_from_fn_pointer(&self,
@@ -536,7 +541,8 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> {
         let b = self.shallow_resolve(b);
         debug!("coerce_from_fn_pointer(a={:?}, b={:?})", a, b);
 
-        self.coerce_from_safe_fn(a, fn_ty_a, b)
+        self.coerce_from_safe_fn(a, fn_ty_a, b,
+            Adjust::UnsafeFnPointer, identity())
     }
 
     fn coerce_from_fn_item(&self,
@@ -554,10 +560,10 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> {
         match b.sty {
             ty::TyFnPtr(_) => {
                 let a_fn_pointer = self.tcx.mk_fn_ptr(fn_ty_a);
-                self.coerce_from_safe_fn(a_fn_pointer, fn_ty_a, b)
-                    .map(|(ty, _)| (ty, Adjust::ReifyFnPointer))
+                self.coerce_from_safe_fn(a_fn_pointer, fn_ty_a, b,
+                    Adjust::ReifyFnPointer, Adjust::ReifyFnPointer)
             }
-            _ => self.unify_and_identity(a, b),
+            _ => self.unify_and(a, b, identity()),
         }
     }
 
@@ -582,7 +588,7 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> {
                                                    self.cause.span,
                                                    feature_gate::GateIssue::Language,
                                                    feature_gate::CLOSURE_TO_FN_COERCION);
-                    return self.unify_and_identity(a, b);
+                    return self.unify_and(a, b, identity());
                 }
                 // We coerce the closure, which has fn type
                 //     `extern "rust-call" fn((arg0,arg1,...)) -> _`
@@ -607,10 +613,9 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> {
                 let pointer_ty = self.tcx.mk_fn_ptr(converted_sig);
                 debug!("coerce_closure_to_fn(a={:?}, b={:?}, pty={:?})",
                        a, b, pointer_ty);
-                self.unify_and_identity(pointer_ty, b)
-                    .map(|(ty, _)| (ty, Adjust::ClosureFnPointer))
+                self.unify_and(pointer_ty, b, Adjust::ClosureFnPointer)
             }
-            _ => self.unify_and_identity(a, b),
+            _ => self.unify_and(a, b, identity()),
         }
     }
 
@@ -625,7 +630,7 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> {
             ty::TyRef(_, mt) => (true, mt),
             ty::TyRawPtr(mt) => (false, mt),
             _ => {
-                return self.unify_and_identity(a, b);
+                return self.unify_and(a, b, identity());
             }
         };
 
@@ -634,50 +639,22 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> {
             mutbl: mutbl_b,
             ty: mt_a.ty,
         });
-        let (ty, noop) = self.unify_and_identity(a_unsafe, b)?;
         coerce_mutbls(mt_a.mutbl, mutbl_b)?;
-
         // Although references and unsafe ptrs have the same
         // representation, we still register an Adjust::DerefRef so that
         // regionck knows that the region for `a` must be valid here.
-        Ok((ty,
-            if is_ref {
-                Adjust::DerefRef {
-                    autoderefs: 1,
-                    autoref: Some(AutoBorrow::RawPtr(mutbl_b)),
-                    unsize: false,
-                }
-            } else if mt_a.mutbl != mutbl_b {
-                Adjust::MutToConstPointer
-            } else {
-                noop
-            }))
-    }
-}
-
-fn apply<'a, 'b, 'gcx, 'tcx, E, I>(coerce: &mut Coerce<'a, 'gcx, 'tcx>,
-                                   exprs: &E,
-                                   a: Ty<'tcx>,
-                                   b: Ty<'tcx>)
-                                   -> RelateResult<'tcx, Adjustment<'tcx>>
-    where E: Fn() -> I,
-          I: IntoIterator<Item = &'b hir::Expr>
-{
-
-    let (ty, adjust) = indent(|| coerce.coerce(exprs, a, b))?;
-
-    let fcx = coerce.fcx;
-    if let Adjust::DerefRef { unsize: true, .. } = adjust {
-        let mut obligations = coerce.unsizing_obligations.borrow_mut();
-        for obligation in obligations.drain(..) {
-            fcx.register_predicate(obligation);
-        }
+        self.unify_and(a_unsafe, b, if is_ref {
+            Adjust::DerefRef {
+                autoderefs: 1,
+                autoref: Some(AutoBorrow::RawPtr(mutbl_b)),
+                unsize: false,
+            }
+        } else if mt_a.mutbl != mutbl_b {
+            Adjust::MutToConstPointer
+        } else {
+            identity()
+        })
     }
-
-    Ok(Adjustment {
-        kind: adjust,
-        target: ty
-    })
 }
 
 impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
@@ -694,9 +671,10 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
         debug!("coercion::try({:?}: {:?} -> {:?})", expr, source, target);
 
         let cause = self.cause(expr.span, ObligationCauseCode::ExprAssignable);
-        let mut coerce = Coerce::new(self, cause);
+        let coerce = Coerce::new(self, cause);
         self.commit_if_ok(|_| {
-            let adjustment = apply(&mut coerce, &|| Some(expr), source, target)?;
+            let ok = coerce.coerce(&|| Some(expr), source, target)?;
+            let adjustment = self.register_infer_ok_obligations(ok);
             if !adjustment.is_identity() {
                 debug!("Success, coerced with {:?}", adjustment);
                 match self.tables.borrow().adjustments.get(&expr.id) {
@@ -773,9 +751,10 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
         // but only if the new expression has no coercion already applied to it.
         let mut first_error = None;
         if !self.tables.borrow().adjustments.contains_key(&new.id) {
-            let result = self.commit_if_ok(|_| apply(&mut coerce, &|| Some(new), new_ty, prev_ty));
+            let result = self.commit_if_ok(|_| coerce.coerce(&|| Some(new), new_ty, prev_ty));
             match result {
-                Ok(adjustment) => {
+                Ok(ok) => {
+                    let adjustment = self.register_infer_ok_obligations(ok);
                     if !adjustment.is_identity() {
                         self.write_adjustment(new.id, adjustment);
                     }
@@ -816,7 +795,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
             }
         }
 
-        match self.commit_if_ok(|_| apply(&mut coerce, &exprs, prev_ty, new_ty)) {
+        match self.commit_if_ok(|_| coerce.coerce(&exprs, prev_ty, new_ty)) {
             Err(_) => {
                 // Avoid giving strange errors on failed attempts.
                 if let Some(e) = first_error {
@@ -828,7 +807,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
                     })
                 }
             }
-            Ok(adjustment) => {
+            Ok(ok) => {
+                let adjustment = self.register_infer_ok_obligations(ok);
                 if !adjustment.is_identity() {
                     let mut tables = self.tables.borrow_mut();
                     for expr in exprs() {