2013-01-10 12:59:58 -06:00
|
|
|
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
|
|
|
|
// file at the top-level directory of this distribution and at
|
|
|
|
// http://rust-lang.org/COPYRIGHT.
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
|
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
|
|
|
// option. This file may not be copied, modified, or distributed
|
|
|
|
// except according to those terms.
|
|
|
|
|
|
|
|
/*!
|
|
|
|
|
|
|
|
# Moves Computation
|
|
|
|
|
|
|
|
The goal of this file is to compute which
|
|
|
|
expressions/patterns/captures correspond to *moves*. This is
|
|
|
|
generally a function of the context in which the expression appears as
|
|
|
|
well as the expression's type.
|
|
|
|
|
|
|
|
## Examples
|
|
|
|
|
|
|
|
We will use the following fragment of code to explain the various
|
|
|
|
considerations. Note that in this code `x` is used after it has been
|
|
|
|
moved here. This is not relevant to this pass, though the information
|
|
|
|
we compute would later be used to detect this error (see the section
|
|
|
|
Enforcement of Moves, below).
|
|
|
|
|
|
|
|
struct Foo { a: int, b: ~int }
|
|
|
|
let x: Foo = ...;
|
|
|
|
let w = (x {Read}).a; // Read
|
|
|
|
let y = (x {Move}).b; // Move
|
|
|
|
let z = copy (x {Read}).b; // Read
|
|
|
|
|
|
|
|
Let's look at these examples one by one. In the first case, `w`, the
|
|
|
|
expression being assigned is `x.a`, which has `int` type. In that
|
|
|
|
case, the value is read, and the container (`x`) is also read.
|
|
|
|
|
|
|
|
In the second case, `y`, `x.b` is being assigned which has type
|
|
|
|
`~int`. Because this type moves by default, that will be a move
|
|
|
|
reference. Whenever we move from a compound expression like `x.b` (or
|
|
|
|
`x[b]` or `*x` or `{x)[b].c`, etc), this invalidates all containing
|
|
|
|
expressions since we do not currently permit "incomplete" variables
|
|
|
|
where part of them has been moved and part has not. In this case,
|
|
|
|
this means that the reference to `x` is also a move. We'll see later,
|
|
|
|
though, that these kind of "partial moves", where part of the
|
|
|
|
expression has been moved, are classified and stored somewhat
|
|
|
|
differently.
|
|
|
|
|
|
|
|
The final example (`z`) is `copy x.b`: in this case, although the
|
|
|
|
expression being assigned has type `~int`, there are no moves
|
|
|
|
involved.
|
|
|
|
|
|
|
|
### Patterns
|
|
|
|
|
|
|
|
For each binding in a match or let pattern, we also compute a read
|
|
|
|
or move designation. A move binding means that the value will be
|
|
|
|
moved from the value being matched. As a result, the expression
|
|
|
|
being matched (aka, the 'discriminant') is either moved or read
|
|
|
|
depending on whethe the bindings move the value they bind to out of
|
|
|
|
the discriminant.
|
|
|
|
|
|
|
|
For examples, consider this match expression:
|
|
|
|
|
|
|
|
match x {Move} {
|
|
|
|
Foo { a: a {Read}, b: b {Move} } => {...}
|
|
|
|
}
|
|
|
|
|
|
|
|
Here, the binding `b` is value (not ref) mode, and `b` has type
|
|
|
|
`~int`, and therefore the discriminant expression `x` would be
|
|
|
|
incomplete so it also considered moved.
|
|
|
|
|
|
|
|
In the following two examples, in contrast, the mode of `b` is either
|
|
|
|
`copy` or `ref` and hence the overall result is a read:
|
|
|
|
|
|
|
|
match x {Read} {
|
|
|
|
Foo { a: a {Read}, b: copy b {Read} } => {...}
|
|
|
|
}
|
|
|
|
|
|
|
|
match x {Read} {
|
|
|
|
Foo { a: a {Read}, b: ref b {Read} } => {...}
|
|
|
|
}
|
|
|
|
|
|
|
|
Similar reasoning can be applied to `let` expressions:
|
|
|
|
|
|
|
|
let Foo { a: a {Read}, b: b {Move} } = x {Move};
|
|
|
|
let Foo { a: a {Read}, b: copy b {Read} } = x {Read};
|
|
|
|
let Foo { a: a {Read}, b: ref b {Read} } = x {Read};
|
|
|
|
|
|
|
|
## Output
|
|
|
|
|
2013-05-22 05:54:35 -05:00
|
|
|
The pass results in the struct `MoveMaps` which contains several
|
|
|
|
maps:
|
|
|
|
|
|
|
|
`moves_map` is a set containing the id of every *outermost expression* or
|
|
|
|
*binding* that causes a move. Note that `moves_map` only contains the *outermost
|
|
|
|
expressions* that are moved. Therefore, if you have a use of `x.b`,
|
|
|
|
as in the example `y` above, the expression `x.b` would be in the
|
|
|
|
`moves_map` but not `x`. The reason for this is that, for most
|
|
|
|
purposes, it's only the outermost expression that is needed. The
|
|
|
|
borrow checker and trans, for example, only care about the outermost
|
|
|
|
expressions that are moved. It is more efficient therefore just to
|
|
|
|
store those entries.
|
|
|
|
|
|
|
|
Sometimes though we want to know the variables that are moved (in
|
|
|
|
particular in the borrow checker). For these cases, the set
|
|
|
|
`moved_variables_set` just collects the ids of variables that are
|
|
|
|
moved.
|
|
|
|
|
|
|
|
Finally, the `capture_map` maps from the node_id of a closure
|
|
|
|
expression to an array of `CaptureVar` structs detailing which
|
|
|
|
variables are captured and how (by ref, by copy, by move).
|
2013-01-10 12:59:58 -06:00
|
|
|
|
|
|
|
## Enforcement of Moves
|
|
|
|
|
2013-05-28 08:33:31 -05:00
|
|
|
The enforcement of moves is done by the borrow checker. Please see
|
|
|
|
the section "Moves and initialization" in `middle/borrowck/doc.rs`.
|
2013-01-10 12:59:58 -06:00
|
|
|
|
|
|
|
## Distributive property
|
|
|
|
|
|
|
|
Copies are "distributive" over parenthesization, but blocks are
|
|
|
|
considered rvalues. What this means is that, for example, neither
|
|
|
|
`a.clone()` nor `(a).clone()` will move `a` (presuming that `a` has a
|
|
|
|
linear type and `clone()` takes its self by reference), but
|
|
|
|
`{a}.clone()` will move `a`, as would `(if cond {a} else {b}).clone()`
|
|
|
|
and so on.
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
2013-05-17 17:28:44 -05:00
|
|
|
|
2013-01-10 12:59:58 -06:00
|
|
|
use middle::pat_util::{pat_bindings};
|
|
|
|
use middle::freevars;
|
|
|
|
use middle::ty;
|
2013-04-02 11:42:08 -05:00
|
|
|
use middle::typeck::{method_map};
|
2013-01-10 12:59:58 -06:00
|
|
|
use util::ppaux;
|
2013-05-22 05:54:35 -05:00
|
|
|
use util::ppaux::Repr;
|
2013-01-10 12:59:58 -06:00
|
|
|
use util::common::indenter;
|
|
|
|
|
2013-06-28 17:32:26 -05:00
|
|
|
use std::at_vec;
|
|
|
|
use std::hashmap::{HashSet, HashMap};
|
2013-01-10 12:59:58 -06:00
|
|
|
use syntax::ast::*;
|
|
|
|
use syntax::ast_util;
|
|
|
|
use syntax::visit;
|
2013-03-26 15:38:07 -05:00
|
|
|
use syntax::visit::vt;
|
2013-01-10 12:59:58 -06:00
|
|
|
use syntax::codemap::span;
|
|
|
|
|
2013-05-15 17:55:57 -05:00
|
|
|
#[deriving(Encodable, Decodable)]
|
2013-01-10 12:59:58 -06:00
|
|
|
pub enum CaptureMode {
|
|
|
|
CapCopy, // Copy the value into the closure.
|
|
|
|
CapMove, // Move the value into the closure.
|
|
|
|
CapRef, // Reference directly from parent stack frame (used by `&fn()`).
|
|
|
|
}
|
|
|
|
|
2013-05-15 17:55:57 -05:00
|
|
|
#[deriving(Encodable, Decodable)]
|
2013-01-10 12:59:58 -06:00
|
|
|
pub struct CaptureVar {
|
|
|
|
def: def, // Variable being accessed free
|
|
|
|
span: span, // Location of an access to this variable
|
|
|
|
mode: CaptureMode // How variable is being accessed
|
|
|
|
}
|
|
|
|
|
2013-04-03 08:28:36 -05:00
|
|
|
pub type CaptureMap = @mut HashMap<node_id, @[CaptureVar]>;
|
2013-01-10 12:59:58 -06:00
|
|
|
|
2013-04-03 08:28:36 -05:00
|
|
|
pub type MovesMap = @mut HashSet<node_id>;
|
2013-01-10 12:59:58 -06:00
|
|
|
|
2013-03-15 14:24:24 -05:00
|
|
|
/**
|
|
|
|
* Set of variable node-ids that are moved.
|
|
|
|
*
|
|
|
|
* Note: The `VariableMovesMap` stores expression ids that
|
|
|
|
* are moves, whereas this set stores the ids of the variables
|
|
|
|
* that are moved at some point */
|
|
|
|
pub type MovedVariablesSet = @mut HashSet<node_id>;
|
|
|
|
|
2013-01-10 12:59:58 -06:00
|
|
|
/** See the section Output on the module comment for explanation. */
|
2013-07-02 14:47:32 -05:00
|
|
|
#[deriving(Clone)]
|
2013-01-10 12:59:58 -06:00
|
|
|
pub struct MoveMaps {
|
|
|
|
moves_map: MovesMap,
|
2013-03-15 14:24:24 -05:00
|
|
|
moved_variables_set: MovedVariablesSet,
|
2013-01-10 12:59:58 -06:00
|
|
|
capture_map: CaptureMap
|
|
|
|
}
|
|
|
|
|
2013-07-02 14:47:32 -05:00
|
|
|
#[deriving(Clone)]
|
2013-01-10 12:59:58 -06:00
|
|
|
struct VisitContext {
|
|
|
|
tcx: ty::ctxt,
|
2013-03-22 21:26:41 -05:00
|
|
|
method_map: method_map,
|
2013-01-10 12:59:58 -06:00
|
|
|
move_maps: MoveMaps
|
|
|
|
}
|
|
|
|
|
2013-06-17 17:02:17 -05:00
|
|
|
#[deriving(Eq)]
|
2013-01-10 12:59:58 -06:00
|
|
|
enum UseMode {
|
2013-05-22 05:54:35 -05:00
|
|
|
Move, // This value or something owned by it is moved.
|
|
|
|
Read // Read no matter what the type.
|
2013-01-10 12:59:58 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn compute_moves(tcx: ty::ctxt,
|
|
|
|
method_map: method_map,
|
2013-07-19 00:38:55 -05:00
|
|
|
crate: &Crate) -> MoveMaps
|
2013-01-10 12:59:58 -06:00
|
|
|
{
|
|
|
|
let visitor = visit::mk_vt(@visit::Visitor {
|
2013-06-20 14:25:52 -05:00
|
|
|
visit_fn: compute_modes_for_fn,
|
2013-01-10 12:59:58 -06:00
|
|
|
visit_expr: compute_modes_for_expr,
|
2013-06-20 14:25:52 -05:00
|
|
|
visit_local: compute_modes_for_local,
|
2013-01-10 12:59:58 -06:00
|
|
|
.. *visit::default_visitor()
|
|
|
|
});
|
|
|
|
let visit_cx = VisitContext {
|
|
|
|
tcx: tcx,
|
|
|
|
method_map: method_map,
|
|
|
|
move_maps: MoveMaps {
|
2013-04-03 08:28:36 -05:00
|
|
|
moves_map: @mut HashSet::new(),
|
2013-03-15 14:24:24 -05:00
|
|
|
capture_map: @mut HashMap::new(),
|
|
|
|
moved_variables_set: @mut HashSet::new()
|
2013-01-10 12:59:58 -06:00
|
|
|
}
|
|
|
|
};
|
2013-06-11 21:55:16 -05:00
|
|
|
visit::visit_crate(crate, (visit_cx, visitor));
|
2013-01-10 12:59:58 -06:00
|
|
|
return visit_cx.move_maps;
|
|
|
|
}
|
|
|
|
|
2013-03-15 14:24:24 -05:00
|
|
|
pub fn moved_variable_node_id_from_def(def: def) -> Option<node_id> {
|
|
|
|
match def {
|
|
|
|
def_binding(nid, _) |
|
2013-04-30 13:10:21 -05:00
|
|
|
def_arg(nid, _) |
|
2013-03-15 14:24:24 -05:00
|
|
|
def_local(nid, _) |
|
|
|
|
def_self(nid, _) => Some(nid),
|
|
|
|
|
|
|
|
_ => None
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-06-20 14:25:52 -05:00
|
|
|
///////////////////////////////////////////////////////////////////////////
|
2013-01-10 12:59:58 -06:00
|
|
|
// Expressions
|
|
|
|
|
2013-07-19 00:38:55 -05:00
|
|
|
fn compute_modes_for_local<'a>(local: @Local,
|
2013-06-20 14:25:52 -05:00
|
|
|
(cx, v): (VisitContext,
|
|
|
|
vt<VisitContext>)) {
|
2013-07-19 00:38:55 -05:00
|
|
|
cx.use_pat(local.pat);
|
|
|
|
for local.init.iter().advance |&init| {
|
2013-06-20 14:25:52 -05:00
|
|
|
cx.use_expr(init, Read, v);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn compute_modes_for_fn(fk: &visit::fn_kind,
|
|
|
|
decl: &fn_decl,
|
2013-07-19 00:38:55 -05:00
|
|
|
body: &Block,
|
2013-06-20 14:25:52 -05:00
|
|
|
span: span,
|
|
|
|
id: node_id,
|
|
|
|
(cx, v): (VisitContext,
|
|
|
|
vt<VisitContext>)) {
|
2013-06-24 12:30:35 -05:00
|
|
|
for decl.inputs.iter().advance |a| {
|
2013-06-20 14:25:52 -05:00
|
|
|
cx.use_pat(a.pat);
|
|
|
|
}
|
|
|
|
visit::visit_fn(fk, decl, body, span, id, (cx, v));
|
|
|
|
}
|
|
|
|
|
2013-01-10 12:59:58 -06:00
|
|
|
fn compute_modes_for_expr(expr: @expr,
|
2013-06-11 21:55:16 -05:00
|
|
|
(cx, v): (VisitContext,
|
|
|
|
vt<VisitContext>))
|
2013-01-10 12:59:58 -06:00
|
|
|
{
|
|
|
|
cx.consume_expr(expr, v);
|
|
|
|
}
|
|
|
|
|
2013-05-31 17:17:22 -05:00
|
|
|
impl VisitContext {
|
|
|
|
pub fn consume_exprs(&self, exprs: &[@expr], visitor: vt<VisitContext>) {
|
2013-06-21 07:29:53 -05:00
|
|
|
for exprs.iter().advance |expr| {
|
2013-01-10 12:59:58 -06:00
|
|
|
self.consume_expr(*expr, visitor);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-05-31 17:17:22 -05:00
|
|
|
pub fn consume_expr(&self, expr: @expr, visitor: vt<VisitContext>) {
|
2013-01-10 12:59:58 -06:00
|
|
|
/*!
|
|
|
|
* Indicates that the value of `expr` will be consumed,
|
|
|
|
* meaning either copied or moved depending on its type.
|
|
|
|
*/
|
|
|
|
|
2013-05-22 05:54:35 -05:00
|
|
|
debug!("consume_expr(expr=%s)",
|
|
|
|
expr.repr(self.tcx));
|
2013-01-10 12:59:58 -06:00
|
|
|
|
|
|
|
let expr_ty = ty::expr_ty_adjusted(self.tcx, expr);
|
2013-05-22 05:54:35 -05:00
|
|
|
if ty::type_moves_by_default(self.tcx, expr_ty) {
|
|
|
|
self.move_maps.moves_map.insert(expr.id);
|
|
|
|
self.use_expr(expr, Move, visitor);
|
|
|
|
} else {
|
|
|
|
self.use_expr(expr, Read, visitor);
|
|
|
|
};
|
2013-01-10 12:59:58 -06:00
|
|
|
}
|
|
|
|
|
2013-07-19 00:38:55 -05:00
|
|
|
pub fn consume_block(&self, blk: &Block, visitor: vt<VisitContext>) {
|
2013-01-10 12:59:58 -06:00
|
|
|
/*!
|
|
|
|
* Indicates that the value of `blk` will be consumed,
|
|
|
|
* meaning either copied or moved depending on its type.
|
|
|
|
*/
|
|
|
|
|
2013-07-16 13:08:35 -05:00
|
|
|
debug!("consume_block(blk.id=%?)", blk.id);
|
2013-01-10 12:59:58 -06:00
|
|
|
|
2013-07-16 13:08:35 -05:00
|
|
|
for blk.stmts.iter().advance |stmt| {
|
2013-06-11 21:55:16 -05:00
|
|
|
(visitor.visit_stmt)(*stmt, (*self, visitor));
|
2013-01-10 12:59:58 -06:00
|
|
|
}
|
|
|
|
|
2013-07-16 13:08:35 -05:00
|
|
|
for blk.expr.iter().advance |tail_expr| {
|
2013-01-10 12:59:58 -06:00
|
|
|
self.consume_expr(*tail_expr, visitor);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-05-31 17:17:22 -05:00
|
|
|
pub fn use_expr(&self,
|
|
|
|
expr: @expr,
|
|
|
|
expr_mode: UseMode,
|
|
|
|
visitor: vt<VisitContext>) {
|
2013-01-10 12:59:58 -06:00
|
|
|
/*!
|
|
|
|
* Indicates that `expr` is used with a given mode. This will
|
|
|
|
* in turn trigger calls to the subcomponents of `expr`.
|
|
|
|
*/
|
|
|
|
|
2013-05-22 05:54:35 -05:00
|
|
|
debug!("use_expr(expr=%s, mode=%?)",
|
|
|
|
expr.repr(self.tcx),
|
2013-01-10 12:59:58 -06:00
|
|
|
expr_mode);
|
|
|
|
|
|
|
|
// `expr_mode` refers to the post-adjustment value. If one of
|
|
|
|
// those adjustments is to take a reference, then it's only
|
|
|
|
// reading the underlying expression, not moving it.
|
2013-02-05 21:41:45 -06:00
|
|
|
let comp_mode = match self.tcx.adjustments.find(&expr.id) {
|
2013-03-22 21:26:41 -05:00
|
|
|
Some(&@ty::AutoDerefRef(
|
2013-02-27 18:28:37 -06:00
|
|
|
ty::AutoDerefRef {
|
|
|
|
autoref: Some(_), _})) => Read,
|
2013-05-22 05:54:35 -05:00
|
|
|
_ => expr_mode
|
2013-01-10 12:59:58 -06:00
|
|
|
};
|
|
|
|
|
|
|
|
debug!("comp_mode = %?", comp_mode);
|
|
|
|
|
|
|
|
match expr.node {
|
2013-05-10 17:15:06 -05:00
|
|
|
expr_path(*) | expr_self => {
|
2013-01-10 12:59:58 -06:00
|
|
|
match comp_mode {
|
2013-05-22 05:54:35 -05:00
|
|
|
Move => {
|
2013-05-05 11:17:59 -05:00
|
|
|
let def = self.tcx.def_map.get_copy(&expr.id);
|
2013-06-10 16:50:12 -05:00
|
|
|
let r = moved_variable_node_id_from_def(def);
|
|
|
|
for r.iter().advance |&id| {
|
2013-03-15 14:24:24 -05:00
|
|
|
self.move_maps.moved_variables_set.insert(id);
|
|
|
|
}
|
2013-01-10 12:59:58 -06:00
|
|
|
}
|
|
|
|
Read => {}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-06-01 17:31:56 -05:00
|
|
|
expr_unary(_, deref, base) => { // *base
|
2013-01-10 12:59:58 -06:00
|
|
|
if !self.use_overloaded_operator(
|
2013-03-26 14:04:30 -05:00
|
|
|
expr, base, [], visitor)
|
2013-01-10 12:59:58 -06:00
|
|
|
{
|
|
|
|
// Moving out of *base moves out of base.
|
|
|
|
self.use_expr(base, comp_mode, visitor);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
expr_field(base, _, _) => { // base.f
|
|
|
|
// Moving out of base.f moves out of base.
|
|
|
|
self.use_expr(base, comp_mode, visitor);
|
|
|
|
}
|
|
|
|
|
2013-06-01 17:31:56 -05:00
|
|
|
expr_index(_, lhs, rhs) => { // lhs[rhs]
|
2013-01-10 12:59:58 -06:00
|
|
|
if !self.use_overloaded_operator(
|
2013-03-26 14:04:30 -05:00
|
|
|
expr, lhs, [rhs], visitor)
|
2013-01-10 12:59:58 -06:00
|
|
|
{
|
|
|
|
self.use_expr(lhs, comp_mode, visitor);
|
|
|
|
self.consume_expr(rhs, visitor);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
expr_call(callee, ref args, _) => { // callee(args)
|
2013-06-17 17:02:17 -05:00
|
|
|
// Figure out whether the called function is consumed.
|
|
|
|
let mode = match ty::get(ty::expr_ty(self.tcx, callee)).sty {
|
|
|
|
ty::ty_closure(ref cty) => {
|
|
|
|
match cty.onceness {
|
|
|
|
Once => Move,
|
|
|
|
Many => Read,
|
|
|
|
}
|
|
|
|
},
|
|
|
|
ty::ty_bare_fn(*) => Read,
|
|
|
|
ref x =>
|
|
|
|
self.tcx.sess.span_bug(callee.span,
|
|
|
|
fmt!("non-function type in moves for expr_call: %?", x)),
|
|
|
|
};
|
|
|
|
// Note we're not using consume_expr, which uses type_moves_by_default
|
|
|
|
// to determine the mode, for this. The reason is that while stack
|
|
|
|
// closures should be noncopyable, they shouldn't move by default;
|
|
|
|
// calling a closure should only consume it if it's once.
|
|
|
|
if mode == Move {
|
|
|
|
self.move_maps.moves_map.insert(callee.id);
|
|
|
|
}
|
|
|
|
self.use_expr(callee, mode, visitor);
|
2013-01-10 12:59:58 -06:00
|
|
|
self.use_fn_args(callee.id, *args, visitor);
|
|
|
|
}
|
|
|
|
|
2013-06-01 17:31:56 -05:00
|
|
|
expr_method_call(callee_id, rcvr, _, _, ref args, _) => { // callee.m(args)
|
2013-01-10 12:59:58 -06:00
|
|
|
// Implicit self is equivalent to & mode, but every
|
|
|
|
// other kind should be + mode.
|
2013-06-01 17:31:56 -05:00
|
|
|
self.use_receiver(rcvr, visitor);
|
|
|
|
self.use_fn_args(callee_id, *args, visitor);
|
2013-01-10 12:59:58 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
expr_struct(_, ref fields, opt_with) => {
|
2013-06-21 07:29:53 -05:00
|
|
|
for fields.iter().advance |field| {
|
2013-07-19 09:24:22 -05:00
|
|
|
self.consume_expr(field.expr, visitor);
|
2013-01-10 12:59:58 -06:00
|
|
|
}
|
|
|
|
|
2013-06-10 16:50:12 -05:00
|
|
|
for opt_with.iter().advance |with_expr| {
|
2013-01-31 19:12:29 -06:00
|
|
|
// If there are any fields whose type is move-by-default,
|
|
|
|
// then `with` is consumed, otherwise it is only read
|
|
|
|
let with_ty = ty::expr_ty(self.tcx, *with_expr);
|
|
|
|
let with_fields = match ty::get(with_ty).sty {
|
|
|
|
ty::ty_struct(did, ref substs) => {
|
|
|
|
ty::struct_fields(self.tcx, did, substs)
|
|
|
|
}
|
|
|
|
ref r => {
|
|
|
|
self.tcx.sess.span_bug(
|
|
|
|
with_expr.span,
|
|
|
|
fmt!("bad base expr type in record: %?", r))
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
// The `with` expr must be consumed if it contains
|
|
|
|
// any fields which (1) were not explicitly
|
|
|
|
// specified and (2) have a type that
|
|
|
|
// moves-by-default:
|
2013-07-04 21:13:26 -05:00
|
|
|
let consume_with = with_fields.iter().any(|tf| {
|
2013-07-19 09:24:22 -05:00
|
|
|
!fields.iter().any(|f| f.ident == tf.ident) &&
|
2013-02-07 21:33:12 -06:00
|
|
|
ty::type_moves_by_default(self.tcx, tf.mt.ty)
|
2013-01-31 19:12:29 -06:00
|
|
|
});
|
|
|
|
|
|
|
|
if consume_with {
|
|
|
|
self.consume_expr(*with_expr, visitor);
|
|
|
|
} else {
|
|
|
|
self.use_expr(*with_expr, Read, visitor);
|
|
|
|
}
|
2013-01-10 12:59:58 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
expr_tup(ref exprs) => {
|
|
|
|
self.consume_exprs(*exprs, visitor);
|
|
|
|
}
|
|
|
|
|
|
|
|
expr_if(cond_expr, ref then_blk, opt_else_expr) => {
|
|
|
|
self.consume_expr(cond_expr, visitor);
|
|
|
|
self.consume_block(then_blk, visitor);
|
2013-06-10 16:50:12 -05:00
|
|
|
for opt_else_expr.iter().advance |else_expr| {
|
2013-01-10 12:59:58 -06:00
|
|
|
self.consume_expr(*else_expr, visitor);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
expr_match(discr, ref arms) => {
|
|
|
|
// We must do this first so that `arms_have_by_move_bindings`
|
|
|
|
// below knows which bindings are moves.
|
2013-06-21 07:29:53 -05:00
|
|
|
for arms.iter().advance |arm| {
|
2013-01-10 12:59:58 -06:00
|
|
|
self.consume_arm(arm, visitor);
|
|
|
|
}
|
|
|
|
|
2013-05-22 05:54:35 -05:00
|
|
|
// The discriminant may, in fact, be partially moved
|
|
|
|
// if there are by-move bindings, but borrowck deals
|
|
|
|
// with that itself.
|
|
|
|
self.use_expr(discr, Read, visitor);
|
2013-01-10 12:59:58 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
expr_paren(base) => {
|
|
|
|
// Note: base is not considered a *component* here, so
|
|
|
|
// use `expr_mode` not `comp_mode`.
|
|
|
|
self.use_expr(base, expr_mode, visitor);
|
|
|
|
}
|
|
|
|
|
|
|
|
expr_vec(ref exprs, _) => {
|
|
|
|
self.consume_exprs(*exprs, visitor);
|
|
|
|
}
|
|
|
|
|
|
|
|
expr_addr_of(_, base) => { // &base
|
|
|
|
self.use_expr(base, Read, visitor);
|
|
|
|
}
|
|
|
|
|
2013-03-15 20:54:39 -05:00
|
|
|
expr_inline_asm(*) |
|
2013-01-10 12:59:58 -06:00
|
|
|
expr_break(*) |
|
|
|
|
expr_again(*) |
|
2013-03-12 19:53:25 -05:00
|
|
|
expr_lit(*) => {}
|
2013-01-10 12:59:58 -06:00
|
|
|
|
|
|
|
expr_loop(ref blk, _) => {
|
|
|
|
self.consume_block(blk, visitor);
|
|
|
|
}
|
|
|
|
|
2013-03-26 23:42:01 -05:00
|
|
|
expr_log(a_expr, b_expr) => {
|
2013-01-10 12:59:58 -06:00
|
|
|
self.consume_expr(a_expr, visitor);
|
|
|
|
self.use_expr(b_expr, Read, visitor);
|
|
|
|
}
|
|
|
|
|
|
|
|
expr_while(cond_expr, ref blk) => {
|
|
|
|
self.consume_expr(cond_expr, visitor);
|
|
|
|
self.consume_block(blk, visitor);
|
|
|
|
}
|
|
|
|
|
2013-06-01 17:31:56 -05:00
|
|
|
expr_unary(_, _, lhs) => {
|
2013-01-10 12:59:58 -06:00
|
|
|
if !self.use_overloaded_operator(
|
2013-03-26 14:04:30 -05:00
|
|
|
expr, lhs, [], visitor)
|
2013-01-10 12:59:58 -06:00
|
|
|
{
|
|
|
|
self.consume_expr(lhs, visitor);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-06-01 17:31:56 -05:00
|
|
|
expr_binary(_, _, lhs, rhs) => {
|
2013-01-10 12:59:58 -06:00
|
|
|
if !self.use_overloaded_operator(
|
2013-03-26 14:04:30 -05:00
|
|
|
expr, lhs, [rhs], visitor)
|
2013-01-10 12:59:58 -06:00
|
|
|
{
|
|
|
|
self.consume_expr(lhs, visitor);
|
|
|
|
self.consume_expr(rhs, visitor);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
expr_block(ref blk) => {
|
|
|
|
self.consume_block(blk, visitor);
|
|
|
|
}
|
|
|
|
|
|
|
|
expr_ret(ref opt_expr) => {
|
2013-06-10 16:50:12 -05:00
|
|
|
for opt_expr.iter().advance |expr| {
|
2013-01-10 12:59:58 -06:00
|
|
|
self.consume_expr(*expr, visitor);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
expr_assign(lhs, rhs) => {
|
|
|
|
self.use_expr(lhs, Read, visitor);
|
|
|
|
self.consume_expr(rhs, visitor);
|
|
|
|
}
|
|
|
|
|
|
|
|
expr_cast(base, _) => {
|
|
|
|
self.consume_expr(base, visitor);
|
|
|
|
}
|
|
|
|
|
2013-06-01 17:31:56 -05:00
|
|
|
expr_assign_op(_, _, lhs, rhs) => {
|
2013-01-10 12:59:58 -06:00
|
|
|
// FIXME(#4712) --- Overloaded operators?
|
|
|
|
//
|
|
|
|
// if !self.use_overloaded_operator(
|
|
|
|
// expr, DoDerefArgs, lhs, [rhs], visitor)
|
|
|
|
// {
|
|
|
|
self.consume_expr(lhs, visitor);
|
|
|
|
self.consume_expr(rhs, visitor);
|
|
|
|
// }
|
|
|
|
}
|
|
|
|
|
|
|
|
expr_repeat(base, count, _) => {
|
|
|
|
self.consume_expr(base, visitor);
|
|
|
|
self.consume_expr(count, visitor);
|
|
|
|
}
|
|
|
|
|
|
|
|
expr_loop_body(base) |
|
|
|
|
expr_do_body(base) => {
|
|
|
|
self.use_expr(base, comp_mode, visitor);
|
|
|
|
}
|
|
|
|
|
2013-06-20 14:25:52 -05:00
|
|
|
expr_fn_block(ref decl, ref body) => {
|
2013-06-24 12:30:35 -05:00
|
|
|
for decl.inputs.iter().advance |a| {
|
2013-06-20 14:25:52 -05:00
|
|
|
self.use_pat(a.pat);
|
|
|
|
}
|
2013-01-10 12:59:58 -06:00
|
|
|
let cap_vars = self.compute_captures(expr.id);
|
|
|
|
self.move_maps.capture_map.insert(expr.id, cap_vars);
|
|
|
|
self.consume_block(body, visitor);
|
|
|
|
}
|
|
|
|
|
|
|
|
expr_vstore(base, _) => {
|
|
|
|
self.use_expr(base, comp_mode, visitor);
|
|
|
|
}
|
|
|
|
|
|
|
|
expr_mac(*) => {
|
|
|
|
self.tcx.sess.span_bug(
|
|
|
|
expr.span,
|
2013-04-30 11:47:52 -05:00
|
|
|
"macro expression remains after expansion");
|
2013-01-10 12:59:58 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-05-31 17:17:22 -05:00
|
|
|
pub fn use_overloaded_operator(&self,
|
2013-06-27 08:04:22 -05:00
|
|
|
expr: &expr,
|
2013-05-31 17:17:22 -05:00
|
|
|
receiver_expr: @expr,
|
|
|
|
arg_exprs: &[@expr],
|
|
|
|
visitor: vt<VisitContext>)
|
|
|
|
-> bool {
|
2013-02-08 16:08:02 -06:00
|
|
|
if !self.method_map.contains_key(&expr.id) {
|
2013-01-10 12:59:58 -06:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2013-03-27 04:40:56 -05:00
|
|
|
self.use_receiver(receiver_expr, visitor);
|
2013-01-10 12:59:58 -06:00
|
|
|
|
2013-03-26 14:04:30 -05:00
|
|
|
// for overloaded operatrs, we are always passing in a
|
|
|
|
// borrowed pointer, so it's always read mode:
|
2013-06-21 07:29:53 -05:00
|
|
|
for arg_exprs.iter().advance |arg_expr| {
|
2013-03-26 14:04:30 -05:00
|
|
|
self.use_expr(*arg_expr, Read, visitor);
|
2013-01-10 12:59:58 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2013-05-31 17:17:22 -05:00
|
|
|
pub fn consume_arm(&self, arm: &arm, visitor: vt<VisitContext>) {
|
2013-06-10 16:50:12 -05:00
|
|
|
for arm.pats.iter().advance |pat| {
|
2013-01-10 12:59:58 -06:00
|
|
|
self.use_pat(*pat);
|
|
|
|
}
|
|
|
|
|
2013-06-10 16:50:12 -05:00
|
|
|
for arm.guard.iter().advance |guard| {
|
2013-01-10 12:59:58 -06:00
|
|
|
self.consume_expr(*guard, visitor);
|
|
|
|
}
|
|
|
|
|
|
|
|
self.consume_block(&arm.body, visitor);
|
|
|
|
}
|
|
|
|
|
2013-05-31 17:17:22 -05:00
|
|
|
pub fn use_pat(&self, pat: @pat) {
|
2013-01-10 12:59:58 -06:00
|
|
|
/*!
|
|
|
|
*
|
|
|
|
* Decides whether each binding in a pattern moves the value
|
|
|
|
* into itself or not based on its type and annotation.
|
|
|
|
*/
|
|
|
|
|
2013-06-20 14:25:52 -05:00
|
|
|
do pat_bindings(self.tcx.def_map, pat) |bm, id, _span, path| {
|
2013-05-22 05:54:35 -05:00
|
|
|
let binding_moves = match bm {
|
|
|
|
bind_by_ref(_) => false,
|
2013-01-10 12:59:58 -06:00
|
|
|
bind_infer => {
|
|
|
|
let pat_ty = ty::node_id_to_type(self.tcx, id);
|
2013-06-20 14:25:52 -05:00
|
|
|
debug!("pattern %? %s type is %s",
|
|
|
|
id,
|
|
|
|
ast_util::path_to_ident(path).repr(self.tcx),
|
|
|
|
pat_ty.repr(self.tcx));
|
2013-05-22 05:54:35 -05:00
|
|
|
ty::type_moves_by_default(self.tcx, pat_ty)
|
2013-01-10 12:59:58 -06:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2013-05-29 18:59:33 -05:00
|
|
|
debug!("pattern binding %?: bm=%?, binding_moves=%b",
|
|
|
|
id, bm, binding_moves);
|
|
|
|
|
2013-05-22 05:54:35 -05:00
|
|
|
if binding_moves {
|
|
|
|
self.move_maps.moves_map.insert(id);
|
2013-01-10 12:59:58 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-05-31 17:17:22 -05:00
|
|
|
pub fn use_receiver(&self,
|
|
|
|
receiver_expr: @expr,
|
|
|
|
visitor: vt<VisitContext>) {
|
2013-04-24 03:29:46 -05:00
|
|
|
self.use_fn_arg(receiver_expr, visitor);
|
2013-01-10 12:59:58 -06:00
|
|
|
}
|
|
|
|
|
2013-05-31 17:17:22 -05:00
|
|
|
pub fn use_fn_args(&self,
|
|
|
|
_: node_id,
|
|
|
|
arg_exprs: &[@expr],
|
|
|
|
visitor: vt<VisitContext>) {
|
2013-04-24 03:29:46 -05:00
|
|
|
//! Uses the argument expressions.
|
2013-06-21 07:29:53 -05:00
|
|
|
for arg_exprs.iter().advance |arg_expr| {
|
2013-04-24 03:29:46 -05:00
|
|
|
self.use_fn_arg(*arg_expr, visitor);
|
2013-01-10 12:59:58 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-05-31 17:17:22 -05:00
|
|
|
pub fn use_fn_arg(&self, arg_expr: @expr, visitor: vt<VisitContext>) {
|
2013-04-24 03:29:46 -05:00
|
|
|
//! Uses the argument.
|
|
|
|
self.consume_expr(arg_expr, visitor)
|
2013-01-10 12:59:58 -06:00
|
|
|
}
|
|
|
|
|
2013-05-31 17:17:22 -05:00
|
|
|
pub fn arms_have_by_move_bindings(&self,
|
|
|
|
moves_map: MovesMap,
|
|
|
|
arms: &[arm])
|
|
|
|
-> Option<@pat> {
|
2013-06-21 07:29:53 -05:00
|
|
|
for arms.iter().advance |arm| {
|
|
|
|
for arm.pats.iter().advance |&pat| {
|
2013-05-22 05:54:35 -05:00
|
|
|
for ast_util::walk_pat(pat) |p| {
|
|
|
|
if moves_map.contains(&p.id) {
|
|
|
|
return Some(p);
|
2013-01-10 12:59:58 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2013-05-22 05:54:35 -05:00
|
|
|
return None;
|
2013-01-10 12:59:58 -06:00
|
|
|
}
|
|
|
|
|
2013-05-31 17:17:22 -05:00
|
|
|
pub fn compute_captures(&self, fn_expr_id: node_id) -> @[CaptureVar] {
|
2013-01-10 12:59:58 -06:00
|
|
|
debug!("compute_capture_vars(fn_expr_id=%?)", fn_expr_id);
|
|
|
|
let _indenter = indenter();
|
|
|
|
|
|
|
|
let fn_ty = ty::node_id_to_type(self.tcx, fn_expr_id);
|
2013-01-31 19:12:29 -06:00
|
|
|
let sigil = ty::ty_closure_sigil(fn_ty);
|
2013-01-10 12:59:58 -06:00
|
|
|
let freevars = freevars::get_freevars(self.tcx, fn_expr_id);
|
2013-01-31 19:12:29 -06:00
|
|
|
if sigil == BorrowedSigil {
|
2013-01-10 12:59:58 -06:00
|
|
|
// &fn() captures everything by ref
|
|
|
|
at_vec::from_fn(freevars.len(), |i| {
|
|
|
|
let fvar = &freevars[i];
|
|
|
|
CaptureVar {def: fvar.def, span: fvar.span, mode: CapRef}
|
|
|
|
})
|
|
|
|
} else {
|
|
|
|
// @fn() and ~fn() capture by copy or by move depending on type
|
|
|
|
at_vec::from_fn(freevars.len(), |i| {
|
|
|
|
let fvar = &freevars[i];
|
|
|
|
let fvar_def_id = ast_util::def_id_of_def(fvar.def).node;
|
|
|
|
let fvar_ty = ty::node_id_to_type(self.tcx, fvar_def_id);
|
|
|
|
debug!("fvar_def_id=%? fvar_ty=%s",
|
|
|
|
fvar_def_id, ppaux::ty_to_str(self.tcx, fvar_ty));
|
2013-02-07 21:33:12 -06:00
|
|
|
let mode = if ty::type_moves_by_default(self.tcx, fvar_ty) {
|
2013-01-10 12:59:58 -06:00
|
|
|
CapMove
|
|
|
|
} else {
|
|
|
|
CapCopy
|
|
|
|
};
|
|
|
|
CaptureVar {def: fvar.def, span: fvar.span, mode:mode}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
2013-01-31 20:24:09 -06:00
|
|
|
}
|