rustc: Implement automatic reference for method receivers

This commit is contained in:
Patrick Walton 2012-08-14 18:17:18 -07:00
parent fe9d07dda6
commit d54db12155
12 changed files with 170 additions and 62 deletions

View file

@ -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)
}
}
}

View file

@ -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})};
}
}
}

View file

@ -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;
}

View 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
}

View file

@ -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();
}
}

View file

@ -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>`
}

View file

@ -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() { }

View file

@ -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/&`
}
}

View file

@ -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() {

View file

@ -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() {

View file

@ -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() {}

View 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();
}