Skip to content

Commit 159a10d

Browse files
committed
rustc: Tweak the borrow on closure invocations
This alters the borrow checker's requirements on invoking closures from requiring an immutable borrow to requiring a unique immutable borrow. This means that it is illegal to invoke a closure through a `&` pointer because there is no guarantee that is not aliased. This does not mean that a closure is required to be in a mutable location, but rather a location which can be proven to be unique (often through a mutable pointer). For example, the following code is unsound and is no longer allowed: type Fn<'a> = ||:'a; fn call(f: |Fn|) { f(|| { f(|| {}) }); } fn main() { call(|a| { a(); }); } There is no replacement for this pattern. For all closures which are stored in structures, it was previously allowed to invoke the closure through `&self` but it now requires invocation through `&mut self`. The standard library has a good number of violations of this new rule, but the fixes will be separated into multiple breaking change commits. Closes rust-lang#12224 [breaking-change]
1 parent 1e33589 commit 159a10d

File tree

5 files changed

+125
-19
lines changed

5 files changed

+125
-19
lines changed

src/librustc/middle/borrowck/check_loans.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -327,7 +327,7 @@ impl<'a> CheckLoanCtxt<'a> {
327327
self.bccx.loan_path_to_str(&*old_loan.loan_path))
328328
}
329329

330-
AddrOf | AutoRef | RefBinding => {
330+
AddrOf | AutoRef | RefBinding | ClosureInvocation => {
331331
format!("previous borrow of `{}` occurs here",
332332
self.bccx.loan_path_to_str(&*old_loan.loan_path))
333333
}

src/librustc/middle/borrowck/gather_loans/mod.rs

+20
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,26 @@ fn gather_loans_in_expr(this: &mut GatherLoanCtxt,
292292
visit::walk_expr(this, ex, ());
293293
}
294294

295+
ast::ExprCall(f, _) => {
296+
let expr_ty = ty::expr_ty_adjusted(tcx, f);
297+
match ty::get(expr_ty).sty {
298+
ty::ty_closure(~ty::ClosureTy {
299+
store: ty::RegionTraitStore(..), ..
300+
}) => {
301+
let scope_r = ty::ReScope(ex.id);
302+
let base_cmt = this.bccx.cat_expr(f);
303+
this.guarantee_valid_kind(f.id,
304+
f.span,
305+
base_cmt,
306+
ty::UniqueImmBorrow,
307+
scope_r,
308+
ClosureInvocation);
309+
}
310+
_ => {}
311+
}
312+
visit::walk_expr(this, ex, ());
313+
}
314+
295315
_ => {
296316
visit::walk_expr(this, ex, ());
297317
}

src/librustc/middle/borrowck/mod.rs

+9
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,7 @@ pub enum LoanCause {
202202
AddrOf,
203203
AutoRef,
204204
RefBinding,
205+
ClosureInvocation,
205206
}
206207

207208
#[deriving(Eq, TotalEq, Hash)]
@@ -629,6 +630,10 @@ impl<'a> BorrowckCtxt<'a> {
629630
AddrOf | RefBinding | AutoRef => {
630631
format!("cannot borrow {} as mutable", descr)
631632
}
633+
ClosureInvocation => {
634+
self.tcx.sess.span_bug(err.span,
635+
"err_mutbl with a closure invocation");
636+
}
632637
}
633638
}
634639
err_out_of_root_scope(..) => {
@@ -677,6 +682,10 @@ impl<'a> BorrowckCtxt<'a> {
677682
BorrowViolation(RefBinding) => {
678683
"cannot borrow data mutably"
679684
}
685+
686+
BorrowViolation(ClosureInvocation) => {
687+
"closure invocation"
688+
}
680689
};
681690

682691
match cause {

src/librustc/middle/typeck/check/regionck.rs

+31-18
Original file line numberDiff line numberDiff line change
@@ -751,7 +751,15 @@ fn constrain_callee(rcx: &mut Rcx,
751751
ty::ty_bare_fn(..) => { }
752752
ty::ty_closure(ref closure_ty) => {
753753
let region = match closure_ty.store {
754-
ty::RegionTraitStore(r, _) => r,
754+
ty::RegionTraitStore(r, _) => {
755+
// While we're here, link the closure's region with a unique
756+
// immutable borrow (gathered later in borrowck)
757+
let mc = mc::MemCategorizationContext { typer: &*rcx };
758+
let expr_cmt = ignore_err!(mc.cat_expr(callee_expr));
759+
link_region(mc.typer, callee_expr.span, call_region,
760+
ty::UniqueImmBorrow, expr_cmt);
761+
r
762+
}
755763
ty::UniqTraitStore => ty::ReStatic
756764
};
757765
rcx.fcx.mk_subr(true, infer::InvokeClosure(callee_expr.span),
@@ -874,7 +882,8 @@ fn constrain_autoderefs(rcx: &mut Rcx,
874882
{
875883
let mc = mc::MemCategorizationContext { typer: &*rcx };
876884
let self_cmt = ignore_err!(mc.cat_expr_autoderefd(deref_expr, i));
877-
link_region(mc.typer, deref_expr.span, r, m, self_cmt);
885+
link_region(mc.typer, deref_expr.span, r,
886+
ty::BorrowKind::from_mutbl(m), self_cmt);
878887
}
879888

880889
// Specialized version of constrain_call.
@@ -1092,7 +1101,8 @@ fn link_pattern(mc: mc::MemCategorizationContext<&Rcx>,
10921101
match mc.cat_slice_pattern(sub_cmt, slice_pat) {
10931102
Ok((slice_cmt, slice_mutbl, slice_r)) => {
10941103
link_region(mc.typer, sub_pat.span, slice_r,
1095-
slice_mutbl, slice_cmt);
1104+
ty::BorrowKind::from_mutbl(slice_mutbl),
1105+
slice_cmt);
10961106
}
10971107
Err(()) => {}
10981108
}
@@ -1118,17 +1128,20 @@ fn link_autoref(rcx: &Rcx,
11181128

11191129
match *autoref {
11201130
ty::AutoPtr(r, m) => {
1121-
link_region(mc.typer, expr.span, r, m, expr_cmt);
1131+
link_region(mc.typer, expr.span, r,
1132+
ty::BorrowKind::from_mutbl(m), expr_cmt);
11221133
}
11231134

11241135
ty::AutoBorrowVec(r, m) | ty::AutoBorrowVecRef(r, m) => {
11251136
let cmt_index = mc.cat_index(expr, expr_cmt, autoderefs+1);
1126-
link_region(mc.typer, expr.span, r, m, cmt_index);
1137+
link_region(mc.typer, expr.span, r,
1138+
ty::BorrowKind::from_mutbl(m), cmt_index);
11271139
}
11281140

11291141
ty::AutoBorrowObj(r, m) => {
11301142
let cmt_deref = mc.cat_deref_obj(expr, expr_cmt);
1131-
link_region(mc.typer, expr.span, r, m, cmt_deref);
1143+
link_region(mc.typer, expr.span, r,
1144+
ty::BorrowKind::from_mutbl(m), cmt_deref);
11321145
}
11331146

11341147
ty::AutoUnsafe(_) => {}
@@ -1150,7 +1163,7 @@ fn link_by_ref(rcx: &Rcx,
11501163
let mc = mc::MemCategorizationContext { typer: rcx };
11511164
let expr_cmt = ignore_err!(mc.cat_expr(expr));
11521165
let region_min = ty::ReScope(callee_scope);
1153-
link_region(mc.typer, expr.span, region_min, ast::MutImmutable, expr_cmt);
1166+
link_region(mc.typer, expr.span, region_min, ty::ImmBorrow, expr_cmt);
11541167
}
11551168

11561169
fn link_region_from_node_type(rcx: &Rcx,
@@ -1169,18 +1182,19 @@ fn link_region_from_node_type(rcx: &Rcx,
11691182
let tcx = rcx.fcx.ccx.tcx;
11701183
debug!("rptr_ty={}", ty_to_str(tcx, rptr_ty));
11711184
let r = ty::ty_region(tcx, span, rptr_ty);
1172-
link_region(rcx, span, r, mutbl, cmt_borrowed);
1185+
link_region(rcx, span, r, ty::BorrowKind::from_mutbl(mutbl),
1186+
cmt_borrowed);
11731187
}
11741188
}
11751189

11761190
fn link_region(rcx: &Rcx,
11771191
span: Span,
11781192
region_min: ty::Region,
1179-
mutbl: ast::Mutability,
1193+
kind: ty::BorrowKind,
11801194
cmt_borrowed: mc::cmt) {
11811195
/*!
11821196
* Informs the inference engine that a borrow of `cmt`
1183-
* must have mutability `mutbl` and lifetime `region_min`.
1197+
* must have the borrow kind `kind` and lifetime `region_min`.
11841198
* If `cmt` is a deref of a region pointer with
11851199
* lifetime `r_borrowed`, this will add the constraint that
11861200
* `region_min <= r_borrowed`.
@@ -1190,9 +1204,9 @@ fn link_region(rcx: &Rcx,
11901204
// for the lifetime `region_min` for the borrow to be valid:
11911205
let mut cmt_borrowed = cmt_borrowed;
11921206
loop {
1193-
debug!("link_region(region_min={}, mutbl={}, cmt_borrowed={})",
1207+
debug!("link_region(region_min={}, kind={}, cmt_borrowed={})",
11941208
region_min.repr(rcx.tcx()),
1195-
mutbl.repr(rcx.tcx()),
1209+
kind.repr(rcx.tcx()),
11961210
cmt_borrowed.repr(rcx.tcx()));
11971211
match cmt_borrowed.cat.clone() {
11981212
mc::cat_deref(base, _, mc::BorrowedPtr(_, r_borrowed)) => {
@@ -1214,7 +1228,7 @@ fn link_region(rcx: &Rcx,
12141228
adjust_upvar_borrow_kind_for_loan(
12151229
*upvar_id,
12161230
upvar_borrow,
1217-
mutbl);
1231+
kind);
12181232
infer::ReborrowUpvar(span, *upvar_id)
12191233
}
12201234
None => {
@@ -1236,7 +1250,7 @@ fn link_region(rcx: &Rcx,
12361250
r_borrowed.repr(rcx.tcx()));
12371251
rcx.fcx.mk_subr(true, cause, region_min, r_borrowed);
12381252

1239-
if mutbl == ast::MutMutable {
1253+
if kind != ty::ImmBorrow {
12401254
// If this is a mutable borrow, then the thing
12411255
// being borrowed will have to be unique.
12421256
// In user code, this means it must be an `&mut`
@@ -1428,12 +1442,11 @@ fn link_upvar_borrow_kind_for_nested_closures(rcx: &mut Rcx,
14281442

14291443
fn adjust_upvar_borrow_kind_for_loan(upvar_id: ty::UpvarId,
14301444
upvar_borrow: &mut ty::UpvarBorrow,
1431-
mutbl: ast::Mutability) {
1445+
kind: ty::BorrowKind) {
14321446
debug!("adjust_upvar_borrow_kind_for_loan: upvar_id={:?} kind={:?} -> {:?}",
1433-
upvar_id, upvar_borrow.kind, mutbl);
1447+
upvar_id, upvar_borrow.kind, kind);
14341448

1435-
adjust_upvar_borrow_kind(upvar_id, upvar_borrow,
1436-
ty::BorrowKind::from_mutbl(mutbl))
1449+
adjust_upvar_borrow_kind(upvar_id, upvar_borrow, kind)
14371450
}
14381451

14391452
fn adjust_upvar_borrow_kind(upvar_id: ty::UpvarId,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// Ensure that invoking a closure counts as a unique immutable borrow
12+
13+
14+
type Fn<'a> = ||:'a;
15+
16+
struct Test<'a> {
17+
f: ||: 'a
18+
}
19+
20+
fn call(f: |Fn|) {
21+
f(|| {
22+
//~^ ERROR: closure requires unique access to `f` but it is already borrowed
23+
f(|| {})
24+
});
25+
}
26+
27+
fn test1() {
28+
call(|a| {
29+
a();
30+
});
31+
}
32+
33+
fn test2(f: &||) {
34+
(*f)(); //~ ERROR: closure invocation in a `&` reference
35+
}
36+
37+
fn test3(f: &mut ||) {
38+
(*f)();
39+
}
40+
41+
fn test4(f: &Test) {
42+
(f.f)() //~ ERROR: closure invocation in a `&` reference
43+
}
44+
45+
fn test5(f: &mut Test) {
46+
(f.f)()
47+
}
48+
49+
fn test6() {
50+
let f = || {};
51+
(|| {
52+
f();
53+
})();
54+
}
55+
56+
fn test7() {
57+
fn foo(_: |g: |int|, b: int|) {}
58+
let f = |g: |int|, b: int| {};
59+
f(|a| { //~ ERROR: cannot borrow `f` as immutable because previous closure
60+
foo(f); //~ ERROR: cannot move out of captured outer variable
61+
}, 3);
62+
}
63+
64+
fn main() {}

0 commit comments

Comments
 (0)