1 // Copyright 2012 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.
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.
15 The goal of this file is to compute which
16 expressions/patterns/captures correspond to *moves*. This is
17 generally a function of the context in which the expression appears as
18 well as the expression's type.
22 We will use the following fragment of code to explain the various
23 considerations. Note that in this code `x` is used after it has been
24 moved here. This is not relevant to this pass, though the information
25 we compute would later be used to detect this error (see the section
26 Enforcement of Moves, below).
28 struct Foo { a: int, b: ~int }
30 let w = (x {Read}).a; // Read
31 let y = (x {Move}).b; // Move
32 let z = copy (x {Read}).b; // Read
34 Let's look at these examples one by one. In the first case, `w`, the
35 expression being assigned is `x.a`, which has `int` type. In that
36 case, the value is read, and the container (`x`) is also read.
38 In the second case, `y`, `x.b` is being assigned which has type
39 `~int`. Because this type moves by default, that will be a move
40 reference. Whenever we move from a compound expression like `x.b` (or
41 `x[b]` or `*x` or `{x)[b].c`, etc), this invalidates all containing
42 expressions since we do not currently permit "incomplete" variables
43 where part of them has been moved and part has not. In this case,
44 this means that the reference to `x` is also a move. We'll see later,
45 though, that these kind of "partial moves", where part of the
46 expression has been moved, are classified and stored somewhat
49 The final example (`z`) is `copy x.b`: in this case, although the
50 expression being assigned has type `~int`, there are no moves
55 For each binding in a match or let pattern, we also compute a read
56 or move designation. A move binding means that the value will be
57 moved from the value being matched. As a result, the expression
58 being matched (aka, the 'discriminant') is either moved or read
59 depending on whethe the bindings move the value they bind to out of
62 For examples, consider this match expression:
65 Foo { a: a {Read}, b: b {Move} } => {...}
68 Here, the binding `b` is value (not ref) mode, and `b` has type
69 `~int`, and therefore the discriminant expression `x` would be
70 incomplete so it also considered moved.
72 In the following two examples, in contrast, the mode of `b` is either
73 `copy` or `ref` and hence the overall result is a read:
76 Foo { a: a {Read}, b: copy b {Read} } => {...}
80 Foo { a: a {Read}, b: ref b {Read} } => {...}
83 Similar reasoning can be applied to `let` expressions:
85 let Foo { a: a {Read}, b: b {Move} } = x {Move};
86 let Foo { a: a {Read}, b: copy b {Read} } = x {Read};
87 let Foo { a: a {Read}, b: ref b {Read} } = x {Read};
91 The pass results in the struct `MoveMaps` which contains several
94 `moves_map` is a set containing the id of every *outermost expression* or
95 *binding* that causes a move. Note that `moves_map` only contains the *outermost
96 expressions* that are moved. Therefore, if you have a use of `x.b`,
97 as in the example `y` above, the expression `x.b` would be in the
98 `moves_map` but not `x`. The reason for this is that, for most
99 purposes, it's only the outermost expression that is needed. The
100 borrow checker and trans, for example, only care about the outermost
101 expressions that are moved. It is more efficient therefore just to
104 Sometimes though we want to know the variables that are moved (in
105 particular in the borrow checker). For these cases, the set
106 `moved_variables_set` just collects the ids of variables that are
109 Finally, the `capture_map` maps from the node_id of a closure
110 expression to an array of `CaptureVar` structs detailing which
111 variables are captured and how (by ref, by copy, by move).
113 ## Enforcement of Moves
115 The enforcement of moves is done by the borrow checker. Please see
116 the section "Moves and initialization" in `middle/borrowck/doc.rs`.
118 ## Distributive property
120 Copies are "distributive" over parenthesization, but blocks are
121 considered rvalues. What this means is that, for example, neither
122 `a.clone()` nor `(a).clone()` will move `a` (presuming that `a` has a
123 linear type and `clone()` takes its self by reference), but
124 `{a}.clone()` will move `a`, as would `(if cond {a} else {b}).clone()`
130 use middle::pat_util::{pat_bindings};
131 use middle::freevars;
133 use middle::typeck::{method_map};
135 use util::ppaux::Repr;
136 use util::common::indenter;
137 use util::ppaux::UserString;
140 use std::hashmap::{HashSet, HashMap};
142 use syntax::ast_util;
144 use syntax::visit::Visitor;
145 use syntax::codemap::Span;
147 #[deriving(Eq, Encodable, Decodable)]
148 pub enum CaptureMode {
149 CapCopy, // Copy the value into the closure.
150 CapMove, // Move the value into the closure.
151 CapRef, // Reference directly from parent stack frame (used by `&fn()`).
154 #[deriving(Encodable, Decodable)]
155 pub struct CaptureVar {
156 def: Def, // Variable being accessed free
157 span: Span, // Location of an access to this variable
158 mode: CaptureMode // How variable is being accessed
161 pub type CaptureMap = @mut HashMap<NodeId, @[CaptureVar]>;
163 pub type MovesMap = @mut HashSet<NodeId>;
166 * Set of variable node-ids that are moved.
168 * Note: The `VariableMovesMap` stores expression ids that
169 * are moves, whereas this set stores the ids of the variables
170 * that are moved at some point */
171 pub type MovedVariablesSet = @mut HashSet<NodeId>;
173 /** See the section Output on the module comment for explanation. */
175 pub struct MoveMaps {
177 moved_variables_set: MovedVariablesSet,
178 capture_map: CaptureMap
182 struct VisitContext {
184 method_map: method_map,
190 Move, // This value or something owned by it is moved.
191 Read // Read no matter what the type.
194 impl visit::Visitor<()> for VisitContext {
195 fn visit_fn(&mut self, fk:&visit::fn_kind, fd:&fn_decl,
196 b:&Block, s:Span, n:NodeId, _:()) {
197 compute_modes_for_fn(self, fk, fd, b, s, n);
199 fn visit_expr(&mut self, ex:@Expr, _:()) {
200 compute_modes_for_expr(self, ex);
202 fn visit_local(&mut self, l:@Local, _:()) {
203 compute_modes_for_local(self, l);
207 pub fn compute_moves(tcx: ty::ctxt,
208 method_map: method_map,
209 crate: &Crate) -> MoveMaps
211 let mut visit_cx = VisitContext {
213 method_map: method_map,
214 move_maps: MoveMaps {
215 moves_map: @mut HashSet::new(),
216 capture_map: @mut HashMap::new(),
217 moved_variables_set: @mut HashSet::new()
220 let visit_cx = &mut visit_cx;
221 visit::walk_crate(visit_cx, crate, ());
222 return visit_cx.move_maps;
225 pub fn moved_variable_node_id_from_def(def: Def) -> Option<NodeId> {
230 DefSelf(nid, _) => Some(nid),
236 ///////////////////////////////////////////////////////////////////////////
239 fn compute_modes_for_local<'a>(cx: &mut VisitContext,
241 cx.use_pat(local.pat);
242 for &init in local.init.iter() {
243 cx.use_expr(init, Read);
247 fn compute_modes_for_fn(cx: &mut VisitContext,
253 for a in decl.inputs.iter() {
256 visit::walk_fn(cx, fk, decl, body, span, id, ());
259 fn compute_modes_for_expr(cx: &mut VisitContext,
262 cx.consume_expr(expr);
266 pub fn consume_exprs(&mut self, exprs: &[@Expr]) {
267 for expr in exprs.iter() {
268 self.consume_expr(*expr);
272 pub fn consume_expr(&mut self, expr: @Expr) {
274 * Indicates that the value of `expr` will be consumed,
275 * meaning either copied or moved depending on its type.
278 debug!("consume_expr(expr={})",
279 expr.repr(self.tcx));
281 let expr_ty = ty::expr_ty_adjusted(self.tcx, expr);
282 if ty::type_moves_by_default(self.tcx, expr_ty) {
283 self.move_maps.moves_map.insert(expr.id);
284 self.use_expr(expr, Move);
286 self.use_expr(expr, Read);
290 pub fn consume_block(&mut self, blk: &Block) {
292 * Indicates that the value of `blk` will be consumed,
293 * meaning either copied or moved depending on its type.
296 debug!("consume_block(blk.id={:?})", blk.id);
298 for stmt in blk.stmts.iter() {
299 self.visit_stmt(*stmt, ());
302 for tail_expr in blk.expr.iter() {
303 self.consume_expr(*tail_expr);
307 pub fn use_expr(&mut self,
309 expr_mode: UseMode) {
311 * Indicates that `expr` is used with a given mode. This will
312 * in turn trigger calls to the subcomponents of `expr`.
315 debug!("use_expr(expr={}, mode={:?})",
319 // `expr_mode` refers to the post-adjustment value. If one of
320 // those adjustments is to take a reference, then it's only
321 // reading the underlying expression, not moving it.
322 let comp_mode = match self.tcx.adjustments.find(&expr.id) {
323 Some(&@ty::AutoDerefRef(
325 autoref: Some(_), _})) => Read,
329 debug!("comp_mode = {:?}", comp_mode);
332 ExprPath(*) | ExprSelf => {
335 let def = self.tcx.def_map.get_copy(&expr.id);
336 let r = moved_variable_node_id_from_def(def);
337 for &id in r.iter() {
338 self.move_maps.moved_variables_set.insert(id);
345 ExprUnary(_, UnDeref, base) => { // *base
346 if !self.use_overloaded_operator(expr, base, [])
348 // Moving out of *base moves out of base.
349 self.use_expr(base, comp_mode);
353 ExprField(base, _, _) => { // base.f
354 // Moving out of base.f moves out of base.
355 self.use_expr(base, comp_mode);
358 ExprIndex(_, lhs, rhs) => { // lhs[rhs]
359 if !self.use_overloaded_operator(expr, lhs, [rhs])
361 self.use_expr(lhs, comp_mode);
362 self.consume_expr(rhs);
366 ExprCall(callee, ref args, _) => { // callee(args)
367 // Figure out whether the called function is consumed.
368 let mode = match ty::get(ty::expr_ty(self.tcx, callee)).sty {
369 ty::ty_closure(ref cty) => {
375 ty::ty_bare_fn(*) => Read,
377 self.tcx.sess.span_bug(callee.span,
378 format!("non-function type in moves for expr_call: {:?}", x)),
380 // Note we're not using consume_expr, which uses type_moves_by_default
381 // to determine the mode, for this. The reason is that while stack
382 // closures should be noncopyable, they shouldn't move by default;
383 // calling a closure should only consume it if it's once.
385 self.move_maps.moves_map.insert(callee.id);
387 self.use_expr(callee, mode);
388 self.use_fn_args(callee.id, *args);
391 ExprMethodCall(callee_id, rcvr, _, _, ref args, _) => { // callee.m(args)
392 // Implicit self is equivalent to & mode, but every
393 // other kind should be + mode.
394 self.use_receiver(rcvr);
395 self.use_fn_args(callee_id, *args);
398 ExprStruct(_, ref fields, opt_with) => {
399 for field in fields.iter() {
400 self.consume_expr(field.expr);
403 for with_expr in opt_with.iter() {
404 // If there are any fields whose type is move-by-default,
405 // then `with` is consumed, otherwise it is only read
406 let with_ty = ty::expr_ty(self.tcx, *with_expr);
407 let with_fields = match ty::get(with_ty).sty {
408 ty::ty_struct(did, ref substs) => {
409 ty::struct_fields(self.tcx, did, substs)
412 self.tcx.sess.span_bug(
414 format!("bad base expr type in record: {:?}", r))
418 // The `with` expr must be consumed if it contains
419 // any fields which (1) were not explicitly
420 // specified and (2) have a type that
422 let consume_with = with_fields.iter().any(|tf| {
423 !fields.iter().any(|f| f.ident.name == tf.ident.name) &&
424 ty::type_moves_by_default(self.tcx, tf.mt.ty)
427 fn has_dtor(tcx: ty::ctxt, ty: ty::t) -> bool {
428 use middle::ty::{get,ty_struct,ty_enum};
430 ty_struct(did, _) | ty_enum(did, _) => ty::has_dtor(tcx, did),
436 if has_dtor(self.tcx, with_ty) {
437 self.tcx.sess.span_err(with_expr.span,
438 format!("cannot move out of type `{}`, \
439 which defines the `Drop` trait",
440 with_ty.user_string(self.tcx)));
442 self.consume_expr(*with_expr);
444 self.use_expr(*with_expr, Read);
449 ExprTup(ref exprs) => {
450 self.consume_exprs(*exprs);
453 ExprIf(cond_expr, ref then_blk, opt_else_expr) => {
454 self.consume_expr(cond_expr);
455 self.consume_block(then_blk);
456 for else_expr in opt_else_expr.iter() {
457 self.consume_expr(*else_expr);
461 ExprMatch(discr, ref arms) => {
462 // We must do this first so that `arms_have_by_move_bindings`
463 // below knows which bindings are moves.
464 for arm in arms.iter() {
465 self.consume_arm(arm);
468 // The discriminant may, in fact, be partially moved
469 // if there are by-move bindings, but borrowck deals
471 self.use_expr(discr, Read);
475 // Note: base is not considered a *component* here, so
476 // use `expr_mode` not `comp_mode`.
477 self.use_expr(base, expr_mode);
480 ExprVec(ref exprs, _) => {
481 self.consume_exprs(*exprs);
484 ExprAddrOf(_, base) => { // &base
485 self.use_expr(base, Read);
494 ExprLoop(ref blk, _) => {
495 self.consume_block(blk);
498 ExprWhile(cond_expr, ref blk) => {
499 self.consume_expr(cond_expr);
500 self.consume_block(blk);
503 ExprForLoop(*) => fail!("non-desugared expr_for_loop"),
505 ExprUnary(_, _, lhs) => {
506 if !self.use_overloaded_operator(expr, lhs, [])
508 self.consume_expr(lhs);
512 ExprBinary(_, _, lhs, rhs) => {
513 if !self.use_overloaded_operator(expr, lhs, [rhs])
515 self.consume_expr(lhs);
516 self.consume_expr(rhs);
520 ExprBlock(ref blk) => {
521 self.consume_block(blk);
524 ExprRet(ref opt_expr) => {
525 for expr in opt_expr.iter() {
526 self.consume_expr(*expr);
530 ExprAssign(lhs, rhs) => {
531 self.use_expr(lhs, Read);
532 self.consume_expr(rhs);
535 ExprCast(base, _) => {
536 self.consume_expr(base);
539 ExprAssignOp(_, _, lhs, rhs) => {
540 // FIXME(#4712) --- Overloaded operators?
542 // if !self.use_overloaded_operator(expr, DoDerefArgs, lhs, [rhs])
544 self.consume_expr(lhs);
545 self.consume_expr(rhs);
549 ExprRepeat(base, count, _) => {
550 self.consume_expr(base);
551 self.consume_expr(count);
554 ExprDoBody(base) => {
555 self.use_expr(base, comp_mode);
558 ExprFnBlock(ref decl, ref body) => {
559 for a in decl.inputs.iter() {
562 let cap_vars = self.compute_captures(expr.id);
563 self.move_maps.capture_map.insert(expr.id, cap_vars);
564 self.consume_block(body);
567 ExprVstore(base, _) => {
568 self.use_expr(base, comp_mode);
572 self.tcx.sess.span_bug(
574 "macro expression remains after expansion");
579 pub fn use_overloaded_operator(&mut self,
581 receiver_expr: @Expr,
584 if !self.method_map.contains_key(&expr.id) {
588 self.use_receiver(receiver_expr);
590 // for overloaded operatrs, we are always passing in a
591 // borrowed pointer, so it's always read mode:
592 for arg_expr in arg_exprs.iter() {
593 self.use_expr(*arg_expr, Read);
599 pub fn consume_arm(&mut self, arm: &Arm) {
600 for pat in arm.pats.iter() {
604 for guard in arm.guard.iter() {
605 self.consume_expr(*guard);
608 self.consume_block(&arm.body);
611 pub fn use_pat(&mut self, pat: @Pat) {
614 * Decides whether each binding in a pattern moves the value
615 * into itself or not based on its type and annotation.
618 do pat_bindings(self.tcx.def_map, pat) |bm, id, _span, path| {
619 let binding_moves = match bm {
620 BindByRef(_) => false,
622 let pat_ty = ty::node_id_to_type(self.tcx, id);
623 debug!("pattern {:?} {} type is {}",
625 ast_util::path_to_ident(path).repr(self.tcx),
626 pat_ty.repr(self.tcx));
627 ty::type_moves_by_default(self.tcx, pat_ty)
631 debug!("pattern binding {:?}: bm={:?}, binding_moves={}",
632 id, bm, binding_moves);
635 self.move_maps.moves_map.insert(id);
640 pub fn use_receiver(&mut self,
641 receiver_expr: @Expr) {
642 self.use_fn_arg(receiver_expr);
645 pub fn use_fn_args(&mut self,
647 arg_exprs: &[@Expr]) {
648 //! Uses the argument expressions.
649 for arg_expr in arg_exprs.iter() {
650 self.use_fn_arg(*arg_expr);
654 pub fn use_fn_arg(&mut self, arg_expr: @Expr) {
655 //! Uses the argument.
656 self.consume_expr(arg_expr)
659 pub fn arms_have_by_move_bindings(&mut self,
664 for arm in arms.iter() {
665 for &pat in arm.pats.iter() {
666 let cont = do ast_util::walk_pat(pat) |p| {
667 if moves_map.contains(&p.id) {
674 if !cont { return ret }
680 pub fn compute_captures(&mut self, fn_expr_id: NodeId) -> @[CaptureVar] {
681 debug!("compute_capture_vars(fn_expr_id={:?})", fn_expr_id);
682 let _indenter = indenter();
684 let fn_ty = ty::node_id_to_type(self.tcx, fn_expr_id);
685 let sigil = ty::ty_closure_sigil(fn_ty);
686 let freevars = freevars::get_freevars(self.tcx, fn_expr_id);
687 if sigil == BorrowedSigil {
688 // &fn() captures everything by ref
689 at_vec::from_fn(freevars.len(), |i| {
690 let fvar = &freevars[i];
691 CaptureVar {def: fvar.def, span: fvar.span, mode: CapRef}
694 // @fn() and ~fn() capture by copy or by move depending on type
695 at_vec::from_fn(freevars.len(), |i| {
696 let fvar = &freevars[i];
697 let fvar_def_id = ast_util::def_id_of_def(fvar.def).node;
698 let fvar_ty = ty::node_id_to_type(self.tcx, fvar_def_id);
699 debug!("fvar_def_id={:?} fvar_ty={}",
700 fvar_def_id, ppaux::ty_to_str(self.tcx, fvar_ty));
701 let mode = if ty::type_moves_by_default(self.tcx, fvar_ty) {
706 CaptureVar {def: fvar.def, span: fvar.span, mode:mode}