about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorPatrick Walton <pcwalton@mimiga.net>2014-10-10 15:17:59 -0700
committerPatrick Walton <pcwalton@mimiga.net>2014-10-14 14:44:17 -0700
commitf7fb38729ea665ef296d423ceb420be9f9d92cc3 (patch)
tree23b7e1535b32e2818b5f64832d8943944f4b2e52 /src
parentdfd52817ee81676e0cdba4ab2c5badf195fcfda7 (diff)
downloadrust-f7fb38729ea665ef296d423ceb420be9f9d92cc3.tar.gz
rust-f7fb38729ea665ef296d423ceb420be9f9d92cc3.zip
librustc: Improve method autoderef/deref/index behavior more, and enable
`IndexMut` on mutable vectors.

This fixes a bug whereby the mutability fixups for method behavior were
not kicking in after autoderef failed to happen at any level. It also
adds support for `Index` to the fixer-upper.

Closes #12825.
Diffstat (limited to 'src')
-rw-r--r--src/libcollections/vec.rs7
-rw-r--r--src/librustc/middle/typeck/check/method.rs107
-rw-r--r--src/librustc/middle/typeck/check/mod.rs10
-rw-r--r--src/test/run-pass/overloaded-deref-count.rs8
4 files changed, 76 insertions, 56 deletions
diff --git a/src/libcollections/vec.rs b/src/libcollections/vec.rs
index 894ca3d7f2e..246e8412edc 100644
--- a/src/libcollections/vec.rs
+++ b/src/libcollections/vec.rs
@@ -452,13 +452,13 @@ impl<T> Index<uint,T> for Vec<T> {
     }
 }
 
-// FIXME(#12825) Indexing will always try IndexMut first and that causes issues.
-/*impl<T> IndexMut<uint,T> for Vec<T> {
+#[cfg(not(stage0))]
+impl<T> IndexMut<uint,T> for Vec<T> {
     #[inline]
     fn index_mut<'a>(&'a mut self, index: &uint) -> &'a mut T {
         self.get_mut(*index)
     }
-}*/
+}
 
 #[cfg(stage0)]
 impl<T> ops::Slice<uint, [T]> for Vec<T> {
@@ -2191,7 +2191,6 @@ impl<T> Vec<T> {
     }
 }
 
-
 #[cfg(test)]
 mod tests {
     extern crate test;
diff --git a/src/librustc/middle/typeck/check/method.rs b/src/librustc/middle/typeck/check/method.rs
index 8711f4de512..45658e8a356 100644
--- a/src/librustc/middle/typeck/check/method.rs
+++ b/src/librustc/middle/typeck/check/method.rs
@@ -359,8 +359,7 @@ impl<'a, 'tcx> LookupContext<'a, 'tcx> {
 
         match result {
             Some(Some(result)) => {
-                self.fixup_derefs_on_method_receiver_if_necessary(&result,
-                                                                  self_ty);
+                self.fixup_derefs_on_method_receiver_if_necessary(&result);
                 Some(result)
             }
             _ => None
@@ -1388,8 +1387,7 @@ impl<'a, 'tcx> LookupContext<'a, 'tcx> {
 
     fn fixup_derefs_on_method_receiver_if_necessary(
             &self,
-            method_callee: &MethodCallee,
-            self_ty: ty::t) {
+            method_callee: &MethodCallee) {
         let sig = match ty::get(method_callee.ty).sty {
             ty::ty_bare_fn(ref f) => f.sig.clone(),
             ty::ty_closure(ref f) => f.sig.clone(),
@@ -1404,55 +1402,82 @@ impl<'a, 'tcx> LookupContext<'a, 'tcx> {
             _ => return,
         }
 
-        // Fix up autoderefs and derefs.
-        let mut self_expr = match self.self_expr {
-            Some(expr) => expr,
-            None => return,
-        };
+        // Gather up expressions we want to munge.
+        let mut exprs = Vec::new();
+        match self.self_expr {
+            Some(expr) => exprs.push(expr),
+            None => {}
+        }
         loop {
+            if exprs.len() == 0 {
+                break
+            }
+            let last = exprs[exprs.len() - 1];
+            match last.node {
+                ast::ExprParen(ref expr) |
+                ast::ExprField(ref expr, _, _) |
+                ast::ExprTupField(ref expr, _, _) |
+                ast::ExprSlice(ref expr, _, _, _) |
+                ast::ExprIndex(ref expr, _) |
+                ast::ExprUnary(ast::UnDeref, ref expr) => exprs.push(&**expr),
+                _ => break,
+            }
+        }
+
+        // Fix up autoderefs and derefs.
+        for (i, expr) in exprs.iter().rev().enumerate() {
             // Count autoderefs.
             let autoderef_count = match self.fcx
                                             .inh
                                             .adjustments
                                             .borrow()
-                                            .find(&self_expr.id) {
+                                            .find(&expr.id) {
                 Some(&ty::AdjustDerefRef(ty::AutoDerefRef {
                     autoderefs: autoderef_count,
                     autoref: _
-                })) if autoderef_count > 0 => autoderef_count,
-                Some(_) | None => return,
+                })) => autoderef_count,
+                Some(_) | None => 0,
             };
 
-            check::autoderef(self.fcx,
-                             self_expr.span,
-                             self.fcx.expr_ty(self_expr),
-                             Some(self_expr.id),
-                             PreferMutLvalue,
-                             |_, autoderefs| {
-                                 if autoderefs == autoderef_count + 1 {
-                                     Some(())
-                                 } else {
-                                     None
-                                 }
-                             });
-
-            match self_expr.node {
-                ast::ExprParen(ref expr) |
-                ast::ExprIndex(ref expr, _) |
-                ast::ExprField(ref expr, _, _) |
-                ast::ExprTupField(ref expr, _, _) |
-                ast::ExprSlice(ref expr, _, _, _) => self_expr = &**expr,
-                ast::ExprUnary(ast::UnDeref, ref expr) => {
-                    drop(check::try_overloaded_deref(
-                            self.fcx,
-                            self_expr.span,
-                            Some(MethodCall::expr(self_expr.id)),
-                            Some(self_expr),
-                            self_ty,
-                            PreferMutLvalue));
-                    self_expr = &**expr
+            if autoderef_count > 0 {
+                check::autoderef(self.fcx,
+                                 expr.span,
+                                 self.fcx.expr_ty(*expr),
+                                 Some(expr.id),
+                                 PreferMutLvalue,
+                                 |_, autoderefs| {
+                                     if autoderefs == autoderef_count + 1 {
+                                         Some(())
+                                     } else {
+                                         None
+                                     }
+                                 });
+            }
+
+            // Don't retry the first one or we might infinite loop!
+            if i != 0 {
+                match expr.node {
+                    ast::ExprIndex(ref base_expr, ref index_expr) => {
+                        check::try_overloaded_index(
+                                self.fcx,
+                                Some(MethodCall::expr(expr.id)),
+                                *expr,
+                                &**base_expr,
+                                self.fcx.expr_ty(&**base_expr),
+                                index_expr,
+                                PreferMutLvalue);
+                    }
+                    ast::ExprUnary(ast::UnDeref, ref base_expr) => {
+                        check::try_overloaded_deref(
+                                self.fcx,
+                                expr.span,
+                                Some(MethodCall::expr(expr.id)),
+                                Some(&**base_expr),
+                                self.fcx.expr_ty(&**base_expr),
+                                PreferMutLvalue);
+                    }
+                    _ => {}
                 }
-                _ => break,
             }
         }
     }
diff --git a/src/librustc/middle/typeck/check/mod.rs b/src/librustc/middle/typeck/check/mod.rs
index 4af71bfda8b..1861d0ec3ff 100644
--- a/src/librustc/middle/typeck/check/mod.rs
+++ b/src/librustc/middle/typeck/check/mod.rs
@@ -2975,12 +2975,10 @@ fn check_expr_with_unifier(fcx: &FnCtxt,
                          expr: &ast::Expr,
                          method_name: ast::SpannedIdent,
                          args: &[P<ast::Expr>],
-                         tps: &[P<ast::Ty>]) {
+                         tps: &[P<ast::Ty>],
+                         lvalue_pref: LvaluePreference) {
         let rcvr = &*args[0];
-        // We can't know if we need &mut self before we look up the method,
-        // so treat the receiver as mutable just in case - only explicit
-        // overloaded dereferences care about the distinction.
-        check_expr_with_lvalue_pref(fcx, &*rcvr, PreferMutLvalue);
+        check_expr_with_lvalue_pref(fcx, &*rcvr, lvalue_pref);
 
         // no need to check for bot/err -- callee does that
         let expr_t = structurally_resolved_type(fcx,
@@ -4195,7 +4193,7 @@ fn check_expr_with_unifier(fcx: &FnCtxt,
           }
       }
       ast::ExprMethodCall(ident, ref tps, ref args) => {
-        check_method_call(fcx, expr, ident, args.as_slice(), tps.as_slice());
+        check_method_call(fcx, expr, ident, args.as_slice(), tps.as_slice(), lvalue_pref);
         let mut arg_tys = args.iter().map(|a| fcx.expr_ty(&**a));
         let (args_bot, args_err) = arg_tys.fold((false, false),
              |(rest_bot, rest_err), a| {
diff --git a/src/test/run-pass/overloaded-deref-count.rs b/src/test/run-pass/overloaded-deref-count.rs
index 2d546639939..7645500c02f 100644
--- a/src/test/run-pass/overloaded-deref-count.rs
+++ b/src/test/run-pass/overloaded-deref-count.rs
@@ -68,12 +68,10 @@ pub fn main() {
     *n -= 3; // Mutable deref + assignment with binary operation.
     assert_eq!(n.counts(), (2, 3));
 
-    // Mutable deref used for calling a method taking &self.
-    // N.B. This is required because method lookup hasn't been performed so
-    // we don't know whether the called method takes mutable self, before
-    // the dereference itself is type-checked (a chicken-and-egg problem).
+    // Immutable deref used for calling a method taking &self. (The
+    // typechecker is smarter now about doing this.)
     (*n).to_string();
-    assert_eq!(n.counts(), (2, 4));
+    assert_eq!(n.counts(), (3, 3));
 
     // Mutable deref used for calling a method taking &mut self.
     (*v).push(2);