mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-23 15:23:46 +00:00
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:
parent
77a975df85
commit
96dfed2b62
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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));
|
||||
|
@ -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)));
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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: ∫
|
||||
/// 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,
|
||||
};
|
||||
|
||||
|
@ -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
|
||||
|
@ -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> {
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
868
src/librustc/middle/expr_use_visitor.rs
Normal file
868
src/librustc/middle/expr_use_visitor.rs
Normal 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 }
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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, ());
|
||||
|
@ -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);
|
||||
|
@ -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(..) |
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
@ -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");
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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),
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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() {}
|
||||
|
Loading…
Reference in New Issue
Block a user