rustc: Implement automatic reference for method receivers
This commit is contained in:
parent
fe9d07dda6
commit
d54db12155
12 changed files with 170 additions and 62 deletions
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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})};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
21
src/test/compile-fail/auto-ref-borrowck-failure.rs
Normal file
21
src/test/compile-fail/auto-ref-borrowck-failure.rs
Normal file
|
@ -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
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<uint,~str> = x;
|
||||
//~^ ERROR mismatched types: expected `std::map::map<uint,~str>`
|
||||
//~^ ERROR mismatched types: expected `@std::map::map<uint,~str>`
|
||||
}
|
||||
|
|
|
@ -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() { }
|
||||
fn main() { }
|
||||
|
|
|
@ -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/&`
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -2,7 +2,7 @@ trait foo<T> { }
|
|||
|
||||
fn bar(x: foo<uint>) -> foo<int> {
|
||||
return (x as foo::<int>);
|
||||
//~^ ERROR mismatched types: expected `foo<int>` but found `foo<uint>`
|
||||
//~^ ERROR mismatched types: expected `@foo<int>` but found `@foo<uint>`
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
|
|
19
src/test/run-pass/auto-ref.rs
Normal file
19
src/test/run-pass/auto-ref.rs
Normal file
|
@ -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();
|
||||
}
|
||||
|
Loading…
Add table
Reference in a new issue