about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2015-01-30 07:57:14 +0000
committerbors <bors@rust-lang.org>2015-01-30 07:57:14 +0000
commite0f5980ead4f88e78a47f4d84da4dc11472f66ba (patch)
treecac24017a222a61c4f9e468c5689387475798853 /src
parent52c74e63dacd49017b19330e0cbecbac0a3fe62e (diff)
parentae076e1e3b037e557b613d97698a9f28a1683d8b (diff)
Auto merge of #21351 - eddyb:x-coerce--a-new-hope, r=nikomatsakis
Coercions will now attempt to autoderef as needed before reborrowing.
This includes overloaded `Deref`, e.g. `&Rc<T>` coerces to `&T`, and
`DerefMut`, e.g. `&mut Vec<T>` coerces to `&mut [T]` (in addition to `&[T]`).
Closes #21432.
Diffstat (limited to 'src')
-rw-r--r--src/librustc/middle/infer/mod.rs43
-rw-r--r--src/librustc_typeck/check/callee.rs2
-rw-r--r--src/librustc_typeck/check/coercion.rs (renamed from src/librustc/middle/infer/coercion.rs)328
-rw-r--r--src/librustc_typeck/check/demand.rs4
-rw-r--r--src/librustc_typeck/check/method/confirm.rs40
-rw-r--r--src/librustc_typeck/check/method/probe.rs25
-rw-r--r--src/librustc_typeck/check/mod.rs80
-rw-r--r--src/test/compile-fail/coerce-overloaded-autoderef.rs40
-rw-r--r--src/test/compile-fail/method-self-arg-1.rs5
-rw-r--r--src/test/run-pass/coerce-overloaded-autoderef.rs69
10 files changed, 343 insertions, 293 deletions
diff --git a/src/librustc/middle/infer/mod.rs b/src/librustc/middle/infer/mod.rs
index cfcead51f78..1665966a5e5 100644
--- a/src/librustc/middle/infer/mod.rs
+++ b/src/librustc/middle/infer/mod.rs
@@ -33,12 +33,10 @@ use std::rc::Rc;
 use syntax::ast;
 use syntax::codemap;
 use syntax::codemap::Span;
-use util::common::indent;
 use util::nodemap::FnvHashMap;
 use util::ppaux::{ty_to_string};
 use util::ppaux::{Repr, UserString};
 
-use self::coercion::Coerce;
 use self::combine::{Combine, Combineable, CombineFields};
 use self::region_inference::{RegionVarBindings, RegionSnapshot};
 use self::equate::Equate;
@@ -47,7 +45,6 @@ use self::lub::Lub;
 use self::unify::{UnificationTable, InferCtxtMethodsForSimplyUnifiableTypes};
 use self::error_reporting::ErrorReporting;
 
-pub mod coercion;
 pub mod combine;
 pub mod doc;
 pub mod equate;
@@ -68,7 +65,6 @@ pub type Bound<T> = Option<T>;
 pub type cres<'tcx, T> = Result<T,ty::type_err<'tcx>>; // "combine result"
 pub type ures<'tcx> = cres<'tcx, ()>; // "unify result"
 pub type fres<T> = Result<T, fixup_err>; // "fixup result"
-pub type CoerceResult<'tcx> = cres<'tcx, Option<ty::AutoAdjustment<'tcx>>>;
 
 pub struct InferCtxt<'a, 'tcx: 'a> {
     pub tcx: &'a ty::ctxt<'tcx>,
@@ -409,24 +405,6 @@ fn expected_found<T>(a_is_expected: bool,
     }
 }
 
-pub fn mk_coercety<'a, 'tcx>(cx: &InferCtxt<'a, 'tcx>,
-                             a_is_expected: bool,
-                             origin: TypeOrigin,
-                             a: Ty<'tcx>,
-                             b: Ty<'tcx>)
-                             -> CoerceResult<'tcx> {
-    debug!("mk_coercety({} -> {})", a.repr(cx.tcx), b.repr(cx.tcx));
-    indent(|| {
-        cx.commit_if_ok(|| {
-            let trace = TypeTrace {
-                origin: origin,
-                values: Types(expected_found(a_is_expected, a, b))
-            };
-            Coerce(cx.combine_fields(a_is_expected, trace)).tys(a, b)
-        })
-    })
-}
-
 trait then<'tcx> {
     fn then<T, F>(&self, f: F) -> Result<T, ty::type_err<'tcx>> where
         T: Clone,
@@ -689,10 +667,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
     {
         debug!("sub_types({} <: {})", a.repr(self.tcx), b.repr(self.tcx));
         self.commit_if_ok(|| {
-            let trace = TypeTrace {
-                origin: origin,
-                values: Types(expected_found(a_is_expected, a, b))
-            };
+            let trace = TypeTrace::types(origin, a_is_expected, a, b);
             self.sub(a_is_expected, trace).tys(a, b).to_ures()
         })
     }
@@ -705,10 +680,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
                     -> ures<'tcx>
     {
         self.commit_if_ok(|| {
-            let trace = TypeTrace {
-                origin: origin,
-                values: Types(expected_found(a_is_expected, a, b))
-            };
+            let trace = TypeTrace::types(origin, a_is_expected, a, b);
             self.equate(a_is_expected, trace).tys(a, b).to_ures()
         })
     }
@@ -1118,6 +1090,17 @@ impl<'tcx> TypeTrace<'tcx> {
         self.origin.span()
     }
 
+    pub fn types(origin: TypeOrigin,
+                 a_is_expected: bool,
+                 a: Ty<'tcx>,
+                 b: Ty<'tcx>)
+                 -> TypeTrace<'tcx> {
+        TypeTrace {
+            origin: origin,
+            values: Types(expected_found(a_is_expected, a, b))
+        }
+    }
+
     pub fn dummy(tcx: &ty::ctxt<'tcx>) -> TypeTrace<'tcx> {
         TypeTrace {
             origin: Misc(codemap::DUMMY_SP),
diff --git a/src/librustc_typeck/check/callee.rs b/src/librustc_typeck/check/callee.rs
index d851206f384..e37c1ab7f0c 100644
--- a/src/librustc_typeck/check/callee.rs
+++ b/src/librustc_typeck/check/callee.rs
@@ -21,6 +21,7 @@ use super::LvaluePreference;
 use super::method;
 use super::structurally_resolved_type;
 use super::TupleArgumentsFlag;
+use super::UnresolvedTypeAction;
 use super::write_call;
 
 use middle::infer;
@@ -77,6 +78,7 @@ pub fn check_call<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
                   callee_expr.span,
                   original_callee_ty,
                   Some(callee_expr),
+                  UnresolvedTypeAction::Error,
                   LvaluePreference::NoPreference,
                   |adj_ty, idx| {
                       let autoderefref = ty::AutoDerefRef { autoderefs: idx, autoref: None };
diff --git a/src/librustc/middle/infer/coercion.rs b/src/librustc_typeck/check/coercion.rs
index e8b8ecc701f..8bac89ac184 100644
--- a/src/librustc/middle/infer/coercion.rs
+++ b/src/librustc_typeck/check/coercion.rs
@@ -60,34 +60,49 @@
 //! sort of a minor point so I've opted to leave it for later---after all
 //! we may want to adjust precisely when coercions occur.
 
-use super::{CoerceResult, Coercion};
-use super::combine::{CombineFields, Combine};
-use super::sub::Sub;
+use check::{autoderef, FnCtxt, NoPreference, PreferMutLvalue, UnresolvedTypeAction};
 
+use middle::infer::{self, cres, Coercion, TypeTrace};
+use middle::infer::combine::Combine;
+use middle::infer::sub::Sub;
 use middle::subst;
 use middle::ty::{AutoPtr, AutoDerefRef, AdjustDerefRef, AutoUnsize, AutoUnsafe};
-use middle::ty::{mt};
-use middle::ty::{self, Ty};
+use middle::ty::{self, mt, Ty};
+use util::common::indent;
 use util::ppaux;
 use util::ppaux::Repr;
 
 use syntax::ast;
 
-// Note: Coerce is not actually a combiner, in that it does not
-// conform to the same interface, though it performs a similar
-// function.
-pub struct Coerce<'f, 'tcx: 'f>(pub CombineFields<'f, 'tcx>);
+struct Coerce<'a, 'tcx: 'a> {
+    fcx: &'a FnCtxt<'a, 'tcx>,
+    trace: TypeTrace<'tcx>
+}
+
+type CoerceResult<'tcx> = cres<'tcx, Option<ty::AutoAdjustment<'tcx>>>;
 
 impl<'f, 'tcx> Coerce<'f, 'tcx> {
-    pub fn get_ref<'a>(&'a self) -> &'a CombineFields<'f, 'tcx> {
-        let Coerce(ref v) = *self; v
+    fn tcx(&self) -> &ty::ctxt<'tcx> {
+        self.fcx.tcx()
     }
 
-    fn tcx(&self) -> &ty::ctxt<'tcx> {
-        self.get_ref().infcx.tcx
+    fn subtype(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceResult<'tcx> {
+        let sub = Sub(self.fcx.infcx().combine_fields(false, self.trace.clone()));
+        try!(sub.tys(a, b));
+        Ok(None) // No coercion required.
+    }
+
+    fn unpack_actual_value<T, F>(&self, a: Ty<'tcx>, f: F) -> T where
+        F: FnOnce(Ty<'tcx>) -> T,
+    {
+        f(self.fcx.infcx().shallow_resolve(a))
     }
 
-    pub fn tys(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceResult<'tcx> {
+    fn coerce(&self,
+              expr_a: &ast::Expr,
+              a: Ty<'tcx>,
+              b: Ty<'tcx>)
+              -> CoerceResult<'tcx> {
         debug!("Coerce.tys({} => {})",
                a.repr(self.tcx()),
                b.repr(self.tcx()));
@@ -106,57 +121,15 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
         // See above for details.
         match b.sty {
             ty::ty_ptr(mt_b) => {
-                match mt_b.ty.sty {
-                    ty::ty_str => {
-                        return self.unpack_actual_value(a, |a| {
-                            self.coerce_unsafe_ptr(a, b, ast::MutImmutable)
-                        });
-                    }
-
-                    ty::ty_trait(..) => {
-                        let result = self.unpack_actual_value(a, |a| {
-                            self.coerce_unsafe_object(a, b, mt_b.mutbl)
-                        });
-
-                        match result {
-                            Ok(t) => return Ok(t),
-                            Err(..) => {}
-                        }
-                    }
-
-                    _ => {
-                        return self.unpack_actual_value(a, |a| {
-                            self.coerce_unsafe_ptr(a, b, mt_b.mutbl)
-                        });
-                    }
-                };
+                return self.unpack_actual_value(a, |a| {
+                    self.coerce_unsafe_ptr(a, b, mt_b.mutbl)
+                });
             }
 
             ty::ty_rptr(_, mt_b) => {
-                match mt_b.ty.sty {
-                    ty::ty_str => {
-                        return self.unpack_actual_value(a, |a| {
-                            self.coerce_borrowed_pointer(a, b, ast::MutImmutable)
-                        });
-                    }
-
-                    ty::ty_trait(..) => {
-                        let result = self.unpack_actual_value(a, |a| {
-                            self.coerce_borrowed_object(a, b, mt_b.mutbl)
-                        });
-
-                        match result {
-                            Ok(t) => return Ok(t),
-                            Err(..) => {}
-                        }
-                    }
-
-                    _ => {
-                        return self.unpack_actual_value(a, |a| {
-                            self.coerce_borrowed_pointer(a, b, mt_b.mutbl)
-                        });
-                    }
-                };
+                return self.unpack_actual_value(a, |a| {
+                    self.coerce_borrowed_pointer(expr_a, a, b, mt_b.mutbl)
+                });
             }
 
             _ => {}
@@ -178,25 +151,15 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
         })
     }
 
-    pub fn subtype(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceResult<'tcx> {
-        match Sub(self.get_ref().clone()).tys(a, b) {
-            Ok(_) => Ok(None),         // No coercion required.
-            Err(ref e) => Err(*e)
-        }
-    }
-
-    pub fn unpack_actual_value<T, F>(&self, a: Ty<'tcx>, f: F) -> T where
-        F: FnOnce(Ty<'tcx>) -> T,
-    {
-        f(self.get_ref().infcx.shallow_resolve(a))
-    }
-
-    // ~T -> &T or &mut T -> &T (including where T = [U] or str)
-    pub fn coerce_borrowed_pointer(&self,
-                                   a: Ty<'tcx>,
-                                   b: Ty<'tcx>,
-                                   mutbl_b: ast::Mutability)
-                                   -> CoerceResult<'tcx> {
+    /// Reborrows `&mut A` to `&mut B` and `&(mut) A` to `&B`.
+    /// To match `A` with `B`, autoderef will be performed,
+    /// calling `deref`/`deref_mut` where necessary.
+    fn coerce_borrowed_pointer(&self,
+                               expr_a: &ast::Expr,
+                               a: Ty<'tcx>,
+                               b: Ty<'tcx>,
+                               mutbl_b: ast::Mutability)
+                               -> CoerceResult<'tcx> {
         debug!("coerce_borrowed_pointer(a={}, b={})",
                a.repr(self.tcx()),
                b.repr(self.tcx()));
@@ -207,32 +170,62 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
         // to type check, we will construct the type that `&M*expr` would
         // yield.
 
-        let sub = Sub(self.get_ref().clone());
-        let coercion = Coercion(self.get_ref().trace.clone());
-        let r_borrow = self.get_ref().infcx.next_region_var(coercion);
-
-        let inner_ty = match a.sty {
-            ty::ty_uniq(_) => return Err(ty::terr_mismatch),
+        match a.sty {
             ty::ty_rptr(_, mt_a) => {
                 if !can_coerce_mutbls(mt_a.mutbl, mutbl_b) {
                     return Err(ty::terr_mutability);
                 }
-                mt_a.ty
-            }
-            _ => {
-                return self.subtype(a, b);
             }
-        };
+            _ => return self.subtype(a, b)
+        }
 
-        let a_borrowed = ty::mk_rptr(self.tcx(),
-                                     self.tcx().mk_region(r_borrow),
-                                     mt {ty: inner_ty, mutbl: mutbl_b});
-        try!(sub.tys(a_borrowed, b));
+        let coercion = Coercion(self.trace.clone());
+        let r_borrow = self.fcx.infcx().next_region_var(coercion);
+        let autoref = Some(AutoPtr(r_borrow, mutbl_b, None));
 
-        Ok(Some(AdjustDerefRef(AutoDerefRef {
-            autoderefs: 1,
-            autoref: Some(AutoPtr(r_borrow, mutbl_b, None))
-        })))
+        let r_borrow = self.tcx().mk_region(r_borrow);
+        let lvalue_pref = match mutbl_b {
+            ast::MutMutable => PreferMutLvalue,
+            ast::MutImmutable => NoPreference
+        };
+        let mut first_error = None;
+        let (_, autoderefs, success) = autoderef(self.fcx,
+                                                 expr_a.span,
+                                                 a,
+                                                 Some(expr_a),
+                                                 UnresolvedTypeAction::Ignore,
+                                                 lvalue_pref,
+                                                 |inner_ty, autoderef| {
+            if autoderef == 0 {
+                // Don't let this pass, otherwise it would cause
+                // &T to autoref to &&T.
+                return None;
+            }
+            let ty = ty::mk_rptr(self.tcx(), r_borrow,
+                                 mt {ty: inner_ty, mutbl: mutbl_b});
+            if let Err(err) = self.fcx.infcx().try(|_| self.subtype(ty, b)) {
+                if first_error.is_none() {
+                    first_error = Some(err);
+                }
+                None
+            } else {
+                Some(())
+            }
+        });
+
+        match success {
+            Some(_) => {
+                Ok(Some(AdjustDerefRef(AutoDerefRef {
+                    autoderefs: autoderefs,
+                    autoref: autoref
+                })))
+            }
+            None => {
+                // Return original error as if overloaded deref was never
+                // attempted, to avoid irrelevant/confusing error messages.
+                Err(first_error.expect("coerce_borrowed_pointer failed with no error?"))
+            }
+        }
     }
 
 
@@ -252,8 +245,6 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
         // we can't unify [T] with U. But to properly support DST, we need to allow
         // that, at which point we will need extra checks on b here.
 
-        let sub = Sub(self.get_ref().clone());
-
         match (&a.sty, &b.sty) {
             (&ty::ty_rptr(_, ty::mt{ty: t_a, mutbl: mutbl_a}), &ty::ty_rptr(_, mt_b)) => {
                 self.unpack_actual_value(t_a, |a| {
@@ -263,12 +254,12 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
                                 return Err(ty::terr_mutability);
                             }
 
-                            let coercion = Coercion(self.get_ref().trace.clone());
-                            let r_borrow = self.get_ref().infcx.next_region_var(coercion);
+                            let coercion = Coercion(self.trace.clone());
+                            let r_borrow = self.fcx.infcx().next_region_var(coercion);
                             let ty = ty::mk_rptr(self.tcx(),
                                                  self.tcx().mk_region(r_borrow),
                                                  ty::mt{ty: ty, mutbl: mt_b.mutbl});
-                            try!(self.get_ref().infcx.try(|_| sub.tys(ty, b)));
+                            try!(self.fcx.infcx().try(|_| self.subtype(ty, b)));
                             debug!("Success, coerced with AutoDerefRef(1, \
                                     AutoPtr(AutoUnsize({:?})))", kind);
                             Ok(Some(AdjustDerefRef(AutoDerefRef {
@@ -291,7 +282,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
 
                             let ty = ty::mk_ptr(self.tcx(),
                                                  ty::mt{ty: ty, mutbl: mt_b.mutbl});
-                            try!(self.get_ref().infcx.try(|_| sub.tys(ty, b)));
+                            try!(self.fcx.infcx().try(|_| self.subtype(ty, b)));
                             debug!("Success, coerced with AutoDerefRef(1, \
                                     AutoPtr(AutoUnsize({:?})))", kind);
                             Ok(Some(AdjustDerefRef(AutoDerefRef {
@@ -309,7 +300,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
                     match self.unsize_ty(t_a, a, t_b) {
                         Some((ty, kind)) => {
                             let ty = ty::mk_uniq(self.tcx(), ty);
-                            try!(self.get_ref().infcx.try(|_| sub.tys(ty, b)));
+                            try!(self.fcx.infcx().try(|_| self.subtype(ty, b)));
                             debug!("Success, coerced with AutoDerefRef(1, \
                                     AutoUnsizeUniq({:?}))", kind);
                             Ok(Some(AdjustDerefRef(AutoDerefRef {
@@ -361,12 +352,10 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
                     let ty_substs_b = substs_b.types.get_slice(subst::TypeSpace);
                     assert!(ty_substs_a.len() == ty_substs_b.len());
 
-                    let sub = Sub(self.get_ref().clone());
-
                     let mut result = None;
                     let mut tps = ty_substs_a.iter().zip(ty_substs_b.iter()).enumerate();
                     for (i, (tp_a, tp_b)) in tps {
-                        if self.get_ref().infcx.try(|_| sub.tys(*tp_a, *tp_b)).is_ok() {
+                        if self.fcx.infcx().try(|_| self.subtype(*tp_a, *tp_b)).is_ok() {
                             continue;
                         }
                         match
@@ -379,7 +368,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
                                 let mut new_substs = substs_a.clone();
                                 new_substs.types.get_mut_slice(subst::TypeSpace)[i] = new_tp;
                                 let ty = ty::mk_struct(tcx, did_a, tcx.mk_substs(new_substs));
-                                if self.get_ref().infcx.try(|_| sub.tys(ty, ty_b)).is_err() {
+                                if self.fcx.infcx().try(|_| self.subtype(ty, ty_b)).is_err() {
                                     debug!("Unsized type parameter '{}', but still \
                                             could not match types {} and {}",
                                            ppaux::ty_to_string(tcx, *tp_a),
@@ -404,92 +393,6 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
         )
     }
 
-    fn coerce_borrowed_object(&self,
-                              a: Ty<'tcx>,
-                              b: Ty<'tcx>,
-                              b_mutbl: ast::Mutability) -> CoerceResult<'tcx>
-    {
-        let tcx = self.tcx();
-
-        debug!("coerce_borrowed_object(a={}, b={}, b_mutbl={:?})",
-               a.repr(tcx),
-               b.repr(tcx), b_mutbl);
-
-        let coercion = Coercion(self.get_ref().trace.clone());
-        let r_a = self.get_ref().infcx.next_region_var(coercion);
-
-        self.coerce_object(a, b, b_mutbl,
-                           |tr| ty::mk_rptr(tcx, tcx.mk_region(r_a),
-                                            ty::mt{ mutbl: b_mutbl, ty: tr }),
-                           || AutoPtr(r_a, b_mutbl, None))
-    }
-
-    fn coerce_unsafe_object(&self,
-                            a: Ty<'tcx>,
-                            b: Ty<'tcx>,
-                            b_mutbl: ast::Mutability) -> CoerceResult<'tcx>
-    {
-        let tcx = self.tcx();
-
-        debug!("coerce_unsafe_object(a={}, b={}, b_mutbl={:?})",
-               a.repr(tcx),
-               b.repr(tcx), b_mutbl);
-
-        self.coerce_object(a, b, b_mutbl,
-                           |tr| ty::mk_ptr(tcx, ty::mt{ mutbl: b_mutbl, ty: tr }),
-                           || AutoUnsafe(b_mutbl, None))
-    }
-
-    fn coerce_object<F, G>(&self,
-                           a: Ty<'tcx>,
-                           b: Ty<'tcx>,
-                           b_mutbl: ast::Mutability,
-                           mk_ty: F,
-                           mk_adjust: G) -> CoerceResult<'tcx> where
-        F: FnOnce(Ty<'tcx>) -> Ty<'tcx>,
-        G: FnOnce() -> ty::AutoRef<'tcx>,
-    {
-        let tcx = self.tcx();
-
-        match a.sty {
-            ty::ty_rptr(_, ty::mt{ty, mutbl}) => match ty.sty {
-                ty::ty_trait(box ty::TyTrait { ref principal, ref bounds }) => {
-                    debug!("mutbl={:?} b_mutbl={:?}", mutbl, b_mutbl);
-                    let tr = ty::mk_trait(tcx, principal.clone(), bounds.clone());
-                    try!(self.subtype(mk_ty(tr), b));
-                    Ok(Some(AdjustDerefRef(AutoDerefRef {
-                        autoderefs: 1,
-                        autoref: Some(mk_adjust())
-                    })))
-                }
-                _ => {
-                    self.subtype(a, b)
-                }
-            },
-            _ => {
-                self.subtype(a, b)
-            }
-        }
-    }
-
-    pub fn coerce_borrowed_fn(&self,
-                              a: Ty<'tcx>,
-                              b: Ty<'tcx>)
-                              -> CoerceResult<'tcx> {
-        debug!("coerce_borrowed_fn(a={}, b={})",
-               a.repr(self.tcx()),
-               b.repr(self.tcx()));
-
-        match a.sty {
-            ty::ty_bare_fn(Some(a_def_id), f) => {
-                self.coerce_from_fn_item(a, a_def_id, f, b)
-            }
-            _ => {
-                self.subtype(a, b)
-            }
-        }
-    }
-
     fn coerce_from_fn_item(&self,
                            a: Ty<'tcx>,
                            fn_def_id_a: ast::DefId,
@@ -518,11 +421,11 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
         })
     }
 
-    pub fn coerce_unsafe_ptr(&self,
-                             a: Ty<'tcx>,
-                             b: Ty<'tcx>,
-                             mutbl_b: ast::Mutability)
-                             -> CoerceResult<'tcx> {
+    fn coerce_unsafe_ptr(&self,
+                         a: Ty<'tcx>,
+                         b: Ty<'tcx>,
+                         mutbl_b: ast::Mutability)
+                         -> CoerceResult<'tcx> {
         debug!("coerce_unsafe_ptr(a={}, b={})",
                a.repr(self.tcx()),
                b.repr(self.tcx()));
@@ -551,6 +454,27 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
     }
 }
 
+pub fn mk_assignty<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
+                             expr: &ast::Expr,
+                             a: Ty<'tcx>,
+                             b: Ty<'tcx>)
+                             -> cres<'tcx, ()> {
+    debug!("mk_assignty({} -> {})", a.repr(fcx.tcx()), b.repr(fcx.tcx()));
+    let adjustment = try!(indent(|| {
+        fcx.infcx().commit_if_ok(|| {
+            let origin = infer::ExprAssignable(expr.span);
+            Coerce {
+                fcx: fcx,
+                trace: infer::TypeTrace::types(origin, false, a, b)
+            }.coerce(expr, a, b)
+        })
+    }));
+    if let Some(adjustment) = adjustment {
+        fcx.write_adjustment(expr.id, expr.span, adjustment);
+    }
+    Ok(())
+}
+
 fn can_coerce_mutbls(from_mutbl: ast::Mutability,
                      to_mutbl: ast::Mutability)
                      -> bool {
diff --git a/src/librustc_typeck/check/demand.rs b/src/librustc_typeck/check/demand.rs
index 8188835718c..6f2d0cb3667 100644
--- a/src/librustc_typeck/check/demand.rs
+++ b/src/librustc_typeck/check/demand.rs
@@ -9,7 +9,7 @@
 // except according to those terms.
 
 
-use check::FnCtxt;
+use check::{coercion, FnCtxt};
 use middle::ty::{self, Ty};
 use middle::infer;
 
@@ -62,7 +62,7 @@ pub fn coerce<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, sp: Span,
            expr_ty.repr(fcx.ccx.tcx));
     let expr_ty = fcx.resolve_type_vars_if_possible(expr_ty);
     let expected = fcx.resolve_type_vars_if_possible(expected);
-    match fcx.mk_assignty(expr, expr_ty, expected) {
+    match coercion::mk_assignty(fcx, expr, expr_ty, expected) {
       Ok(()) => { /* ok */ }
       Err(ref err) => {
         fcx.report_mismatched_types(sp, expected, expr_ty, err);
diff --git a/src/librustc_typeck/check/method/confirm.rs b/src/librustc_typeck/check/method/confirm.rs
index 67b055ac946..56a32186c9e 100644
--- a/src/librustc_typeck/check/method/confirm.rs
+++ b/src/librustc_typeck/check/method/confirm.rs
@@ -11,6 +11,7 @@
 use super::probe;
 
 use check::{self, FnCtxt, NoPreference, PreferMutLvalue, callee, demand};
+use check::UnresolvedTypeAction;
 use middle::mem_categorization::Typer;
 use middle::subst::{self};
 use middle::traits;
@@ -141,10 +142,19 @@ impl<'a,'tcx> ConfirmContext<'a,'tcx> {
 
         // Commit the autoderefs by calling `autoderef again, but this
         // time writing the results into the various tables.
-        let (autoderefd_ty, n, result) =
-            check::autoderef(
-                self.fcx, self.span, unadjusted_self_ty, Some(self.self_expr), NoPreference,
-                |_, n| if n == auto_deref_ref.autoderefs { Some(()) } else { None });
+        let (autoderefd_ty, n, result) = check::autoderef(self.fcx,
+                                                          self.span,
+                                                          unadjusted_self_ty,
+                                                          Some(self.self_expr),
+                                                          UnresolvedTypeAction::Error,
+                                                          NoPreference,
+                                                          |_, n| {
+            if n == auto_deref_ref.autoderefs {
+                Some(())
+            } else {
+                None
+            }
+        });
         assert_eq!(n, auto_deref_ref.autoderefs);
         assert_eq!(result, Some(()));
 
@@ -302,15 +312,18 @@ impl<'a,'tcx> ConfirmContext<'a,'tcx> {
         // yield an object-type (e.g., `&Object` or `Box<Object>`
         // etc).
 
-        let (_, _, result) =
-            check::autoderef(
-                self.fcx, self.span, self_ty, None, NoPreference,
-                |ty, _| {
-                    match ty.sty {
-                        ty::ty_trait(ref data) => Some(closure(self, ty, &**data)),
-                        _ => None,
-                    }
-                });
+        let (_, _, result) = check::autoderef(self.fcx,
+                                              self.span,
+                                              self_ty,
+                                              None,
+                                              UnresolvedTypeAction::Error,
+                                              NoPreference,
+                                              |ty, _| {
+            match ty.sty {
+                ty::ty_trait(ref data) => Some(closure(self, ty, &**data)),
+                _ => None,
+            }
+        });
 
         match result {
             Some(r) => r,
@@ -517,6 +530,7 @@ impl<'a,'tcx> ConfirmContext<'a,'tcx> {
                                  expr.span,
                                  self.fcx.expr_ty(expr),
                                  Some(expr),
+                                 UnresolvedTypeAction::Error,
                                  PreferMutLvalue,
                                  |_, autoderefs| {
                                      if autoderefs == autoderef_count + 1 {
diff --git a/src/librustc_typeck/check/method/probe.rs b/src/librustc_typeck/check/method/probe.rs
index 4980630a035..2e366f44507 100644
--- a/src/librustc_typeck/check/method/probe.rs
+++ b/src/librustc_typeck/check/method/probe.rs
@@ -14,7 +14,7 @@ use super::{CandidateSource,ImplSource,TraitSource};
 use super::suggest;
 
 use check;
-use check::{FnCtxt, NoPreference};
+use check::{FnCtxt, NoPreference, UnresolvedTypeAction};
 use middle::fast_reject;
 use middle::subst;
 use middle::subst::Subst;
@@ -169,16 +169,19 @@ fn create_steps<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
                           -> Option<Vec<CandidateStep<'tcx>>> {
     let mut steps = Vec::new();
 
-    let (fully_dereferenced_ty, dereferences, _) =
-        check::autoderef(
-            fcx, span, self_ty, None, NoPreference,
-            |t, d| {
-                let adjustment = AutoDeref(d);
-                steps.push(CandidateStep { self_ty: t, adjustment: adjustment });
-                None::<()> // keep iterating until we can't anymore
-            });
-
-    match fully_dereferenced_ty.sty {
+    let (final_ty, dereferences, _) = check::autoderef(fcx,
+                                                       span,
+                                                       self_ty,
+                                                       None,
+                                                       UnresolvedTypeAction::Error,
+                                                       NoPreference,
+                                                       |t, d| {
+        let adjustment = AutoDeref(d);
+        steps.push(CandidateStep { self_ty: t, adjustment: adjustment });
+        None::<()> // keep iterating until we can't anymore
+    });
+
+    match final_ty.sty {
         ty::ty_vec(elem_ty, Some(len)) => {
             steps.push(CandidateStep {
                 self_ty: ty::mk_vec(fcx.tcx(), elem_ty, None),
diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs
index 6dabec31e2c..1f04cab572a 100644
--- a/src/librustc_typeck/check/mod.rs
+++ b/src/librustc_typeck/check/mod.rs
@@ -132,6 +132,7 @@ pub mod vtable;
 pub mod writeback;
 pub mod regionmanip;
 pub mod regionck;
+pub mod coercion;
 pub mod demand;
 pub mod method;
 mod upvar;
@@ -1725,25 +1726,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         infer::mk_subty(self.infcx(), a_is_expected, origin, sub, sup)
     }
 
-    pub fn mk_assignty(&self,
-                       expr: &ast::Expr,
-                       sub: Ty<'tcx>,
-                       sup: Ty<'tcx>)
-                       -> Result<(), ty::type_err<'tcx>> {
-        match infer::mk_coercety(self.infcx(),
-                                 false,
-                                 infer::ExprAssignable(expr.span),
-                                 sub,
-                                 sup) {
-            Ok(None) => Ok(()),
-            Err(ref e) => Err((*e)),
-            Ok(Some(adjustment)) => {
-                self.write_adjustment(expr.id, expr.span, adjustment);
-                Ok(())
-            }
-        }
-    }
-
     pub fn mk_eqty(&self,
                    a_is_expected: bool,
                    origin: infer::TypeOrigin,
@@ -1883,6 +1865,17 @@ pub enum LvaluePreference {
     NoPreference
 }
 
+/// Whether `autoderef` requires types to resolve.
+#[derive(Copy, Show, PartialEq, Eq)]
+pub enum UnresolvedTypeAction {
+    /// Produce an error and return `ty_err` whenever a type cannot
+    /// be resolved (i.e. it is `ty_infer`).
+    Error,
+    /// Go on without emitting any errors, and return the unresolved
+    /// type. Useful for probing, e.g. in coercions.
+    Ignore
+}
+
 /// Executes an autoderef loop for the type `t`. At each step, invokes `should_stop` to decide
 /// whether to terminate the loop. Returns the final type and number of derefs that it performed.
 ///
@@ -1892,6 +1885,7 @@ pub fn autoderef<'a, 'tcx, T, F>(fcx: &FnCtxt<'a, 'tcx>,
                                  sp: Span,
                                  base_ty: Ty<'tcx>,
                                  opt_expr: Option<&ast::Expr>,
+                                 unresolved_type_action: UnresolvedTypeAction,
                                  mut lvalue_pref: LvaluePreference,
                                  mut should_stop: F)
                                  -> (Ty<'tcx>, uint, Option<T>)
@@ -1904,11 +1898,22 @@ pub fn autoderef<'a, 'tcx, T, F>(fcx: &FnCtxt<'a, 'tcx>,
 
     let mut t = base_ty;
     for autoderefs in 0..fcx.tcx().sess.recursion_limit.get() {
-        let resolved_t = structurally_resolved_type(fcx, sp, t);
-
-        if ty::type_is_error(resolved_t) {
-            return (resolved_t, autoderefs, None);
-        }
+        let resolved_t = match unresolved_type_action {
+            UnresolvedTypeAction::Error => {
+                let resolved_t = structurally_resolved_type(fcx, sp, t);
+                if ty::type_is_error(resolved_t) {
+                    return (resolved_t, autoderefs, None);
+                }
+                resolved_t
+            }
+            UnresolvedTypeAction::Ignore => {
+                // We can continue even when the type cannot be resolved
+                // (i.e. it is an inference variable) because `ty::deref`
+                // and `try_overloaded_deref` both simply return `None`
+                // in such a case without producing spurious errors.
+                fcx.resolve_type_vars_if_possible(t)
+            }
+        };
 
         match should_stop(resolved_t, autoderefs) {
             Some(x) => return (resolved_t, autoderefs, Some(x)),
@@ -2029,8 +2034,13 @@ fn autoderef_for_index<'a, 'tcx, T, F>(fcx: &FnCtxt<'a, 'tcx>,
     // autoderef that normal method probing does. They could likely be
     // consolidated.
 
-    let (ty, autoderefs, final_mt) =
-        autoderef(fcx, base_expr.span, base_ty, Some(base_expr), lvalue_pref, |adj_ty, idx| {
+    let (ty, autoderefs, final_mt) = autoderef(fcx,
+                                               base_expr.span,
+                                               base_ty,
+                                               Some(base_expr),
+                                               UnresolvedTypeAction::Error,
+                                               lvalue_pref,
+                                               |adj_ty, idx| {
             let autoderefref = ty::AutoDerefRef { autoderefs: idx, autoref: None };
             step(adj_ty, autoderefref)
         });
@@ -3071,8 +3081,13 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
         let expr_t = structurally_resolved_type(fcx, expr.span,
                                                 fcx.expr_ty(base));
         // FIXME(eddyb) #12808 Integrate privacy into this auto-deref loop.
-        let (_, autoderefs, field_ty) =
-            autoderef(fcx, expr.span, expr_t, Some(base), lvalue_pref, |base_t, _| {
+        let (_, autoderefs, field_ty) = autoderef(fcx,
+                                                  expr.span,
+                                                  expr_t,
+                                                  Some(base),
+                                                  UnresolvedTypeAction::Error,
+                                                  lvalue_pref,
+                                                  |base_t, _| {
                 match base_t.sty {
                     ty::ty_struct(base_id, substs) => {
                         debug!("struct named {}", ppaux::ty_to_string(tcx, base_t));
@@ -3164,8 +3179,13 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
                                                 fcx.expr_ty(base));
         let mut tuple_like = false;
         // FIXME(eddyb) #12808 Integrate privacy into this auto-deref loop.
-        let (_, autoderefs, field_ty) =
-            autoderef(fcx, expr.span, expr_t, Some(base), lvalue_pref, |base_t, _| {
+        let (_, autoderefs, field_ty) = autoderef(fcx,
+                                                  expr.span,
+                                                  expr_t,
+                                                  Some(base),
+                                                  UnresolvedTypeAction::Error,
+                                                  lvalue_pref,
+                                                  |base_t, _| {
                 match base_t.sty {
                     ty::ty_struct(base_id, substs) => {
                         tuple_like = ty::is_tuple_struct(tcx, base_id);
diff --git a/src/test/compile-fail/coerce-overloaded-autoderef.rs b/src/test/compile-fail/coerce-overloaded-autoderef.rs
new file mode 100644
index 00000000000..14fbc34c43b
--- /dev/null
+++ b/src/test/compile-fail/coerce-overloaded-autoderef.rs
@@ -0,0 +1,40 @@
+// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+fn borrow_mut<T>(x: &mut T) -> &mut T { x }
+fn borrow<T>(x: &T) -> &T { x }
+
+fn borrow_mut2<T>(_: &mut T, _: &mut T) {}
+fn borrow2<T>(_: &mut T, _: &T) {}
+
+fn double_mut_borrow<T>(x: &mut Box<T>) {
+    let y = borrow_mut(x);
+    let z = borrow_mut(x);
+    //~^ ERROR cannot borrow `*x` as mutable more than once at a time
+}
+
+fn double_imm_borrow(x: &mut Box<i32>) {
+    let y = borrow(x);
+    let z = borrow(x);
+    **x += 1;
+    //~^ ERROR cannot assign to `**x` because it is borrowed
+}
+
+fn double_mut_borrow2<T>(x: &mut Box<T>) {
+    borrow_mut2(x, x);
+    //~^ ERROR cannot borrow `*x` as mutable more than once at a time
+}
+
+fn double_borrow2<T>(x: &mut Box<T>) {
+    borrow2(x, x);
+    //~^ ERROR cannot borrow `*x` as immutable because it is also borrowed as mutable
+}
+
+pub fn main() {}
diff --git a/src/test/compile-fail/method-self-arg-1.rs b/src/test/compile-fail/method-self-arg-1.rs
index 4d416ed42de..98b9e453889 100644
--- a/src/test/compile-fail/method-self-arg-1.rs
+++ b/src/test/compile-fail/method-self-arg-1.rs
@@ -23,11 +23,6 @@ fn main() {
                  //~| found `Foo`
                  //~| expected &-ptr
                  //~| found struct `Foo`
-    Foo::bar(&&x); //~  ERROR mismatched types
-                   //~| expected `&Foo`
-                   //~| found `&&Foo`
-                   //~| expected struct `Foo`
-                   //~| found &-ptr
     Foo::bar(&42is); //~  ERROR mismatched types
                      //~| expected `&Foo`
                      //~| found `&isize`
diff --git a/src/test/run-pass/coerce-overloaded-autoderef.rs b/src/test/run-pass/coerce-overloaded-autoderef.rs
new file mode 100644
index 00000000000..ae4db404115
--- /dev/null
+++ b/src/test/run-pass/coerce-overloaded-autoderef.rs
@@ -0,0 +1,69 @@
+// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use std::rc::Rc;
+
+// Examples from the "deref coercions" RFC, at rust-lang/rfcs#241.
+
+fn use_ref<T>(_: &T) {}
+fn use_mut<T>(_: &mut T) {}
+
+fn use_rc<T>(t: Rc<T>) {
+    use_ref(&*t);  // what you have to write today
+    use_ref(&t);   // what you'd be able to write
+    use_ref(&&&&&&t);
+    use_ref(&mut &&&&&t);
+    use_ref(&&&mut &&&t);
+}
+
+fn use_mut_box<T>(mut t: &mut Box<T>) {
+    use_mut(&mut *t); // what you have to write today
+    use_mut(t);       // what you'd be able to write
+    use_mut(&mut &mut &mut t);
+
+    use_ref(&*t);      // what you have to write today
+    use_ref(t);        // what you'd be able to write
+    use_ref(&&&&&&t);
+    use_ref(&mut &&&&&t);
+    use_ref(&&&mut &&&t);
+}
+
+fn use_nested<T>(t: &Box<T>) {
+    use_ref(&**t);  // what you have to write today
+    use_ref(t);     // what you'd be able to write (note: recursive deref)
+    use_ref(&&&&&&t);
+    use_ref(&mut &&&&&t);
+    use_ref(&&&mut &&&t);
+}
+
+fn use_slice(_: &[u8]) {}
+fn use_slice_mut(_: &mut [u8]) {}
+
+fn use_vec(mut v: Vec<u8>) {
+    use_slice_mut(&mut v[]); // what you have to write today
+    use_slice_mut(&mut v);   // what you'd be able to write
+    use_slice_mut(&mut &mut &mut v);
+
+    use_slice(&v[]);    // what you have to write today
+    use_slice(&v);      // what you'd be able to write
+    use_slice(&&&&&&v);
+    use_slice(&mut &&&&&v);
+    use_slice(&&&mut &&&v);
+}
+
+fn use_vec_ref(v: &Vec<u8>) {
+    use_slice(&v[]);    // what you have to write today
+    use_slice(v);       // what you'd be able to write
+    use_slice(&&&&&&v);
+    use_slice(&mut &&&&&v);
+    use_slice(&&&mut &&&v);
+}
+
+pub fn main() {}