From 96dfed2b62c6e84a542f9a170133ec528e2191c2 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 21 Apr 2014 19:21:53 -0400 Subject: [PATCH] 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. --- src/librustc/driver/driver.rs | 20 +- src/librustc/lib.rs | 2 +- src/librustc/middle/astencode.rs | 69 +- src/librustc/middle/borrowck/check_loans.rs | 41 +- .../borrowck/gather_loans/gather_moves.rs | 49 +- .../middle/borrowck/gather_loans/lifetime.rs | 13 +- .../middle/borrowck/gather_loans/mod.rs | 692 +++----------- .../borrowck/gather_loans/move_error.rs | 3 + .../borrowck/gather_loans/restrictions.rs | 5 +- src/librustc/middle/borrowck/mod.rs | 106 +-- src/librustc/middle/borrowck/move_data.rs | 10 +- src/librustc/middle/check_match.rs | 22 +- src/librustc/middle/const_eval.rs | 13 +- src/librustc/middle/expr_use_visitor.rs | 868 ++++++++++++++++++ src/librustc/middle/freevars.rs | 21 +- src/librustc/middle/kind.rs | 6 +- src/librustc/middle/liveness.rs | 69 +- src/librustc/middle/mem_categorization.rs | 20 +- src/librustc/middle/moves.rs | 655 ------------- src/librustc/middle/trans/base.rs | 6 +- src/librustc/middle/trans/closure.rs | 94 +- src/librustc/middle/trans/context.rs | 4 - src/librustc/middle/trans/inline.rs | 2 +- src/librustc/middle/ty.rs | 33 +- src/librustc/middle/typeck/check/regionck.rs | 49 +- src/libsyntax/ast_util.rs | 25 +- .../compile-fail/moves-sru-moved-field.rs | 4 +- 27 files changed, 1271 insertions(+), 1630 deletions(-) create mode 100644 src/librustc/middle/expr_use_visitor.rs delete mode 100644 src/librustc/middle/moves.rs diff --git a/src/librustc/driver/driver.rs b/src/librustc/driver/driver.rs index 62ecacd2c3a..2bc3ed0fce6 100644 --- a/src/librustc/driver/driver.rs +++ b/src/librustc/driver/driver.rs @@ -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 } } diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs index 8ffba905773..25a4adcfca1 100644 --- a/src/librustc/lib.rs +++ b/src/librustc/lib.rs @@ -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 { diff --git a/src/librustc/middle/astencode.rs b/src/librustc/middle/astencode.rs index 7f3757c478c..5ada0068a47 100644 --- a/src/librustc/middle/astencode.rs +++ b/src/librustc/middle/astencode.rs @@ -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, -} - 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, par_doc: ebml::Doc) -> Result> { 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)); diff --git a/src/librustc/middle/borrowck/check_loans.rs b/src/librustc/middle/borrowck/check_loans.rs index d0f47966832..a24e7d66380 100644 --- a/src/librustc/middle/borrowck/check_loans.rs +++ b/src/librustc/middle/borrowck/check_loans.rs @@ -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))); diff --git a/src/librustc/middle/borrowck/gather_loans/gather_moves.rs b/src/librustc/middle/borrowck/gather_loans/gather_moves.rs index f4eee0526dd..16fd03c1aad 100644 --- a/src/librustc/middle/borrowck/gather_loans/gather_moves.rs +++ b/src/librustc/middle/borrowck/gather_loans/gather_moves.rs @@ -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, - 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, - 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, diff --git a/src/librustc/middle/borrowck/gather_loans/lifetime.rs b/src/librustc/middle/borrowck/gather_loans/lifetime.rs index 4c17ddbb31f..3337a56edf0 100644 --- a/src/librustc/middle/borrowck/gather_loans/lifetime.rs +++ b/src/librustc/middle/borrowck/gather_loans/lifetime.rs @@ -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 diff --git a/src/librustc/middle/borrowck/gather_loans/mod.rs b/src/librustc/middle/borrowck/gather_loans/mod.rs index 8bb95b798d0..b351c99084d 100644 --- a/src/librustc/middle/borrowck/gather_loans/mod.rs +++ b/src/librustc/middle/borrowck/gather_loans/mod.rs @@ -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, 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, item_ub: ast::NodeId, - repeating_ids: Vec } - -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, 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|) { - 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, }; diff --git a/src/librustc/middle/borrowck/gather_loans/move_error.rs b/src/librustc/middle/borrowck/gather_loans/move_error.rs index 24d873e0ff7..4662074aa47 100644 --- a/src/librustc/middle/borrowck/gather_loans/move_error.rs +++ b/src/librustc/middle/borrowck/gather_loans/move_error.rs @@ -100,6 +100,7 @@ fn group_errors_with_same_origin(errors: &Vec) fn append_to_grouped_errors(grouped_errors: &mut Vec, 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) }; 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 diff --git a/src/librustc/middle/borrowck/gather_loans/restrictions.rs b/src/librustc/middle/borrowck/gather_loans/restrictions.rs index 4d0249a3011..552381e4216 100644 --- a/src/librustc/middle/borrowck/gather_loans/restrictions.rs +++ b/src/librustc/middle/borrowck/gather_loans/restrictions.rs @@ -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> { diff --git a/src/librustc/middle/borrowck/mod.rs b/src/librustc/middle/borrowck/mod.rs index 1af0e48c46d..4efb0e44e8e 100644 --- a/src/librustc/middle/borrowck/mod.rs +++ b/src/librustc/middle/borrowck/mod.rs @@ -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 { - Ok(ty::node_id_to_type(*self, id)) - } - - fn node_method_ty(&self, method_call: typeck::MethodCall) -> Option { - self.method_map.borrow().find(&method_call).map(|method| method.ty) - } - - fn adjustments<'a>(&'a self) -> &'a RefCell> { - &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 { - self.region_maps.temporary_scope(id) - } - - fn upvar_borrow(&self, id: ty::UpvarId) -> ty::UpvarBorrow { - self.upvar_borrow_map.borrow().get_copy(&id) - } -} diff --git a/src/librustc/middle/borrowck/move_data.rs b/src/librustc/middle/borrowck/move_data.rs index efdc103d14d..2ae07e9a480 100644 --- a/src/librustc/middle/borrowck/move_data.rs +++ b/src/librustc/middle/borrowck/move_data.rs @@ -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 { diff --git a/src/librustc/middle/check_match.rs b/src/librustc/middle/check_match.rs index 2ef009e36e4..3e7d1ef6a16 100644 --- a/src/librustc/middle/check_match.rs +++ b/src/librustc/middle/check_match.rs @@ -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, diff --git a/src/librustc/middle/const_eval.rs b/src/librustc/middle/const_eval.rs index 6a2262aeaef..56284984a24 100644 --- a/src/librustc/middle/const_eval.rs +++ b/src/librustc/middle/const_eval.rs @@ -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 diff --git a/src/librustc/middle/expr_use_visitor.rs b/src/librustc/middle/expr_use_visitor.rs new file mode 100644 index 00000000000..17f423aa592 --- /dev/null +++ b/src/librustc/middle/expr_use_visitor.rs @@ -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 or the MIT license +// , 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, + 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) + -> 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`, 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 { + // 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 } +} + diff --git a/src/librustc/middle/freevars.rs b/src/librustc/middle/freevars.rs index 83f6d5abfa4..8839164434a 100644 --- a/src/librustc/middle/freevars.rs +++ b/src/librustc/middle/freevars.rs @@ -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 for CollectFreevarsVisitor<'a> { - fn visit_item(&mut self, _: &ast::Item, _: int) { // ignore_item } @@ -133,3 +141,14 @@ pub fn with_freevars(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 + } +} diff --git a/src/librustc/middle/kind.rs b/src/librustc/middle/kind.rs index 02f9309bca2..92695d7a1d6 100644 --- a/src/librustc/middle/kind.rs +++ b/src/librustc/middle/kind.rs @@ -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, ()); diff --git a/src/librustc/middle/liveness.rs b/src/librustc/middle/liveness.rs index 4be9992367d..43ffe3b2d9d 100644 --- a/src/librustc/middle/liveness.rs +++ b/src/librustc/middle/liveness.rs @@ -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, } -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 { + 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); diff --git a/src/librustc/middle/mem_categorization.rs b/src/librustc/middle/mem_categorization.rs index 35124aa9916..57f6e9632b1 100644 --- a/src/librustc/middle/mem_categorization.rs +++ b/src/librustc/middle/mem_categorization.rs @@ -241,8 +241,8 @@ impl ast_node for ast::Pat { fn span(&self) -> Span { self.span } } -pub struct MemCategorizationContext { - pub typer: TYPER +pub struct MemCategorizationContext<'t,TYPER> { + typer: &'t TYPER } pub type McResult = Result; @@ -349,8 +349,12 @@ macro_rules! if_ok( ) ) -impl MemCategorizationContext { - 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 MemCategorizationContext { } } - pub fn cat_expr_autoderefd(&self, expr: &ast::Expr, autoderefs: uint) + pub fn cat_expr_autoderefd(&self, + expr: &ast::Expr, + autoderefs: uint) -> McResult { let mut cmt = if_ok!(self.cat_expr_unadjusted(expr)); for deref in range(1u, autoderefs + 1) { @@ -456,7 +462,9 @@ impl MemCategorizationContext { 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(..) | diff --git a/src/librustc/middle/moves.rs b/src/librustc/middle/moves.rs deleted file mode 100644 index 1e44e1ab7f9..00000000000 --- a/src/librustc/middle/moves.rs +++ /dev/null @@ -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 or the MIT license -// , 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>>; - -/** 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 { - 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 { - 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() - } - } - }) - } -} diff --git a/src/librustc/middle/trans/base.rs b/src/librustc/middle/trans/base.rs index fda51e744ce..e5ddf07b682 100644 --- a/src/librustc/middle/trans/base.rs +++ b/src/librustc/middle/trans/base.rs @@ -2073,7 +2073,7 @@ pub fn write_metadata(cx: &CrateContext, krate: &ast::Crate) -> Vec { } 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 { 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"); diff --git a/src/librustc/middle/trans/closure.rs b/src/librustc/middle/trans/closure.rs index df17fb516a7..a846848f494 100644 --- a/src/librustc/middle/trans/closure.rs +++ b/src/librustc/middle/trans/closure.rs @@ -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 } -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, 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, + 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::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 } diff --git a/src/librustc/middle/trans/context.rs b/src/librustc/middle/trans/context.rs index 119750cd9ce..f46c3232326 100644 --- a/src/librustc/middle/trans/context.rs +++ b/src/librustc/middle/trans/context.rs @@ -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>, pub all_llvm_symbols: RefCell>, 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), diff --git a/src/librustc/middle/trans/inline.rs b/src/librustc/middle/trans/inline.rs index b96d71366e0..fa4a62319db 100644 --- a/src/librustc/middle/trans/inline.rs +++ b/src/librustc/middle/trans/inline.rs @@ -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); diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index 30d75eda3ef..aa460cc9c18 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -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 { + Ok(ty::node_id_to_type(self, id)) + } + + fn node_method_ty(&self, method_call: typeck::MethodCall) -> Option { + self.method_map.borrow().find(&method_call).map(|method| method.ty) + } + + fn adjustments<'a>(&'a self) -> &'a RefCell> { + &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 { + 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) + } +} diff --git a/src/librustc/middle/typeck/check/regionck.rs b/src/librustc/middle/typeck/check/regionck.rs index 9f36e903e58..4922ed116d8 100644 --- a/src/librustc/middle/typeck/check/regionck.rs +++ b/src/librustc/middle/typeck/check/regionck.rs @@ -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, 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, diff --git a/src/libsyntax/ast_util.rs b/src/libsyntax/ast_util.rs index adb97af7490..0fb9a2491db 100644 --- a/src/libsyntax/ast_util.rs +++ b/src/libsyntax/ast_util.rs @@ -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, diff --git a/src/test/compile-fail/moves-sru-moved-field.rs b/src/test/compile-fail/moves-sru-moved-field.rs index a4cbb30f93f..3306d19186c 100644 --- a/src/test/compile-fail/moves-sru-moved-field.rs +++ b/src/test/compile-fail/moves-sru-moved-field.rs @@ -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() {}