Skip to content

Commit 77c470d

Browse files
committed
Allow rcvrs to be borrowed; check rcvrs in borrowck properly
1 parent cfac9b6 commit 77c470d

File tree

11 files changed

+362
-92
lines changed

11 files changed

+362
-92
lines changed

src/rustc/middle/borrowck/check_loans.rs

Lines changed: 86 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -145,12 +145,22 @@ impl methods for check_loan_ctxt {
145145

146146
// when we are in a pure context, we check each call to ensure
147147
// that the function which is invoked is itself pure.
148-
fn check_pure_callee_or_arg(pc: purity_cause, expr: @ast::expr) {
148+
//
149+
// note: we take opt_expr and expr_id separately because for
150+
// overloaded operators the callee has an id but no expr.
151+
// annoying.
152+
fn check_pure_callee_or_arg(pc: purity_cause,
153+
opt_expr: option<@ast::expr>,
154+
callee_id: ast::node_id,
155+
callee_span: span) {
149156
let tcx = self.tcx();
150157

151-
#debug["check_pure_callee_or_arg(pc=%?, expr=%s, ty=%s)",
152-
pc, pprust::expr_to_str(expr),
153-
ty_to_str(self.tcx(), tcx.ty(expr))];
158+
#debug["check_pure_callee_or_arg(pc=%?, expr=%?, \
159+
callee_id=%d, ty=%s)",
160+
pc,
161+
opt_expr.map({|e| pprust::expr_to_str(e)}),
162+
callee_id,
163+
ty_to_str(self.tcx(), ty::node_id_to_type(tcx, callee_id))];
154164

155165
// Purity rules: an expr B is a legal callee or argument to a
156166
// call within a pure function A if at least one of the
@@ -161,29 +171,35 @@ impl methods for check_loan_ctxt {
161171
// (c) B is a pure fn;
162172
// (d) B is not a fn.
163173

164-
alt expr.node {
165-
ast::expr_path(_) if pc == pc_pure_fn {
166-
let def = self.tcx().def_map.get(expr.id);
167-
let did = ast_util::def_id_of_def(def);
168-
let is_fn_arg =
169-
did.crate == ast::local_crate &&
170-
self.fn_args.contains(did.node);
171-
if is_fn_arg { ret; } // case (a) above
172-
}
173-
ast::expr_fn_block(*) | ast::expr_fn(*) | ast::expr_loop_body(*) {
174-
if self.is_stack_closure(expr.id) { ret; } // case (b) above
174+
alt opt_expr {
175+
some(expr) {
176+
alt expr.node {
177+
ast::expr_path(_) if pc == pc_pure_fn {
178+
let def = self.tcx().def_map.get(expr.id);
179+
let did = ast_util::def_id_of_def(def);
180+
let is_fn_arg =
181+
did.crate == ast::local_crate &&
182+
self.fn_args.contains(did.node);
183+
if is_fn_arg { ret; } // case (a) above
184+
}
185+
ast::expr_fn_block(*) | ast::expr_fn(*) |
186+
ast::expr_loop_body(*) {
187+
if self.is_stack_closure(expr.id) { ret; } // case (b) above
188+
}
189+
_ {}
190+
}
175191
}
176-
_ {}
192+
none {}
177193
}
178194

179-
let expr_ty = tcx.ty(expr);
180-
alt ty::get(expr_ty).struct {
195+
let callee_ty = ty::node_id_to_type(tcx, callee_id);
196+
alt ty::get(callee_ty).struct {
181197
ty::ty_fn(fn_ty) {
182198
alt fn_ty.purity {
183199
ast::pure_fn { ret; } // case (c) above
184200
ast::impure_fn | ast::unsafe_fn | ast::crust_fn {
185201
self.report_purity_error(
186-
pc, expr.span,
202+
pc, callee_span,
187203
#fmt["access to %s function",
188204
pprust::purity_to_str(fn_ty.purity)]);
189205
}
@@ -429,6 +445,39 @@ impl methods for check_loan_ctxt {
429445
ret;
430446
}
431447
}
448+
449+
fn check_call(expr: @ast::expr,
450+
callee: option<@ast::expr>,
451+
callee_id: ast::node_id,
452+
callee_span: span,
453+
args: [@ast::expr]) {
454+
alt self.purity(expr.id) {
455+
none {}
456+
some(pc) {
457+
self.check_pure_callee_or_arg(
458+
pc, callee, callee_id, callee_span);
459+
for args.each { |arg|
460+
self.check_pure_callee_or_arg(
461+
pc, some(arg), arg.id, arg.span);
462+
}
463+
}
464+
}
465+
let arg_tys =
466+
ty::ty_fn_args(
467+
ty::node_id_to_type(self.tcx(), callee_id));
468+
vec::iter2(args, arg_tys) { |arg, arg_ty|
469+
alt ty::resolved_mode(self.tcx(), arg_ty.mode) {
470+
ast::by_move {
471+
self.check_move_out(arg);
472+
}
473+
ast::by_mutbl_ref {
474+
self.check_assignment(at_mutbl_ref, arg);
475+
}
476+
ast::by_ref | ast::by_copy | ast::by_val {
477+
}
478+
}
479+
}
480+
}
432481
}
433482

434483
fn check_loans_in_fn(fk: visit::fn_kind, decl: ast::fn_decl, body: ast::blk,
@@ -521,26 +570,24 @@ fn check_loans_in_expr(expr: @ast::expr,
521570
}
522571
}
523572
ast::expr_call(f, args, _) {
524-
alt self.purity(expr.id) {
525-
none {}
526-
some(pc) {
527-
self.check_pure_callee_or_arg(pc, f);
528-
for args.each { |arg| self.check_pure_callee_or_arg(pc, arg) }
529-
}
530-
}
531-
let arg_tys = ty::ty_fn_args(ty::expr_ty(self.tcx(), f));
532-
vec::iter2(args, arg_tys) { |arg, arg_ty|
533-
alt ty::resolved_mode(self.tcx(), arg_ty.mode) {
534-
ast::by_move {
535-
self.check_move_out(arg);
536-
}
537-
ast::by_mutbl_ref {
538-
self.check_assignment(at_mutbl_ref, arg);
539-
}
540-
ast::by_ref | ast::by_copy | ast::by_val {
541-
}
542-
}
543-
}
573+
self.check_call(expr, some(f), f.id, f.span, args);
574+
}
575+
ast::expr_index(_, rval) |
576+
ast::expr_binary(_, _, rval)
577+
if self.bccx.method_map.contains_key(expr.id) {
578+
self.check_call(expr,
579+
none,
580+
ast_util::op_expr_callee_id(expr),
581+
expr.span,
582+
[rval]);
583+
}
584+
ast::expr_unary(_, _)
585+
if self.bccx.method_map.contains_key(expr.id) {
586+
self.check_call(expr,
587+
none,
588+
ast_util::op_expr_callee_id(expr),
589+
expr.span,
590+
[]);
544591
}
545592
_ { }
546593
}

src/rustc/middle/borrowck/gather_loans.rs

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -112,19 +112,33 @@ fn req_loans_in_expr(ex: @ast::expr,
112112
}
113113
}
114114

115-
ast::expr_field(rcvr, _, _) |
115+
ast::expr_index(rcvr, _) |
116116
ast::expr_binary(_, rcvr, _) |
117117
ast::expr_unary(_, rcvr) if self.bccx.method_map.contains_key(ex.id) {
118118
// Receivers in method calls are always passed by ref.
119119
//
120-
// FIXME--this scope is both too large and too small. We make
121-
// the scope the enclosing block, which surely includes any
122-
// immediate call (a.b()) but which is too big. OTOH, in the
123-
// case of a naked field `a.b`, the value is copied
124-
// anyhow. This is probably best fixed if we address the
125-
// syntactic ambiguity.
126-
127-
// let scope_r = ty::re_scope(ex.id);
120+
// Here, in an overloaded operator, the call is this expression,
121+
// and hence the scope of the borrow is this call.
122+
//
123+
// FIXME/NOT REALLY---technically we should check the other
124+
// argument and consider the argument mode. But how annoying.
125+
// And this problem when goes away when argument modes are
126+
// phased out. So I elect to leave this undone.
127+
let scope_r = ty::re_scope(ex.id);
128+
let rcvr_cmt = self.bccx.cat_expr(rcvr);
129+
self.guarantee_valid(rcvr_cmt, m_imm, scope_r);
130+
}
131+
132+
ast::expr_field(rcvr, _, _)
133+
if self.bccx.method_map.contains_key(ex.id) {
134+
// Receivers in method calls are always passed by ref.
135+
//
136+
// Here, the field a.b is in fact a closure. Eventually, this
137+
// should be an fn&, but for now it's an fn@. In any case,
138+
// the enclosing scope is either the call where it is a rcvr
139+
// (if used like `a.b(...)`), the call where it's an argument
140+
// (if used like `x(a.b)`), or the block (if used like `let x
141+
// = a.b`).
128142
let scope_r = ty::re_scope(self.tcx().region_map.get(ex.id));
129143
let rcvr_cmt = self.bccx.cat_expr(rcvr);
130144
self.guarantee_valid(rcvr_cmt, m_imm, scope_r);

src/rustc/middle/trans/base.rs

Lines changed: 30 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2883,7 +2883,7 @@ fn trans_arg_expr(cx: block, arg: ty::arg, lldestty: TypeRef, e: @ast::expr,
28832883
none { trans_temp_lval(cx, e) }
28842884
};
28852885
#debug(" pre-adaptation value: %s", val_str(lv.bcx.ccx().tn, lv.val));
2886-
let lv = adapt_borrowed_value(lv, arg, e);
2886+
let {lv, arg} = adapt_borrowed_value(lv, arg, e);
28872887
let mut bcx = lv.bcx;
28882888
let mut val = lv.val;
28892889
#debug(" adapted value: %s", val_str(bcx.ccx().tn, val));
@@ -2897,6 +2897,8 @@ fn trans_arg_expr(cx: block, arg: ty::arg, lldestty: TypeRef, e: @ast::expr,
28972897
} else if arg_mode == ast::by_ref || arg_mode == ast::by_val {
28982898
let mut copied = false;
28992899
let imm = ty::type_is_immediate(arg.ty);
2900+
#debug[" arg.ty=%s, imm=%b, arg_mode=%?, lv.kind=%?",
2901+
ty_to_str(bcx.tcx(), arg.ty), imm, arg_mode, lv.kind];
29002902
if arg_mode == ast::by_ref && lv.kind != owned && imm {
29012903
val = do_spill_noroot(bcx, val);
29022904
copied = true;
@@ -2953,29 +2955,29 @@ fn load_value_from_lval_result(lv: lval_result) -> ValueRef {
29532955
}
29542956
}
29552957

2956-
fn adapt_borrowed_value(lv: lval_result, _arg: ty::arg,
2957-
e: @ast::expr) -> lval_result {
2958+
// when invoking a method, an argument of type @T or ~T can be implicltly
2959+
// converted to an argument of type &T. Similarly, [T] can be converted to
2960+
// [T]/& and so on. If such a conversion (called borrowing) is necessary,
2961+
// then the borrowings table will have an appropriate entry inserted. This
2962+
// routine consults this table and performs these adaptations. It returns a
2963+
// new location for the borrowed result as well as a new type for the argument
2964+
// that reflects the borrowed value and not the original.
2965+
fn adapt_borrowed_value(lv: lval_result, arg: ty::arg,
2966+
e: @ast::expr) -> {lv: lval_result,
2967+
arg: ty::arg} {
29582968
let bcx = lv.bcx;
2959-
if !expr_is_borrowed(bcx, e) { ret lv; }
2969+
if !expr_is_borrowed(bcx, e) {
2970+
ret {lv:lv, arg:arg};
2971+
}
29602972

29612973
let e_ty = expr_ty(bcx, e);
29622974
alt ty::get(e_ty).struct {
2963-
ty::ty_box(mt) {
2975+
ty::ty_uniq(mt) | ty::ty_box(mt) {
29642976
let box_ptr = load_value_from_lval_result(lv);
29652977
let body_ptr = GEPi(bcx, box_ptr, [0u, abi::box_field_body]);
2966-
ret lval_temp(bcx, body_ptr);
2967-
}
2968-
2969-
ty::ty_uniq(_) {
2970-
let box_ptr = {
2971-
alt lv.kind {
2972-
temporary { lv.val }
2973-
owned { Load(bcx, lv.val) }
2974-
owned_imm { lv.val }
2975-
}
2976-
};
2977-
let body_ptr = GEPi(bcx, box_ptr, [0u, abi::box_field_body]);
2978-
ret lval_temp(bcx, body_ptr);
2978+
let rptr_ty = ty::mk_rptr(bcx.tcx(), ty::re_static, mt);
2979+
ret {lv: lval_temp(bcx, body_ptr),
2980+
arg: {ty: rptr_ty with arg}};
29792981
}
29802982

29812983
ty::ty_str | ty::ty_vec(_) |
@@ -2999,7 +3001,16 @@ fn adapt_borrowed_value(lv: lval_result, _arg: ty::arg,
29993001

30003002
Store(bcx, base, GEPi(bcx, p, [0u, abi::slice_elt_base]));
30013003
Store(bcx, len, GEPi(bcx, p, [0u, abi::slice_elt_len]));
3002-
ret lval_temp(bcx, p);
3004+
3005+
// this isn't necessarily the type that rust would assign but it's
3006+
// close enough for trans purposes, as it will have the same runtime
3007+
// representation
3008+
let slice_ty = ty::mk_evec(bcx.tcx(),
3009+
{ty: unit_ty, mutbl: ast::m_imm},
3010+
ty::vstore_slice(ty::re_static));
3011+
3012+
ret {lv: lval_temp(bcx, p),
3013+
arg: {ty: slice_ty with arg}};
30033014
}
30043015

30053016
_ {

0 commit comments

Comments
 (0)