Allow rcvrs to be borrowed; check rcvrs in borrowck properly
This commit is contained in:
parent
cfac9b6833
commit
77c470d183
11 changed files with 361 additions and 91 deletions
|
@ -145,12 +145,22 @@ impl methods for check_loan_ctxt {
|
|||
|
||||
// when we are in a pure context, we check each call to ensure
|
||||
// that the function which is invoked is itself pure.
|
||||
fn check_pure_callee_or_arg(pc: purity_cause, expr: @ast::expr) {
|
||||
//
|
||||
// note: we take opt_expr and expr_id separately because for
|
||||
// overloaded operators the callee has an id but no expr.
|
||||
// annoying.
|
||||
fn check_pure_callee_or_arg(pc: purity_cause,
|
||||
opt_expr: option<@ast::expr>,
|
||||
callee_id: ast::node_id,
|
||||
callee_span: span) {
|
||||
let tcx = self.tcx();
|
||||
|
||||
#debug["check_pure_callee_or_arg(pc=%?, expr=%s, ty=%s)",
|
||||
pc, pprust::expr_to_str(expr),
|
||||
ty_to_str(self.tcx(), tcx.ty(expr))];
|
||||
#debug["check_pure_callee_or_arg(pc=%?, expr=%?, \
|
||||
callee_id=%d, ty=%s)",
|
||||
pc,
|
||||
opt_expr.map({|e| pprust::expr_to_str(e)}),
|
||||
callee_id,
|
||||
ty_to_str(self.tcx(), ty::node_id_to_type(tcx, callee_id))];
|
||||
|
||||
// Purity rules: an expr B is a legal callee or argument to a
|
||||
// call within a pure function A if at least one of the
|
||||
|
@ -161,29 +171,35 @@ impl methods for check_loan_ctxt {
|
|||
// (c) B is a pure fn;
|
||||
// (d) B is not a fn.
|
||||
|
||||
alt expr.node {
|
||||
ast::expr_path(_) if pc == pc_pure_fn {
|
||||
let def = self.tcx().def_map.get(expr.id);
|
||||
let did = ast_util::def_id_of_def(def);
|
||||
let is_fn_arg =
|
||||
did.crate == ast::local_crate &&
|
||||
self.fn_args.contains(did.node);
|
||||
if is_fn_arg { ret; } // case (a) above
|
||||
alt opt_expr {
|
||||
some(expr) {
|
||||
alt expr.node {
|
||||
ast::expr_path(_) if pc == pc_pure_fn {
|
||||
let def = self.tcx().def_map.get(expr.id);
|
||||
let did = ast_util::def_id_of_def(def);
|
||||
let is_fn_arg =
|
||||
did.crate == ast::local_crate &&
|
||||
self.fn_args.contains(did.node);
|
||||
if is_fn_arg { ret; } // case (a) above
|
||||
}
|
||||
ast::expr_fn_block(*) | ast::expr_fn(*) |
|
||||
ast::expr_loop_body(*) {
|
||||
if self.is_stack_closure(expr.id) { ret; } // case (b) above
|
||||
}
|
||||
_ {}
|
||||
}
|
||||
}
|
||||
ast::expr_fn_block(*) | ast::expr_fn(*) | ast::expr_loop_body(*) {
|
||||
if self.is_stack_closure(expr.id) { ret; } // case (b) above
|
||||
}
|
||||
_ {}
|
||||
none {}
|
||||
}
|
||||
|
||||
let expr_ty = tcx.ty(expr);
|
||||
alt ty::get(expr_ty).struct {
|
||||
let callee_ty = ty::node_id_to_type(tcx, callee_id);
|
||||
alt ty::get(callee_ty).struct {
|
||||
ty::ty_fn(fn_ty) {
|
||||
alt fn_ty.purity {
|
||||
ast::pure_fn { ret; } // case (c) above
|
||||
ast::impure_fn | ast::unsafe_fn | ast::crust_fn {
|
||||
self.report_purity_error(
|
||||
pc, expr.span,
|
||||
pc, callee_span,
|
||||
#fmt["access to %s function",
|
||||
pprust::purity_to_str(fn_ty.purity)]);
|
||||
}
|
||||
|
@ -429,6 +445,39 @@ impl methods for check_loan_ctxt {
|
|||
ret;
|
||||
}
|
||||
}
|
||||
|
||||
fn check_call(expr: @ast::expr,
|
||||
callee: option<@ast::expr>,
|
||||
callee_id: ast::node_id,
|
||||
callee_span: span,
|
||||
args: [@ast::expr]) {
|
||||
alt self.purity(expr.id) {
|
||||
none {}
|
||||
some(pc) {
|
||||
self.check_pure_callee_or_arg(
|
||||
pc, callee, callee_id, callee_span);
|
||||
for args.each { |arg|
|
||||
self.check_pure_callee_or_arg(
|
||||
pc, some(arg), arg.id, arg.span);
|
||||
}
|
||||
}
|
||||
}
|
||||
let arg_tys =
|
||||
ty::ty_fn_args(
|
||||
ty::node_id_to_type(self.tcx(), callee_id));
|
||||
vec::iter2(args, arg_tys) { |arg, arg_ty|
|
||||
alt ty::resolved_mode(self.tcx(), arg_ty.mode) {
|
||||
ast::by_move {
|
||||
self.check_move_out(arg);
|
||||
}
|
||||
ast::by_mutbl_ref {
|
||||
self.check_assignment(at_mutbl_ref, arg);
|
||||
}
|
||||
ast::by_ref | ast::by_copy | ast::by_val {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
|||
}
|
||||
}
|
||||
ast::expr_call(f, args, _) {
|
||||
alt self.purity(expr.id) {
|
||||
none {}
|
||||
some(pc) {
|
||||
self.check_pure_callee_or_arg(pc, f);
|
||||
for args.each { |arg| self.check_pure_callee_or_arg(pc, arg) }
|
||||
}
|
||||
}
|
||||
let arg_tys = ty::ty_fn_args(ty::expr_ty(self.tcx(), f));
|
||||
vec::iter2(args, arg_tys) { |arg, arg_ty|
|
||||
alt ty::resolved_mode(self.tcx(), arg_ty.mode) {
|
||||
ast::by_move {
|
||||
self.check_move_out(arg);
|
||||
}
|
||||
ast::by_mutbl_ref {
|
||||
self.check_assignment(at_mutbl_ref, arg);
|
||||
}
|
||||
ast::by_ref | ast::by_copy | ast::by_val {
|
||||
}
|
||||
}
|
||||
}
|
||||
self.check_call(expr, some(f), f.id, f.span, args);
|
||||
}
|
||||
ast::expr_index(_, rval) |
|
||||
ast::expr_binary(_, _, rval)
|
||||
if self.bccx.method_map.contains_key(expr.id) {
|
||||
self.check_call(expr,
|
||||
none,
|
||||
ast_util::op_expr_callee_id(expr),
|
||||
expr.span,
|
||||
[rval]);
|
||||
}
|
||||
ast::expr_unary(_, _)
|
||||
if self.bccx.method_map.contains_key(expr.id) {
|
||||
self.check_call(expr,
|
||||
none,
|
||||
ast_util::op_expr_callee_id(expr),
|
||||
expr.span,
|
||||
[]);
|
||||
}
|
||||
_ { }
|
||||
}
|
||||
|
|
|
@ -112,19 +112,33 @@ fn req_loans_in_expr(ex: @ast::expr,
|
|||
}
|
||||
}
|
||||
|
||||
ast::expr_field(rcvr, _, _) |
|
||||
ast::expr_index(rcvr, _) |
|
||||
ast::expr_binary(_, rcvr, _) |
|
||||
ast::expr_unary(_, rcvr) if self.bccx.method_map.contains_key(ex.id) {
|
||||
// Receivers in method calls are always passed by ref.
|
||||
//
|
||||
// FIXME--this scope is both too large and too small. We make
|
||||
// the scope the enclosing block, which surely includes any
|
||||
// immediate call (a.b()) but which is too big. OTOH, in the
|
||||
// case of a naked field `a.b`, the value is copied
|
||||
// anyhow. This is probably best fixed if we address the
|
||||
// syntactic ambiguity.
|
||||
// Here, in an overloaded operator, the call is this expression,
|
||||
// and hence the scope of the borrow is this call.
|
||||
//
|
||||
// FIXME/NOT REALLY---technically we should check the other
|
||||
// argument and consider the argument mode. But how annoying.
|
||||
// And this problem when goes away when argument modes are
|
||||
// phased out. So I elect to leave this undone.
|
||||
let scope_r = ty::re_scope(ex.id);
|
||||
let rcvr_cmt = self.bccx.cat_expr(rcvr);
|
||||
self.guarantee_valid(rcvr_cmt, m_imm, scope_r);
|
||||
}
|
||||
|
||||
// let scope_r = ty::re_scope(ex.id);
|
||||
ast::expr_field(rcvr, _, _)
|
||||
if self.bccx.method_map.contains_key(ex.id) {
|
||||
// Receivers in method calls are always passed by ref.
|
||||
//
|
||||
// Here, the field a.b is in fact a closure. Eventually, this
|
||||
// should be an fn&, but for now it's an fn@. In any case,
|
||||
// the enclosing scope is either the call where it is a rcvr
|
||||
// (if used like `a.b(...)`), the call where it's an argument
|
||||
// (if used like `x(a.b)`), or the block (if used like `let x
|
||||
// = a.b`).
|
||||
let scope_r = ty::re_scope(self.tcx().region_map.get(ex.id));
|
||||
let rcvr_cmt = self.bccx.cat_expr(rcvr);
|
||||
self.guarantee_valid(rcvr_cmt, m_imm, scope_r);
|
||||
|
|
|
@ -2883,7 +2883,7 @@ fn trans_arg_expr(cx: block, arg: ty::arg, lldestty: TypeRef, e: @ast::expr,
|
|||
none { trans_temp_lval(cx, e) }
|
||||
};
|
||||
#debug(" pre-adaptation value: %s", val_str(lv.bcx.ccx().tn, lv.val));
|
||||
let lv = adapt_borrowed_value(lv, arg, e);
|
||||
let {lv, arg} = adapt_borrowed_value(lv, arg, e);
|
||||
let mut bcx = lv.bcx;
|
||||
let mut val = lv.val;
|
||||
#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,
|
|||
} else if arg_mode == ast::by_ref || arg_mode == ast::by_val {
|
||||
let mut copied = false;
|
||||
let imm = ty::type_is_immediate(arg.ty);
|
||||
#debug[" arg.ty=%s, imm=%b, arg_mode=%?, lv.kind=%?",
|
||||
ty_to_str(bcx.tcx(), arg.ty), imm, arg_mode, lv.kind];
|
||||
if arg_mode == ast::by_ref && lv.kind != owned && imm {
|
||||
val = do_spill_noroot(bcx, val);
|
||||
copied = true;
|
||||
|
@ -2953,29 +2955,29 @@ fn load_value_from_lval_result(lv: lval_result) -> ValueRef {
|
|||
}
|
||||
}
|
||||
|
||||
fn adapt_borrowed_value(lv: lval_result, _arg: ty::arg,
|
||||
e: @ast::expr) -> lval_result {
|
||||
// when invoking a method, an argument of type @T or ~T can be implicltly
|
||||
// converted to an argument of type &T. Similarly, [T] can be converted to
|
||||
// [T]/& and so on. If such a conversion (called borrowing) is necessary,
|
||||
// then the borrowings table will have an appropriate entry inserted. This
|
||||
// routine consults this table and performs these adaptations. It returns a
|
||||
// new location for the borrowed result as well as a new type for the argument
|
||||
// that reflects the borrowed value and not the original.
|
||||
fn adapt_borrowed_value(lv: lval_result, arg: ty::arg,
|
||||
e: @ast::expr) -> {lv: lval_result,
|
||||
arg: ty::arg} {
|
||||
let bcx = lv.bcx;
|
||||
if !expr_is_borrowed(bcx, e) { ret lv; }
|
||||
if !expr_is_borrowed(bcx, e) {
|
||||
ret {lv:lv, arg:arg};
|
||||
}
|
||||
|
||||
let e_ty = expr_ty(bcx, e);
|
||||
alt ty::get(e_ty).struct {
|
||||
ty::ty_box(mt) {
|
||||
ty::ty_uniq(mt) | ty::ty_box(mt) {
|
||||
let box_ptr = load_value_from_lval_result(lv);
|
||||
let body_ptr = GEPi(bcx, box_ptr, [0u, abi::box_field_body]);
|
||||
ret lval_temp(bcx, body_ptr);
|
||||
}
|
||||
|
||||
ty::ty_uniq(_) {
|
||||
let box_ptr = {
|
||||
alt lv.kind {
|
||||
temporary { lv.val }
|
||||
owned { Load(bcx, lv.val) }
|
||||
owned_imm { lv.val }
|
||||
}
|
||||
};
|
||||
let body_ptr = GEPi(bcx, box_ptr, [0u, abi::box_field_body]);
|
||||
ret lval_temp(bcx, body_ptr);
|
||||
let rptr_ty = ty::mk_rptr(bcx.tcx(), ty::re_static, mt);
|
||||
ret {lv: lval_temp(bcx, body_ptr),
|
||||
arg: {ty: rptr_ty with arg}};
|
||||
}
|
||||
|
||||
ty::ty_str | ty::ty_vec(_) |
|
||||
|
@ -2999,7 +3001,16 @@ fn adapt_borrowed_value(lv: lval_result, _arg: ty::arg,
|
|||
|
||||
Store(bcx, base, GEPi(bcx, p, [0u, abi::slice_elt_base]));
|
||||
Store(bcx, len, GEPi(bcx, p, [0u, abi::slice_elt_len]));
|
||||
ret lval_temp(bcx, p);
|
||||
|
||||
// this isn't necessarily the type that rust would assign but it's
|
||||
// close enough for trans purposes, as it will have the same runtime
|
||||
// representation
|
||||
let slice_ty = ty::mk_evec(bcx.tcx(),
|
||||
{ty: unit_ty, mutbl: ast::m_imm},
|
||||
ty::vstore_slice(ty::re_static));
|
||||
|
||||
ret {lv: lval_temp(bcx, p),
|
||||
arg: {ty: slice_ty with arg}};
|
||||
}
|
||||
|
||||
_ {
|
||||
|
|
|
@ -515,6 +515,18 @@ impl methods for @fn_ctxt {
|
|||
infer::can_mk_subty(self.infcx, sub, sup)
|
||||
}
|
||||
|
||||
fn mk_assignty(expr: @ast::expr, borrow_scope: ast::node_id,
|
||||
sub: ty::t, sup: ty::t) -> result<(), ty::type_err> {
|
||||
let anmnt = {expr_id: expr.id, borrow_scope: borrow_scope};
|
||||
infer::mk_assignty(self.infcx, anmnt, sub, sup)
|
||||
}
|
||||
|
||||
fn can_mk_assignty(expr: @ast::expr, borrow_scope: ast::node_id,
|
||||
sub: ty::t, sup: ty::t) -> result<(), ty::type_err> {
|
||||
let anmnt = {expr_id: expr.id, borrow_scope: borrow_scope};
|
||||
infer::can_mk_assignty(self.infcx, anmnt, sub, sup)
|
||||
}
|
||||
|
||||
fn mk_eqty(sub: ty::t, sup: ty::t) -> result<(), ty::type_err> {
|
||||
infer::mk_eqty(self.infcx, sub, sup)
|
||||
}
|
||||
|
@ -867,12 +879,15 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
|
|||
_ { none }
|
||||
}
|
||||
}
|
||||
fn lookup_op_method(fcx: @fn_ctxt, op_ex: @ast::expr, self_t: ty::t,
|
||||
fn lookup_op_method(fcx: @fn_ctxt, op_ex: @ast::expr,
|
||||
self_ex: @ast::expr, self_t: ty::t,
|
||||
opname: str, args: [option<@ast::expr>])
|
||||
-> option<(ty::t, bool)> {
|
||||
let callee_id = ast_util::op_expr_callee_id(op_ex);
|
||||
let lkup = method::lookup({fcx: fcx,
|
||||
expr: op_ex,
|
||||
self_expr: self_ex,
|
||||
borrow_scope: op_ex.id,
|
||||
node_id: callee_id,
|
||||
m_name: opname,
|
||||
self_ty: self_t,
|
||||
|
@ -950,18 +965,21 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
|
|||
|
||||
(_, _) {
|
||||
let (result, rhs_bot) =
|
||||
check_user_binop(fcx, expr, lhs_t, op, rhs);
|
||||
check_user_binop(fcx, expr, lhs, lhs_t, op, rhs);
|
||||
fcx.write_ty(expr.id, result);
|
||||
lhs_bot | rhs_bot
|
||||
}
|
||||
};
|
||||
}
|
||||
fn check_user_binop(fcx: @fn_ctxt, ex: @ast::expr, lhs_resolved_t: ty::t,
|
||||
fn check_user_binop(fcx: @fn_ctxt, ex: @ast::expr,
|
||||
lhs_expr: @ast::expr, lhs_resolved_t: ty::t,
|
||||
op: ast::binop, rhs: @ast::expr) -> (ty::t, bool) {
|
||||
let tcx = fcx.ccx.tcx;
|
||||
alt binop_method(op) {
|
||||
some(name) {
|
||||
alt lookup_op_method(fcx, ex, lhs_resolved_t, name, [some(rhs)]) {
|
||||
alt lookup_op_method(fcx, ex,
|
||||
lhs_expr, lhs_resolved_t,
|
||||
name, [some(rhs)]) {
|
||||
some(pair) { ret pair; }
|
||||
_ {}
|
||||
}
|
||||
|
@ -977,8 +995,9 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
|
|||
(lhs_resolved_t, false)
|
||||
}
|
||||
fn check_user_unop(fcx: @fn_ctxt, op_str: str, mname: str,
|
||||
ex: @ast::expr, rhs_t: ty::t) -> ty::t {
|
||||
alt lookup_op_method(fcx, ex, rhs_t, mname, []) {
|
||||
ex: @ast::expr,
|
||||
rhs_expr: @ast::expr, rhs_t: ty::t) -> ty::t {
|
||||
alt lookup_op_method(fcx, ex, rhs_expr, rhs_t, mname, []) {
|
||||
some((ret_ty, _)) { ret_ty }
|
||||
_ {
|
||||
fcx.ccx.tcx.sess.span_err(
|
||||
|
@ -1161,14 +1180,16 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
|
|||
oper_t = structurally_resolved_type(fcx, oper.span, oper_t);
|
||||
if !(ty::type_is_integral(oper_t) ||
|
||||
ty::get(oper_t).struct == ty::ty_bool) {
|
||||
oper_t = check_user_unop(fcx, "!", "!", expr, oper_t);
|
||||
oper_t = check_user_unop(fcx, "!", "!", expr,
|
||||
oper, oper_t);
|
||||
}
|
||||
}
|
||||
ast::neg {
|
||||
oper_t = structurally_resolved_type(fcx, oper.span, oper_t);
|
||||
if !(ty::type_is_integral(oper_t) ||
|
||||
ty::type_is_fp(oper_t)) {
|
||||
oper_t = check_user_unop(fcx, "-", "unary-", expr, oper_t);
|
||||
oper_t = check_user_unop(fcx, "-", "unary-", expr,
|
||||
oper, oper_t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1554,13 +1575,20 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
|
|||
if !handled {
|
||||
let tps = vec::map(tys) { |ty| fcx.to_ty(ty) };
|
||||
let is_self_ref = self_ref(fcx, base.id);
|
||||
|
||||
// this will be the call or block that immediately
|
||||
// encloses the method call
|
||||
let borrow_scope = fcx.tcx().region_map.get(expr.id);
|
||||
|
||||
let lkup = method::lookup({fcx: fcx,
|
||||
expr: expr,
|
||||
node_id: expr.id,
|
||||
m_name: field,
|
||||
self_ty: expr_t,
|
||||
supplied_tps: tps,
|
||||
include_private: is_self_ref});
|
||||
expr: expr,
|
||||
self_expr: base,
|
||||
borrow_scope: borrow_scope,
|
||||
node_id: expr.id,
|
||||
m_name: field,
|
||||
self_ty: expr_t,
|
||||
supplied_tps: tps,
|
||||
include_private: is_self_ref});
|
||||
alt lkup.method() {
|
||||
some(origin) {
|
||||
fcx.ccx.method_map.insert(id, origin);
|
||||
|
@ -1591,7 +1619,7 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
|
|||
none {
|
||||
let resolved = structurally_resolved_type(fcx, expr.span,
|
||||
raw_base_t);
|
||||
alt lookup_op_method(fcx, expr, resolved, "[]",
|
||||
alt lookup_op_method(fcx, expr, base, resolved, "[]",
|
||||
[some(idx)]) {
|
||||
some((ret_ty, _)) { fcx.write_ty(id, ret_ty); }
|
||||
_ {
|
||||
|
@ -1611,6 +1639,8 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
|
|||
|
||||
let lkup = method::lookup({fcx: fcx,
|
||||
expr: p,
|
||||
self_expr: p,
|
||||
borrow_scope: expr.id,
|
||||
node_id: alloc_id,
|
||||
m_name: "alloc",
|
||||
self_ty: p_ty,
|
||||
|
|
|
@ -6,6 +6,8 @@ import middle::typeck::infer::methods; // next_ty_vars
|
|||
enum lookup = {
|
||||
fcx: @fn_ctxt,
|
||||
expr: @ast::expr, // expr for a.b in a.b()
|
||||
self_expr: @ast::expr, // a in a.b(...)
|
||||
borrow_scope: ast::node_id, // if we have to borrow the expr, what scope?
|
||||
node_id: ast::node_id, // node id of call (not always expr.id)
|
||||
m_name: ast::ident, // b in a.b(...)
|
||||
self_ty: ty::t, // type of a in a.b(...)
|
||||
|
@ -207,7 +209,9 @@ impl methods for lookup {
|
|||
|
||||
// if we can assign the caller to the callee, that's a
|
||||
// potential match. Collect those in the vector.
|
||||
alt self.fcx.can_mk_subty(self.self_ty, impl_ty) {
|
||||
alt self.fcx.can_mk_assignty(
|
||||
self.self_expr, self.borrow_scope,
|
||||
self.self_ty, impl_ty) {
|
||||
result::err(_) { /* keep looking */ }
|
||||
result::ok(_) {
|
||||
results += [(impl_ty, impl_substs, m.n_tps, m.did)];
|
||||
|
@ -243,12 +247,15 @@ impl methods for lookup {
|
|||
}
|
||||
|
||||
let (impl_ty, impl_substs, n_tps, did) = results[0];
|
||||
alt self.fcx.mk_subty(self.self_ty, impl_ty) {
|
||||
alt self.fcx.mk_assignty(self.self_expr, self.borrow_scope,
|
||||
self.self_ty, impl_ty) {
|
||||
result::ok(_) {}
|
||||
result::err(_) {
|
||||
self.tcx().sess.span_bug(
|
||||
self.expr.span,
|
||||
"what was a subtype now is not?");
|
||||
#fmt["%s was assignable to %s but now is not?",
|
||||
self.fcx.infcx.ty_to_str(self.self_ty),
|
||||
self.fcx.infcx.ty_to_str(impl_ty)]);
|
||||
}
|
||||
}
|
||||
let fty = self.ty_from_did(did);
|
||||
|
|
|
@ -164,13 +164,14 @@ export new_infer_ctxt;
|
|||
export mk_subty, can_mk_subty;
|
||||
export mk_subr;
|
||||
export mk_eqty;
|
||||
export mk_assignty;
|
||||
export mk_assignty, can_mk_assignty;
|
||||
export resolve_shallow;
|
||||
export resolve_deep;
|
||||
export resolve_deep_var;
|
||||
export methods; // for infer_ctxt
|
||||
export compare_tys;
|
||||
export fixup_err, fixup_err_to_str;
|
||||
export assignment;
|
||||
|
||||
// Extra information needed to perform an assignment that may borrow.
|
||||
// The `expr_id` is the is of the expression whose type is being
|
||||
|
@ -260,6 +261,21 @@ fn mk_assignty(cx: infer_ctxt, anmnt: assignment,
|
|||
} }.to_ures()
|
||||
}
|
||||
|
||||
fn can_mk_assignty(cx: infer_ctxt, anmnt: assignment,
|
||||
a: ty::t, b: ty::t) -> ures {
|
||||
#debug["can_mk_assignty(%? / %s <: %s)",
|
||||
anmnt, a.to_str(cx), b.to_str(cx)];
|
||||
|
||||
// FIXME---this will not unroll any entries we make in the
|
||||
// borrowings table. But this is OK for the moment because this
|
||||
// is only used in method lookup, and there must be exactly one
|
||||
// match or an error is reported. Still, it should be fixed.
|
||||
|
||||
indent {|| cx.probe {||
|
||||
cx.assign_tys(anmnt, a, b)
|
||||
} }.to_ures()
|
||||
}
|
||||
|
||||
fn compare_tys(tcx: ty::ctxt, a: ty::t, b: ty::t) -> ures {
|
||||
let infcx = new_infer_ctxt(tcx);
|
||||
mk_eqty(infcx, a, b)
|
||||
|
|
47
src/test/compile-fail/borrowck-loan-rcvr-overloaded-op.rs
Normal file
47
src/test/compile-fail/borrowck-loan-rcvr-overloaded-op.rs
Normal file
|
@ -0,0 +1,47 @@
|
|||
// xfail-fast (compile-flags unsupported on windows)
|
||||
// compile-flags:--borrowck=err
|
||||
|
||||
type point = { x: int, y: int };
|
||||
|
||||
impl foo for point {
|
||||
pure fn +(z: int) -> int { self.x + self.y + z }
|
||||
fn *(z: int) -> int { self.x * self.y * z }
|
||||
}
|
||||
|
||||
fn a() {
|
||||
let mut p = {x: 3, y: 4};
|
||||
|
||||
// ok (we can loan out rcvr)
|
||||
p + 3;
|
||||
p * 3;
|
||||
}
|
||||
|
||||
fn b() {
|
||||
let mut p = {x: 3, y: 4};
|
||||
|
||||
// Here I create an outstanding loan and check that we get conflicts:
|
||||
|
||||
&mut p; //! NOTE prior loan as mutable granted here
|
||||
//!^ NOTE prior loan as mutable granted here
|
||||
|
||||
p + 3; //! ERROR loan of mutable local variable as immutable conflicts with prior loan
|
||||
p * 3; //! ERROR loan of mutable local variable as immutable conflicts with prior loan
|
||||
}
|
||||
|
||||
fn c() {
|
||||
// Here the receiver is in aliased memory and hence we cannot
|
||||
// consider it immutable:
|
||||
let q = @mut {x: 3, y: 4};
|
||||
|
||||
// ...this is ok for pure fns
|
||||
*q + 3;
|
||||
|
||||
|
||||
// ...but not impure fns
|
||||
*q * 3; //! ERROR illegal borrow unless pure: creating immutable alias to aliasable, mutable memory
|
||||
//!^ NOTE impure due to access to impure function
|
||||
}
|
||||
|
||||
fn main() {
|
||||
}
|
||||
|
|
@ -7,26 +7,31 @@ impl foo for point {
|
|||
fn impurem() {
|
||||
}
|
||||
|
||||
fn blockm(f: fn()) { f() }
|
||||
|
||||
pure fn purem() {
|
||||
}
|
||||
}
|
||||
|
||||
fn a() {
|
||||
let mut p = {x: 3, y: 4};
|
||||
p.purem();
|
||||
p.impurem();
|
||||
}
|
||||
|
||||
fn a2() {
|
||||
let mut p = {x: 3, y: 4};
|
||||
// Here: it's ok to call even though receiver is mutable, because we
|
||||
// can loan it out.
|
||||
p.purem();
|
||||
p.impurem();
|
||||
p.x = p.y;
|
||||
|
||||
// But in this case we do not honor the loan:
|
||||
p.blockm {|| //! NOTE loan of mutable local variable granted here
|
||||
p.x = 10; //! ERROR assigning to mutable field prohibited due to outstanding loan
|
||||
}
|
||||
}
|
||||
|
||||
fn b() {
|
||||
let mut p = {x: 3, y: 4};
|
||||
|
||||
// Here I create an outstanding loan and check that we get conflicts:
|
||||
|
||||
&mut p; //! NOTE prior loan as mutable granted here
|
||||
//!^ NOTE prior loan as mutable granted here
|
||||
|
||||
|
@ -35,8 +40,14 @@ fn b() {
|
|||
}
|
||||
|
||||
fn c() {
|
||||
// Here the receiver is in aliased memory and hence we cannot
|
||||
// consider it immutable:
|
||||
let q = @mut {x: 3, y: 4};
|
||||
|
||||
// ...this is ok for pure fns
|
||||
(*q).purem();
|
||||
|
||||
// ...but not impure fns
|
||||
(*q).impurem(); //! ERROR illegal borrow unless pure: creating immutable alias to aliasable, mutable memory
|
||||
//!^ NOTE impure due to access to impure function
|
||||
}
|
||||
|
|
26
src/test/compile-fail/pure-overloaded-op.rs
Normal file
26
src/test/compile-fail/pure-overloaded-op.rs
Normal file
|
@ -0,0 +1,26 @@
|
|||
type point = { x: int, y: int };
|
||||
|
||||
impl foo for point {
|
||||
// expr_binary
|
||||
pure fn +(z: int) -> int { self.x + self.y + z }
|
||||
fn *(z: int) -> int { self.x * self.y * z }
|
||||
|
||||
// expr_index
|
||||
fn [](z: int) -> int { self.x * self.y * z }
|
||||
|
||||
// expr_unary
|
||||
fn unary-() -> int { -(self.x * self.y) }
|
||||
}
|
||||
|
||||
pure fn a(p: point) -> int { p + 3 }
|
||||
|
||||
pure fn b(p: point) -> int { p * 3 }
|
||||
//!^ ERROR access to impure function prohibited in pure context
|
||||
|
||||
pure fn c(p: point) -> int { p[3] }
|
||||
//!^ ERROR access to impure function prohibited in pure context
|
||||
|
||||
pure fn d(p: point) -> int { -p }
|
||||
//!^ ERROR access to impure function prohibited in pure context
|
||||
|
||||
fn main() {}
|
34
src/test/run-pass/rcvr-borrowed-to-region.rs
Normal file
34
src/test/run-pass/rcvr-borrowed-to-region.rs
Normal file
|
@ -0,0 +1,34 @@
|
|||
// Note: impl on a slice
|
||||
impl foo/& for &int {
|
||||
fn get() -> int {
|
||||
ret *self;
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
/*
|
||||
let x = @mut 6;
|
||||
let y = x.get();
|
||||
assert y == 6;
|
||||
*/
|
||||
|
||||
let x = @6;
|
||||
let y = x.get();
|
||||
#debug["y=%d", y];
|
||||
assert y == 6;
|
||||
|
||||
let x = ~mut 6;
|
||||
let y = x.get();
|
||||
#debug["y=%d", y];
|
||||
assert y == 6;
|
||||
|
||||
let x = ~6;
|
||||
let y = x.get();
|
||||
#debug["y=%d", y];
|
||||
assert y == 6;
|
||||
|
||||
let x = &6;
|
||||
let y = x.get();
|
||||
#debug["y=%d", y];
|
||||
assert y == 6;
|
||||
}
|
27
src/test/run-pass/rcvr-borrowed-to-slice.rs
Normal file
27
src/test/run-pass/rcvr-borrowed-to-slice.rs
Normal file
|
@ -0,0 +1,27 @@
|
|||
// Note: impl on a slice
|
||||
impl foo/& for [int]/& {
|
||||
fn sum() -> int {
|
||||
let mut sum = 0;
|
||||
for vec::each(self) { |e| sum += e; }
|
||||
ret sum;
|
||||
}
|
||||
}
|
||||
|
||||
fn call_sum(x: [int]/&) -> int { x.sum() }
|
||||
|
||||
fn main() {
|
||||
let x = [1, 2, 3];
|
||||
let y = call_sum(x);
|
||||
#debug["y==%d", y];
|
||||
assert y == 6;
|
||||
|
||||
let x = [mut 1, 2, 3];
|
||||
let y = x.sum();
|
||||
#debug["y==%d", y];
|
||||
assert y == 6;
|
||||
|
||||
let x = [1, 2, 3];
|
||||
let y = x.sum();
|
||||
#debug["y==%d", y];
|
||||
assert y == 6;
|
||||
}
|
Loading…
Add table
Reference in a new issue