737e115646
This is an interim fix to address the "Beware!" unsoundness. I have a more comprehensive rewrite of mode.rs in the pipeline. r=pcwalton
269 lines
9.8 KiB
Rust
269 lines
9.8 KiB
Rust
// 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.
|
|
|
|
use core::prelude::*;
|
|
|
|
use middle::pat_util;
|
|
use middle::ty;
|
|
use middle::ty::{CopyValue, MoveValue, ReadValue, ValueMode, ctxt};
|
|
use middle::typeck::{method_map, method_map_entry};
|
|
|
|
use core::vec;
|
|
use std::map::HashMap;
|
|
use syntax::ast::{bind_infer, box, by_copy, by_move, by_ref, by_val, crate};
|
|
use syntax::ast::{deref, expr, expr_addr_of, expr_assign, expr_assign_op};
|
|
use syntax::ast::{expr_binary, expr_call, expr_copy, expr_field, expr_index};
|
|
use syntax::ast::{expr_match, expr_method_call, expr_paren, expr_path};
|
|
use syntax::ast::{expr_swap, expr_unary, neg, node_id, not, pat, pat_ident};
|
|
use syntax::ast::{sty_uniq, sty_value, uniq};
|
|
use syntax::ast::{fn_decl, blk};
|
|
use syntax::visit;
|
|
use syntax::visit::{fn_kind, vt};
|
|
use syntax::print::pprust;
|
|
use syntax::codemap::span;
|
|
|
|
struct VisitContext {
|
|
tcx: ctxt,
|
|
method_map: HashMap<node_id,method_map_entry>,
|
|
mode: ValueMode,
|
|
}
|
|
|
|
fn compute_modes_for_fn(fk: fn_kind,
|
|
decl: fn_decl,
|
|
body: blk,
|
|
sp: span,
|
|
id: node_id,
|
|
&&cx: VisitContext,
|
|
v: vt<VisitContext>) {
|
|
let body_cx = VisitContext { mode: MoveValue, ..cx };
|
|
visit::visit_fn(fk, decl, body, sp, id, body_cx, v);
|
|
}
|
|
|
|
fn compute_modes_for_fn_args(callee_id: node_id,
|
|
args: &[@expr],
|
|
last_arg_is_block: bool,
|
|
&&cx: VisitContext,
|
|
v: vt<VisitContext>) {
|
|
let arg_tys = ty::ty_fn_args(ty::node_id_to_type(cx.tcx, callee_id));
|
|
let mut i = 0;
|
|
for vec::each2(args, arg_tys) |arg, arg_ty| {
|
|
if last_arg_is_block && i == args.len() - 1 {
|
|
let block_cx = VisitContext { mode: MoveValue, ..cx };
|
|
compute_modes_for_expr(*arg, block_cx, v);
|
|
} else {
|
|
match ty::resolved_mode(cx.tcx, arg_ty.mode) {
|
|
by_ref => {
|
|
let arg_cx = VisitContext { mode: ReadValue, ..cx };
|
|
compute_modes_for_expr(*arg, arg_cx, v);
|
|
}
|
|
by_val | by_move | by_copy => {
|
|
compute_modes_for_expr(*arg, cx, v);
|
|
}
|
|
}
|
|
}
|
|
i += 1;
|
|
}
|
|
}
|
|
|
|
fn record_mode_for_expr(expr: @expr, &&cx: VisitContext) {
|
|
match cx.mode {
|
|
ReadValue | CopyValue => {
|
|
cx.tcx.value_modes.insert(expr.id, cx.mode);
|
|
}
|
|
MoveValue => {
|
|
// This is, contextually, a move, but if this expression
|
|
// is implicitly copyable it's cheaper to copy.
|
|
let e_ty = ty::expr_ty(cx.tcx, expr);
|
|
if ty::type_implicitly_moves(cx.tcx, e_ty) {
|
|
cx.tcx.value_modes.insert(expr.id, MoveValue);
|
|
} else {
|
|
cx.tcx.value_modes.insert(expr.id, CopyValue);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fn compute_modes_for_expr(expr: @expr,
|
|
&&cx: VisitContext,
|
|
v: vt<VisitContext>) {
|
|
debug!("compute_modes_for_expr(expr=%?/%s, mode=%?)",
|
|
expr.id, pprust::expr_to_str(expr, cx.tcx.sess.intr()),
|
|
cx.mode);
|
|
|
|
// Adjust the mode if there was an implicit reference here.
|
|
let cx = match cx.tcx.adjustments.find(expr.id) {
|
|
None => cx,
|
|
Some(adjustment) => {
|
|
if adjustment.autoref.is_some() {
|
|
VisitContext { mode: ReadValue, ..cx }
|
|
} else {
|
|
cx
|
|
}
|
|
}
|
|
};
|
|
|
|
match copy expr.node {
|
|
expr_call(callee, args, is_block) => {
|
|
let callee_cx = VisitContext { mode: ReadValue, ..cx };
|
|
compute_modes_for_expr(callee, callee_cx, v);
|
|
compute_modes_for_fn_args(callee.id, args, is_block, cx, v);
|
|
}
|
|
expr_path(*) => {
|
|
record_mode_for_expr(expr, cx);
|
|
}
|
|
expr_copy(expr) => {
|
|
let callee_cx = VisitContext { mode: CopyValue, ..cx };
|
|
compute_modes_for_expr(expr, callee_cx, v);
|
|
}
|
|
expr_method_call(callee, _, _, args, is_block) => {
|
|
// The LHS of the dot may or may not result in a move, depending
|
|
// on the method map entry.
|
|
let callee_mode;
|
|
match cx.method_map.find(expr.id) {
|
|
Some(ref method_map_entry) => {
|
|
match method_map_entry.explicit_self {
|
|
sty_uniq(_) | sty_value => callee_mode = MoveValue,
|
|
_ => callee_mode = ReadValue
|
|
}
|
|
}
|
|
None => {
|
|
cx.tcx.sess.span_bug(expr.span, ~"no method map entry");
|
|
}
|
|
}
|
|
|
|
let callee_cx = VisitContext { mode: callee_mode, ..cx };
|
|
compute_modes_for_expr(callee, callee_cx, v);
|
|
|
|
compute_modes_for_fn_args(expr.callee_id, args, is_block, cx, v);
|
|
}
|
|
expr_binary(_, lhs, rhs) | expr_assign_op(_, lhs, rhs) => {
|
|
// The signatures of these take their arguments by-ref, so they
|
|
// don't copy or move.
|
|
let arg_cx = VisitContext { mode: ReadValue, ..cx };
|
|
compute_modes_for_expr(lhs, arg_cx, v);
|
|
compute_modes_for_expr(rhs, arg_cx, v);
|
|
}
|
|
expr_addr_of(_, arg) => {
|
|
// Takes its argument by-ref, so it doesn't copy or move.
|
|
let arg_cx = VisitContext { mode: ReadValue, ..cx };
|
|
compute_modes_for_expr(arg, arg_cx, v);
|
|
}
|
|
expr_unary(unop, arg) => {
|
|
match unop {
|
|
deref => {
|
|
// Derefs function as reads.
|
|
let arg_cx = VisitContext { mode: ReadValue, ..cx };
|
|
compute_modes_for_expr(arg, arg_cx, v);
|
|
|
|
// This is an lvalue, so it needs a value mode recorded
|
|
// for it.
|
|
record_mode_for_expr(expr, cx);
|
|
}
|
|
box(_) | uniq(_) => {
|
|
let arg_cx = VisitContext { mode: MoveValue, ..cx };
|
|
compute_modes_for_expr(arg, arg_cx, v);
|
|
}
|
|
not | neg => {
|
|
// Takes its argument by ref.
|
|
let arg_cx = VisitContext { mode: ReadValue, ..cx };
|
|
compute_modes_for_expr(arg, arg_cx, v);
|
|
}
|
|
}
|
|
}
|
|
expr_field(arg, _, _) => {
|
|
let arg_cx = VisitContext { mode: ReadValue, ..cx };
|
|
compute_modes_for_expr(arg, arg_cx, v);
|
|
|
|
record_mode_for_expr(expr, cx);
|
|
}
|
|
expr_assign(lhs, rhs) => {
|
|
// The signatures of these take their arguments by-ref, so they
|
|
// don't copy or move.
|
|
let arg_cx = VisitContext { mode: ReadValue, ..cx };
|
|
compute_modes_for_expr(lhs, arg_cx, v);
|
|
compute_modes_for_expr(rhs, cx, v);
|
|
}
|
|
expr_swap(lhs, rhs) => {
|
|
let arg_cx = VisitContext { mode: ReadValue, ..cx };
|
|
compute_modes_for_expr(lhs, arg_cx, v);
|
|
compute_modes_for_expr(rhs, arg_cx, v);
|
|
}
|
|
expr_index(lhs, rhs) => {
|
|
let lhs_cx = VisitContext { mode: ReadValue, ..cx };
|
|
compute_modes_for_expr(lhs, lhs_cx, v);
|
|
let rhs_cx = VisitContext { mode: MoveValue, ..cx };
|
|
compute_modes_for_expr(rhs, rhs_cx, v);
|
|
|
|
record_mode_for_expr(expr, cx);
|
|
}
|
|
expr_paren(arg) => {
|
|
compute_modes_for_expr(arg, cx, v);
|
|
record_mode_for_expr(expr, cx);
|
|
}
|
|
expr_match(head, ref arms) => {
|
|
// We must do this first so that `arms_have_by_move_bindings`
|
|
// below knows which bindings are moves.
|
|
for arms.each |arm| {
|
|
(v.visit_arm)(*arm, cx, v);
|
|
}
|
|
|
|
let by_move_bindings_present =
|
|
pat_util::arms_have_by_move_bindings(cx.tcx, *arms);
|
|
if by_move_bindings_present {
|
|
// Propagate the current mode flag downward.
|
|
visit::visit_expr(expr, cx, v);
|
|
} else {
|
|
// We aren't moving into any pattern, so this is just a read.
|
|
let head_cx = VisitContext { mode: ReadValue, ..cx };
|
|
compute_modes_for_expr(head, head_cx, v);
|
|
}
|
|
}
|
|
_ => {
|
|
// XXX: Spell out every expression above so when we add them we
|
|
// don't forget to update this file.
|
|
visit::visit_expr(expr, cx, v)
|
|
}
|
|
}
|
|
}
|
|
|
|
fn compute_modes_for_pat(pat: @pat,
|
|
&&cx: VisitContext,
|
|
v: vt<VisitContext>) {
|
|
match pat.node {
|
|
pat_ident(bind_infer, _, _)
|
|
if pat_util::pat_is_binding(cx.tcx.def_map, pat) => {
|
|
if ty::type_implicitly_moves(cx.tcx, ty::pat_ty(cx.tcx, pat)) {
|
|
cx.tcx.value_modes.insert(pat.id, MoveValue);
|
|
} else {
|
|
cx.tcx.value_modes.insert(pat.id, CopyValue);
|
|
}
|
|
}
|
|
_ => {}
|
|
}
|
|
|
|
visit::visit_pat(pat, cx, v);
|
|
}
|
|
|
|
pub fn compute_modes(tcx: ctxt, method_map: method_map, crate: @crate) {
|
|
let visitor = visit::mk_vt(@visit::Visitor {
|
|
visit_fn: compute_modes_for_fn,
|
|
visit_expr: compute_modes_for_expr,
|
|
visit_pat: compute_modes_for_pat,
|
|
.. *visit::default_visitor()
|
|
});
|
|
let callee_cx = VisitContext {
|
|
tcx: tcx,
|
|
method_map: method_map,
|
|
mode: MoveValue
|
|
};
|
|
visit::visit_crate(*crate, callee_cx, visitor);
|
|
}
|
|
|