Pre-step towards issue #12624 and others: Introduce ExprUseVisitor, remove the

moves computation. ExprUseVisitor is a visitor that walks the AST for a
function and calls a delegate to inform it where borrows, copies, and moves
occur.

In this patch, I rewrite the gather_loans visitor to use ExprUseVisitor, but in
future patches, I think we could rewrite regionck, check_loans, and possibly
other passes to use it as well. This would refactor the repeated code between
those places that tries to determine where copies/moves/etc occur.
This commit is contained in:
Niko Matsakis 2014-04-21 19:21:53 -04:00
parent 77a975df85
commit 96dfed2b62
27 changed files with 1271 additions and 1630 deletions

View File

@ -23,7 +23,7 @@ use metadata::{creader, filesearch};
use metadata::cstore::CStore;
use metadata::creader::Loader;
use metadata;
use middle::{trans, freevars, kind, ty, typeck, lint, astencode, reachable};
use middle::{trans, freevars, kind, ty, typeck, lint, reachable};
use middle;
use util::common::time;
use util::ppaux;
@ -35,7 +35,6 @@ use std::cell::{Cell, RefCell};
use std::io;
use std::io::fs;
use std::io::MemReader;
use std::mem::drop;
use std::os;
use getopts::{optopt, optmulti, optflag, optflagopt};
use getopts;
@ -270,7 +269,6 @@ pub struct CrateAnalysis {
pub exported_items: middle::privacy::ExportedItems,
pub public_items: middle::privacy::PublicItems,
pub ty_cx: ty::ctxt,
pub maps: astencode::Maps,
pub reachable: NodeSet,
}
@ -346,21 +344,14 @@ pub fn phase_3_run_analysis_passes(sess: Session,
time(time_passes, "effect checking", (), |_|
middle::effect::check_crate(&ty_cx, krate));
let middle::moves::MoveMaps {moves_map, capture_map} =
time(time_passes, "compute moves", (), |_|
middle::moves::compute_moves(&ty_cx, krate));
time(time_passes, "match checking", (), |_|
middle::check_match::check_crate(&ty_cx, &moves_map, krate));
middle::check_match::check_crate(&ty_cx, krate));
time(time_passes, "liveness checking", (), |_|
middle::liveness::check_crate(&ty_cx, &capture_map, krate));
middle::liveness::check_crate(&ty_cx, krate));
time(time_passes, "borrow checking", (), |_|
middle::borrowck::check_crate(&ty_cx, &moves_map,
&capture_map, krate));
drop(moves_map);
middle::borrowck::check_crate(&ty_cx, krate));
time(time_passes, "kind checking", (), |_|
kind::check_crate(&ty_cx, krate));
@ -384,9 +375,6 @@ pub fn phase_3_run_analysis_passes(sess: Session,
ty_cx: ty_cx,
exported_items: exported_items,
public_items: public_items,
maps: astencode::Maps {
capture_map: RefCell::new(capture_map)
},
reachable: reachable_map
}
}

View File

@ -86,13 +86,13 @@ pub mod middle {
pub mod astencode;
pub mod lang_items;
pub mod privacy;
pub mod moves;
pub mod entry;
pub mod effect;
pub mod reachable;
pub mod graph;
pub mod cfg;
pub mod dead;
pub mod expr_use_visitor;
}
pub mod front {

View File

@ -24,8 +24,7 @@ use metadata::tydecode::{DefIdSource, NominalType, TypeWithId, TypeParameter,
RegionParameter};
use metadata::tyencode;
use middle::typeck::{MethodCall, MethodCallee, MethodOrigin};
use middle::{ty, typeck, moves};
use middle;
use middle::{ty, typeck};
use util::ppaux::ty_to_str;
use syntax::{ast, ast_map, ast_util, codemap, fold};
@ -36,7 +35,6 @@ use syntax;
use libc;
use std::cast;
use std::cell::RefCell;
use std::io::Seek;
use std::io::MemWriter;
use std::rc::Rc;
@ -52,15 +50,9 @@ use writer = serialize::ebml::writer;
#[cfg(test)] use syntax::parse;
#[cfg(test)] use syntax::print::pprust;
// Auxiliary maps of things to be encoded
pub struct Maps {
pub capture_map: RefCell<middle::moves::CaptureMap>,
}
struct DecodeContext<'a> {
cdata: &'a cstore::crate_metadata,
tcx: &'a ty::ctxt,
maps: &'a Maps
}
struct ExtendedDecodeContext<'a> {
@ -84,8 +76,7 @@ pub type Encoder<'a> = writer::Encoder<'a, MemWriter>;
pub fn encode_inlined_item(ecx: &e::EncodeContext,
ebml_w: &mut Encoder,
ii: e::InlinedItemRef,
maps: &Maps) {
ii: e::InlinedItemRef) {
let id = match ii {
e::IIItemRef(i) => i.id,
e::IIForeignRef(i) => i.id,
@ -101,7 +92,7 @@ pub fn encode_inlined_item(ecx: &e::EncodeContext,
ebml_w.start_tag(c::tag_ast as uint);
id_range.encode(ebml_w);
encode_ast(ebml_w, ii);
encode_side_tables_for_ii(ecx, maps, ebml_w, &ii);
encode_side_tables_for_ii(ecx, ebml_w, &ii);
ebml_w.end_tag();
debug!("< Encoded inlined fn: {} ({})",
@ -111,14 +102,12 @@ pub fn encode_inlined_item(ecx: &e::EncodeContext,
pub fn decode_inlined_item(cdata: &cstore::crate_metadata,
tcx: &ty::ctxt,
maps: &Maps,
path: Vec<ast_map::PathElem>,
par_doc: ebml::Doc)
-> Result<ast::InlinedItem, Vec<ast_map::PathElem>> {
let dcx = &DecodeContext {
cdata: cdata,
tcx: tcx,
maps: maps
};
match par_doc.opt_child(c::tag_ast) {
None => Err(path),
@ -551,32 +540,6 @@ impl tr for freevar_entry {
}
}
// ______________________________________________________________________
// Encoding and decoding of CaptureVar information
trait capture_var_helper {
fn read_capture_var(&mut self, xcx: &ExtendedDecodeContext)
-> moves::CaptureVar;
}
impl<'a> capture_var_helper for reader::Decoder<'a> {
fn read_capture_var(&mut self, xcx: &ExtendedDecodeContext)
-> moves::CaptureVar {
let cvar: moves::CaptureVar = Decodable::decode(self).unwrap();
cvar.tr(xcx)
}
}
impl tr for moves::CaptureVar {
fn tr(&self, xcx: &ExtendedDecodeContext) -> moves::CaptureVar {
moves::CaptureVar {
def: self.def.tr(xcx),
span: self.span.tr(xcx),
mode: self.mode
}
}
}
// ______________________________________________________________________
// Encoding and decoding of MethodCallee
@ -935,7 +898,6 @@ impl<'a> write_tag_and_id for Encoder<'a> {
struct SideTableEncodingIdVisitor<'a,'b> {
ecx_ptr: *libc::c_void,
new_ebml_w: &'a mut Encoder<'b>,
maps: &'a Maps,
}
impl<'a,'b> ast_util::IdVisitingOperation for
@ -953,12 +915,11 @@ impl<'a,'b> ast_util::IdVisitingOperation for
let ecx: &e::EncodeContext = unsafe {
cast::transmute(self.ecx_ptr)
};
encode_side_tables_for_id(ecx, self.maps, &mut new_ebml_w, id)
encode_side_tables_for_id(ecx, &mut new_ebml_w, id)
}
}
fn encode_side_tables_for_ii(ecx: &e::EncodeContext,
maps: &Maps,
ebml_w: &mut Encoder,
ii: &ast::InlinedItem) {
ebml_w.start_tag(c::tag_table as uint);
@ -974,13 +935,11 @@ fn encode_side_tables_for_ii(ecx: &e::EncodeContext,
cast::transmute(ecx)
},
new_ebml_w: &mut new_ebml_w,
maps: maps,
});
ebml_w.end_tag();
}
fn encode_side_tables_for_id(ecx: &e::EncodeContext,
maps: &Maps,
ebml_w: &mut Encoder,
id: ast::NodeId) {
let tcx = ecx.tcx;
@ -1096,17 +1055,6 @@ fn encode_side_tables_for_id(ecx: &e::EncodeContext,
})
})
}
for &cap_vars in maps.capture_map.borrow().find(&id).iter() {
ebml_w.tag(c::tag_table_capture_map, |ebml_w| {
ebml_w.id(id);
ebml_w.tag(c::tag_table_val, |ebml_w| {
ebml_w.emit_from_vec(cap_vars.as_slice(), |ebml_w, cap_var| {
cap_var.encode(ebml_w)
});
})
})
}
}
trait doc_decoder_helpers {
@ -1405,15 +1353,6 @@ fn decode_side_tables(xcx: &ExtendedDecodeContext,
let adj: ty::AutoAdjustment = val_dsr.read_auto_adjustment(xcx);
dcx.tcx.adjustments.borrow_mut().insert(id, adj);
}
c::tag_table_capture_map => {
let cvars =
val_dsr.read_to_vec(
|val_dsr| Ok(val_dsr.read_capture_var(xcx)))
.unwrap()
.move_iter()
.collect();
dcx.maps.capture_map.borrow_mut().insert(id, Rc::new(cvars));
}
_ => {
xcx.dcx.tcx.sess.bug(
format!("unknown tag found in side tables: {:x}", tag));

View File

@ -18,9 +18,10 @@
// 4. moves do not affect things loaned out in any way
use mc = middle::mem_categorization;
use middle::borrowck::*;
use middle::moves;
use euv = middle::expr_use_visitor;
use middle::freevars;
use mc = middle::mem_categorization;
use middle::ty;
use middle::typeck::MethodCall;
use syntax::ast;
@ -288,7 +289,7 @@ impl<'a> CheckLoanCtxt<'a> {
}
match new_loan.cause {
ClosureCapture(span) => {
euv::ClosureCapture(span) => {
self.bccx.span_note(
span,
format!("borrow occurs due to use of `{}` in closure",
@ -321,13 +322,17 @@ impl<'a> CheckLoanCtxt<'a> {
};
let borrow_summary = match old_loan.cause {
ClosureCapture(_) => {
euv::ClosureCapture(_) => {
format!("previous borrow of `{}` occurs here due to \
use in closure",
self.bccx.loan_path_to_str(&*old_loan.loan_path))
}
AddrOf | AutoRef | RefBinding | ClosureInvocation => {
euv::OverloadedOperator(..) |
euv::AddrOf(..) |
euv::AutoRef(..) |
euv::ClosureInvocation(..) |
euv::RefBinding(..) => {
format!("previous borrow of `{}` occurs here",
self.bccx.loan_path_to_str(&*old_loan.loan_path))
}
@ -711,29 +716,33 @@ impl<'a> CheckLoanCtxt<'a> {
fn check_captured_variables(&self,
closure_id: ast::NodeId,
span: Span) {
for cap_var in self.bccx.capture_map.get(&closure_id).iter() {
let var_id = ast_util::def_id_of_def(cap_var.def).node;
self.check_if_path_is_moved(closure_id, span,
MovedInCapture, &Rc::new(LpVar(var_id)));
match cap_var.mode {
moves::CapRef | moves::CapCopy => {}
moves::CapMove => {
check_by_move_capture(self, closure_id, cap_var, &LpVar(var_id));
let freevar_mode = freevars::get_capture_mode(self.tcx(), closure_id);
freevars::with_freevars(self.tcx(), closure_id, |freevars| {
for freevar in freevars.iter() {
let var_id = ast_util::def_id_of_def(freevar.def).node;
let var_path = Rc::new(LpVar(var_id));
self.check_if_path_is_moved(closure_id, span,
MovedInCapture, &var_path);
match freevar_mode {
freevars::CaptureByRef => { }
freevars::CaptureByValue => {
check_by_move_capture(self, closure_id, freevar, &*var_path);
}
}
}
}
});
return;
fn check_by_move_capture(this: &CheckLoanCtxt,
closure_id: ast::NodeId,
cap_var: &moves::CaptureVar,
freevar: &freevars::freevar_entry,
move_path: &LoanPath) {
let move_err = this.analyze_move_out_from(closure_id, move_path);
match move_err {
MoveOk => {}
MoveWhileBorrowed(loan_path, loan_span) => {
this.bccx.span_err(
cap_var.span,
freevar.span,
format!("cannot move `{}` into closure \
because it is borrowed",
this.bccx.loan_path_to_str(move_path)));

View File

@ -17,7 +17,7 @@ use middle::borrowck::*;
use middle::borrowck::gather_loans::move_error::{MoveError, MoveErrorCollector};
use middle::borrowck::gather_loans::move_error::MoveSpanAndPath;
use middle::borrowck::move_data::*;
use middle::moves;
use euv = middle::expr_use_visitor;
use middle::ty;
use syntax::ast;
use syntax::codemap::Span;
@ -44,10 +44,10 @@ pub fn gather_decl(bccx: &BorrowckCtxt,
pub fn gather_move_from_expr(bccx: &BorrowckCtxt,
move_data: &MoveData,
move_error_collector: &MoveErrorCollector,
move_expr: &ast::Expr,
move_expr_id: ast::NodeId,
cmt: mc::cmt) {
let move_info = GatherMoveInfo {
id: move_expr.id,
id: move_expr_id,
kind: MoveExpr,
cmt: cmt,
span_path_opt: None,
@ -76,29 +76,6 @@ pub fn gather_move_from_pat(bccx: &BorrowckCtxt,
gather_move(bccx, move_data, move_error_collector, move_info);
}
pub fn gather_captures(bccx: &BorrowckCtxt,
move_data: &MoveData,
move_error_collector: &MoveErrorCollector,
closure_expr: &ast::Expr) {
for captured_var in bccx.capture_map.get(&closure_expr.id).iter() {
match captured_var.mode {
moves::CapMove => {
let cmt = bccx.cat_captured_var(closure_expr.id,
closure_expr.span,
captured_var);
let move_info = GatherMoveInfo {
id: closure_expr.id,
kind: Captured,
cmt: cmt,
span_path_opt: None
};
gather_move(bccx, move_data, move_error_collector, move_info);
}
moves::CapCopy | moves::CapRef => {}
}
}
}
fn gather_move(bccx: &BorrowckCtxt,
move_data: &MoveData,
move_error_collector: &MoveErrorCollector,
@ -110,6 +87,7 @@ fn gather_move(bccx: &BorrowckCtxt,
check_and_get_illegal_move_origin(bccx, &move_info.cmt);
match potentially_illegal_move {
Some(illegal_move_origin) => {
debug!("illegal_move_origin={}", illegal_move_origin.repr(bccx.tcx));
let error = MoveError::with_move_info(illegal_move_origin,
move_info.span_path_opt);
move_error_collector.add_error(error);
@ -134,27 +112,14 @@ pub fn gather_assignment(bccx: &BorrowckCtxt,
assignment_id: ast::NodeId,
assignment_span: Span,
assignee_loan_path: Rc<LoanPath>,
assignee_id: ast::NodeId) {
assignee_id: ast::NodeId,
mode: euv::MutateMode) {
move_data.add_assignment(bccx.tcx,
assignee_loan_path,
assignment_id,
assignment_span,
assignee_id,
false);
}
pub fn gather_move_and_assignment(bccx: &BorrowckCtxt,
move_data: &MoveData,
assignment_id: ast::NodeId,
assignment_span: Span,
assignee_loan_path: Rc<LoanPath>,
assignee_id: ast::NodeId) {
move_data.add_assignment(bccx.tcx,
assignee_loan_path,
assignment_id,
assignment_span,
assignee_id,
true);
mode);
}
fn check_and_get_illegal_move_origin(bccx: &BorrowckCtxt,

View File

@ -14,6 +14,7 @@
*/
use middle::borrowck::*;
use euv = middle::expr_use_visitor;
use mc = middle::mem_categorization;
use middle::ty;
use util::ppaux::Repr;
@ -24,9 +25,8 @@ type R = Result<(),()>;
pub fn guarantee_lifetime(bccx: &BorrowckCtxt,
item_scope_id: ast::NodeId,
root_scope_id: ast::NodeId,
span: Span,
cause: LoanCause,
cause: euv::LoanCause,
cmt: mc::cmt,
loan_region: ty::Region,
loan_kind: ty::BorrowKind)
@ -39,8 +39,7 @@ pub fn guarantee_lifetime(bccx: &BorrowckCtxt,
cause: cause,
loan_region: loan_region,
loan_kind: loan_kind,
cmt_original: cmt.clone(),
root_scope_id: root_scope_id};
cmt_original: cmt.clone()};
ctxt.check(&cmt, None)
}
@ -53,12 +52,8 @@ struct GuaranteeLifetimeContext<'a> {
// the node id of the function body for the enclosing item
item_scope_id: ast::NodeId,
// the node id of the innermost loop / function body; this is the
// longest scope for which we can root managed boxes
root_scope_id: ast::NodeId,
span: Span,
cause: LoanCause,
cause: euv::LoanCause,
loan_region: ty::Region,
loan_kind: ty::BorrowKind,
cmt_original: mc::cmt

View File

@ -18,323 +18,144 @@
use middle::borrowck::*;
use middle::borrowck::move_data::MoveData;
use euv = middle::expr_use_visitor;
use mc = middle::mem_categorization;
use middle::moves;
use middle::pat_util;
use middle::ty::{ty_region};
use middle::ty;
use middle::typeck::MethodCall;
use util::common::indenter;
use util::ppaux::{Repr};
use syntax::ast;
use syntax::ast_util;
use syntax::ast_util::IdRange;
use syntax::codemap::Span;
use syntax::print::pprust;
use syntax::visit;
use syntax::visit::{Visitor, FnKind};
use syntax::ast::{Expr, FnDecl, Block, NodeId, Stmt, Pat, Local};
use std::rc::Rc;
use syntax::visit::{Visitor};
use syntax::ast::{Expr, FnDecl, Block, NodeId, Pat};
mod lifetime;
mod restrictions;
mod gather_moves;
mod move_error;
/// Context used while gathering loans:
///
/// - `bccx`: the borrow check context
/// - `item_ub`: the id of the block for the enclosing fn/method item
/// - `root_ub`: the id of the outermost block for which we can root
/// an `@T`. This is the id of the innermost enclosing
/// loop or function body.
///
/// The role of `root_ub` is to prevent us from having to accumulate
/// vectors of rooted items at runtime. Consider this case:
///
/// fn foo(...) -> int {
/// let mut ptr: &int;
/// while some_cond {
/// let x: @int = ...;
/// ptr = &*x;
/// }
/// *ptr
/// }
///
/// If we are not careful here, we would infer the scope of the borrow `&*x`
/// to be the body of the function `foo()` as a whole. We would then
/// have root each `@int` that is produced, which is an unbounded number.
/// No good. Instead what will happen is that `root_ub` will be set to the
/// body of the while loop and we will refuse to root the pointer `&*x`
/// because it would have to be rooted for a region greater than `root_ub`.
pub fn gather_loans_in_fn(bccx: &BorrowckCtxt,
decl: &ast::FnDecl,
body: &ast::Block)
-> (Vec<Loan>, move_data::MoveData)
{
let mut glcx = GatherLoanCtxt {
bccx: bccx,
all_loans: Vec::new(),
item_ub: body.id,
move_data: MoveData::new(),
move_error_collector: move_error::MoveErrorCollector::new(),
};
{
let mut euv = euv::ExprUseVisitor::new(&mut glcx, bccx.tcx);
euv.walk_fn(decl, body);
}
glcx.report_potential_errors();
let GatherLoanCtxt { all_loans, move_data, .. } = glcx;
(all_loans, move_data)
}
struct GatherLoanCtxt<'a> {
bccx: &'a BorrowckCtxt<'a>,
id_range: IdRange,
move_data: move_data::MoveData,
move_error_collector: move_error::MoveErrorCollector,
all_loans: Vec<Loan>,
item_ub: ast::NodeId,
repeating_ids: Vec<ast::NodeId> }
impl<'a> visit::Visitor<()> for GatherLoanCtxt<'a> {
fn visit_expr(&mut self, ex: &Expr, _: ()) {
gather_loans_in_expr(self, ex);
}
fn visit_block(&mut self, b: &Block, _: ()) {
gather_loans_in_block(self, b);
}
/// Do not visit closures or fn items here, the outer loop in
/// borrowck/mod will visit them for us in turn.
fn visit_fn(&mut self, _: &FnKind, _: &FnDecl, _: &Block,
_: Span, _: NodeId, _: ()) {}
fn visit_stmt(&mut self, s: &Stmt, _: ()) {
visit::walk_stmt(self, s, ());
}
fn visit_pat(&mut self, p: &Pat, _: ()) {
add_pat_to_id_range(self, p);
}
fn visit_local(&mut self, l: &Local, _: ()) {
gather_loans_in_local(self, l);
}
// #7740: Do not visit items here, not even fn items nor methods
// of impl items; the outer loop in borrowck/mod will visit them
// for us in turn. Thus override visit_item's walk with a no-op.
fn visit_item(&mut self, _: &ast::Item, _: ()) {}
}
fn add_pat_to_id_range(this: &mut GatherLoanCtxt,
p: &ast::Pat) {
// NB: This visitor function just adds the pat ids into the id
// range. We gather loans that occur in patterns using the
// `gather_pat()` method below. Eventually these two should be
// brought together.
this.id_range.add(p.id);
visit::walk_pat(this, p, ());
}
impl<'a> euv::Delegate for GatherLoanCtxt<'a> {
fn consume(&mut self,
consume_id: ast::NodeId,
_consume_span: Span,
cmt: mc::cmt,
mode: euv::ConsumeMode) {
debug!("consume(consume_id={}, cmt={}, mode={})",
consume_id, cmt.repr(self.tcx()), mode);
pub fn gather_loans_in_fn(bccx: &BorrowckCtxt, decl: &ast::FnDecl, body: &ast::Block)
-> (IdRange, Vec<Loan>, move_data::MoveData) {
let mut glcx = GatherLoanCtxt {
bccx: bccx,
id_range: IdRange::max(),
all_loans: Vec::new(),
item_ub: body.id,
repeating_ids: vec!(body.id),
move_data: MoveData::new(),
move_error_collector: move_error::MoveErrorCollector::new(),
};
glcx.gather_fn_arg_patterns(decl, body);
glcx.visit_block(body, ());
glcx.report_potential_errors();
let GatherLoanCtxt { id_range, all_loans, move_data, .. } = glcx;
(id_range, all_loans, move_data)
}
fn gather_loans_in_block(this: &mut GatherLoanCtxt,
blk: &ast::Block) {
this.id_range.add(blk.id);
visit::walk_block(this, blk, ());
}
fn gather_loans_in_local(this: &mut GatherLoanCtxt,
local: &ast::Local) {
match local.init {
None => {
// Variable declarations without initializers are considered "moves":
let tcx = this.bccx.tcx;
pat_util::pat_bindings(&tcx.def_map, local.pat, |_, id, span, _| {
gather_moves::gather_decl(this.bccx,
&this.move_data,
id,
span,
id);
})
match mode {
euv::Copy => { return; }
euv::Move => { }
}
Some(init) => {
// Variable declarations with initializers are considered "assigns",
// which is handled by `gather_pat`:
let init_cmt = this.bccx.cat_expr(init);
this.gather_pat(init_cmt, local.pat, None);
}
}
visit::walk_local(this, local, ());
}
fn gather_loans_in_expr(this: &mut GatherLoanCtxt,
ex: &ast::Expr) {
let bccx = this.bccx;
let tcx = bccx.tcx;
debug!("gather_loans_in_expr(expr={:?}/{})",
ex.id, pprust::expr_to_str(ex));
this.id_range.add(ex.id);
// If this expression is borrowed, have to ensure it remains valid:
for &adjustments in tcx.adjustments.borrow().find(&ex.id).iter() {
this.guarantee_adjustments(ex, adjustments);
}
// If this expression is a move, gather it:
if this.bccx.is_move(ex.id) {
let cmt = this.bccx.cat_expr(ex);
gather_moves::gather_move_from_expr(
this.bccx, &this.move_data, &this.move_error_collector, ex, cmt);
self.bccx, &self.move_data, &self.move_error_collector,
consume_id, cmt);
}
// Special checks for various kinds of expressions:
let method_map = this.bccx.tcx.method_map.borrow();
match ex.node {
ast::ExprAddrOf(mutbl, base) => {
let base_cmt = this.bccx.cat_expr(base);
fn consume_pat(&mut self,
consume_pat: &ast::Pat,
cmt: mc::cmt,
mode: euv::ConsumeMode) {
debug!("consume_pat(consume_pat={}, cmt={}, mode={})",
consume_pat.repr(self.tcx()),
cmt.repr(self.tcx()),
mode);
// make sure that the thing we are pointing out stays valid
// for the lifetime `scope_r` of the resulting ptr:
let expr_ty = ty::expr_ty(tcx, ex);
if !ty::type_is_bot(expr_ty) {
let scope_r = ty_region(tcx, ex.span, expr_ty);
this.guarantee_valid(ex.id,
ex.span,
base_cmt,
mutbl,
scope_r,
AddrOf);
match mode {
euv::Copy => { return; }
euv::Move => { }
}
visit::walk_expr(this, ex, ());
}
ast::ExprAssign(l, _) => {
with_assignee_loan_path(
this.bccx, l,
|lp| gather_moves::gather_assignment(this.bccx, &this.move_data,
ex.id, ex.span, lp, l.id));
visit::walk_expr(this, ex, ());
}
gather_moves::gather_move_from_pat(
self.bccx, &self.move_data, &self.move_error_collector,
consume_pat, cmt);
}
ast::ExprAssignOp(_, l, _) => {
with_assignee_loan_path(
this.bccx, l,
|lp| gather_moves::gather_move_and_assignment(this.bccx, &this.move_data,
ex.id, ex.span, lp, l.id));
visit::walk_expr(this, ex, ());
}
fn borrow(&mut self,
borrow_id: ast::NodeId,
borrow_span: Span,
cmt: mc::cmt,
loan_region: ty::Region,
bk: ty::BorrowKind,
loan_cause: euv::LoanCause)
{
debug!("borrow(borrow_id={}, cmt={}, loan_region={}, \
bk={}, loan_cause={:?})",
borrow_id, cmt.repr(self.tcx()), loan_region,
bk, loan_cause);
ast::ExprMatch(ex_v, ref arms) => {
let cmt = this.bccx.cat_expr(ex_v);
for arm in arms.iter() {
for pat in arm.pats.iter() {
this.gather_pat(cmt.clone(), *pat, Some((arm.body.id, ex.id)));
self.guarantee_valid(borrow_id,
borrow_span,
cmt,
bk,
loan_region,
loan_cause);
}
fn mutate(&mut self,
assignment_id: ast::NodeId,
assignment_span: Span,
assignee_cmt: mc::cmt,
mode: euv::MutateMode)
{
debug!("mutate(assignment_id={}, assignee_cmt={})",
assignment_id, assignee_cmt.repr(self.tcx()));
match opt_loan_path(&assignee_cmt) {
Some(lp) => {
gather_moves::gather_assignment(self.bccx, &self.move_data,
assignment_id, assignment_span,
lp, assignee_cmt.id, mode);
}
None => {
// This can occur with e.g. `*foo() = 5`. In such
// cases, there is no need to check for conflicts
// with moves etc, just ignore.
}
}
visit::walk_expr(this, ex, ());
}
}
ast::ExprIndex(_, arg) |
ast::ExprBinary(_, _, arg)
if method_map.contains_key(&MethodCall::expr(ex.id)) => {
// Arguments in method calls are always passed by ref.
//
// Currently these do not use adjustments, so we have to
// hardcode this check here (note that the receiver DOES use
// adjustments).
let scope_r = ty::ReScope(ex.id);
let arg_cmt = this.bccx.cat_expr(arg);
this.guarantee_valid(arg.id,
arg.span,
arg_cmt,
ast::MutImmutable,
scope_r,
AutoRef);
visit::walk_expr(this, ex, ());
}
// see explanation attached to the `root_ub` field:
ast::ExprWhile(cond, body) => {
// during the condition, can only root for the condition
this.push_repeating_id(cond.id);
this.visit_expr(cond, ());
this.pop_repeating_id(cond.id);
// during body, can only root for the body
this.push_repeating_id(body.id);
this.visit_block(body, ());
this.pop_repeating_id(body.id);
}
// see explanation attached to the `root_ub` field:
ast::ExprLoop(body, _) => {
this.push_repeating_id(body.id);
visit::walk_expr(this, ex, ());
this.pop_repeating_id(body.id);
}
ast::ExprFnBlock(..) | ast::ExprProc(..) => {
gather_moves::gather_captures(this.bccx, &this.move_data,
&this.move_error_collector, ex);
this.guarantee_captures(ex);
visit::walk_expr(this, ex, ());
}
ast::ExprInlineAsm(ref ia) => {
for &(_, out) in ia.outputs.iter() {
with_assignee_loan_path(
this.bccx, out,
|lp| gather_moves::gather_assignment(this.bccx, &this.move_data,
ex.id, ex.span, lp, out.id));
}
visit::walk_expr(this, ex, ());
}
ast::ExprCall(f, _) => {
let expr_ty = ty::expr_ty_adjusted(tcx, f);
match ty::get(expr_ty).sty {
ty::ty_closure(~ty::ClosureTy {
store: ty::RegionTraitStore(..), ..
}) => {
let scope_r = ty::ReScope(ex.id);
let base_cmt = this.bccx.cat_expr(f);
this.guarantee_valid_kind(f.id,
f.span,
base_cmt,
ty::UniqueImmBorrow,
scope_r,
ClosureInvocation);
}
_ => {}
}
visit::walk_expr(this, ex, ());
}
_ => {
visit::walk_expr(this, ex, ());
}
fn decl_without_init(&mut self, id: ast::NodeId, span: Span) {
gather_moves::gather_decl(self.bccx, &self.move_data, id, span, id);
}
}
fn with_assignee_loan_path(bccx: &BorrowckCtxt, expr: &ast::Expr, op: |Rc<LoanPath>|) {
let cmt = bccx.cat_expr(expr);
match opt_loan_path(&cmt) {
Some(lp) => op(lp),
None => {
// This can occur with e.g. `*foo() = 5`. In such
// cases, there is no need to check for conflicts
// with moves etc, just ignore.
}
}
}
/// Implements the A-* rules in doc.rs.
fn check_aliasability(bccx: &BorrowckCtxt,
borrow_span: Span,
loan_cause: LoanCause,
loan_cause: euv::LoanCause,
cmt: mc::cmt,
req_kind: ty::BorrowKind)
-> Result<(),()> {
@ -385,167 +206,13 @@ fn check_aliasability(bccx: &BorrowckCtxt,
impl<'a> GatherLoanCtxt<'a> {
pub fn tcx(&self) -> &'a ty::ctxt { self.bccx.tcx }
pub fn push_repeating_id(&mut self, id: ast::NodeId) {
self.repeating_ids.push(id);
}
pub fn pop_repeating_id(&mut self, id: ast::NodeId) {
let popped = self.repeating_ids.pop().unwrap();
assert_eq!(id, popped);
}
pub fn guarantee_autoderefs(&mut self,
expr: &ast::Expr,
autoderefs: uint) {
let method_map = self.bccx.tcx.method_map.borrow();
for i in range(0, autoderefs) {
match method_map.find(&MethodCall::autoderef(expr.id, i as u32)) {
Some(method) => {
// Treat overloaded autoderefs as if an AutoRef adjustment
// was applied on the base type, as that is always the case.
let cmt = match self.bccx.mc().cat_expr_autoderefd(expr, i) {
Ok(v) => v,
Err(()) => self.tcx().sess.span_bug(expr.span, "Err from mc")
};
let self_ty = *ty::ty_fn_args(method.ty).get(0);
let (m, r) = match ty::get(self_ty).sty {
ty::ty_rptr(r, ref m) => (m.mutbl, r),
_ => self.tcx().sess.span_bug(expr.span,
format!("bad overloaded deref type {}",
method.ty.repr(self.tcx())))
};
self.guarantee_valid(expr.id,
expr.span,
cmt,
m,
r,
AutoRef);
}
None => {}
}
}
}
pub fn guarantee_adjustments(&mut self,
expr: &ast::Expr,
adjustment: &ty::AutoAdjustment) {
debug!("guarantee_adjustments(expr={}, adjustment={:?})",
expr.repr(self.tcx()), adjustment);
let _i = indenter();
match *adjustment {
ty::AutoAddEnv(..) => {
debug!("autoaddenv -- no autoref");
return;
}
ty::AutoDerefRef(
ty::AutoDerefRef {
autoref: None, autoderefs }) => {
debug!("no autoref");
self.guarantee_autoderefs(expr, autoderefs);
return;
}
ty::AutoDerefRef(
ty::AutoDerefRef {
autoref: Some(ref autoref),
autoderefs}) => {
self.guarantee_autoderefs(expr, autoderefs);
let mc = self.bccx.mc();
let cmt = match mc.cat_expr_autoderefd(expr, autoderefs) {
Ok(v) => v,
Err(()) => self.tcx().sess.span_bug(expr.span, "Err from mc")
};
debug!("after autoderef, cmt={}", cmt.repr(self.tcx()));
match *autoref {
ty::AutoPtr(r, m) => {
self.guarantee_valid(expr.id,
expr.span,
cmt,
m,
r,
AutoRef)
}
ty::AutoBorrowVec(r, m) | ty::AutoBorrowVecRef(r, m) => {
let cmt_index = mc.cat_index(expr, cmt, autoderefs+1);
self.guarantee_valid(expr.id,
expr.span,
cmt_index,
m,
r,
AutoRef)
}
ty::AutoBorrowObj(r, m) => {
let cmt_deref = mc.cat_deref_obj(expr, cmt);
self.guarantee_valid(expr.id,
expr.span,
cmt_deref,
m,
r,
AutoRef)
}
ty::AutoUnsafe(_) => {}
}
}
ty::AutoObject(..) => {
// FIXME: Handle ~Trait to &Trait casts here?
}
}
}
fn guarantee_captures(&mut self,
closure_expr: &ast::Expr) {
for captured_var in self.bccx.capture_map.get(&closure_expr.id).iter() {
match captured_var.mode {
moves::CapCopy | moves::CapMove => { continue; }
moves::CapRef => { }
}
let var_id = ast_util::def_id_of_def(captured_var.def).node;
let var_cmt = self.bccx.cat_captured_var(closure_expr.id,
closure_expr.span,
captured_var);
// Lookup the kind of borrow the callee requires
let upvar_id = ty::UpvarId { var_id: var_id,
closure_expr_id: closure_expr.id };
let upvar_borrow = self.tcx().upvar_borrow_map.borrow()
.get_copy(&upvar_id);
self.guarantee_valid_kind(closure_expr.id,
closure_expr.span,
var_cmt,
upvar_borrow.kind,
upvar_borrow.region,
ClosureCapture(captured_var.span));
}
}
pub fn guarantee_valid(&mut self,
borrow_id: ast::NodeId,
borrow_span: Span,
cmt: mc::cmt,
req_mutbl: ast::Mutability,
loan_region: ty::Region,
cause: LoanCause) {
self.guarantee_valid_kind(borrow_id,
borrow_span,
cmt,
ty::BorrowKind::from_mutbl(req_mutbl),
loan_region,
cause);
}
fn guarantee_valid_kind(&mut self,
borrow_id: ast::NodeId,
borrow_span: Span,
cmt: mc::cmt,
req_kind: ty::BorrowKind,
loan_region: ty::Region,
cause: LoanCause) {
fn guarantee_valid(&mut self,
borrow_id: ast::NodeId,
borrow_span: Span,
cmt: mc::cmt,
req_kind: ty::BorrowKind,
loan_region: ty::Region,
cause: euv::LoanCause) {
/*!
* Guarantees that `addr_of(cmt)` will be valid for the duration of
* `static_scope_r`, or reports an error. This may entail taking
@ -567,11 +234,9 @@ impl<'a> GatherLoanCtxt<'a> {
return;
}
let root_ub = { *self.repeating_ids.last().unwrap() }; // FIXME(#5074)
// Check that the lifetime of the borrow does not exceed
// the lifetime of the data being borrowed.
if lifetime::guarantee_lifetime(self.bccx, self.item_ub, root_ub,
if lifetime::guarantee_lifetime(self.bccx, self.item_ub,
borrow_span, cause, cmt.clone(), loan_region,
req_kind).is_err() {
return; // reported an error, no sense in reporting more.
@ -689,7 +354,7 @@ impl<'a> GatherLoanCtxt<'a> {
fn check_mutability(bccx: &BorrowckCtxt,
borrow_span: Span,
cause: LoanCause,
cause: euv::LoanCause,
cmt: mc::cmt,
req_kind: ty::BorrowKind)
-> Result<(),()> {
@ -804,146 +469,6 @@ impl<'a> GatherLoanCtxt<'a> {
}
}
fn gather_fn_arg_patterns(&mut self,
decl: &ast::FnDecl,
body: &ast::Block) {
/*!
* Walks the patterns for fn arguments, checking that they
* do not attempt illegal moves or create refs that outlive
* the arguments themselves. Just a shallow wrapper around
* `gather_pat()`.
*/
let mc = self.bccx.mc();
for arg in decl.inputs.iter() {
let arg_ty = ty::node_id_to_type(self.tcx(), arg.pat.id);
let arg_cmt = mc.cat_rvalue(
arg.id,
arg.pat.span,
ty::ReScope(body.id), // Args live only as long as the fn body.
arg_ty);
self.gather_pat(arg_cmt, arg.pat, None);
}
}
fn gather_pat(&mut self,
discr_cmt: mc::cmt,
root_pat: @ast::Pat,
arm_match_ids: Option<(ast::NodeId, ast::NodeId)>) {
/*!
* Walks patterns, examining the bindings to determine if they
* cause borrows (`ref` bindings, vector patterns) or
* moves (non-`ref` bindings with linear type).
*/
self.bccx.cat_pattern(discr_cmt, root_pat, |cmt, pat| {
match pat.node {
ast::PatIdent(bm, _, _) if self.pat_is_binding(pat) => {
// Each match binding is effectively an assignment.
let tcx = self.bccx.tcx;
pat_util::pat_bindings(&tcx.def_map, pat, |_, id, span, _| {
gather_moves::gather_assignment(self.bccx,
&self.move_data,
id,
span,
Rc::new(LpVar(id)),
id);
});
match bm {
ast::BindByRef(mutbl) => {
// ref x or ref x @ p --- creates a ptr which must
// remain valid for the scope of the match
// find the region of the resulting pointer (note that
// the type of such a pattern will *always* be a
// region pointer)
let scope_r =
ty_region(self.tcx(), pat.span,
ty::node_id_to_type(self.tcx(), pat.id));
// if the scope of the region ptr turns out to be
// specific to this arm, wrap the categorization
// with a cat_discr() node. There is a detailed
// discussion of the function of this node in
// `lifetime.rs`:
let cmt_discr = match arm_match_ids {
None => cmt,
Some((arm_id, match_id)) => {
let arm_scope = ty::ReScope(arm_id);
if self.bccx.is_subregion_of(scope_r, arm_scope) {
self.bccx.cat_discr(cmt, match_id)
} else {
cmt
}
}
};
self.guarantee_valid(pat.id,
pat.span,
cmt_discr,
mutbl,
scope_r,
RefBinding);
}
ast::BindByValue(_) => {
// No borrows here, but there may be moves
if self.bccx.is_move(pat.id) {
gather_moves::gather_move_from_pat(
self.bccx, &self.move_data,
&self.move_error_collector, pat, cmt);
}
}
}
}
ast::PatVec(_, Some(slice_pat), _) => {
// The `slice_pat` here creates a slice into the
// original vector. This is effectively a borrow of
// the elements of the vector being matched.
let (slice_cmt, slice_borrow_kind, slice_r) = {
match self.bccx.mc().cat_slice_pattern(cmt, slice_pat) {
Ok(v) => v,
Err(()) => {
self.tcx().sess.span_bug(slice_pat.span,
"Err from mc")
}
}
};
// Note: We declare here that the borrow occurs upon
// entering the `[...]` pattern. This implies that
// something like `[a, ..b]` where `a` is a move is
// illegal, because the borrow is already in effect.
// In fact such a move would be safe-ish, but it
// effectively *requires* that we use the nulling
// out semantics to indicate when a value has been
// moved, which we are trying to move away from.
// Otherwise, how can we indicate that the first
// element in the vector has been moved?
// Eventually, we could perhaps modify this rule to
// permit `[..a, b]` where `b` is a move, because in
// that case we can adjust the length of the
// original vec accordingly, but we'd have to make
// trans do the right thing, and it would only work
// for `~` vectors. It seems simpler to just require
// that people call `vec.pop()` or `vec.unshift()`.
self.guarantee_valid(pat.id, pat.span,
slice_cmt, slice_borrow_kind, slice_r,
RefBinding);
}
_ => {}
}
})
}
pub fn pat_is_binding(&self, pat: &ast::Pat) -> bool {
pat_util::pat_is_binding(&self.bccx.tcx.def_map, pat)
}
pub fn report_potential_errors(&self) {
self.move_error_collector.report_potential_errors(self.bccx);
}
@ -955,7 +480,6 @@ impl<'a> GatherLoanCtxt<'a> {
/// sure the loans being taken are sound.
struct StaticInitializerCtxt<'a> {
bccx: &'a BorrowckCtxt<'a>,
id_range: IdRange,
item_ub: ast::NodeId,
}
@ -966,7 +490,8 @@ impl<'a> visit::Visitor<()> for StaticInitializerCtxt<'a> {
let base_cmt = self.bccx.cat_expr(base);
let borrow_kind = ty::BorrowKind::from_mutbl(mutbl);
// Check that we don't allow borrows of unsafe static items.
if check_aliasability(self.bccx, ex.span, AddrOf, base_cmt, borrow_kind).is_err() {
if check_aliasability(self.bccx, ex.span, euv::AddrOf,
base_cmt, borrow_kind).is_err() {
return; // reported an error, no sense in reporting more.
}
}
@ -983,7 +508,6 @@ pub fn gather_loans_in_static_initializer(bccx: &mut BorrowckCtxt, expr: &ast::E
let mut sicx = StaticInitializerCtxt {
bccx: bccx,
id_range: IdRange::max(),
item_ub: expr.id,
};

View File

@ -100,6 +100,7 @@ fn group_errors_with_same_origin(errors: &Vec<MoveError>)
fn append_to_grouped_errors(grouped_errors: &mut Vec<GroupedMoveErrors>,
error: &MoveError) {
let move_from_id = error.move_from.id;
debug!("append_to_grouped_errors(move_from_id={})", move_from_id);
let move_to = if error.move_to.is_some() {
vec!(error.move_to.clone().unwrap())
} else {
@ -107,10 +108,12 @@ fn group_errors_with_same_origin(errors: &Vec<MoveError>)
};
for ge in grouped_errors.mut_iter() {
if move_from_id == ge.move_from.id && error.move_to.is_some() {
debug!("appending move_to to list");
ge.move_to_places.push_all_move(move_to);
return
}
}
debug!("found a new move from location");
grouped_errors.push(GroupedMoveErrors {
move_from: error.move_from.clone(),
move_to_places: move_to

View File

@ -13,6 +13,7 @@
*/
use middle::borrowck::*;
use euv = middle::expr_use_visitor;
use mc = middle::mem_categorization;
use middle::ty;
use syntax::codemap::Span;
@ -27,7 +28,7 @@ pub enum RestrictionResult {
pub fn compute_restrictions(bccx: &BorrowckCtxt,
span: Span,
cause: LoanCause,
cause: euv::LoanCause,
cmt: mc::cmt,
loan_region: ty::Region,
restr: RestrictionSet) -> RestrictionResult {
@ -50,7 +51,7 @@ struct RestrictionsContext<'a> {
span: Span,
cmt_original: mc::cmt,
loan_region: ty::Region,
cause: LoanCause,
cause: euv::LoanCause,
}
impl<'a> RestrictionsContext<'a> {

View File

@ -12,16 +12,14 @@
#![allow(non_camel_case_types)]
use mc = middle::mem_categorization;
use middle::ty;
use middle::typeck;
use middle::moves;
use middle::dataflow::DataFlowContext;
use middle::dataflow::DataFlowOperator;
use util::nodemap::{NodeMap, NodeSet};
use euv = middle::expr_use_visitor;
use mc = middle::mem_categorization;
use middle::ty;
use util::ppaux::{note_and_explain_region, Repr, UserString};
use std::cell::{Cell, RefCell};
use std::cell::{Cell};
use std::ops::{BitOr, BitAnd};
use std::rc::Rc;
use std::strbuf::StrBuf;
@ -75,13 +73,9 @@ impl<'a> Visitor<()> for BorrowckCtxt<'a> {
}
pub fn check_crate(tcx: &ty::ctxt,
moves_map: &NodeSet,
capture_map: &moves::CaptureMap,
krate: &ast::Crate) {
let mut bccx = BorrowckCtxt {
tcx: tcx,
moves_map: moves_map,
capture_map: capture_map,
stats: @BorrowStats {
loaned_paths_same: Cell::new(0),
loaned_paths_imm: Cell::new(0),
@ -135,7 +129,8 @@ fn borrowck_fn(this: &mut BorrowckCtxt,
debug!("borrowck_fn(id={})", id);
// Check the body of fn items.
let (id_range, all_loans, move_data) =
let id_range = ast_util::compute_id_range_for_fn_body(fk, decl, body, sp, id);
let (all_loans, move_data) =
gather_loans::gather_loans_in_fn(this, decl, body);
let mut loan_dfcx =
DataFlowContext::new(this.tcx,
@ -164,8 +159,6 @@ fn borrowck_fn(this: &mut BorrowckCtxt,
pub struct BorrowckCtxt<'a> {
tcx: &'a ty::ctxt,
moves_map: &'a NodeSet,
capture_map: &'a moves::CaptureMap,
// Statistics:
stats: @BorrowStats
@ -199,16 +192,7 @@ pub struct Loan {
gen_scope: ast::NodeId,
kill_scope: ast::NodeId,
span: Span,
cause: LoanCause,
}
#[deriving(Eq)]
pub enum LoanCause {
ClosureCapture(Span),
AddrOf,
AutoRef,
RefBinding,
ClosureInvocation,
cause: euv::LoanCause,
}
#[deriving(Eq, TotalEq, Hash)]
@ -341,14 +325,14 @@ pub enum bckerr_code {
#[deriving(Eq)]
pub struct BckError {
span: Span,
cause: LoanCause,
cause: euv::LoanCause,
cmt: mc::cmt,
code: bckerr_code
}
pub enum AliasableViolationKind {
MutabilityViolation,
BorrowViolation(LoanCause)
BorrowViolation(euv::LoanCause)
}
pub enum MovedValueUseKind {
@ -370,14 +354,8 @@ impl<'a> BorrowckCtxt<'a> {
self.tcx.region_maps.is_subscope_of(r_sub, r_sup)
}
pub fn is_move(&self, id: ast::NodeId) -> bool {
self.moves_map.contains(&id)
}
pub fn mc(&self) -> mc::MemCategorizationContext<&'a ty::ctxt> {
mc::MemCategorizationContext {
typer: self.tcx,
}
pub fn mc(&self) -> mc::MemCategorizationContext<'a,ty::ctxt> {
mc::MemCategorizationContext::new(self.tcx)
}
pub fn cat_expr(&self, expr: &ast::Expr) -> mc::cmt {
@ -439,14 +417,15 @@ impl<'a> BorrowckCtxt<'a> {
}
pub fn cat_captured_var(&self,
id: ast::NodeId,
span: Span,
captured_var: &moves::CaptureVar) -> mc::cmt {
closure_id: ast::NodeId,
closure_span: Span,
upvar_def: ast::Def)
-> mc::cmt {
// Create the cmt for the variable being borrowed, from the
// caller's perspective
let var_id = ast_util::def_id_of_def(captured_var.def).node;
let var_id = ast_util::def_id_of_def(upvar_def).node;
let var_ty = ty::node_id_to_type(self.tcx, var_id);
self.cat_def(id, span, var_ty, captured_var.def)
self.cat_def(closure_id, closure_span, var_ty, upvar_def)
}
pub fn cat_discr(&self, cmt: mc::cmt, match_id: ast::NodeId) -> mc::cmt {
@ -604,13 +583,16 @@ impl<'a> BorrowckCtxt<'a> {
};
match err.cause {
ClosureCapture(_) => {
euv::ClosureCapture(_) => {
format!("closure cannot assign to {}", descr)
}
AddrOf | RefBinding | AutoRef => {
euv::OverloadedOperator |
euv::AddrOf |
euv::RefBinding |
euv::AutoRef => {
format!("cannot borrow {} as mutable", descr)
}
ClosureInvocation => {
euv::ClosureInvocation => {
self.tcx.sess.span_bug(err.span,
"err_mutbl with a closure invocation");
}
@ -644,7 +626,7 @@ impl<'a> BorrowckCtxt<'a> {
MutabilityViolation => {
"cannot assign to data"
}
BorrowViolation(ClosureCapture(_)) => {
BorrowViolation(euv::ClosureCapture(_)) => {
// I don't think we can get aliasability violations
// with closure captures, so no need to come up with a
// good error message. The reason this cannot happen
@ -654,13 +636,14 @@ impl<'a> BorrowckCtxt<'a> {
span,
"aliasability violation with closure");
}
BorrowViolation(AddrOf) |
BorrowViolation(AutoRef) |
BorrowViolation(RefBinding) => {
BorrowViolation(euv::OverloadedOperator) |
BorrowViolation(euv::AddrOf) |
BorrowViolation(euv::AutoRef) |
BorrowViolation(euv::RefBinding) => {
"cannot borrow data mutably"
}
BorrowViolation(ClosureInvocation) => {
BorrowViolation(euv::ClosureInvocation) => {
"closure invocation"
}
};
@ -839,34 +822,3 @@ impl Repr for LoanPath {
}
}
///////////////////////////////////////////////////////////////////////////
impl<'a> mc::Typer for &'a ty::ctxt {
fn tcx<'a>(&'a self) -> &'a ty::ctxt {
*self
}
fn node_ty(&self, id: ast::NodeId) -> mc::McResult<ty::t> {
Ok(ty::node_id_to_type(*self, id))
}
fn node_method_ty(&self, method_call: typeck::MethodCall) -> Option<ty::t> {
self.method_map.borrow().find(&method_call).map(|method| method.ty)
}
fn adjustments<'a>(&'a self) -> &'a RefCell<NodeMap<ty::AutoAdjustment>> {
&self.adjustments
}
fn is_method_call(&self, id: ast::NodeId) -> bool {
self.method_map.borrow().contains_key(&typeck::MethodCall::expr(id))
}
fn temporary_scope(&self, id: ast::NodeId) -> Option<ast::NodeId> {
self.region_maps.temporary_scope(id)
}
fn upvar_borrow(&self, id: ty::UpvarId) -> ty::UpvarBorrow {
self.upvar_borrow_map.borrow().get_copy(&id)
}
}

View File

@ -22,6 +22,7 @@ use collections::{HashMap, HashSet};
use middle::borrowck::*;
use middle::dataflow::DataFlowContext;
use middle::dataflow::DataFlowOperator;
use euv = middle::expr_use_visitor;
use middle::ty;
use syntax::ast;
use syntax::ast_util;
@ -357,7 +358,7 @@ impl MoveData {
assign_id: ast::NodeId,
span: Span,
assignee_id: ast::NodeId,
is_also_move: bool) {
mode: euv::MutateMode) {
/*!
* Adds a new record for an assignment to `lp` that occurs at
* location `id` with the given `span`.
@ -368,8 +369,11 @@ impl MoveData {
let path_index = self.move_path(tcx, lp.clone());
if !is_also_move {
self.assignee_ids.borrow_mut().insert(assignee_id);
match mode {
euv::JustWrite => {
self.assignee_ids.borrow_mut().insert(assignee_id);
}
euv::WriteAndRead => { }
}
let assignment = Assignment {

View File

@ -15,7 +15,6 @@ use middle::const_eval::{eval_const_expr, const_val, const_bool, const_float};
use middle::pat_util::*;
use middle::ty::*;
use middle::ty;
use util::nodemap::NodeSet;
use util::ppaux::ty_to_str;
use std::cmp;
@ -29,7 +28,6 @@ use syntax::visit::{Visitor, FnKind};
struct MatchCheckCtxt<'a> {
tcx: &'a ty::ctxt,
moves_map: &'a NodeSet
}
impl<'a> Visitor<()> for MatchCheckCtxt<'a> {
@ -45,11 +43,9 @@ impl<'a> Visitor<()> for MatchCheckCtxt<'a> {
}
pub fn check_crate(tcx: &ty::ctxt,
moves_map: &NodeSet,
krate: &Crate) {
let mut cx = MatchCheckCtxt {
tcx: tcx,
moves_map: moves_map
};
visit::walk_crate(&mut cx, krate, ());
@ -931,22 +927,18 @@ fn is_refutable(cx: &MatchCheckCtxt, pat: &Pat) -> bool {
// Legality of move bindings checking
fn check_legality_of_move_bindings(cx: &MatchCheckCtxt,
has_guard: bool,
pats: &[@Pat]) {
has_guard: bool,
pats: &[@Pat]) {
let tcx = cx.tcx;
let def_map = &tcx.def_map;
let mut by_ref_span = None;
let mut any_by_move = false;
for pat in pats.iter() {
pat_bindings(def_map, *pat, |bm, id, span, _path| {
pat_bindings(def_map, *pat, |bm, _, span, _path| {
match bm {
BindByRef(_) => {
by_ref_span = Some(span);
}
BindByValue(_) => {
if cx.moves_map.contains(&id) {
any_by_move = true;
}
}
}
})
@ -975,16 +967,18 @@ fn check_legality_of_move_bindings(cx: &MatchCheckCtxt,
}
};
if !any_by_move { return; } // pointless micro-optimization
for pat in pats.iter() {
walk_pat(*pat, |p| {
if pat_is_binding(def_map, p) {
match p.node {
PatIdent(_, _, sub) => {
if cx.moves_map.contains(&p.id) {
PatIdent(BindByValue(_), _, sub) => {
let pat_ty = ty::node_id_to_type(tcx, p.id);
if ty::type_moves_by_default(tcx, pat_ty) {
check_move(p, sub);
}
}
PatIdent(BindByRef(_), _, _) => {
}
_ => {
cx.tcx.sess.span_bug(
p.span,

View File

@ -15,7 +15,7 @@ use middle::astencode;
use middle::ty;
use middle::typeck::astconv;
use util::nodemap::{DefIdMap, NodeMap};
use util::nodemap::{DefIdMap};
use syntax::ast::*;
use syntax::parse::token::InternedString;
@ -23,7 +23,6 @@ use syntax::visit::Visitor;
use syntax::visit;
use syntax::{ast, ast_map, ast_util};
use std::cell::RefCell;
use std::rc::Rc;
//
@ -125,11 +124,8 @@ pub fn lookup_variant_by_id(tcx: &ty::ctxt,
Some(&e) => return e,
None => {}
}
let maps = astencode::Maps {
capture_map: RefCell::new(NodeMap::new())
};
let e = match csearch::maybe_get_item_ast(tcx, enum_def,
|a, b, c, d| astencode::decode_inlined_item(a, b, &maps, c, d)) {
|a, b, c, d| astencode::decode_inlined_item(a, b, c, d)) {
csearch::found(ast::IIItem(item)) => match item.node {
ItemEnum(ast::EnumDef { variants: ref variants }, _) => {
variant_expr(variants.as_slice(), variant_def.node)
@ -163,11 +159,8 @@ pub fn lookup_const_by_id(tcx: &ty::ctxt, def_id: ast::DefId)
Some(&e) => return e,
None => {}
}
let maps = astencode::Maps {
capture_map: RefCell::new(NodeMap::new())
};
let e = match csearch::maybe_get_item_ast(tcx, def_id,
|a, b, c, d| astencode::decode_inlined_item(a, b, &maps, c, d)) {
|a, b, c, d| astencode::decode_inlined_item(a, b, c, d)) {
csearch::found(ast::IIItem(item)) => match item.node {
ItemStatic(_, ast::MutImmutable, const_expr) => Some(const_expr),
_ => None

View File

@ -0,0 +1,868 @@
// Copyright 2014 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.
/*!
* A different sort of visitor for walking fn bodies. Unlike the
* normal visitor, which just walks the entire body in one shot, the
* `ExprUseVisitor` determines how expressions are being used.
*/
use mc = middle::mem_categorization;
use middle::freevars;
use middle::pat_util;
use middle::ty;
use middle::typeck;
use syntax::ast;
use syntax::ast_util;
use syntax::codemap::{Span};
use util::ppaux::Repr;
///////////////////////////////////////////////////////////////////////////
// The Delegate trait
//
// This trait defines the callbacks you can expect to receiver when
// employing the ExprUseVisitor.
#[deriving(Eq)]
pub enum LoanCause {
ClosureCapture(Span),
AddrOf,
AutoRef,
RefBinding,
OverloadedOperator,
ClosureInvocation
}
#[deriving(Eq,Show)]
pub enum ConsumeMode {
Copy, // reference to x where x has a type that copies
Move, // reference to x where x has a type that moves
}
#[deriving(Eq,Show)]
pub enum MutateMode {
JustWrite, // x = y
WriteAndRead, // x += y
}
pub trait Delegate {
// The value found at `cmt` is either copied or moved, depending
// on mode.
fn consume(&mut self,
_consume_id: ast::NodeId,
_consume_span: Span,
_cmt: mc::cmt,
_mode: ConsumeMode)
{ }
// The value found at `cmt` is either copied or moved via the
// pattern binding `consume_pat`, depending on mode.
fn consume_pat(&mut self,
_consume_pat: &ast::Pat,
_cmt: mc::cmt,
_mode: ConsumeMode)
{ }
// The value found at `borrow` is being borrowed at the point
// `borrow_id` for the region `loan_region` with kind `bk`.
fn borrow(&mut self,
_borrow_id: ast::NodeId,
_borrow_span: Span,
_cmt: mc::cmt,
_loan_region: ty::Region,
_bk: ty::BorrowKind,
_loan_cause: LoanCause)
{ }
// The local variable `id` is declared but not initialized.
fn decl_without_init(&mut self,
_id: ast::NodeId,
_span: Span)
{ }
// The path at `cmt` is being assigned to.
fn mutate(&mut self,
_assignment_id: ast::NodeId,
_assignment_span: Span,
_assignee_cmt: mc::cmt,
_mode: MutateMode)
{ }
}
///////////////////////////////////////////////////////////////////////////
// The ExprUseVisitor type
//
// This is the code that actually walks the tree. Like
// mem_categorization, it requires a TYPER, which is a type that
// supplies types from the tree. After type checking is complete, you
// can just use the tcx as the typer.
pub struct ExprUseVisitor<'d,'t,D,TYPER> {
typer: &'t TYPER,
mc: mc::MemCategorizationContext<'t,TYPER>,
delegate: &'d mut D,
}
// If the TYPER results in an error, it's because the type check
// failed (or will fail, when the error is uncovered and reported
// during writeback). In this case, we just ignore this part of the
// code.
//
// Note that this macro appears similar to try!(), but, unlike try!(),
// it does not propagate the error.
macro_rules! ignore_err(
($inp: expr) => (
match $inp {
Ok(v) => v,
Err(()) => return
}
)
)
impl<'d,'t,D:Delegate,TYPER:mc::Typer> ExprUseVisitor<'d,'t,D,TYPER> {
pub fn new(delegate: &'d mut D,
typer: &'t TYPER)
-> ExprUseVisitor<'d,'t,D,TYPER> {
ExprUseVisitor { typer: typer,
mc: mc::MemCategorizationContext::new(typer),
delegate: delegate }
}
pub fn walk_fn(&mut self,
decl: &ast::FnDecl,
body: &ast::Block) {
self.walk_arg_patterns(decl, body);
self.walk_block(body);
}
fn walk_arg_patterns(&mut self,
decl: &ast::FnDecl,
body: &ast::Block) {
for arg in decl.inputs.iter() {
let arg_ty = ty::node_id_to_type(self.tcx(), arg.pat.id);
let arg_cmt = self.mc.cat_rvalue(
arg.id,
arg.pat.span,
ty::ReScope(body.id), // Args live only as long as the fn body.
arg_ty);
self.walk_pat(arg_cmt, arg.pat);
}
}
fn tcx<'a>(&'a self) -> &'a ty::ctxt {
self.typer.tcx()
}
fn delegate_consume(&mut self,
consume_id: ast::NodeId,
consume_span: Span,
cmt: mc::cmt) {
let mode = copy_or_move(self.tcx(), cmt.ty);
self.delegate.consume(consume_id, consume_span, cmt, mode);
}
fn consume_exprs(&mut self, exprs: &Vec<@ast::Expr>) {
for &expr in exprs.iter() {
self.consume_expr(expr);
}
}
fn consume_expr(&mut self, expr: &ast::Expr) {
debug!("consume_expr(expr={})", expr.repr(self.tcx()));
let cmt = ignore_err!(self.mc.cat_expr(expr));
self.delegate_consume(expr.id, expr.span, cmt);
match expr.node {
ast::ExprParen(subexpr) => {
// Argh but is ExprParen horrible. So, if we consume
// `(x)`, that generally is also consuming `x`, UNLESS
// there are adjustments on the `(x)` expression
// (e.g., autoderefs and autorefs).
if self.typer.adjustments().borrow().contains_key(&expr.id) {
self.walk_expr(expr);
} else {
self.consume_expr(subexpr);
}
}
_ => {
self.walk_expr(expr)
}
}
}
fn mutate_expr(&mut self,
assignment_expr: &ast::Expr,
expr: &ast::Expr,
mode: MutateMode) {
let cmt = ignore_err!(self.mc.cat_expr(expr));
self.delegate.mutate(assignment_expr.id, assignment_expr.span, cmt, mode);
self.walk_expr(expr);
}
fn borrow_expr(&mut self,
expr: &ast::Expr,
r: ty::Region,
bk: ty::BorrowKind,
cause: LoanCause) {
debug!("borrow_expr(expr={}, r={}, bk={})",
expr.repr(self.tcx()), r.repr(self.tcx()), bk.repr(self.tcx()));
let cmt = ignore_err!(self.mc.cat_expr(expr));
self.delegate.borrow(expr.id, expr.span, cmt, r, bk, cause);
// Note: Unlike consume, we can ignore ExprParen. cat_expr
// already skips over them, and walk will uncover any
// attachments or whatever.
self.walk_expr(expr)
}
fn select_from_expr(&mut self, expr: &ast::Expr) {
self.walk_expr(expr)
}
fn walk_expr(&mut self, expr: &ast::Expr) {
debug!("walk_expr(expr={})", expr.repr(self.tcx()));
self.walk_adjustment(expr);
match expr.node {
ast::ExprParen(subexpr) => {
self.walk_expr(subexpr)
}
ast::ExprPath(..) => { }
ast::ExprUnary(ast::UnDeref, base) => { // *base
if !self.walk_overloaded_operator(expr, base, []) {
self.select_from_expr(base);
}
}
ast::ExprField(base, _, _) => { // base.f
self.select_from_expr(base);
}
ast::ExprIndex(lhs, rhs) => { // lhs[rhs]
if !self.walk_overloaded_operator(expr, lhs, [rhs]) {
self.select_from_expr(lhs);
self.consume_expr(rhs);
}
}
ast::ExprCall(callee, ref args) => { // callee(args)
self.walk_callee(expr, callee);
self.consume_exprs(args);
}
ast::ExprMethodCall(_, _, ref args) => { // callee.m(args)
self.consume_exprs(args);
}
ast::ExprStruct(_, ref fields, opt_with) => {
self.walk_struct_expr(expr, fields, opt_with);
}
ast::ExprTup(ref exprs) => {
self.consume_exprs(exprs);
}
ast::ExprIf(cond_expr, then_blk, opt_else_expr) => {
self.consume_expr(cond_expr);
self.walk_block(then_blk);
for else_expr in opt_else_expr.iter() {
self.consume_expr(*else_expr);
}
}
ast::ExprMatch(discr, ref arms) => {
// treatment of the discriminant is handled while
// walking the arms:
self.walk_expr(discr);
let discr_cmt = ignore_err!(self.mc.cat_expr(discr));
for arm in arms.iter() {
self.walk_arm(discr_cmt.clone(), arm);
}
}
ast::ExprVec(ref exprs) => {
self.consume_exprs(exprs);
}
ast::ExprAddrOf(m, base) => { // &base
// make sure that the thing we are pointing out stays valid
// for the lifetime `scope_r` of the resulting ptr:
let expr_ty = ty::expr_ty(self.tcx(), expr);
if !ty::type_is_bot(expr_ty) {
let r = ty::ty_region(self.tcx(), expr.span, expr_ty);
let bk = ty::BorrowKind::from_mutbl(m);
self.borrow_expr(base, r, bk, AddrOf);
} else {
self.walk_expr(base);
}
}
ast::ExprInlineAsm(ref ia) => {
for &(_, input) in ia.inputs.iter() {
self.consume_expr(input);
}
for &(_, output) in ia.outputs.iter() {
self.mutate_expr(expr, output, JustWrite);
}
}
ast::ExprBreak(..) |
ast::ExprAgain(..) |
ast::ExprLit(..) => {}
ast::ExprLoop(blk, _) => {
self.walk_block(blk);
}
ast::ExprWhile(cond_expr, blk) => {
self.consume_expr(cond_expr);
self.walk_block(blk);
}
ast::ExprForLoop(..) => fail!("non-desugared expr_for_loop"),
ast::ExprUnary(_, lhs) => {
if !self.walk_overloaded_operator(expr, lhs, []) {
self.consume_expr(lhs);
}
}
ast::ExprBinary(_, lhs, rhs) => {
if !self.walk_overloaded_operator(expr, lhs, [rhs]) {
self.consume_expr(lhs);
self.consume_expr(rhs);
}
}
ast::ExprBlock(blk) => {
self.walk_block(blk);
}
ast::ExprRet(ref opt_expr) => {
for expr in opt_expr.iter() {
self.consume_expr(*expr);
}
}
ast::ExprAssign(lhs, rhs) => {
self.mutate_expr(expr, lhs, JustWrite);
self.consume_expr(rhs);
}
ast::ExprCast(base, _) => {
self.consume_expr(base);
}
ast::ExprAssignOp(_, lhs, rhs) => {
// FIXME(#4712) --- Overloaded operators?
//
// if !self.walk_overloaded_operator(expr, [lhs, rhs])
// {
self.mutate_expr(expr, lhs, WriteAndRead);
self.consume_expr(rhs);
// }
}
ast::ExprRepeat(base, count) => {
self.consume_expr(base);
self.consume_expr(count);
}
ast::ExprFnBlock(..) |
ast::ExprProc(..) => {
self.walk_captures(expr)
}
ast::ExprVstore(base, _) => {
self.consume_expr(base);
}
ast::ExprBox(place, base) => {
self.consume_expr(place);
self.consume_expr(base);
}
ast::ExprMac(..) => {
self.tcx().sess.span_bug(
expr.span,
"macro expression remains after expansion");
}
}
}
fn walk_callee(&mut self, call: &ast::Expr, callee: &ast::Expr) {
let callee_ty = ty::expr_ty_adjusted(self.tcx(), callee);
debug!("walk_callee: callee={} callee_ty={}",
callee.repr(self.tcx()), callee_ty.repr(self.tcx()));
match ty::get(callee_ty).sty {
ty::ty_bare_fn(..) => {
self.consume_expr(callee);
}
ty::ty_closure(ref f) => {
match f.onceness {
ast::Many => {
self.borrow_expr(callee,
ty::ReScope(call.id),
ty::UniqueImmBorrow,
ClosureInvocation);
}
ast::Once => {
self.consume_expr(callee);
}
}
}
_ => {
self.tcx().sess.span_bug(
callee.span,
format!("unxpected callee type {}",
callee_ty.repr(self.tcx())));
}
}
}
fn walk_stmt(&mut self, stmt: &ast::Stmt) {
match stmt.node {
ast::StmtDecl(decl, _) => {
match decl.node {
ast::DeclLocal(local) => {
self.walk_local(local);
}
ast::DeclItem(_) => {
// we don't visit nested items in this visitor,
// only the fn body we were given.
}
}
}
ast::StmtExpr(expr, _) |
ast::StmtSemi(expr, _) => {
self.consume_expr(expr);
}
ast::StmtMac(..) => {
self.tcx().sess.span_bug(
stmt.span,
format!("unexpanded stmt macro"));
}
}
}
fn walk_local(&mut self, local: @ast::Local) {
match local.init {
None => {
let delegate = &mut self.delegate;
pat_util::pat_bindings(&self.typer.tcx().def_map, local.pat, |_, id, span, _| {
delegate.decl_without_init(id, span);
})
}
Some(expr) => {
// Variable declarations with
// initializers are considered
// "assigns", which is handled by
// `walk_pat`:
self.walk_expr(expr);
let init_cmt = ignore_err!(self.mc.cat_expr(expr));
self.walk_pat(init_cmt, local.pat);
}
}
}
fn walk_block(&mut self, blk: &ast::Block) {
/*!
* Indicates that the value of `blk` will be consumed,
* meaning either copied or moved depending on its type.
*/
debug!("walk_block(blk.id={:?})", blk.id);
for stmt in blk.stmts.iter() {
self.walk_stmt(*stmt);
}
for tail_expr in blk.expr.iter() {
self.consume_expr(*tail_expr);
}
}
fn walk_struct_expr(&mut self,
_expr: &ast::Expr,
fields: &Vec<ast::Field>,
opt_with: Option<@ast::Expr>) {
// Consume the expressions supplying values for each field.
for field in fields.iter() {
self.consume_expr(field.expr);
}
let with_expr = match opt_with {
Some(w) => { w }
None => { return; }
};
let with_cmt = ignore_err!(self.mc.cat_expr(with_expr));
// Select just those fields of the `with`
// expression that will actually be used
let with_fields = match ty::get(with_cmt.ty).sty {
ty::ty_struct(did, ref substs) => {
ty::struct_fields(self.tcx(), did, substs)
}
_ => {
self.tcx().sess.span_bug(
with_expr.span,
format!("with expression doesn't evaluate to a struct"));
}
};
// Consume those fields of the with expression that are needed.
for with_field in with_fields.iter() {
if !contains_field_named(with_field, fields) {
let cmt_field = self.mc.cat_field(with_expr,
with_cmt.clone(),
with_field.ident,
with_field.mt.ty);
self.delegate_consume(with_expr.id, with_expr.span, cmt_field);
}
}
fn contains_field_named(field: &ty::field,
fields: &Vec<ast::Field>)
-> bool
{
fields.iter().any(
|f| f.ident.node.name == field.ident.name)
}
}
// Invoke the appropriate delegate calls for anything that gets
// consumed or borrowed as part of the automatic adjustment
// process.
fn walk_adjustment(&mut self, expr: &ast::Expr) {
let typer = self.typer;
match typer.adjustments().borrow().find(&expr.id) {
None => { }
Some(adjustment) => {
match *adjustment {
ty::AutoAddEnv(..) |
ty::AutoObject(..) => {
// Creating an object or closure consumes the
// input and stores it into the resulting rvalue.
debug!("walk_adjustment(AutoAddEnv|AutoObject)");
let cmt_unadjusted =
ignore_err!(self.mc.cat_expr_unadjusted(expr));
self.delegate_consume(expr.id, expr.span, cmt_unadjusted);
}
ty::AutoDerefRef(ty::AutoDerefRef {
autoref: ref opt_autoref,
autoderefs: n
}) => {
self.walk_autoderefs(expr, n);
match *opt_autoref {
None => { }
Some(ref r) => {
self.walk_autoref(expr, r, n);
}
}
}
}
}
}
}
fn walk_autoderefs(&mut self,
expr: &ast::Expr,
autoderefs: uint) {
/*!
* Autoderefs for overloaded Deref calls in fact reference
* their receiver. That is, if we have `(*x)` where `x` is of
* type `Rc<T>`, then this in fact is equivalent to
* `x.deref()`. Since `deref()` is declared with `&self`, this
* is an autoref of `x`.
*/
debug!("walk_autoderefs expr={} autoderefs={}", expr.repr(self.tcx()), autoderefs);
for i in range(0, autoderefs) {
let deref_id = typeck::MethodCall::autoderef(expr.id, i as u32);
match self.typer.node_method_ty(deref_id) {
None => {}
Some(method_ty) => {
let cmt = ignore_err!(self.mc.cat_expr_autoderefd(expr, i));
let self_ty = *ty::ty_fn_args(method_ty).get(0);
let (m, r) = match ty::get(self_ty).sty {
ty::ty_rptr(r, ref m) => (m.mutbl, r),
_ => self.tcx().sess.span_bug(expr.span,
format!("bad overloaded deref type {}",
method_ty.repr(self.tcx())))
};
let bk = ty::BorrowKind::from_mutbl(m);
self.delegate.borrow(expr.id, expr.span, cmt,
r, bk, AutoRef);
}
}
}
}
fn walk_autoref(&mut self,
expr: &ast::Expr,
autoref: &ty::AutoRef,
autoderefs: uint) {
debug!("walk_autoref expr={} autoderefs={}", expr.repr(self.tcx()), autoderefs);
let cmt_derefd = ignore_err!(
self.mc.cat_expr_autoderefd(expr, autoderefs));
debug!("walk_autoref: cmt_derefd={}", cmt_derefd.repr(self.tcx()));
match *autoref {
ty::AutoPtr(r, m) => {
self.delegate.borrow(expr.id,
expr.span,
cmt_derefd,
r,
ty::BorrowKind::from_mutbl(m),
AutoRef)
}
ty::AutoBorrowVec(r, m) | ty::AutoBorrowVecRef(r, m) => {
let cmt_index = self.mc.cat_index(expr, cmt_derefd, autoderefs+1);
self.delegate.borrow(expr.id,
expr.span,
cmt_index,
r,
ty::BorrowKind::from_mutbl(m),
AutoRef)
}
ty::AutoBorrowObj(r, m) => {
let cmt_deref = self.mc.cat_deref_obj(expr, cmt_derefd);
self.delegate.borrow(expr.id,
expr.span,
cmt_deref,
r,
ty::BorrowKind::from_mutbl(m),
AutoRef)
}
ty::AutoUnsafe(_) => {}
}
}
fn walk_overloaded_operator(&mut self,
expr: &ast::Expr,
receiver: &ast::Expr,
args: &[@ast::Expr])
-> bool
{
if !self.typer.is_method_call(expr.id) {
return false;
}
self.walk_expr(receiver);
// Arguments (but not receivers) to overloaded operator
// methods are implicitly autoref'd which sadly does not use
// adjustments, so we must hardcode the borrow here.
let r = ty::ReScope(expr.id);
let bk = ty::ImmBorrow;
for &arg in args.iter() {
self.borrow_expr(arg, r, bk, OverloadedOperator);
}
return true;
}
fn walk_arm(&mut self, discr_cmt: mc::cmt, arm: &ast::Arm) {
for &pat in arm.pats.iter() {
self.walk_pat(discr_cmt.clone(), pat);
}
for guard in arm.guard.iter() {
self.consume_expr(*guard);
}
self.consume_expr(arm.body);
}
fn walk_pat(&mut self, cmt_discr: mc::cmt, pat: @ast::Pat) {
debug!("walk_pat cmt_discr={} pat={}", cmt_discr.repr(self.tcx()),
pat.repr(self.tcx()));
let mc = &self.mc;
let typer = self.typer;
let tcx = typer.tcx();
let def_map = &self.typer.tcx().def_map;
let delegate = &mut self.delegate;
ignore_err!(mc.cat_pattern(cmt_discr, pat, |mc, cmt_pat, pat| {
if pat_util::pat_is_binding(def_map, pat) {
let tcx = typer.tcx();
debug!("binding cmt_pat={} pat={}",
cmt_pat.repr(tcx),
pat.repr(tcx));
// pat_ty: the type of the binding being produced.
let pat_ty = ty::node_id_to_type(tcx, pat.id);
// Each match binding is effectively an assignment to the
// binding being produced.
let def = def_map.borrow().get_copy(&pat.id);
match mc.cat_def(pat.id, pat.span, pat_ty, def) {
Ok(binding_cmt) => {
delegate.mutate(pat.id, pat.span, binding_cmt, JustWrite);
}
Err(_) => { }
}
// It is also a borrow or copy/move of the value being matched.
match pat.node {
ast::PatIdent(ast::BindByRef(m), _, _) => {
let (r, bk) = {
(ty::ty_region(tcx, pat.span, pat_ty),
ty::BorrowKind::from_mutbl(m))
};
delegate.borrow(pat.id, pat.span, cmt_pat,
r, bk, RefBinding);
}
ast::PatIdent(ast::BindByValue(_), _, _) => {
let mode = copy_or_move(typer.tcx(), cmt_pat.ty);
delegate.consume_pat(pat, cmt_pat, mode);
}
_ => {
typer.tcx().sess.span_bug(
pat.span,
"binding pattern not an identifier");
}
}
} else {
match pat.node {
ast::PatVec(_, Some(slice_pat), _) => {
// The `slice_pat` here creates a slice into
// the original vector. This is effectively a
// borrow of the elements of the vector being
// matched.
let (slice_cmt, slice_mutbl, slice_r) = {
match mc.cat_slice_pattern(cmt_pat, slice_pat) {
Ok(v) => v,
Err(()) => {
tcx.sess.span_bug(slice_pat.span,
"Err from mc")
}
}
};
// Note: We declare here that the borrow
// occurs upon entering the `[...]`
// pattern. This implies that something like
// `[a, ..b]` where `a` is a move is illegal,
// because the borrow is already in effect.
// In fact such a move would be safe-ish, but
// it effectively *requires* that we use the
// nulling out semantics to indicate when a
// value has been moved, which we are trying
// to move away from. Otherwise, how can we
// indicate that the first element in the
// vector has been moved? Eventually, we
// could perhaps modify this rule to permit
// `[..a, b]` where `b` is a move, because in
// that case we can adjust the length of the
// original vec accordingly, but we'd have to
// make trans do the right thing, and it would
// only work for `~` vectors. It seems simpler
// to just require that people call
// `vec.pop()` or `vec.unshift()`.
let slice_bk = ty::BorrowKind::from_mutbl(slice_mutbl);
delegate.borrow(pat.id, pat.span,
slice_cmt, slice_r,
slice_bk, RefBinding);
}
_ => { }
}
}
}));
}
fn walk_captures(&mut self, closure_expr: &ast::Expr) {
debug!("walk_captures({})", closure_expr.repr(self.tcx()));
let tcx = self.typer.tcx();
freevars::with_freevars(tcx, closure_expr.id, |freevars| {
match freevars::get_capture_mode(self.tcx(), closure_expr.id) {
freevars::CaptureByRef => {
self.walk_by_ref_captures(closure_expr, freevars);
}
freevars::CaptureByValue => {
self.walk_by_value_captures(closure_expr, freevars);
}
}
});
}
fn walk_by_ref_captures(&mut self,
closure_expr: &ast::Expr,
freevars: &[freevars::freevar_entry]) {
for freevar in freevars.iter() {
let id_var = ast_util::def_id_of_def(freevar.def).node;
let cmt_var = ignore_err!(self.cat_captured_var(closure_expr.id,
closure_expr.span,
freevar.def));
// Lookup the kind of borrow the callee requires, as
// inferred by regionbk
let upvar_id = ty::UpvarId { var_id: id_var,
closure_expr_id: closure_expr.id };
let upvar_borrow = self.tcx().upvar_borrow_map.borrow()
.get_copy(&upvar_id);
self.delegate.borrow(closure_expr.id,
closure_expr.span,
cmt_var,
upvar_borrow.region,
upvar_borrow.kind,
ClosureCapture(freevar.span));
}
}
fn walk_by_value_captures(&mut self,
closure_expr: &ast::Expr,
freevars: &[freevars::freevar_entry]) {
for freevar in freevars.iter() {
let cmt_var = ignore_err!(self.cat_captured_var(closure_expr.id,
closure_expr.span,
freevar.def));
self.delegate_consume(closure_expr.id, freevar.span, cmt_var);
}
}
fn cat_captured_var(&mut self,
closure_id: ast::NodeId,
closure_span: Span,
upvar_def: ast::Def)
-> mc::McResult<mc::cmt> {
// Create the cmt for the variable being borrowed, from the
// caller's perspective
let var_id = ast_util::def_id_of_def(upvar_def).node;
let var_ty = ty::node_id_to_type(self.tcx(), var_id);
self.mc.cat_def(closure_id, closure_span, var_ty, upvar_def)
}
}
fn copy_or_move(tcx: &ty::ctxt, ty: ty::t) -> ConsumeMode {
if ty::type_moves_by_default(tcx, ty) { Move } else { Copy }
}

View File

@ -22,6 +22,15 @@ use syntax::{ast, ast_util};
use syntax::visit;
use syntax::visit::Visitor;
#[deriving(Show)]
pub enum CaptureMode {
/// Copy/move the value from this llvm ValueRef into the environment.
CaptureByValue,
/// Access by reference (used for stack closures).
CaptureByRef
}
// A vector of defs representing the free variables referred to in a function.
// (The def_upvar will already have been stripped).
#[deriving(Encodable, Decodable)]
@ -38,7 +47,6 @@ struct CollectFreevarsVisitor<'a> {
}
impl<'a> Visitor<int> for CollectFreevarsVisitor<'a> {
fn visit_item(&mut self, _: &ast::Item, _: int) {
// ignore_item
}
@ -133,3 +141,14 @@ pub fn with_freevars<T>(tcx: &ty::ctxt, fid: ast::NodeId, f: |&[freevar_entry]|
Some(d) => f(d.as_slice())
}
}
pub fn get_capture_mode(tcx: &ty::ctxt,
closure_expr_id: ast::NodeId)
-> CaptureMode
{
let fn_ty = ty::node_id_to_type(tcx, closure_expr_id);
match ty::ty_closure_store(fn_ty) {
ty::RegionTraitStore(..) => CaptureByRef,
ty::UniqTraitStore => CaptureByValue
}
}

View File

@ -228,11 +228,11 @@ fn check_fn(
// Check kinds on free variables:
with_appropriate_checker(cx, fn_id, |chk| {
freevars::with_freevars(cx.tcx, fn_id, |r| {
for fv in r.iter() {
freevars::with_freevars(cx.tcx, fn_id, |freevars| {
for fv in freevars.iter() {
chk(cx, fv);
}
})
});
});
visit::walk_fn(cx, fk, decl, body, sp, fn_id, ());

View File

@ -103,10 +103,10 @@
*/
use middle::freevars;
use middle::lint::{UnusedVariable, DeadAssignment};
use middle::pat_util;
use middle::ty;
use middle::moves;
use util::nodemap::NodeMap;
use std::cast::transmute;
@ -170,9 +170,8 @@ impl<'a> Visitor<()> for IrMaps<'a> {
}
pub fn check_crate(tcx: &ty::ctxt,
capture_map: &moves::CaptureMap,
krate: &Crate) {
visit::walk_crate(&mut IrMaps(tcx, capture_map), krate, ());
visit::walk_crate(&mut IrMaps(tcx), krate, ());
tcx.sess.abort_if_errors();
}
@ -245,7 +244,6 @@ enum VarKind {
struct IrMaps<'a> {
tcx: &'a ty::ctxt,
capture_map: &'a moves::CaptureMap,
num_live_nodes: uint,
num_vars: uint,
@ -256,12 +254,10 @@ struct IrMaps<'a> {
lnks: Vec<LiveNodeKind>,
}
fn IrMaps<'a>(tcx: &'a ty::ctxt,
capture_map: &'a moves::CaptureMap)
fn IrMaps<'a>(tcx: &'a ty::ctxt)
-> IrMaps<'a> {
IrMaps {
tcx: tcx,
capture_map: capture_map,
num_live_nodes: 0,
num_vars: 0,
live_node_map: NodeMap::new(),
@ -361,7 +357,7 @@ fn visit_fn(ir: &mut IrMaps,
let _i = ::util::common::indenter();
// swap in a new set of IR maps for this function body:
let mut fn_maps = IrMaps(ir.tcx, ir.capture_map);
let mut fn_maps = IrMaps(ir.tcx);
unsafe {
debug!("creating fn_maps: {}", transmute::<&IrMaps, *IrMaps>(&fn_maps));
@ -446,13 +442,23 @@ fn visit_arm(ir: &mut IrMaps, arm: &Arm) {
visit::walk_arm(ir, arm, ());
}
fn moved_variable_node_id_from_def(def: Def) -> Option<NodeId> {
match def {
DefBinding(nid, _) |
DefArg(nid, _) |
DefLocal(nid, _) => Some(nid),
_ => None
}
}
fn visit_expr(ir: &mut IrMaps, expr: &Expr) {
match expr.node {
// live nodes required for uses or definitions of variables:
ExprPath(_) => {
let def = ir.tcx.def_map.borrow().get_copy(&expr.id);
debug!("expr {}: path that leads to {:?}", expr.id, def);
if moves::moved_variable_node_id_from_def(def).is_some() {
if moved_variable_node_id_from_def(def).is_some() {
ir.add_live_node_for_node(expr.id, ExprNode(expr.span));
}
visit::walk_expr(ir, expr, ());
@ -467,24 +473,33 @@ fn visit_expr(ir: &mut IrMaps, expr: &Expr) {
// in better error messages than just pointing at the closure
// construction site.
let mut call_caps = Vec::new();
for cv in ir.capture_map.get(&expr.id).iter() {
match moves::moved_variable_node_id_from_def(cv.def) {
Some(rv) => {
let cv_ln = ir.add_live_node(FreeVarNode(cv.span));
let is_move = match cv.mode {
// var must be dead afterwards
moves::CapMove => true,
let fv_mode = freevars::get_capture_mode(ir.tcx, expr.id);
freevars::with_freevars(ir.tcx, expr.id, |freevars| {
for fv in freevars.iter() {
match moved_variable_node_id_from_def(fv.def) {
Some(rv) => {
let fv_ln = ir.add_live_node(FreeVarNode(fv.span));
let fv_id = ast_util::def_id_of_def(fv.def).node;
let fv_ty = ty::node_id_to_type(ir.tcx, fv_id);
let is_move = match fv_mode {
// var must be dead afterwards
freevars::CaptureByValue => {
ty::type_moves_by_default(ir.tcx, fv_ty)
}
// var can still be used
moves::CapCopy | moves::CapRef => false
};
call_caps.push(CaptureInfo {ln: cv_ln,
is_move: is_move,
var_nid: rv});
}
None => {}
// var can still be used
freevars::CaptureByRef => {
false
}
};
call_caps.push(CaptureInfo {ln: fv_ln,
is_move: is_move,
var_nid: rv});
}
None => {}
}
}
}
});
ir.set_captures(expr.id, call_caps);
visit::walk_expr(ir, expr, ());
@ -1270,7 +1285,7 @@ impl<'a> Liveness<'a> {
fn access_path(&mut self, expr: &Expr, succ: LiveNode, acc: uint)
-> LiveNode {
let def = self.ir.tcx.def_map.borrow().get_copy(&expr.id);
match moves::moved_variable_node_id_from_def(def) {
match moved_variable_node_id_from_def(def) {
Some(nid) => {
let ln = self.live_node(expr.id, expr.span);
if acc != 0u {
@ -1497,7 +1512,7 @@ impl<'a> Liveness<'a> {
self.warn_about_dead_assign(expr.span, expr.id, ln, var);
}
def => {
match moves::moved_variable_node_id_from_def(def) {
match moved_variable_node_id_from_def(def) {
Some(nid) => {
let ln = self.live_node(expr.id, expr.span);
let var = self.variable(nid, expr.span);

View File

@ -241,8 +241,8 @@ impl ast_node for ast::Pat {
fn span(&self) -> Span { self.span }
}
pub struct MemCategorizationContext<TYPER> {
pub typer: TYPER
pub struct MemCategorizationContext<'t,TYPER> {
typer: &'t TYPER
}
pub type McResult<T> = Result<T, ()>;
@ -349,8 +349,12 @@ macro_rules! if_ok(
)
)
impl<TYPER:Typer> MemCategorizationContext<TYPER> {
fn tcx<'a>(&'a self) -> &'a ty::ctxt {
impl<'t,TYPER:Typer> MemCategorizationContext<'t,TYPER> {
pub fn new(typer: &'t TYPER) -> MemCategorizationContext<'t,TYPER> {
MemCategorizationContext { typer: typer }
}
fn tcx(&self) -> &'t ty::ctxt {
self.typer.tcx()
}
@ -418,7 +422,9 @@ impl<TYPER:Typer> MemCategorizationContext<TYPER> {
}
}
pub fn cat_expr_autoderefd(&self, expr: &ast::Expr, autoderefs: uint)
pub fn cat_expr_autoderefd(&self,
expr: &ast::Expr,
autoderefs: uint)
-> McResult<cmt> {
let mut cmt = if_ok!(self.cat_expr_unadjusted(expr));
for deref in range(1u, autoderefs + 1) {
@ -456,7 +462,9 @@ impl<TYPER:Typer> MemCategorizationContext<TYPER> {
self.cat_def(expr.id, expr.span, expr_ty, def)
}
ast::ExprParen(e) => self.cat_expr_unadjusted(e),
ast::ExprParen(e) => {
self.cat_expr(e)
}
ast::ExprAddrOf(..) | ast::ExprCall(..) |
ast::ExprAssign(..) | ast::ExprAssignOp(..) |

View File

@ -1,655 +0,0 @@
// 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 whether 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
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.
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).
## Enforcement of Moves
The enforcement of moves is done by the borrow checker. Please see
the section "Moves and initialization" in `middle/borrowck/doc.rs`.
## 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.
*/
use middle::pat_util::{pat_bindings};
use middle::freevars;
use middle::ty;
use middle::typeck::MethodCall;
use util::ppaux;
use util::ppaux::Repr;
use util::common::indenter;
use util::ppaux::UserString;
use util::nodemap::{NodeMap, NodeSet};
use std::rc::Rc;
use syntax::ast::*;
use syntax::ast_util;
use syntax::visit;
use syntax::visit::Visitor;
use syntax::codemap::Span;
#[deriving(Eq, Encodable, Decodable)]
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 `||`).
}
#[deriving(Encodable, Decodable)]
pub struct CaptureVar {
pub def: Def, // Variable being accessed free
pub span: Span, // Location of an access to this variable
pub mode: CaptureMode // How variable is being accessed
}
pub type CaptureMap = NodeMap<Rc<Vec<CaptureVar>>>;
/** See the section Output on the module comment for explanation. */
#[deriving(Clone)]
pub struct MoveMaps {
pub moves_map: NodeSet,
/**
* Set of variable node-ids that are moved.
*
* pub Note: The `moves_map` stores expression ids that are moves,
* whereas this set stores the ids of the variables that are
* moved at some point */
pub capture_map: CaptureMap
}
#[deriving(Clone)]
struct VisitContext<'a> {
tcx: &'a ty::ctxt,
move_maps: MoveMaps
}
#[deriving(Eq)]
enum UseMode {
Move, // This value or something owned by it is moved.
Read // Read no matter what the type.
}
impl<'a> visit::Visitor<()> for VisitContext<'a> {
fn visit_fn(&mut self, fk: &visit::FnKind, fd: &FnDecl,
b: &Block, s: Span, n: NodeId, _: ()) {
compute_modes_for_fn(self, fk, fd, b, s, n);
}
fn visit_expr(&mut self, ex: &Expr, _: ()) {
compute_modes_for_expr(self, ex);
}
fn visit_local(&mut self, l: &Local, _: ()) {
compute_modes_for_local(self, l);
}
// FIXME(#10894) should continue recursing
fn visit_ty(&mut self, _t: &Ty, _: ()) {}
}
pub fn compute_moves(tcx: &ty::ctxt, krate: &Crate) -> MoveMaps {
let mut visit_cx = VisitContext {
tcx: tcx,
move_maps: MoveMaps {
moves_map: NodeSet::new(),
capture_map: NodeMap::new()
}
};
visit::walk_crate(&mut visit_cx, krate, ());
visit_cx.move_maps
}
pub fn moved_variable_node_id_from_def(def: Def) -> Option<NodeId> {
match def {
DefBinding(nid, _) |
DefArg(nid, _) |
DefLocal(nid, _) => Some(nid),
_ => None
}
}
///////////////////////////////////////////////////////////////////////////
// Expressions
fn compute_modes_for_local<'a>(cx: &mut VisitContext,
local: &Local) {
cx.use_pat(local.pat);
for &init in local.init.iter() {
cx.use_expr(init, Read);
}
}
fn compute_modes_for_fn(cx: &mut VisitContext,
fk: &visit::FnKind,
decl: &FnDecl,
body: &Block,
span: Span,
id: NodeId) {
for a in decl.inputs.iter() {
cx.use_pat(a.pat);
}
visit::walk_fn(cx, fk, decl, body, span, id, ());
}
fn compute_modes_for_expr(cx: &mut VisitContext,
expr: &Expr)
{
cx.consume_expr(expr);
}
impl<'a> VisitContext<'a> {
pub fn consume_exprs(&mut self, exprs: &[@Expr]) {
for expr in exprs.iter() {
self.consume_expr(*expr);
}
}
pub fn consume_expr(&mut self, expr: &Expr) {
/*!
* Indicates that the value of `expr` will be consumed,
* meaning either copied or moved depending on its type.
*/
debug!("consume_expr(expr={})",
expr.repr(self.tcx));
let expr_ty = ty::expr_ty_adjusted(self.tcx, expr);
if ty::type_moves_by_default(self.tcx, expr_ty) {
self.move_maps.moves_map.insert(expr.id);
self.use_expr(expr, Move);
} else {
self.use_expr(expr, Read);
};
}
pub fn consume_block(&mut self, blk: &Block) {
/*!
* Indicates that the value of `blk` will be consumed,
* meaning either copied or moved depending on its type.
*/
debug!("consume_block(blk.id={:?})", blk.id);
for stmt in blk.stmts.iter() {
self.visit_stmt(*stmt, ());
}
for tail_expr in blk.expr.iter() {
self.consume_expr(*tail_expr);
}
}
pub fn use_expr(&mut self,
expr: &Expr,
expr_mode: UseMode) {
/*!
* Indicates that `expr` is used with a given mode. This will
* in turn trigger calls to the subcomponents of `expr`.
*/
debug!("use_expr(expr={}, mode={:?})",
expr.repr(self.tcx),
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.
let comp_mode = match self.tcx.adjustments.borrow().find(&expr.id) {
Some(adjustment) => {
match *adjustment {
ty::AutoDerefRef(ty::AutoDerefRef {
autoref: Some(_),
..
}) => Read,
_ => expr_mode,
}
}
_ => expr_mode,
};
debug!("comp_mode = {:?}", comp_mode);
match expr.node {
ExprUnary(UnDeref, base) => { // *base
if !self.use_overloaded_operator(expr, base, []) {
// Moving out of *base moves out of base.
self.use_expr(base, comp_mode);
}
}
ExprField(base, _, _) => { // base.f
// Moving out of base.f moves out of base.
self.use_expr(base, comp_mode);
}
ExprIndex(lhs, rhs) => { // lhs[rhs]
if !self.use_overloaded_operator(expr, lhs, [rhs]) {
self.use_expr(lhs, comp_mode);
self.consume_expr(rhs);
}
}
ExprCall(callee, ref args) => { // callee(args)
// 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,
format!("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);
self.use_fn_args(args.as_slice());
}
ExprMethodCall(_, _, ref args) => { // callee.m(args)
self.use_fn_args(args.as_slice());
}
ExprStruct(_, ref fields, opt_with) => {
for field in fields.iter() {
self.consume_expr(field.expr);
}
for with_expr in opt_with.iter() {
// 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,
format!("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:
let consume_with = with_fields.iter().any(|tf| {
!fields.iter().any(|f| f.ident.node.name == tf.ident.name) &&
ty::type_moves_by_default(self.tcx, tf.mt.ty)
});
fn has_dtor(tcx: &ty::ctxt, ty: ty::t) -> bool {
use middle::ty::{get,ty_struct,ty_enum};
match get(ty).sty {
ty_struct(did, _) | ty_enum(did, _) => ty::has_dtor(tcx, did),
_ => false,
}
}
if consume_with {
if has_dtor(self.tcx, with_ty) {
self.tcx.sess.span_err(with_expr.span,
format!("cannot move out of type `{}`, \
which defines the `Drop` trait",
with_ty.user_string(self.tcx)));
}
self.consume_expr(*with_expr);
} else {
self.use_expr(*with_expr, Read);
}
}
}
ExprTup(ref exprs) => {
self.consume_exprs(exprs.as_slice());
}
ExprIf(cond_expr, then_blk, opt_else_expr) => {
self.consume_expr(cond_expr);
self.consume_block(then_blk);
for else_expr in opt_else_expr.iter() {
self.consume_expr(*else_expr);
}
}
ExprMatch(discr, ref arms) => {
for arm in arms.iter() {
self.consume_arm(arm);
}
// 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);
}
ExprParen(base) => {
// Note: base is not considered a *component* here, so
// use `expr_mode` not `comp_mode`.
self.use_expr(base, expr_mode);
}
ExprVec(ref exprs) => {
self.consume_exprs(exprs.as_slice());
}
ExprAddrOf(_, base) => { // &base
self.use_expr(base, Read);
}
ExprPath(..) |
ExprInlineAsm(..) |
ExprBreak(..) |
ExprAgain(..) |
ExprLit(..) => {}
ExprLoop(blk, _) => {
self.consume_block(blk);
}
ExprWhile(cond_expr, blk) => {
self.consume_expr(cond_expr);
self.consume_block(blk);
}
ExprForLoop(..) => fail!("non-desugared expr_for_loop"),
ExprUnary(_, lhs) => {
if !self.use_overloaded_operator(expr, lhs, []) {
self.consume_expr(lhs);
}
}
ExprBinary(_, lhs, rhs) => {
if !self.use_overloaded_operator(expr, lhs, [rhs]) {
self.consume_expr(lhs);
self.consume_expr(rhs);
}
}
ExprBlock(blk) => {
self.consume_block(blk);
}
ExprRet(ref opt_expr) => {
for expr in opt_expr.iter() {
self.consume_expr(*expr);
}
}
ExprAssign(lhs, rhs) => {
self.use_expr(lhs, Read);
self.consume_expr(rhs);
}
ExprCast(base, _) => {
self.consume_expr(base);
}
ExprAssignOp(_, lhs, rhs) => {
// FIXME(#4712) --- Overloaded operators?
//
// if !self.use_overloaded_operator(expr, DoDerefArgs, lhs, [rhs])
// {
self.consume_expr(lhs);
self.consume_expr(rhs);
// }
}
ExprRepeat(base, count) => {
self.consume_expr(base);
self.consume_expr(count);
}
ExprFnBlock(ref decl, body) |
ExprProc(ref decl, body) => {
for a in decl.inputs.iter() {
self.use_pat(a.pat);
}
let cap_vars = self.compute_captures(expr.id);
self.move_maps.capture_map.insert(expr.id, Rc::new(cap_vars));
self.consume_block(body);
}
ExprVstore(base, _) => {
self.use_expr(base, comp_mode);
}
ExprBox(place, base) => {
self.use_expr(place, comp_mode);
self.use_expr(base, comp_mode);
}
ExprMac(..) => {
self.tcx.sess.span_bug(
expr.span,
"macro expression remains after expansion");
}
}
}
pub fn use_overloaded_operator(&mut self,
expr: &Expr,
receiver_expr: @Expr,
arg_exprs: &[@Expr])
-> bool {
let method_call = MethodCall::expr(expr.id);
if !self.tcx.method_map.borrow().contains_key(&method_call) {
return false;
}
self.use_fn_arg(receiver_expr);
// for overloaded operatrs, we are always passing in a
// reference, so it's always read mode:
for arg_expr in arg_exprs.iter() {
self.use_expr(*arg_expr, Read);
}
return true;
}
pub fn consume_arm(&mut self, arm: &Arm) {
for pat in arm.pats.iter() {
self.use_pat(*pat);
}
for guard in arm.guard.iter() {
self.consume_expr(*guard);
}
self.consume_expr(arm.body);
}
pub fn use_pat(&mut self, pat: @Pat) {
/*!
*
* Decides whether each binding in a pattern moves the value
* into itself or not based on its type and annotation.
*/
pat_bindings(&self.tcx.def_map, pat, |bm, id, _span, path| {
let binding_moves = match bm {
BindByRef(_) => false,
BindByValue(_) => {
let pat_ty = ty::node_id_to_type(self.tcx, id);
debug!("pattern {:?} {} type is {}",
id,
ast_util::path_to_ident(path).repr(self.tcx),
pat_ty.repr(self.tcx));
ty::type_moves_by_default(self.tcx, pat_ty)
}
};
debug!("pattern binding {:?}: bm={:?}, binding_moves={}",
id, bm, binding_moves);
if binding_moves {
self.move_maps.moves_map.insert(id);
}
})
}
pub fn use_fn_args(&mut self, arg_exprs: &[@Expr]) {
//! Uses the argument expressions.
for arg_expr in arg_exprs.iter() {
self.use_fn_arg(*arg_expr);
}
}
pub fn use_fn_arg(&mut self, arg_expr: @Expr) {
//! Uses the argument.
self.consume_expr(arg_expr)
}
fn compute_captures(&mut self, fn_expr_id: NodeId) -> Vec<CaptureVar> {
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);
freevars::with_freevars(self.tcx, fn_expr_id, |freevars| {
match ty::ty_closure_store(fn_ty) {
ty::RegionTraitStore(..) => {
// || captures everything by ref
freevars.iter()
.map(|fvar| CaptureVar {def: fvar.def, span: fvar.span, mode: CapRef})
.collect()
}
ty::UniqTraitStore => {
// proc captures by copy or by move depending on type
freevars.iter()
.map(|fvar| {
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={}",
fvar_def_id, ppaux::ty_to_str(self.tcx, fvar_ty));
let mode = if ty::type_moves_by_default(self.tcx, fvar_ty) {
CapMove
} else {
CapCopy
};
CaptureVar {def: fvar.def, span: fvar.span, mode:mode}
}).collect()
}
}
})
}
}

View File

@ -2073,7 +2073,7 @@ pub fn write_metadata(cx: &CrateContext, krate: &ast::Crate) -> Vec<u8> {
}
let encode_inlined_item: encoder::EncodeInlinedItem =
|ecx, ebml_w, ii| astencode::encode_inlined_item(ecx, ebml_w, ii, &cx.maps);
|ecx, ebml_w, ii| astencode::encode_inlined_item(ecx, ebml_w, ii);
let encode_parms = crate_ctxt_to_encode_parms(cx, encode_inlined_item);
let metadata = encoder::encode_metadata(encode_parms, krate);
@ -2103,7 +2103,7 @@ pub fn write_metadata(cx: &CrateContext, krate: &ast::Crate) -> Vec<u8> {
pub fn trans_crate(krate: ast::Crate,
analysis: CrateAnalysis,
output: &OutputFilenames) -> (ty::ctxt, CrateTranslation) {
let CrateAnalysis { ty_cx: tcx, exp_map2, maps, reachable, .. } = analysis;
let CrateAnalysis { ty_cx: tcx, exp_map2, reachable, .. } = analysis;
// Before we touch LLVM, make sure that multithreading is enabled.
unsafe {
@ -2135,7 +2135,7 @@ pub fn trans_crate(krate: ast::Crate,
// 1. http://llvm.org/bugs/show_bug.cgi?id=11479
let llmod_id = link_meta.crateid.name + ".rs";
let ccx = CrateContext::new(llmod_id, tcx, exp_map2, maps,
let ccx = CrateContext::new(llmod_id, tcx, exp_map2,
Sha256::new(), link_meta, reachable);
{
let _icx = push_ctxt("text");

View File

@ -13,8 +13,8 @@ use back::abi;
use back::link::mangle_internal_name_by_path_and_seq;
use driver::session::FullDebugInfo;
use lib::llvm::ValueRef;
use middle::freevars;
use middle::lang_items::ClosureExchangeMallocFnLangItem;
use middle::moves;
use middle::trans::base::*;
use middle::trans::build::*;
use middle::trans::common::*;
@ -98,35 +98,14 @@ use syntax::ast_util;
//
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
pub enum EnvAction {
/// Copy the value from this llvm ValueRef into the environment.
EnvCopy,
/// Move the value from this llvm ValueRef into the environment.
EnvMove,
/// Access by reference (used for stack closures).
EnvRef
}
pub struct EnvValue {
action: EnvAction,
action: freevars::CaptureMode,
datum: Datum<Lvalue>
}
impl EnvAction {
pub fn to_str(&self) -> ~str {
match *self {
EnvCopy => "EnvCopy".to_owned(),
EnvMove => "EnvMove".to_owned(),
EnvRef => "EnvRef".to_owned()
}
}
}
impl EnvValue {
pub fn to_str(&self, ccx: &CrateContext) -> ~str {
format!("{}({})", self.action.to_str(), self.datum.to_str(ccx))
format!("{}({})", self.action, self.datum.to_str(ccx))
}
}
@ -140,8 +119,8 @@ pub fn mk_closure_tys(tcx: &ty::ctxt,
// converted to ptrs.
let bound_tys = bound_values.iter().map(|bv| {
match bv.action {
EnvCopy | EnvMove => bv.datum.ty,
EnvRef => ty::mk_mut_ptr(tcx, bv.datum.ty)
freevars::CaptureByValue => bv.datum.ty,
freevars::CaptureByRef => ty::mk_mut_ptr(tcx, bv.datum.ty)
}
}).collect();
let cdata_ty = ty::mk_tup(tcx, bound_tys);
@ -232,10 +211,10 @@ pub fn store_environment<'a>(
let bound_data = GEPi(bcx, llbox, [0u, abi::box_field_body, i]);
match bv.action {
EnvCopy | EnvMove => {
freevars::CaptureByValue => {
bcx = bv.datum.store_to(bcx, bound_data);
}
EnvRef => {
freevars::CaptureByRef => {
Store(bcx, bv.datum.to_llref(), bound_data);
}
}
@ -247,9 +226,11 @@ pub fn store_environment<'a>(
// Given a context and a list of upvars, build a closure. This just
// collects the upvars and packages them up for store_environment.
fn build_closure<'a>(bcx0: &'a Block<'a>,
cap_vars: &[moves::CaptureVar],
freevar_mode: freevars::CaptureMode,
freevars: &Vec<freevars::freevar_entry>,
store: ty::TraitStore)
-> ClosureResult<'a> {
-> ClosureResult<'a>
{
let _icx = push_ctxt("closure::build_closure");
// If we need to, package up the iterator body to call
@ -257,28 +238,9 @@ fn build_closure<'a>(bcx0: &'a Block<'a>,
// Package up the captured upvars
let mut env_vals = Vec::new();
for cap_var in cap_vars.iter() {
debug!("Building closure: captured variable {:?}", *cap_var);
let datum = expr::trans_local_var(bcx, cap_var.def);
match cap_var.mode {
moves::CapRef => {
let is_region_closure = match store {
ty::RegionTraitStore(..) => true,
ty::UniqTraitStore => false
};
assert!(is_region_closure);
env_vals.push(EnvValue {action: EnvRef,
datum: datum});
}
moves::CapCopy => {
env_vals.push(EnvValue {action: EnvCopy,
datum: datum});
}
moves::CapMove => {
env_vals.push(EnvValue {action: EnvMove,
datum: datum});
}
}
for freevar in freevars.iter() {
let datum = expr::trans_local_var(bcx, freevar.def);
env_vals.push(EnvValue {action: freevar_mode, datum: datum});
}
store_environment(bcx, env_vals, store)
@ -287,13 +249,15 @@ fn build_closure<'a>(bcx0: &'a Block<'a>,
// Given an enclosing block context, a new function context, a closure type,
// and a list of upvars, generate code to load and populate the environment
// with the upvars and type descriptors.
fn load_environment<'a>(bcx: &'a Block<'a>, cdata_ty: ty::t,
cap_vars: &[moves::CaptureVar],
store: ty::TraitStore) -> &'a Block<'a> {
fn load_environment<'a>(bcx: &'a Block<'a>,
cdata_ty: ty::t,
freevars: &Vec<freevars::freevar_entry>,
store: ty::TraitStore)
-> &'a Block<'a> {
let _icx = push_ctxt("closure::load_environment");
// Don't bother to create the block if there's nothing to load
if cap_vars.len() == 0 {
if freevars.len() == 0 {
return bcx;
}
@ -312,13 +276,13 @@ fn load_environment<'a>(bcx: &'a Block<'a>, cdata_ty: ty::t,
// Populate the upvars from the environment
let mut i = 0u;
for cap_var in cap_vars.iter() {
for freevar in freevars.iter() {
let mut upvarptr = GEPi(bcx, llcdata, [0u, i]);
match store {
ty::RegionTraitStore(..) => { upvarptr = Load(bcx, upvarptr); }
ty::UniqTraitStore => {}
}
let def_id = ast_util::def_id_of_def(cap_var.def);
let def_id = ast_util::def_id_of_def(freevar.def);
bcx.fcx.llupvars.borrow_mut().insert(def_id.node, upvarptr);
@ -330,7 +294,7 @@ fn load_environment<'a>(bcx: &'a Block<'a>, cdata_ty: ty::t,
env_pointer_alloca,
i,
store,
cap_var.span);
freevar.span);
}
i += 1u;
@ -395,15 +359,19 @@ pub fn trans_expr_fn<'a>(
// set an inline hint for all closures
set_inline_hint(llfn);
let cap_vars = ccx.maps.capture_map.borrow().get_copy(&id);
let freevar_mode = freevars::get_capture_mode(tcx, id);
let freevars: Vec<freevars::freevar_entry> =
freevars::with_freevars(
tcx, id,
|fv| fv.iter().map(|&fv| fv).collect());
let ClosureResult {llbox, cdata_ty, bcx} =
build_closure(bcx, cap_vars.as_slice(), store);
build_closure(bcx, freevar_mode, &freevars, store);
trans_closure(ccx, decl, body, llfn,
bcx.fcx.param_substs, id,
[], ty::ty_fn_ret(fty),
|bcx| load_environment(bcx, cdata_ty, cap_vars.as_slice(), store));
|bcx| load_environment(bcx, cdata_ty, &freevars, store));
fill_fn_pair(bcx, dest_addr, llfn, llbox);
bcx
}

View File

@ -15,7 +15,6 @@ use lib::llvm::{ContextRef, ModuleRef, ValueRef};
use lib::llvm::{llvm, TargetData, TypeNames};
use lib::llvm::mk_target_data;
use metadata::common::LinkMeta;
use middle::astencode;
use middle::resolve;
use middle::trans::adt;
use middle::trans::base;
@ -113,7 +112,6 @@ pub struct CrateContext {
pub type_hashcodes: RefCell<HashMap<ty::t, ~str>>,
pub all_llvm_symbols: RefCell<HashSet<~str>>,
pub tcx: ty::ctxt,
pub maps: astencode::Maps,
pub stats: Stats,
pub int_type: Type,
pub opaque_vec_type: Type,
@ -131,7 +129,6 @@ impl CrateContext {
pub fn new(name: &str,
tcx: ty::ctxt,
emap2: resolve::ExportMap2,
maps: astencode::Maps,
symbol_hasher: Sha256,
link_meta: LinkMeta,
reachable: NodeSet)
@ -195,7 +192,6 @@ impl CrateContext {
type_hashcodes: RefCell::new(HashMap::new()),
all_llvm_symbols: RefCell::new(HashSet::new()),
tcx: tcx,
maps: maps,
stats: Stats {
n_static_tydescs: Cell::new(0u),
n_glues_created: Cell::new(0u),

View File

@ -40,7 +40,7 @@ pub fn maybe_instantiate_inline(ccx: &CrateContext, fn_id: ast::DefId)
let csearch_result =
csearch::maybe_get_item_ast(
ccx.tcx(), fn_id,
|a,b,c,d| astencode::decode_inlined_item(a, b, &ccx.maps, c, d));
|a,b,c,d| astencode::decode_inlined_item(a, b, c, d));
return match csearch_result {
csearch::not_found => {
ccx.external.borrow_mut().insert(fn_id, None);

View File

@ -13,6 +13,7 @@
use back::svh::Svh;
use driver::session::Session;
use metadata::csearch;
use mc = middle::mem_categorization;
use middle::const_eval;
use middle::lang_items::{ExchangeHeapLangItem, OpaqueStructLangItem};
use middle::lang_items::{TyDescStructLangItem, TyVisitorTraitLangItem};
@ -508,7 +509,7 @@ pub struct UpvarId {
pub closure_expr_id: ast::NodeId,
}
#[deriving(Clone, Eq, TotalEq, Hash)]
#[deriving(Clone, Eq, TotalEq, Hash, Show)]
pub enum BorrowKind {
/// Data must be immutable and is aliasable.
ImmBorrow,
@ -4779,3 +4780,33 @@ impl BorrowKind {
}
}
}
impl mc::Typer for ty::ctxt {
fn tcx<'a>(&'a self) -> &'a ty::ctxt {
self
}
fn node_ty(&self, id: ast::NodeId) -> mc::McResult<ty::t> {
Ok(ty::node_id_to_type(self, id))
}
fn node_method_ty(&self, method_call: typeck::MethodCall) -> Option<ty::t> {
self.method_map.borrow().find(&method_call).map(|method| method.ty)
}
fn adjustments<'a>(&'a self) -> &'a RefCell<NodeMap<ty::AutoAdjustment>> {
&self.adjustments
}
fn is_method_call(&self, id: ast::NodeId) -> bool {
self.method_map.borrow().contains_key(&typeck::MethodCall::expr(id))
}
fn temporary_scope(&self, rvalue_id: ast::NodeId) -> Option<ast::NodeId> {
self.region_maps.temporary_scope(rvalue_id)
}
fn upvar_borrow(&self, upvar_id: ty::UpvarId) -> ty::UpvarBorrow {
self.upvar_borrow_map.borrow().get_copy(&upvar_id)
}
}

View File

@ -261,7 +261,7 @@ impl<'a> Rcx<'a> {
}
}
impl<'a, 'b> mc::Typer for &'a Rcx<'b> {
impl<'fcx> mc::Typer for Rcx<'fcx> {
fn tcx<'a>(&'a self) -> &'a ty::ctxt {
self.fcx.tcx()
}
@ -638,7 +638,7 @@ fn check_expr_fn_block(rcx: &mut Rcx,
ty::ty_closure(~ty::ClosureTy {store: ty::RegionTraitStore(..), ..}) => {
freevars::with_freevars(tcx, expr.id, |freevars| {
propagate_upupvar_borrow_kind(rcx, expr, freevars);
});
})
}
_ => ()
}
@ -754,9 +754,9 @@ fn constrain_callee(rcx: &mut Rcx,
ty::RegionTraitStore(r, _) => {
// While we're here, link the closure's region with a unique
// immutable borrow (gathered later in borrowck)
let mc = mc::MemCategorizationContext { typer: &*rcx };
let mc = mc::MemCategorizationContext::new(rcx);
let expr_cmt = ignore_err!(mc.cat_expr(callee_expr));
link_region(mc.typer, callee_expr.span, call_region,
link_region(rcx, callee_expr.span, call_region,
ty::UniqueImmBorrow, expr_cmt);
r
}
@ -880,9 +880,9 @@ fn constrain_autoderefs(rcx: &mut Rcx,
method.ty.repr(rcx.tcx())))
};
{
let mc = mc::MemCategorizationContext { typer: &*rcx };
let mc = mc::MemCategorizationContext::new(rcx);
let self_cmt = ignore_err!(mc.cat_expr_autoderefd(deref_expr, i));
link_region(mc.typer, deref_expr.span, r,
link_region(rcx, deref_expr.span, r,
ty::BorrowKind::from_mutbl(m), self_cmt);
}
@ -1038,7 +1038,7 @@ fn link_addr_of(rcx: &mut Rcx, expr: &ast::Expr,
debug!("link_addr_of(base=?)");
let cmt = {
let mc = mc::MemCategorizationContext { typer: &*rcx };
let mc = mc::MemCategorizationContext::new(rcx);
ignore_err!(mc.cat_expr(base))
};
link_region_from_node_type(rcx, expr.span, expr.id, mutability, cmt);
@ -1056,9 +1056,9 @@ fn link_local(rcx: &Rcx, local: &ast::Local) {
None => { return; }
Some(expr) => expr,
};
let mc = mc::MemCategorizationContext { typer: rcx };
let mc = mc::MemCategorizationContext::new(rcx);
let discr_cmt = ignore_err!(mc.cat_expr(init_expr));
link_pattern(mc, discr_cmt, local.pat);
link_pattern(rcx, mc, discr_cmt, local.pat);
}
fn link_match(rcx: &Rcx, discr: &ast::Expr, arms: &[ast::Arm]) {
@ -1069,17 +1069,18 @@ fn link_match(rcx: &Rcx, discr: &ast::Expr, arms: &[ast::Arm]) {
*/
debug!("regionck::for_match()");
let mc = mc::MemCategorizationContext { typer: rcx };
let mc = mc::MemCategorizationContext::new(rcx);
let discr_cmt = ignore_err!(mc.cat_expr(discr));
debug!("discr_cmt={}", discr_cmt.repr(mc.typer.tcx()));
debug!("discr_cmt={}", discr_cmt.repr(rcx.tcx()));
for arm in arms.iter() {
for &root_pat in arm.pats.iter() {
link_pattern(mc, discr_cmt.clone(), root_pat);
link_pattern(rcx, mc, discr_cmt.clone(), root_pat);
}
}
}
fn link_pattern(mc: mc::MemCategorizationContext<&Rcx>,
fn link_pattern(rcx: &Rcx,
mc: mc::MemCategorizationContext<Rcx>,
discr_cmt: mc::cmt,
root_pat: &ast::Pat) {
/*!
@ -1092,7 +1093,7 @@ fn link_pattern(mc: mc::MemCategorizationContext<&Rcx>,
// `ref x` pattern
ast::PatIdent(ast::BindByRef(mutbl), _, _) => {
link_region_from_node_type(
mc.typer, sub_pat.span, sub_pat.id,
rcx, sub_pat.span, sub_pat.id,
mutbl, sub_cmt);
}
@ -1100,7 +1101,7 @@ fn link_pattern(mc: mc::MemCategorizationContext<&Rcx>,
ast::PatVec(_, Some(slice_pat), _) => {
match mc.cat_slice_pattern(sub_cmt, slice_pat) {
Ok((slice_cmt, slice_mutbl, slice_r)) => {
link_region(mc.typer, sub_pat.span, slice_r,
link_region(rcx, sub_pat.span, slice_r,
ty::BorrowKind::from_mutbl(slice_mutbl),
slice_cmt);
}
@ -1122,25 +1123,25 @@ fn link_autoref(rcx: &Rcx,
*/
debug!("link_autoref(autoref={:?})", autoref);
let mc = mc::MemCategorizationContext { typer: rcx };
let mc = mc::MemCategorizationContext::new(rcx);
let expr_cmt = ignore_err!(mc.cat_expr_autoderefd(expr, autoderefs));
debug!("expr_cmt={}", expr_cmt.repr(mc.typer.tcx()));
debug!("expr_cmt={}", expr_cmt.repr(rcx.tcx()));
match *autoref {
ty::AutoPtr(r, m) => {
link_region(mc.typer, expr.span, r,
link_region(rcx, expr.span, r,
ty::BorrowKind::from_mutbl(m), expr_cmt);
}
ty::AutoBorrowVec(r, m) | ty::AutoBorrowVecRef(r, m) => {
let cmt_index = mc.cat_index(expr, expr_cmt, autoderefs+1);
link_region(mc.typer, expr.span, r,
link_region(rcx, expr.span, r,
ty::BorrowKind::from_mutbl(m), cmt_index);
}
ty::AutoBorrowObj(r, m) => {
let cmt_deref = mc.cat_deref_obj(expr, expr_cmt);
link_region(mc.typer, expr.span, r,
link_region(rcx, expr.span, r,
ty::BorrowKind::from_mutbl(m), cmt_deref);
}
@ -1160,10 +1161,10 @@ fn link_by_ref(rcx: &Rcx,
let tcx = rcx.tcx();
debug!("link_by_ref(expr={}, callee_scope={})",
expr.repr(tcx), callee_scope);
let mc = mc::MemCategorizationContext { typer: rcx };
let mc = mc::MemCategorizationContext::new(rcx);
let expr_cmt = ignore_err!(mc.cat_expr(expr));
let region_min = ty::ReScope(callee_scope);
link_region(mc.typer, expr.span, region_min, ty::ImmBorrow, expr_cmt);
link_region(rcx, expr.span, region_min, ty::ImmBorrow, expr_cmt);
}
fn link_region_from_node_type(rcx: &Rcx,
@ -1306,9 +1307,9 @@ fn adjust_borrow_kind_for_assignment_lhs(rcx: &Rcx,
* expression.
*/
let mc = mc::MemCategorizationContext { typer: rcx };
let mc = mc::MemCategorizationContext::new(rcx);
let cmt = ignore_err!(mc.cat_expr(lhs));
adjust_upvar_borrow_kind_for_mut(mc.typer, cmt);
adjust_upvar_borrow_kind_for_mut(rcx, cmt);
}
fn adjust_upvar_borrow_kind_for_mut(rcx: &Rcx,

View File

@ -469,7 +469,6 @@ impl<'a, O: IdVisitingOperation> Visitor<()> for IdVisitor<'a, O> {
visit::walk_pat(self, pattern, env)
}
fn visit_expr(&mut self, expression: &Expr, env: ()) {
self.operation.visit_id(expression.id);
visit::walk_expr(self, expression, env)
@ -590,6 +589,30 @@ pub fn compute_id_range_for_inlined_item(item: &InlinedItem) -> IdRange {
visitor.result.get()
}
pub fn compute_id_range_for_fn_body(fk: &visit::FnKind,
decl: &FnDecl,
body: &Block,
sp: Span,
id: NodeId)
-> IdRange
{
/*!
* Computes the id range for a single fn body,
* ignoring nested items.
*/
let visitor = IdRangeComputingVisitor {
result: Cell::new(IdRange::max())
};
let mut id_visitor = IdVisitor {
operation: &visitor,
pass_through_items: false,
visited_outermost: false,
};
id_visitor.visit_fn(fk, decl, body, sp, id, ());
visitor.result.get()
}
pub fn is_item_impl(item: @ast::Item) -> bool {
match item.node {
ItemImpl(..) => true,

View File

@ -25,13 +25,13 @@ fn test0(f: Foo, g: Noncopyable, h: Noncopyable) {
fn test1(f: Foo, g: Noncopyable, h: Noncopyable) {
// copying move-by-default fields from `f`, so move:
let _b = Foo {noncopyable: g, ..f};
let _c = Foo {noncopyable: h, ..f}; //~ ERROR use of moved value: `f`
let _c = Foo {noncopyable: h, ..f}; //~ ERROR use of partially moved value: `f`
}
fn test2(f: Foo, g: Noncopyable) {
// move non-copyable field
let _b = Foo {copied: 22, moved: ~23, ..f};
let _c = Foo {noncopyable: g, ..f}; //~ ERROR use of moved value: `f`
let _c = Foo {noncopyable: g, ..f}; //~ ERROR use of partially moved value: `f`
}
fn main() {}