Skip to content

Commit 136ab3c

Browse files
committed
auto merge of #17731 : bkoropoff/rust/unboxed-by-ref, r=pcwalton
This began as an attempt to fix an ICE in borrowck (issue #17655), but the rabbit hole went pretty deep. I ended up plumbing support for capture-by-reference unboxed closures all the way into trans. Closes issue #17655.
2 parents c348550 + 521ca31 commit 136ab3c

File tree

8 files changed

+147
-38
lines changed

8 files changed

+147
-38
lines changed

src/librustc/middle/mem_categorization.rs

+15-11
Original file line numberDiff line numberDiff line change
@@ -596,17 +596,21 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> {
596596
ty::FnMutUnboxedClosureKind => ast::Many,
597597
ty::FnOnceUnboxedClosureKind => ast::Once,
598598
};
599-
Ok(Rc::new(cmt_ {
600-
id: id,
601-
span: span,
602-
cat: cat_copied_upvar(CopiedUpvar {
603-
upvar_id: var_id,
604-
onceness: onceness,
605-
capturing_proc: fn_node_id,
606-
}),
607-
mutbl: MutabilityCategory::from_local(self.tcx(), var_id),
608-
ty: expr_ty
609-
}))
599+
if self.typer.capture_mode(fn_node_id) == ast::CaptureByRef {
600+
self.cat_upvar(id, span, var_id, fn_node_id)
601+
} else {
602+
Ok(Rc::new(cmt_ {
603+
id: id,
604+
span: span,
605+
cat: cat_copied_upvar(CopiedUpvar {
606+
upvar_id: var_id,
607+
onceness: onceness,
608+
capturing_proc: fn_node_id,
609+
}),
610+
mutbl: MutabilityCategory::from_local(self.tcx(), var_id),
611+
ty: expr_ty
612+
}))
613+
}
610614
}
611615
_ => {
612616
self.tcx().sess.span_bug(

src/librustc/middle/trans/closure.rs

+16-3
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,7 @@ fn load_environment<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
301301
fn load_unboxed_closure_environment<'blk, 'tcx>(
302302
bcx: Block<'blk, 'tcx>,
303303
arg_scope_id: ScopeId,
304+
freevar_mode: ast::CaptureClause,
304305
freevars: &Vec<ty::Freevar>,
305306
closure_id: ast::DefId)
306307
-> Block<'blk, 'tcx> {
@@ -326,11 +327,14 @@ fn load_unboxed_closure_environment<'blk, 'tcx>(
326327
};
327328

328329
for (i, freevar) in freevars.iter().enumerate() {
329-
let upvar_ptr = GEPi(bcx, llenv, [0, i]);
330+
let mut upvar_ptr = GEPi(bcx, llenv, [0, i]);
331+
if freevar_mode == ast::CaptureByRef {
332+
upvar_ptr = Load(bcx, upvar_ptr);
333+
}
330334
let def_id = freevar.def.def_id();
331335
bcx.fcx.llupvars.borrow_mut().insert(def_id.node, upvar_ptr);
332336

333-
if kind == ty::FnOnceUnboxedClosureKind {
337+
if kind == ty::FnOnceUnboxedClosureKind && freevar_mode == ast::CaptureByValue {
334338
bcx.fcx.schedule_drop_mem(arg_scope_id,
335339
upvar_ptr,
336340
node_id_type(bcx, def_id.node))
@@ -477,6 +481,7 @@ pub fn trans_unboxed_closure<'blk, 'tcx>(
477481
let freevars: Vec<ty::Freevar> =
478482
ty::with_freevars(bcx.tcx(), id, |fv| fv.iter().map(|&fv| fv).collect());
479483
let freevars_ptr = &freevars;
484+
let freevar_mode = bcx.tcx().capture_mode(id);
480485

481486
trans_closure(bcx.ccx(),
482487
decl,
@@ -493,6 +498,7 @@ pub fn trans_unboxed_closure<'blk, 'tcx>(
493498
|bcx, arg_scope| {
494499
load_unboxed_closure_environment(bcx,
495500
arg_scope,
501+
freevar_mode,
496502
freevars_ptr,
497503
closure_id)
498504
});
@@ -518,7 +524,14 @@ pub fn trans_unboxed_closure<'blk, 'tcx>(
518524
dest_addr,
519525
0,
520526
i);
521-
bcx = datum.store_to(bcx, upvar_slot_dest);
527+
match freevar_mode {
528+
ast::CaptureByValue => {
529+
bcx = datum.store_to(bcx, upvar_slot_dest);
530+
}
531+
ast::CaptureByRef => {
532+
Store(bcx, datum.to_llref(), upvar_slot_dest);
533+
}
534+
}
522535
}
523536
adt::trans_set_discr(bcx, &*repr, dest_addr, 0);
524537

src/librustc/middle/ty.rs

+13-1
Original file line numberDiff line numberDiff line change
@@ -4634,15 +4634,27 @@ pub struct UnboxedClosureUpvar {
46344634
pub fn unboxed_closure_upvars(tcx: &ctxt, closure_id: ast::DefId)
46354635
-> Vec<UnboxedClosureUpvar> {
46364636
if closure_id.krate == ast::LOCAL_CRATE {
4637+
let capture_mode = tcx.capture_modes.borrow().get_copy(&closure_id.node);
46374638
match tcx.freevars.borrow().find(&closure_id.node) {
46384639
None => vec![],
46394640
Some(ref freevars) => {
46404641
freevars.iter().map(|freevar| {
46414642
let freevar_def_id = freevar.def.def_id();
4643+
let mut freevar_ty = node_id_to_type(tcx, freevar_def_id.node);
4644+
if capture_mode == ast::CaptureByRef {
4645+
let borrow = tcx.upvar_borrow_map.borrow().get_copy(&ty::UpvarId {
4646+
var_id: freevar_def_id.node,
4647+
closure_expr_id: closure_id.node
4648+
});
4649+
freevar_ty = mk_rptr(tcx, borrow.region, ty::mt {
4650+
ty: freevar_ty,
4651+
mutbl: borrow.kind.to_mutbl_lossy()
4652+
});
4653+
}
46424654
UnboxedClosureUpvar {
46434655
def: freevar.def,
46444656
span: freevar.span,
4645-
ty: node_id_to_type(tcx, freevar_def_id.node),
4657+
ty: freevar_ty
46464658
}
46474659
}).collect()
46484660
}

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

+26-16
Original file line numberDiff line numberDiff line change
@@ -836,7 +836,7 @@ fn check_expr_fn_block(rcx: &mut Rcx,
836836
// has static lifetime.
837837
} else {
838838
// Variables being referenced must outlive closure.
839-
constrain_free_variables_in_stack_closure(
839+
constrain_free_variables_in_by_ref_closure(
840840
rcx, bounds.region_bound, expr, freevars);
841841

842842
// Closure is stack allocated and hence cannot
@@ -848,20 +848,17 @@ fn check_expr_fn_block(rcx: &mut Rcx,
848848
});
849849
}
850850
ty::ty_unboxed_closure(_, region) => {
851-
ty::with_freevars(tcx, expr.id, |freevars| {
852-
// No free variables means that there is no environment and
853-
// hence the closure has static lifetime. Otherwise, the
854-
// closure must not outlive the variables it closes over
855-
// by-reference.
856-
//
857-
// NDM -- this seems wrong, discuss with pcwalton, should
858-
// be straightforward enough.
859-
if !freevars.is_empty() {
860-
let bounds = ty::region_existential_bound(region);
861-
ensure_free_variable_types_outlive_closure_bound(
862-
rcx, bounds, expr, freevars);
863-
}
864-
})
851+
let bounds = ty::region_existential_bound(region);
852+
if tcx.capture_modes.borrow().get_copy(&expr.id) == ast::CaptureByRef {
853+
ty::with_freevars(tcx, expr.id, |freevars| {
854+
if !freevars.is_empty() {
855+
// Variables being referenced must be constrained and registered
856+
// in the upvar borrow map
857+
constrain_free_variables_in_by_ref_closure(
858+
rcx, bounds.region_bound, expr, freevars);
859+
}
860+
})
861+
}
865862
}
866863
_ => { }
867864
}
@@ -876,6 +873,13 @@ fn check_expr_fn_block(rcx: &mut Rcx,
876873
propagate_upupvar_borrow_kind(rcx, expr, freevars);
877874
})
878875
}
876+
ty::ty_unboxed_closure(..) => {
877+
if tcx.capture_modes.borrow().get_copy(&expr.id) == ast::CaptureByRef {
878+
ty::with_freevars(tcx, expr.id, |freevars| {
879+
propagate_upupvar_borrow_kind(rcx, expr, freevars);
880+
});
881+
}
882+
}
879883
_ => {}
880884
}
881885

@@ -885,6 +889,12 @@ fn check_expr_fn_block(rcx: &mut Rcx,
885889
ensure_free_variable_types_outlive_closure_bound(rcx, bounds, expr, freevars);
886890
})
887891
}
892+
ty::ty_unboxed_closure(_, region) => {
893+
ty::with_freevars(tcx, expr.id, |freevars| {
894+
let bounds = ty::region_existential_bound(region);
895+
ensure_free_variable_types_outlive_closure_bound(rcx, bounds, expr, freevars);
896+
})
897+
}
888898
_ => {}
889899
}
890900

@@ -951,7 +961,7 @@ fn check_expr_fn_block(rcx: &mut Rcx,
951961
}
952962
}
953963

954-
fn constrain_free_variables_in_stack_closure(
964+
fn constrain_free_variables_in_by_ref_closure(
955965
rcx: &mut Rcx,
956966
region_bound: ty::Region,
957967
expr: &ast::Expr,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
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+
#![feature(unboxed_closures)]
12+
13+
// Test that an unboxed closure that captures a free variable by
14+
// reference cannot escape the region of that variable.
15+
fn main() {
16+
let _f = {
17+
let x = 0u;
18+
|:| x //~ ERROR cannot infer an appropriate lifetime due to conflicting requirements
19+
};
20+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
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+
#![feature(unboxed_closures)]
12+
13+
// Test that an unboxed closure that mutates a free variable will
14+
// cause borrow conflicts.
15+
16+
fn main() {
17+
let mut x = 0u;
18+
let f = |:| x += 1;
19+
let _y = x; //~ ERROR cannot use `x` because it was mutably borrowed
20+
}

src/test/run-pass/capture-clauses-unboxed-closures.rs

+2-7
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,9 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11-
// ignore-test
12-
//
13-
// This is ignored because it depends on #16122.
14-
1511
#![feature(overloaded_calls, unboxed_closures)]
1612

17-
fn each<'a,T,F:|&mut: &'a T|>(x: &'a [T], mut f: F) {
13+
fn each<'a,T,F:FnMut(&'a T)>(x: &'a [T], mut f: F) {
1814
for val in x.iter() {
1915
f(val)
2016
}
@@ -23,7 +19,6 @@ fn each<'a,T,F:|&mut: &'a T|>(x: &'a [T], mut f: F) {
2319
fn main() {
2420
let mut sum = 0u;
2521
let elems = [ 1u, 2, 3, 4, 5 ];
26-
each(elems, ref |&mut: val: &uint| sum += *val);
22+
each(elems, |&mut: val: &uint| sum += *val);
2723
assert_eq!(sum, 15);
2824
}
29-
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
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+
#![feature(overloaded_calls, unboxed_closures)]
12+
13+
// Test by-ref capture of environment in unboxed closure types
14+
15+
fn call_fn<F: Fn()>(f: F) {
16+
f()
17+
}
18+
19+
fn call_fn_mut<F: FnMut()>(mut f: F) {
20+
f()
21+
}
22+
23+
fn call_fn_once<F: FnOnce()>(f: F) {
24+
f()
25+
}
26+
27+
fn main() {
28+
let mut x = 0u;
29+
let y = 2u;
30+
31+
call_fn(|&:| x += y);
32+
call_fn_mut(|&mut:| x += y);
33+
call_fn_once(|:| x += y);
34+
assert_eq!(x, y * 3);
35+
}

0 commit comments

Comments
 (0)