Skip to content

Commit 80c793c

Browse files
committed
Allow individual upvars to be inferred to move semantics. Fixes #21603.
1 parent 2f29cde commit 80c793c

7 files changed

+167
-7
lines changed

src/librustc/middle/expr_use_visitor.rs

+3
Original file line numberDiff line numberDiff line change
@@ -366,6 +366,9 @@ impl<'d,'t,'tcx,TYPER:mc::Typer<'tcx>> ExprUseVisitor<'d,'t,'tcx,TYPER> {
366366
consume_id: ast::NodeId,
367367
consume_span: Span,
368368
cmt: mc::cmt<'tcx>) {
369+
debug!("delegate_consume(consume_id={}, cmt={})",
370+
consume_id, cmt.repr(self.tcx()));
371+
369372
let mode = copy_or_move(self.typer, &cmt, DirectRefMove);
370373
self.delegate.consume(consume_id, consume_span, cmt, mode);
371374
}

src/librustc_typeck/check/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -305,7 +305,7 @@ impl<'a, 'tcx> mc::Typer<'tcx> for FnCtxt<'a, 'tcx> {
305305
}
306306
fn type_moves_by_default(&self, span: Span, ty: Ty<'tcx>) -> bool {
307307
let ty = self.infcx().resolve_type_vars_if_possible(&ty);
308-
traits::type_known_to_meet_builtin_bound(self.infcx(), self, ty, ty::BoundCopy, span)
308+
!traits::type_known_to_meet_builtin_bound(self.infcx(), self, ty, ty::BoundCopy, span)
309309
}
310310
fn node_method_ty(&self, method_call: ty::MethodCall)
311311
-> Option<Ty<'tcx>> {

src/librustc_typeck/check/upvar.rs

+47-6
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,41 @@ impl<'a,'tcx> AdjustBorrowKind<'a,'tcx> {
176176
euv.walk_fn(decl, body);
177177
}
178178

179+
fn adjust_upvar_borrow_kind_for_consume(&self,
180+
cmt: mc::cmt<'tcx>,
181+
mode: euv::ConsumeMode)
182+
{
183+
debug!("adjust_upvar_borrow_kind_for_consume(cmt={}, mode={:?})",
184+
cmt.repr(self.tcx()), mode);
185+
186+
// we only care about moves
187+
match mode {
188+
euv::Copy => { return; }
189+
euv::Move(_) => { }
190+
}
191+
192+
// watch out for a move of the deref of a borrowed pointer;
193+
// for that to be legal, the upvar would have to be borrowed
194+
// by value instead
195+
let guarantor = cmt.guarantor();
196+
debug!("adjust_upvar_borrow_kind_for_consume: guarantor={}",
197+
guarantor.repr(self.tcx()));
198+
match guarantor.cat {
199+
mc::cat_deref(_, _, mc::BorrowedPtr(..)) |
200+
mc::cat_deref(_, _, mc::Implicit(..)) => {
201+
if let mc::NoteUpvarRef(upvar_id) = cmt.note {
202+
debug!("adjust_upvar_borrow_kind_for_consume: \
203+
setting upvar_id={:?} to by value",
204+
upvar_id);
205+
206+
let mut upvar_capture_map = self.fcx.inh.upvar_capture_map.borrow_mut();
207+
upvar_capture_map.insert(upvar_id, ty::UpvarCapture::ByValue);
208+
}
209+
}
210+
_ => { }
211+
}
212+
}
213+
179214
/// Indicates that `cmt` is being directly mutated (e.g., assigned
180215
/// to). If cmt contains any by-ref upvars, this implies that
181216
/// those upvars must be borrowed using an `&mut` borow.
@@ -319,9 +354,12 @@ impl<'a,'tcx> euv::Delegate<'tcx> for AdjustBorrowKind<'a,'tcx> {
319354
fn consume(&mut self,
320355
_consume_id: ast::NodeId,
321356
_consume_span: Span,
322-
_cmt: mc::cmt<'tcx>,
323-
_mode: euv::ConsumeMode)
324-
{}
357+
cmt: mc::cmt<'tcx>,
358+
mode: euv::ConsumeMode)
359+
{
360+
debug!("consume(cmt={},mode={:?})", cmt.repr(self.tcx()), mode);
361+
self.adjust_upvar_borrow_kind_for_consume(cmt, mode);
362+
}
325363

326364
fn matched_pat(&mut self,
327365
_matched_pat: &ast::Pat,
@@ -331,9 +369,12 @@ impl<'a,'tcx> euv::Delegate<'tcx> for AdjustBorrowKind<'a,'tcx> {
331369

332370
fn consume_pat(&mut self,
333371
_consume_pat: &ast::Pat,
334-
_cmt: mc::cmt<'tcx>,
335-
_mode: euv::ConsumeMode)
336-
{}
372+
cmt: mc::cmt<'tcx>,
373+
mode: euv::ConsumeMode)
374+
{
375+
debug!("consume_pat(cmt={},mode={:?})", cmt.repr(self.tcx()), mode);
376+
self.adjust_upvar_borrow_kind_for_consume(cmt, mode);
377+
}
337378

338379
fn borrow(&mut self,
339380
borrow_id: ast::NodeId,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// Copyright 2015 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+
// Test that a by-ref `FnMut` closure gets an error when it tries to
12+
// consume a value.
13+
14+
fn call<F>(f: F) where F : Fn() {
15+
f();
16+
}
17+
18+
fn main() {
19+
let y = vec!(format!("World"));
20+
call(|| {
21+
y.into_iter();
22+
//~^ ERROR cannot move out of captured outer variable in an `Fn` closure
23+
});
24+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// Copyright 2015 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+
// Test that a by-ref `FnMut` closure gets an error when it tries to
12+
// mutate a value.
13+
14+
fn call<F>(f: F) where F : Fn() {
15+
f();
16+
}
17+
18+
fn main() {
19+
let mut counter = 0_u32;
20+
call(|| {
21+
counter += 1;
22+
//~^ ERROR cannot assign to data in a captured outer variable in an `Fn` closure
23+
});
24+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// Copyright 2015 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+
// Test that we mutate a counter on the stack only when we expect to.
12+
13+
fn call<F>(f: F) where F : FnOnce() {
14+
f();
15+
}
16+
17+
fn main() {
18+
let y = vec!(format!("Hello"), format!("World"));
19+
let mut counter = 22_u32;
20+
21+
call(|| {
22+
// Move `y`, but do not move `counter`, even though it is read
23+
// by value (note that it is also mutated).
24+
for item in y.into_iter() {
25+
let v = counter;
26+
counter += v;
27+
}
28+
});
29+
assert_eq!(counter, 88);
30+
31+
call(move || {
32+
// this mutates a moved copy, and hence doesn't affect original
33+
counter += 1;
34+
});
35+
assert_eq!(counter, 88);
36+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// Copyright 2015 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+
// Test that in a by-ref once closure we move some variables even as
12+
// we capture others by mutable reference.
13+
14+
fn call<F>(f: F) where F : FnOnce() {
15+
f();
16+
}
17+
18+
fn main() {
19+
let mut x = vec!(format!("Hello"));
20+
let y = vec!(format!("World"));
21+
call(|| {
22+
// Here: `x` must be captured with a mutable reference in
23+
// order for us to append on it, and `y` must be captured by
24+
// value.
25+
for item in y.into_iter() {
26+
x.push(item);
27+
}
28+
});
29+
assert_eq!(x.len(), 2);
30+
assert_eq!(&*x[0], "Hello");
31+
assert_eq!(&*x[1], "World");
32+
}

0 commit comments

Comments
 (0)