Skip to content

Commit d54db12

Browse files
committed
rustc: Implement automatic reference for method receivers
1 parent fe9d07d commit d54db12

File tree

12 files changed

+170
-62
lines changed

12 files changed

+170
-62
lines changed

src/rustc/middle/mem_categorization.rs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,8 @@ struct mem_categorization_ctxt {
236236

237237
impl &mem_categorization_ctxt {
238238
fn cat_borrow_of_expr(expr: @ast::expr) -> cmt {
239-
// a borrowed expression must be either an @, ~, or a @vec, ~vec
239+
// Any expression can be borrowed (to account for auto-ref on method
240+
// receivers), but @, ~, @vec, and ~vec are handled specially.
240241
let expr_ty = ty::expr_ty(self.tcx, expr);
241242
match ty::get(expr_ty).struct {
242243
ty::ty_evec(*) | ty::ty_estr(*) => {
@@ -255,10 +256,7 @@ impl &mem_categorization_ctxt {
255256
*/
256257

257258
_ => {
258-
self.tcx.sess.span_bug(
259-
expr.span,
260-
fmt!{"Borrowing of non-derefable type `%s`",
261-
ty_to_str(self.tcx, expr_ty)});
259+
self.cat_rvalue(expr, expr_ty)
262260
}
263261
}
264262
}

src/rustc/middle/trans/base.rs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3056,9 +3056,15 @@ fn adapt_borrowed_value(lv: lval_result,
30563056
}
30573057

30583058
_ => {
3059-
bcx.tcx().sess.span_bug(
3060-
e.span, fmt!{"cannot borrow a value of type %s",
3061-
ppaux::ty_to_str(bcx.tcx(), e_ty)});
3059+
// Just take a reference. This is basically like trans_addr_of.
3060+
let mut {bcx, val, kind} = trans_temp_lval(bcx, e);
3061+
let is_immediate = ty::type_is_immediate(e_ty);
3062+
if (kind == lv_temporary && is_immediate) || kind == lv_owned_imm {
3063+
val = do_spill(bcx, val, e_ty);
3064+
}
3065+
return {lv: {bcx: bcx, val: val, kind: lv_temporary},
3066+
ty: ty::mk_rptr(bcx.tcx(), ty::re_static,
3067+
{ty: e_ty, mutbl: ast::m_imm})};
30623068
}
30633069
}
30643070
}

src/rustc/middle/typeck/check/method.rs

Lines changed: 107 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,21 @@ import syntax::ast_map::node_id_to_str;
1111
import syntax::ast_util::{dummy_sp, new_def_hash};
1212
import dvec::{DVec, dvec};
1313

14+
enum method_lookup_mode {
15+
subtyping_mode,
16+
assignability_mode,
17+
immutable_reference_mode,
18+
mutable_reference_mode
19+
}
20+
1421
type candidate = {
15-
self_ty: ty::t, // type of a in a.b()
16-
self_substs: ty::substs, // values for any tvars def'd on the class
17-
rcvr_ty: ty::t, // type of receiver in the method def
18-
n_tps_m: uint, // number of tvars defined on the method
19-
fty: ty::t, // type of the method
20-
entry: method_map_entry
22+
self_ty: ty::t, // type of a in a.b()
23+
self_substs: ty::substs, // values for any tvars def'd on the class
24+
rcvr_ty: ty::t, // type of receiver in the method def
25+
n_tps_m: uint, // number of tvars defined on the method
26+
fty: ty::t, // type of the method
27+
entry: method_map_entry,
28+
mode: method_lookup_mode // the mode we used
2129
};
2230

2331
fn transform_self_type_for_method
@@ -141,16 +149,33 @@ class lookup {
141149
// it.
142150
if self.candidates.len() > 0u { break; }
143151

144-
// Look for inherent methods.
152+
// Look for inherent and extension methods, using subtyping.
145153
self.add_inherent_and_extension_candidates
146-
(optional_inherent_methods, false);
154+
(optional_inherent_methods, subtyping_mode);
147155

148156
// if we found anything, stop before trying borrows
149157
if self.candidates.len() > 0u { break; }
150158

151-
// Again, look for inherent methods.
159+
// Again, look for inherent and extension methods, this time using
160+
// assignability.
161+
self.add_inherent_and_extension_candidates
162+
(optional_inherent_methods, assignability_mode);
163+
164+
// If we found anything, stop before trying auto-ref.
165+
if self.candidates.len() > 0u { break; }
166+
167+
// Now look for inherent and extension methods, this time using an
168+
// immutable reference.
152169
self.add_inherent_and_extension_candidates
153-
(optional_inherent_methods, true);
170+
(optional_inherent_methods, immutable_reference_mode);
171+
172+
// if we found anything, stop before attempting auto-deref.
173+
if self.candidates.len() > 0u { break; }
174+
175+
// Now look for inherent and extension methods, this time using a
176+
// mutable reference.
177+
self.add_inherent_and_extension_candidates
178+
(optional_inherent_methods, mutable_reference_mode);
154179

155180
// if we found anything, stop before attempting auto-deref.
156181
if self.candidates.len() > 0u { break; }
@@ -362,9 +387,8 @@ class lookup {
362387
}
363388

364389
// Returns true if any were added and false otherwise.
365-
fn add_candidates_from_impl(im: @resolve3::Impl,
366-
use_assignability: bool) -> bool {
367-
390+
fn add_candidates_from_impl(im: @resolve3::Impl, mode: method_lookup_mode)
391+
-> bool {
368392
let mut added_any = false;
369393

370394
// Check whether this impl has a method with the right name.
@@ -382,15 +406,33 @@ class lookup {
382406
self.tcx(), impl_substs.self_r,
383407
impl_ty, m.self_type);
384408

385-
// Depending on our argument, we find potential
386-
// matches either by checking subtypability or
387-
// type assignability. Collect the matches.
388-
let matches = if use_assignability {
389-
self.fcx.can_mk_assignty(self.self_expr, self.borrow_lb,
390-
self.self_ty, impl_ty)
391-
} else {
392-
self.fcx.can_mk_subty(self.self_ty, impl_ty)
393-
};
409+
// Depending on our argument, we find potential matches by
410+
// checking subtypability, type assignability, or reference
411+
// subtypability. Collect the matches.
412+
let matches;
413+
match mode {
414+
subtyping_mode =>
415+
matches = self.fcx.can_mk_subty(self.self_ty, impl_ty),
416+
assignability_mode =>
417+
matches = self.fcx.can_mk_assignty(self.self_expr,
418+
self.borrow_lb,
419+
self.self_ty,
420+
impl_ty),
421+
immutable_reference_mode => {
422+
let region = self.fcx.infcx.next_region_var_with_scope_lb
423+
(self.self_expr.id);
424+
let tm = { ty: self.self_ty, mutbl: ast::m_imm };
425+
let ref_ty = ty::mk_rptr(self.tcx(), region, tm);
426+
matches = self.fcx.can_mk_subty(ref_ty, impl_ty);
427+
}
428+
mutable_reference_mode => {
429+
let region = self.fcx.infcx.next_region_var_with_scope_lb
430+
(self.self_expr.id);
431+
let tm = { ty: self.self_ty, mutbl: ast::m_mutbl };
432+
let ref_ty = ty::mk_rptr(self.tcx(), region, tm);
433+
matches = self.fcx.can_mk_subty(ref_ty, impl_ty);
434+
}
435+
}
394436
debug!{"matches = %?", matches};
395437
match matches {
396438
result::err(_) => { /* keep looking */ }
@@ -404,7 +446,8 @@ class lookup {
404446
n_tps_m: m.n_tps,
405447
fty: fty,
406448
entry: {derefs: self.derefs,
407-
origin: method_static(m.did)}});
449+
origin: method_static(m.did)},
450+
mode: mode});
408451
self.candidate_impls.insert(im.did, ());
409452
added_any = true;
410453
}
@@ -431,12 +474,13 @@ class lookup {
431474
rcvr_ty: self.self_ty,
432475
n_tps_m: (*m.tps).len(),
433476
fty: fty,
434-
entry: {derefs: self.derefs, origin: origin}});
477+
entry: {derefs: self.derefs, origin: origin},
478+
mode: subtyping_mode});
435479
}
436480

437481
fn add_inherent_and_extension_candidates(optional_inherent_methods:
438482
option<@DVec<@Impl>>,
439-
use_assignability: bool) {
483+
mode: method_lookup_mode) {
440484

441485
// Add inherent methods.
442486
match optional_inherent_methods {
@@ -451,8 +495,7 @@ class lookup {
451495
adding candidates from impl: %s",
452496
node_id_to_str(self.tcx().items,
453497
implementation.did.node)};
454-
self.add_candidates_from_impl(implementation,
455-
use_assignability);
498+
self.add_candidates_from_impl(implementation, mode);
456499
}
457500
}
458501
}
@@ -479,8 +522,7 @@ class lookup {
479522
candidates) adding impl %s",
480523
self.def_id_to_str
481524
(implementation.did)};
482-
self.add_candidates_from_impl
483-
(implementation, use_assignability);
525+
self.add_candidates_from_impl(implementation, mode);
484526
}
485527
}
486528
}
@@ -505,19 +547,41 @@ class lookup {
505547
self.fcx.infcx.ty_to_str(cand.fty),
506548
cand.entry};
507549

508-
// Make the actual receiver type (cand.self_ty) assignable to the
509-
// required receiver type (cand.rcvr_ty). If this method is not
510-
// from an impl, this'll basically be a no-nop.
511-
match self.fcx.mk_assignty(self.self_expr, self.borrow_lb,
512-
cand.self_ty, cand.rcvr_ty) {
513-
result::ok(_) => (),
514-
result::err(_) => {
515-
self.tcx().sess.span_bug(
516-
self.expr.span,
517-
fmt!{"%s was assignable to %s but now is not?",
518-
self.fcx.infcx.ty_to_str(cand.self_ty),
519-
self.fcx.infcx.ty_to_str(cand.rcvr_ty)});
520-
}
550+
match cand.mode {
551+
subtyping_mode | assignability_mode => {
552+
// Make the actual receiver type (cand.self_ty) assignable to
553+
// the required receiver type (cand.rcvr_ty). If this method
554+
// is not from an impl, this'll basically be a no-nop.
555+
match self.fcx.mk_assignty(self.self_expr, self.borrow_lb,
556+
cand.self_ty, cand.rcvr_ty) {
557+
result::ok(_) => (),
558+
result::err(_) => {
559+
self.tcx().sess.span_bug(
560+
self.expr.span,
561+
fmt!{"%s was assignable to %s but now is not?",
562+
self.fcx.infcx.ty_to_str(cand.self_ty),
563+
self.fcx.infcx.ty_to_str(cand.rcvr_ty)});
564+
}
565+
}
566+
}
567+
immutable_reference_mode => {
568+
// Borrow as an immutable reference.
569+
let region_var = self.fcx.infcx.next_region_var_with_scope_lb
570+
(self.self_expr.id);
571+
self.fcx.infcx.borrowings.push({expr_id: self.self_expr.id,
572+
span: self.self_expr.span,
573+
scope: region_var,
574+
mutbl: ast::m_imm});
575+
}
576+
mutable_reference_mode => {
577+
// Borrow as a mutable reference.
578+
let region_var = self.fcx.infcx.next_region_var_with_scope_lb
579+
(self.self_expr.id);
580+
self.fcx.infcx.borrowings.push({expr_id: self.self_expr.id,
581+
span: self.self_expr.span,
582+
scope: region_var,
583+
mutbl: ast::m_mutbl});
584+
}
521585
}
522586

523587
// Construct the full set of type parameters for the method,
@@ -546,7 +610,7 @@ class lookup {
546610
let all_substs = {tps: vec::append(cand.self_substs.tps, m_substs)
547611
with cand.self_substs};
548612

549-
self.fcx.write_ty_substs(self.node_id, cand.fty, all_substs);
613+
self.fcx.write_ty_substs(self.node_id, cand.fty, all_substs);
550614

551615
return cand.entry;
552616
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// Tests that auto-ref can't create mutable aliases to immutable memory.
2+
3+
struct Foo {
4+
x: int;
5+
}
6+
7+
trait Stuff {
8+
fn printme();
9+
}
10+
11+
impl &mut Foo : Stuff {
12+
fn printme() {
13+
io::println(fmt!("%d", self.x));
14+
}
15+
}
16+
17+
fn main() {
18+
let x = Foo { x: 3 };
19+
x.printme(); //~ ERROR illegal borrow
20+
}
21+

src/test/compile-fail/class-cast-to-trait.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// error-pattern: attempted access of field `eat` on type `noisy`
1+
// error-pattern: attempted access of field `eat` on type `@noisy`
22
trait noisy {
33
fn speak();
44
}
@@ -39,4 +39,4 @@ class cat : noisy {
3939
fn main() {
4040
let nyan : noisy = cat(0u, 2, "nyan") as noisy;
4141
nyan.eat();
42-
}
42+
}

src/test/compile-fail/map-types.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import std::map::map;
66
// Test that trait types printed in error msgs include the type arguments.
77

88
fn main() {
9-
let x: map<~str,~str> = map::str_hash::<~str>() as map::<~str,~str>;
9+
let x: map<~str,~str> = map::str_hash::<~str>() as @map::<~str,~str>;
1010
let y: map<uint,~str> = x;
11-
//~^ ERROR mismatched types: expected `std::map::map<uint,~str>`
11+
//~^ ERROR mismatched types: expected `@std::map::map<uint,~str>`
1212
}

src/test/compile-fail/regions-bounds.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ fn a_fn1(e: an_enum/&a) -> an_enum/&b {
1111
}
1212

1313
fn a_fn2(e: a_trait/&a) -> a_trait/&b {
14-
return e; //~ ERROR mismatched types: expected `a_trait/&b` but found `a_trait/&a`
14+
return e; //~ ERROR mismatched types: expected `@a_trait/&b` but found `@a_trait/&a`
1515
}
1616

1717
fn a_fn3(e: a_class/&a) -> a_class/&b {
@@ -24,4 +24,4 @@ fn a_fn4(e: int/&a) -> int/&b {
2424
return e;
2525
}
2626

27-
fn main() { }
27+
fn main() { }

src/test/compile-fail/regions-infer-paramd-method.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ trait set_foo_foo {
1515

1616
impl with_foo: set_foo_foo {
1717
fn set_foo(f: foo) {
18-
self.f = f; //~ ERROR mismatched types: expected `foo/&self` but found `foo/&`
18+
self.f = f; //~ ERROR mismatched types: expected `@foo/&self` but found `@foo/&`
1919
}
2020
}
2121

src/test/compile-fail/regions-trait-2.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ impl has_ctxt: get_ctxt {
1313
fn make_gc() -> get_ctxt {
1414
let ctxt = { v: 22u };
1515
let hc = { c: &ctxt };
16-
return hc as get_ctxt; //~ ERROR mismatched types: expected `get_ctxt/&`
16+
return hc as get_ctxt; //~ ERROR mismatched types: expected `@get_ctxt/&`
1717
}
1818

1919
fn main() {

src/test/compile-fail/regions-trait-3.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@ trait get_ctxt {
33
}
44

55
fn make_gc1(gc: get_ctxt/&a) -> get_ctxt/&b {
6-
return gc; //~ ERROR mismatched types: expected `get_ctxt/&b` but found `get_ctxt/&a`
6+
return gc; //~ ERROR mismatched types: expected `@get_ctxt/&b` but found `@get_ctxt/&a`
77
}
88

99
fn make_gc2(gc: get_ctxt/&a) -> get_ctxt/&b {
10-
return gc as get_ctxt; //~ ERROR mismatched types: expected `get_ctxt/&b` but found `get_ctxt/&a`
10+
return gc as get_ctxt; //~ ERROR mismatched types: expected `@get_ctxt/&b` but found `@get_ctxt/&a`
1111
}
1212

1313
fn main() {

src/test/compile-fail/trait-cast.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ trait foo<T> { }
22

33
fn bar(x: foo<uint>) -> foo<int> {
44
return (x as foo::<int>);
5-
//~^ ERROR mismatched types: expected `foo<int>` but found `foo<uint>`
5+
//~^ ERROR mismatched types: expected `@foo<int>` but found `@foo<uint>`
66
}
77

88
fn main() {}

src/test/run-pass/auto-ref.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
struct Foo {
2+
x: int;
3+
}
4+
5+
trait Stuff {
6+
fn printme();
7+
}
8+
9+
impl &Foo : Stuff {
10+
fn printme() {
11+
io::println(fmt!("%d", self.x));
12+
}
13+
}
14+
15+
fn main() {
16+
let x = Foo { x: 3 };
17+
x.printme();
18+
}
19+

0 commit comments

Comments
 (0)