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.
This commit is contained in:
Patrick Walton 2014-10-10 15:17:59 -07:00
parent dfd52817ee
commit f7fb38729e
4 changed files with 75 additions and 55 deletions

View File

@ -452,13 +452,13 @@ impl<T> Index<uint,T> for Vec<T> {
} }
} }
// FIXME(#12825) Indexing will always try IndexMut first and that causes issues. #[cfg(not(stage0))]
/*impl<T> IndexMut<uint,T> for Vec<T> { impl<T> IndexMut<uint,T> for Vec<T> {
#[inline] #[inline]
fn index_mut<'a>(&'a mut self, index: &uint) -> &'a mut T { fn index_mut<'a>(&'a mut self, index: &uint) -> &'a mut T {
self.get_mut(*index) self.get_mut(*index)
} }
}*/ }
#[cfg(stage0)] #[cfg(stage0)]
impl<T> ops::Slice<uint, [T]> for Vec<T> { impl<T> ops::Slice<uint, [T]> for Vec<T> {
@ -2191,7 +2191,6 @@ impl<T> Vec<T> {
} }
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
extern crate test; extern crate test;

View File

@ -359,8 +359,7 @@ impl<'a, 'tcx> LookupContext<'a, 'tcx> {
match result { match result {
Some(Some(result)) => { Some(Some(result)) => {
self.fixup_derefs_on_method_receiver_if_necessary(&result, self.fixup_derefs_on_method_receiver_if_necessary(&result);
self_ty);
Some(result) Some(result)
} }
_ => None _ => None
@ -1388,8 +1387,7 @@ impl<'a, 'tcx> LookupContext<'a, 'tcx> {
fn fixup_derefs_on_method_receiver_if_necessary( fn fixup_derefs_on_method_receiver_if_necessary(
&self, &self,
method_callee: &MethodCallee, method_callee: &MethodCallee) {
self_ty: ty::t) {
let sig = match ty::get(method_callee.ty).sty { let sig = match ty::get(method_callee.ty).sty {
ty::ty_bare_fn(ref f) => f.sig.clone(), ty::ty_bare_fn(ref f) => f.sig.clone(),
ty::ty_closure(ref f) => f.sig.clone(), ty::ty_closure(ref f) => f.sig.clone(),
@ -1404,55 +1402,82 @@ impl<'a, 'tcx> LookupContext<'a, 'tcx> {
_ => return, _ => return,
} }
// Fix up autoderefs and derefs. // Gather up expressions we want to munge.
let mut self_expr = match self.self_expr { let mut exprs = Vec::new();
Some(expr) => expr, match self.self_expr {
None => return, Some(expr) => exprs.push(expr),
}; None => {}
}
loop { 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. // Count autoderefs.
let autoderef_count = match self.fcx let autoderef_count = match self.fcx
.inh .inh
.adjustments .adjustments
.borrow() .borrow()
.find(&self_expr.id) { .find(&expr.id) {
Some(&ty::AdjustDerefRef(ty::AutoDerefRef { Some(&ty::AdjustDerefRef(ty::AutoDerefRef {
autoderefs: autoderef_count, autoderefs: autoderef_count,
autoref: _ autoref: _
})) if autoderef_count > 0 => autoderef_count, })) => autoderef_count,
Some(_) | None => return, Some(_) | None => 0,
}; };
check::autoderef(self.fcx, if autoderef_count > 0 {
self_expr.span, check::autoderef(self.fcx,
self.fcx.expr_ty(self_expr), expr.span,
Some(self_expr.id), self.fcx.expr_ty(*expr),
PreferMutLvalue, Some(expr.id),
|_, autoderefs| { PreferMutLvalue,
if autoderefs == autoderef_count + 1 { |_, autoderefs| {
Some(()) if autoderefs == autoderef_count + 1 {
} else { Some(())
None } else {
} None
}); }
});
}
match self_expr.node { // Don't retry the first one or we might infinite loop!
ast::ExprParen(ref expr) | if i != 0 {
ast::ExprIndex(ref expr, _) | match expr.node {
ast::ExprField(ref expr, _, _) | ast::ExprIndex(ref base_expr, ref index_expr) => {
ast::ExprTupField(ref expr, _, _) | check::try_overloaded_index(
ast::ExprSlice(ref expr, _, _, _) => self_expr = &**expr, self.fcx,
ast::ExprUnary(ast::UnDeref, ref expr) => { Some(MethodCall::expr(expr.id)),
drop(check::try_overloaded_deref( *expr,
self.fcx, &**base_expr,
self_expr.span, self.fcx.expr_ty(&**base_expr),
Some(MethodCall::expr(self_expr.id)), index_expr,
Some(self_expr), PreferMutLvalue);
self_ty, }
PreferMutLvalue)); ast::ExprUnary(ast::UnDeref, ref base_expr) => {
self_expr = &**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,
} }
} }
} }

View File

@ -2975,12 +2975,10 @@ fn check_expr_with_unifier(fcx: &FnCtxt,
expr: &ast::Expr, expr: &ast::Expr,
method_name: ast::SpannedIdent, method_name: ast::SpannedIdent,
args: &[P<ast::Expr>], args: &[P<ast::Expr>],
tps: &[P<ast::Ty>]) { tps: &[P<ast::Ty>],
lvalue_pref: LvaluePreference) {
let rcvr = &*args[0]; let rcvr = &*args[0];
// We can't know if we need &mut self before we look up the method, check_expr_with_lvalue_pref(fcx, &*rcvr, lvalue_pref);
// 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);
// no need to check for bot/err -- callee does that // no need to check for bot/err -- callee does that
let expr_t = structurally_resolved_type(fcx, 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) => { 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 mut arg_tys = args.iter().map(|a| fcx.expr_ty(&**a));
let (args_bot, args_err) = arg_tys.fold((false, false), let (args_bot, args_err) = arg_tys.fold((false, false),
|(rest_bot, rest_err), a| { |(rest_bot, rest_err), a| {

View File

@ -68,12 +68,10 @@ pub fn main() {
*n -= 3; // Mutable deref + assignment with binary operation. *n -= 3; // Mutable deref + assignment with binary operation.
assert_eq!(n.counts(), (2, 3)); assert_eq!(n.counts(), (2, 3));
// Mutable deref used for calling a method taking &self. // Immutable deref used for calling a method taking &self. (The
// N.B. This is required because method lookup hasn't been performed so // typechecker is smarter now about doing this.)
// we don't know whether the called method takes mutable self, before
// the dereference itself is type-checked (a chicken-and-egg problem).
(*n).to_string(); (*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. // Mutable deref used for calling a method taking &mut self.
(*v).push(2); (*v).push(2);