From 038f9255863d4e07805d8bbefa25e0b61ddfcad0 Mon Sep 17 00:00:00 2001 From: Michael Sullivan Date: Fri, 6 Jul 2012 15:50:50 -0700 Subject: [PATCH] Be less eager about implicit borrowing when doing method resolution. Closes #2796. --- src/rustc/middle/typeck/check/method.rs | 39 ++++++++++++--------- src/test/run-pass/assignability-iface.rs | 43 ++++++++++++++++++++++++ 2 files changed, 67 insertions(+), 15 deletions(-) create mode 100644 src/test/run-pass/assignability-iface.rs diff --git a/src/rustc/middle/typeck/check/method.rs b/src/rustc/middle/typeck/check/method.rs index 709db8733d5..b91f9778e1c 100644 --- a/src/rustc/middle/typeck/check/method.rs +++ b/src/rustc/middle/typeck/check/method.rs @@ -77,16 +77,20 @@ class lookup { // loop for impls in scope. Note: I don't love these // semantics, but that's what we had so I am preserving // it. - if self.candidates.len() > 0u { - break; - } + if self.candidates.len() > 0u { break; } - self.add_candidates_from_scope(); + // now look for impls in scope, but don't look for impls that + // would require doing an implicit borrow of the lhs. + self.add_candidates_from_scope(false); + + // if we found anything, stop before trying borrows + if self.candidates.len() > 0u { break; } + + // now look for impls in scope that might require a borrow + self.add_candidates_from_scope(true); // if we found anything, stop before attempting auto-deref. - if self.candidates.len() > 0u { - break; - } + if self.candidates.len() > 0u { break; } // check whether we can autoderef and if so loop around again. alt ty::deref(self.tcx(), self.self_ty, false) { @@ -290,7 +294,7 @@ class lookup { */ } - fn add_candidates_from_scope() { + fn add_candidates_from_scope(use_assignability: bool) { let impls_vecs = self.fcx.ccx.impl_map.get(self.expr.id); let mut added_any = false; @@ -306,13 +310,18 @@ class lookup { let {substs: impl_substs, ty: impl_ty} = impl_self_ty(self.fcx, im.did); - // if we can assign the caller to the callee, that's a - // potential match. Collect those in the vector. - let can_assign = self.fcx.can_mk_assignty( - self.self_expr, self.borrow_scope, - self.self_ty, impl_ty); - #debug["can_assign = %?", can_assign]; - alt can_assign { + // Depending on our argument, we find potential + // matches either by checking subtypability or + // type assignability. Collect the matches. + let matches = if use_assignability { + self.fcx.can_mk_assignty( + self.self_expr, self.borrow_scope, + self.self_ty, impl_ty) + } else { + self.fcx.can_mk_subty(self.self_ty, impl_ty) + }; + #debug["matches = %?", matches]; + alt matches { result::err(_) { /* keep looking */ } result::ok(_) { if !self.candidate_impls.contains_key(im.did) { diff --git a/src/test/run-pass/assignability-iface.rs b/src/test/run-pass/assignability-iface.rs new file mode 100644 index 00000000000..47cf7535a6e --- /dev/null +++ b/src/test/run-pass/assignability-iface.rs @@ -0,0 +1,43 @@ +// Tests that type assignability is used to search for instances when +// making method calls, but only if there aren't any matches without +// it. + +iface iterable { + fn iterate(blk: fn(A) -> bool); +} + +impl vec/& of iterable for &[const A] { + fn iterate(f: fn(A) -> bool) { + vec::each(self, f); + } +} + +impl vec of iterable for ~[const A] { + fn iterate(f: fn(A) -> bool) { + vec::each(self, f); + } +} + +fn length>(x: T) -> uint { + let mut len = 0; + for x.iterate() |_y| { len += 1 } + ret len; +} + +fn main() { + let x = ~[0,1,2,3]; + // Call a method + for x.iterate() |y| { assert x[y] == y; } + // Call a parameterized function + assert length(x) == vec::len(x); + // Call a parameterized function, with type arguments that require + // a borrow + assert length::(x) == vec::len(x); + + // Now try it with a type that *needs* to be borrowed + let z = [0,1,2,3]/_; + // Call a method + for z.iterate() |y| { assert z[y] == y; } + // Call a parameterized function + assert length::(z) == vec::len(z); +}