From 77c470d183472b357737b6d3e6c7cafc26174c91 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 1 Jun 2012 21:54:38 -0700 Subject: [PATCH] Allow rcvrs to be borrowed; check rcvrs in borrowck properly --- src/rustc/middle/borrowck/check_loans.rs | 125 ++++++++++++------ src/rustc/middle/borrowck/gather_loans.rs | 30 +++-- src/rustc/middle/trans/base.rs | 49 ++++--- src/rustc/middle/typeck/check.rs | 60 ++++++--- src/rustc/middle/typeck/check/method.rs | 13 +- src/rustc/middle/typeck/infer.rs | 18 ++- .../borrowck-loan-rcvr-overloaded-op.rs | 47 +++++++ src/test/compile-fail/borrowck-loan-rcvr.rs | 23 +++- src/test/compile-fail/pure-overloaded-op.rs | 26 ++++ src/test/run-pass/rcvr-borrowed-to-region.rs | 34 +++++ src/test/run-pass/rcvr-borrowed-to-slice.rs | 27 ++++ 11 files changed, 361 insertions(+), 91 deletions(-) create mode 100644 src/test/compile-fail/borrowck-loan-rcvr-overloaded-op.rs create mode 100644 src/test/compile-fail/pure-overloaded-op.rs create mode 100644 src/test/run-pass/rcvr-borrowed-to-region.rs create mode 100644 src/test/run-pass/rcvr-borrowed-to-slice.rs diff --git a/src/rustc/middle/borrowck/check_loans.rs b/src/rustc/middle/borrowck/check_loans.rs index 414f7f3dac7..615a6737c83 100644 --- a/src/rustc/middle/borrowck/check_loans.rs +++ b/src/rustc/middle/borrowck/check_loans.rs @@ -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, + []); } _ { } } diff --git a/src/rustc/middle/borrowck/gather_loans.rs b/src/rustc/middle/borrowck/gather_loans.rs index da6dbe460ed..1b18ff3f94b 100644 --- a/src/rustc/middle/borrowck/gather_loans.rs +++ b/src/rustc/middle/borrowck/gather_loans.rs @@ -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); diff --git a/src/rustc/middle/trans/base.rs b/src/rustc/middle/trans/base.rs index a95797fd7e3..df1f21ce735 100644 --- a/src/rustc/middle/trans/base.rs +++ b/src/rustc/middle/trans/base.rs @@ -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}}; } _ { diff --git a/src/rustc/middle/typeck/check.rs b/src/rustc/middle/typeck/check.rs index 3bd9d56db58..f8c2f1c36eb 100644 --- a/src/rustc/middle/typeck/check.rs +++ b/src/rustc/middle/typeck/check.rs @@ -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, diff --git a/src/rustc/middle/typeck/check/method.rs b/src/rustc/middle/typeck/check/method.rs index 76a3acb702e..d2fb60d51f2 100644 --- a/src/rustc/middle/typeck/check/method.rs +++ b/src/rustc/middle/typeck/check/method.rs @@ -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); diff --git a/src/rustc/middle/typeck/infer.rs b/src/rustc/middle/typeck/infer.rs index 38466400e57..2896e8875da 100644 --- a/src/rustc/middle/typeck/infer.rs +++ b/src/rustc/middle/typeck/infer.rs @@ -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) diff --git a/src/test/compile-fail/borrowck-loan-rcvr-overloaded-op.rs b/src/test/compile-fail/borrowck-loan-rcvr-overloaded-op.rs new file mode 100644 index 00000000000..7f45a50cbf4 --- /dev/null +++ b/src/test/compile-fail/borrowck-loan-rcvr-overloaded-op.rs @@ -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() { +} + diff --git a/src/test/compile-fail/borrowck-loan-rcvr.rs b/src/test/compile-fail/borrowck-loan-rcvr.rs index 87dd933aa7f..1fba825d5ab 100644 --- a/src/test/compile-fail/borrowck-loan-rcvr.rs +++ b/src/test/compile-fail/borrowck-loan-rcvr.rs @@ -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 } diff --git a/src/test/compile-fail/pure-overloaded-op.rs b/src/test/compile-fail/pure-overloaded-op.rs new file mode 100644 index 00000000000..cb7bdeb6442 --- /dev/null +++ b/src/test/compile-fail/pure-overloaded-op.rs @@ -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() {} diff --git a/src/test/run-pass/rcvr-borrowed-to-region.rs b/src/test/run-pass/rcvr-borrowed-to-region.rs new file mode 100644 index 00000000000..cae6462ceb2 --- /dev/null +++ b/src/test/run-pass/rcvr-borrowed-to-region.rs @@ -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; +} \ No newline at end of file diff --git a/src/test/run-pass/rcvr-borrowed-to-slice.rs b/src/test/run-pass/rcvr-borrowed-to-slice.rs new file mode 100644 index 00000000000..38f9f685050 --- /dev/null +++ b/src/test/run-pass/rcvr-borrowed-to-slice.rs @@ -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; +} \ No newline at end of file