From d54db12155257787797cd9c7eaf06a215ff516e5 Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Tue, 14 Aug 2012 18:17:18 -0700 Subject: [PATCH] rustc: Implement automatic reference for method receivers --- src/rustc/middle/mem_categorization.rs | 8 +- src/rustc/middle/trans/base.rs | 12 +- src/rustc/middle/typeck/check/method.rs | 150 +++++++++++++----- .../compile-fail/auto-ref-borrowck-failure.rs | 21 +++ src/test/compile-fail/class-cast-to-trait.rs | 4 +- src/test/compile-fail/map-types.rs | 4 +- src/test/compile-fail/regions-bounds.rs | 4 +- .../regions-infer-paramd-method.rs | 2 +- src/test/compile-fail/regions-trait-2.rs | 2 +- src/test/compile-fail/regions-trait-3.rs | 4 +- src/test/compile-fail/trait-cast.rs | 2 +- src/test/run-pass/auto-ref.rs | 19 +++ 12 files changed, 170 insertions(+), 62 deletions(-) create mode 100644 src/test/compile-fail/auto-ref-borrowck-failure.rs create mode 100644 src/test/run-pass/auto-ref.rs diff --git a/src/rustc/middle/mem_categorization.rs b/src/rustc/middle/mem_categorization.rs index cf9ee1bc190e..b509880ab5fb 100644 --- a/src/rustc/middle/mem_categorization.rs +++ b/src/rustc/middle/mem_categorization.rs @@ -236,7 +236,8 @@ struct mem_categorization_ctxt { impl &mem_categorization_ctxt { fn cat_borrow_of_expr(expr: @ast::expr) -> cmt { - // a borrowed expression must be either an @, ~, or a @vec, ~vec + // Any expression can be borrowed (to account for auto-ref on method + // receivers), but @, ~, @vec, and ~vec are handled specially. let expr_ty = ty::expr_ty(self.tcx, expr); match ty::get(expr_ty).struct { ty::ty_evec(*) | ty::ty_estr(*) => { @@ -255,10 +256,7 @@ impl &mem_categorization_ctxt { */ _ => { - self.tcx.sess.span_bug( - expr.span, - fmt!{"Borrowing of non-derefable type `%s`", - ty_to_str(self.tcx, expr_ty)}); + self.cat_rvalue(expr, expr_ty) } } } diff --git a/src/rustc/middle/trans/base.rs b/src/rustc/middle/trans/base.rs index 83bdb941f463..2734fa85ae61 100644 --- a/src/rustc/middle/trans/base.rs +++ b/src/rustc/middle/trans/base.rs @@ -3056,9 +3056,15 @@ fn adapt_borrowed_value(lv: lval_result, } _ => { - bcx.tcx().sess.span_bug( - e.span, fmt!{"cannot borrow a value of type %s", - ppaux::ty_to_str(bcx.tcx(), e_ty)}); + // Just take a reference. This is basically like trans_addr_of. + let mut {bcx, val, kind} = trans_temp_lval(bcx, e); + let is_immediate = ty::type_is_immediate(e_ty); + if (kind == lv_temporary && is_immediate) || kind == lv_owned_imm { + val = do_spill(bcx, val, e_ty); + } + return {lv: {bcx: bcx, val: val, kind: lv_temporary}, + ty: ty::mk_rptr(bcx.tcx(), ty::re_static, + {ty: e_ty, mutbl: ast::m_imm})}; } } } diff --git a/src/rustc/middle/typeck/check/method.rs b/src/rustc/middle/typeck/check/method.rs index 3bb0e566dc62..6e45762eca6f 100644 --- a/src/rustc/middle/typeck/check/method.rs +++ b/src/rustc/middle/typeck/check/method.rs @@ -11,13 +11,21 @@ import syntax::ast_map::node_id_to_str; import syntax::ast_util::{dummy_sp, new_def_hash}; import dvec::{DVec, dvec}; +enum method_lookup_mode { + subtyping_mode, + assignability_mode, + immutable_reference_mode, + mutable_reference_mode +} + type candidate = { - self_ty: ty::t, // type of a in a.b() - self_substs: ty::substs, // values for any tvars def'd on the class - rcvr_ty: ty::t, // type of receiver in the method def - n_tps_m: uint, // number of tvars defined on the method - fty: ty::t, // type of the method - entry: method_map_entry + self_ty: ty::t, // type of a in a.b() + self_substs: ty::substs, // values for any tvars def'd on the class + rcvr_ty: ty::t, // type of receiver in the method def + n_tps_m: uint, // number of tvars defined on the method + fty: ty::t, // type of the method + entry: method_map_entry, + mode: method_lookup_mode // the mode we used }; fn transform_self_type_for_method @@ -141,16 +149,33 @@ class lookup { // it. if self.candidates.len() > 0u { break; } - // Look for inherent methods. + // Look for inherent and extension methods, using subtyping. self.add_inherent_and_extension_candidates - (optional_inherent_methods, false); + (optional_inherent_methods, subtyping_mode); // if we found anything, stop before trying borrows if self.candidates.len() > 0u { break; } - // Again, look for inherent methods. + // Again, look for inherent and extension methods, this time using + // assignability. self.add_inherent_and_extension_candidates - (optional_inherent_methods, true); + (optional_inherent_methods, assignability_mode); + + // If we found anything, stop before trying auto-ref. + if self.candidates.len() > 0u { break; } + + // Now look for inherent and extension methods, this time using an + // immutable reference. + self.add_inherent_and_extension_candidates + (optional_inherent_methods, immutable_reference_mode); + + // if we found anything, stop before attempting auto-deref. + if self.candidates.len() > 0u { break; } + + // Now look for inherent and extension methods, this time using a + // mutable reference. + self.add_inherent_and_extension_candidates + (optional_inherent_methods, mutable_reference_mode); // if we found anything, stop before attempting auto-deref. if self.candidates.len() > 0u { break; } @@ -362,9 +387,8 @@ class lookup { } // Returns true if any were added and false otherwise. - fn add_candidates_from_impl(im: @resolve3::Impl, - use_assignability: bool) -> bool { - + fn add_candidates_from_impl(im: @resolve3::Impl, mode: method_lookup_mode) + -> bool { let mut added_any = false; // Check whether this impl has a method with the right name. @@ -382,15 +406,33 @@ class lookup { self.tcx(), impl_substs.self_r, impl_ty, m.self_type); - // 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_lb, - self.self_ty, impl_ty) - } else { - self.fcx.can_mk_subty(self.self_ty, impl_ty) - }; + // Depending on our argument, we find potential matches by + // checking subtypability, type assignability, or reference + // subtypability. Collect the matches. + let matches; + match mode { + subtyping_mode => + matches = self.fcx.can_mk_subty(self.self_ty, impl_ty), + assignability_mode => + matches = self.fcx.can_mk_assignty(self.self_expr, + self.borrow_lb, + self.self_ty, + impl_ty), + immutable_reference_mode => { + let region = self.fcx.infcx.next_region_var_with_scope_lb + (self.self_expr.id); + let tm = { ty: self.self_ty, mutbl: ast::m_imm }; + let ref_ty = ty::mk_rptr(self.tcx(), region, tm); + matches = self.fcx.can_mk_subty(ref_ty, impl_ty); + } + mutable_reference_mode => { + let region = self.fcx.infcx.next_region_var_with_scope_lb + (self.self_expr.id); + let tm = { ty: self.self_ty, mutbl: ast::m_mutbl }; + let ref_ty = ty::mk_rptr(self.tcx(), region, tm); + matches = self.fcx.can_mk_subty(ref_ty, impl_ty); + } + } debug!{"matches = %?", matches}; match matches { result::err(_) => { /* keep looking */ } @@ -404,7 +446,8 @@ class lookup { n_tps_m: m.n_tps, fty: fty, entry: {derefs: self.derefs, - origin: method_static(m.did)}}); + origin: method_static(m.did)}, + mode: mode}); self.candidate_impls.insert(im.did, ()); added_any = true; } @@ -431,12 +474,13 @@ class lookup { rcvr_ty: self.self_ty, n_tps_m: (*m.tps).len(), fty: fty, - entry: {derefs: self.derefs, origin: origin}}); + entry: {derefs: self.derefs, origin: origin}, + mode: subtyping_mode}); } fn add_inherent_and_extension_candidates(optional_inherent_methods: option<@DVec<@Impl>>, - use_assignability: bool) { + mode: method_lookup_mode) { // Add inherent methods. match optional_inherent_methods { @@ -451,8 +495,7 @@ class lookup { adding candidates from impl: %s", node_id_to_str(self.tcx().items, implementation.did.node)}; - self.add_candidates_from_impl(implementation, - use_assignability); + self.add_candidates_from_impl(implementation, mode); } } } @@ -479,8 +522,7 @@ class lookup { candidates) adding impl %s", self.def_id_to_str (implementation.did)}; - self.add_candidates_from_impl - (implementation, use_assignability); + self.add_candidates_from_impl(implementation, mode); } } } @@ -505,19 +547,41 @@ class lookup { self.fcx.infcx.ty_to_str(cand.fty), cand.entry}; - // Make the actual receiver type (cand.self_ty) assignable to the - // required receiver type (cand.rcvr_ty). If this method is not - // from an impl, this'll basically be a no-nop. - match self.fcx.mk_assignty(self.self_expr, self.borrow_lb, - cand.self_ty, cand.rcvr_ty) { - result::ok(_) => (), - result::err(_) => { - self.tcx().sess.span_bug( - self.expr.span, - fmt!{"%s was assignable to %s but now is not?", - self.fcx.infcx.ty_to_str(cand.self_ty), - self.fcx.infcx.ty_to_str(cand.rcvr_ty)}); - } + match cand.mode { + subtyping_mode | assignability_mode => { + // Make the actual receiver type (cand.self_ty) assignable to + // the required receiver type (cand.rcvr_ty). If this method + // is not from an impl, this'll basically be a no-nop. + match self.fcx.mk_assignty(self.self_expr, self.borrow_lb, + cand.self_ty, cand.rcvr_ty) { + result::ok(_) => (), + result::err(_) => { + self.tcx().sess.span_bug( + self.expr.span, + fmt!{"%s was assignable to %s but now is not?", + self.fcx.infcx.ty_to_str(cand.self_ty), + self.fcx.infcx.ty_to_str(cand.rcvr_ty)}); + } + } + } + immutable_reference_mode => { + // Borrow as an immutable reference. + let region_var = self.fcx.infcx.next_region_var_with_scope_lb + (self.self_expr.id); + self.fcx.infcx.borrowings.push({expr_id: self.self_expr.id, + span: self.self_expr.span, + scope: region_var, + mutbl: ast::m_imm}); + } + mutable_reference_mode => { + // Borrow as a mutable reference. + let region_var = self.fcx.infcx.next_region_var_with_scope_lb + (self.self_expr.id); + self.fcx.infcx.borrowings.push({expr_id: self.self_expr.id, + span: self.self_expr.span, + scope: region_var, + mutbl: ast::m_mutbl}); + } } // Construct the full set of type parameters for the method, @@ -546,7 +610,7 @@ class lookup { let all_substs = {tps: vec::append(cand.self_substs.tps, m_substs) with cand.self_substs}; - self.fcx.write_ty_substs(self.node_id, cand.fty, all_substs); + self.fcx.write_ty_substs(self.node_id, cand.fty, all_substs); return cand.entry; } diff --git a/src/test/compile-fail/auto-ref-borrowck-failure.rs b/src/test/compile-fail/auto-ref-borrowck-failure.rs new file mode 100644 index 000000000000..316f37971960 --- /dev/null +++ b/src/test/compile-fail/auto-ref-borrowck-failure.rs @@ -0,0 +1,21 @@ +// Tests that auto-ref can't create mutable aliases to immutable memory. + +struct Foo { + x: int; +} + +trait Stuff { + fn printme(); +} + +impl &mut Foo : Stuff { + fn printme() { + io::println(fmt!("%d", self.x)); + } +} + +fn main() { + let x = Foo { x: 3 }; + x.printme(); //~ ERROR illegal borrow +} + diff --git a/src/test/compile-fail/class-cast-to-trait.rs b/src/test/compile-fail/class-cast-to-trait.rs index 159e881ee8a1..eba939d76e7e 100644 --- a/src/test/compile-fail/class-cast-to-trait.rs +++ b/src/test/compile-fail/class-cast-to-trait.rs @@ -1,4 +1,4 @@ -// error-pattern: attempted access of field `eat` on type `noisy` +// error-pattern: attempted access of field `eat` on type `@noisy` trait noisy { fn speak(); } @@ -39,4 +39,4 @@ class cat : noisy { fn main() { let nyan : noisy = cat(0u, 2, "nyan") as noisy; nyan.eat(); -} \ No newline at end of file +} diff --git a/src/test/compile-fail/map-types.rs b/src/test/compile-fail/map-types.rs index 0a506367341a..5131bd81c7ca 100644 --- a/src/test/compile-fail/map-types.rs +++ b/src/test/compile-fail/map-types.rs @@ -6,7 +6,7 @@ import std::map::map; // Test that trait types printed in error msgs include the type arguments. fn main() { - let x: map<~str,~str> = map::str_hash::<~str>() as map::<~str,~str>; + let x: map<~str,~str> = map::str_hash::<~str>() as @map::<~str,~str>; let y: map = x; - //~^ ERROR mismatched types: expected `std::map::map` + //~^ ERROR mismatched types: expected `@std::map::map` } diff --git a/src/test/compile-fail/regions-bounds.rs b/src/test/compile-fail/regions-bounds.rs index e795c5b86865..28c3b1c4463e 100644 --- a/src/test/compile-fail/regions-bounds.rs +++ b/src/test/compile-fail/regions-bounds.rs @@ -11,7 +11,7 @@ fn a_fn1(e: an_enum/&a) -> an_enum/&b { } fn a_fn2(e: a_trait/&a) -> a_trait/&b { - return e; //~ ERROR mismatched types: expected `a_trait/&b` but found `a_trait/&a` + return e; //~ ERROR mismatched types: expected `@a_trait/&b` but found `@a_trait/&a` } fn a_fn3(e: a_class/&a) -> a_class/&b { @@ -24,4 +24,4 @@ fn a_fn4(e: int/&a) -> int/&b { return e; } -fn main() { } \ No newline at end of file +fn main() { } diff --git a/src/test/compile-fail/regions-infer-paramd-method.rs b/src/test/compile-fail/regions-infer-paramd-method.rs index 73b834c2624b..613897639559 100644 --- a/src/test/compile-fail/regions-infer-paramd-method.rs +++ b/src/test/compile-fail/regions-infer-paramd-method.rs @@ -15,7 +15,7 @@ trait set_foo_foo { impl with_foo: set_foo_foo { fn set_foo(f: foo) { - self.f = f; //~ ERROR mismatched types: expected `foo/&self` but found `foo/&` + self.f = f; //~ ERROR mismatched types: expected `@foo/&self` but found `@foo/&` } } diff --git a/src/test/compile-fail/regions-trait-2.rs b/src/test/compile-fail/regions-trait-2.rs index a34a903e3ba6..661f64ec651a 100644 --- a/src/test/compile-fail/regions-trait-2.rs +++ b/src/test/compile-fail/regions-trait-2.rs @@ -13,7 +13,7 @@ impl has_ctxt: get_ctxt { fn make_gc() -> get_ctxt { let ctxt = { v: 22u }; let hc = { c: &ctxt }; - return hc as get_ctxt; //~ ERROR mismatched types: expected `get_ctxt/&` + return hc as get_ctxt; //~ ERROR mismatched types: expected `@get_ctxt/&` } fn main() { diff --git a/src/test/compile-fail/regions-trait-3.rs b/src/test/compile-fail/regions-trait-3.rs index a0be6c901771..b3a0d0fcf626 100644 --- a/src/test/compile-fail/regions-trait-3.rs +++ b/src/test/compile-fail/regions-trait-3.rs @@ -3,11 +3,11 @@ trait get_ctxt { } fn make_gc1(gc: get_ctxt/&a) -> get_ctxt/&b { - return gc; //~ ERROR mismatched types: expected `get_ctxt/&b` but found `get_ctxt/&a` + return gc; //~ ERROR mismatched types: expected `@get_ctxt/&b` but found `@get_ctxt/&a` } fn make_gc2(gc: get_ctxt/&a) -> get_ctxt/&b { - return gc as get_ctxt; //~ ERROR mismatched types: expected `get_ctxt/&b` but found `get_ctxt/&a` + return gc as get_ctxt; //~ ERROR mismatched types: expected `@get_ctxt/&b` but found `@get_ctxt/&a` } fn main() { diff --git a/src/test/compile-fail/trait-cast.rs b/src/test/compile-fail/trait-cast.rs index 736634c13e86..9e15a31d95aa 100644 --- a/src/test/compile-fail/trait-cast.rs +++ b/src/test/compile-fail/trait-cast.rs @@ -2,7 +2,7 @@ trait foo { } fn bar(x: foo) -> foo { return (x as foo::); - //~^ ERROR mismatched types: expected `foo` but found `foo` + //~^ ERROR mismatched types: expected `@foo` but found `@foo` } fn main() {} diff --git a/src/test/run-pass/auto-ref.rs b/src/test/run-pass/auto-ref.rs new file mode 100644 index 000000000000..e5d4d106b532 --- /dev/null +++ b/src/test/run-pass/auto-ref.rs @@ -0,0 +1,19 @@ +struct Foo { + x: int; +} + +trait Stuff { + fn printme(); +} + +impl &Foo : Stuff { + fn printme() { + io::println(fmt!("%d", self.x)); + } +} + +fn main() { + let x = Foo { x: 3 }; + x.printme(); +} +