diff --git a/src/librustc/metadata/common.rs b/src/librustc/metadata/common.rs index a42880b2d36c7..8ed471ec58a5e 100644 --- a/src/librustc/metadata/common.rs +++ b/src/librustc/metadata/common.rs @@ -140,9 +140,10 @@ pub enum astencode_tag { // Reserves 0x40 -- 0x5f tag_table_moves_map = 0x52, tag_table_capture_map = 0x53, tag_table_unboxed_closure_type = 0x54, + tag_table_upvar_borrow_map = 0x55, } static first_astencode_tag: uint = tag_ast as uint; -static last_astencode_tag: uint = tag_table_unboxed_closure_type as uint; +static last_astencode_tag: uint = tag_table_upvar_borrow_map as uint; impl astencode_tag { pub fn from_uint(value : uint) -> Option { let is_a_tag = first_astencode_tag <= value && value <= last_astencode_tag; diff --git a/src/librustc/middle/astencode.rs b/src/librustc/middle/astencode.rs index f68501bbb9143..33b663dea1557 100644 --- a/src/librustc/middle/astencode.rs +++ b/src/librustc/middle/astencode.rs @@ -18,6 +18,7 @@ use driver::session::Session; use metadata::decoder; use middle::def; use e = metadata::encoder; +use middle::freevars; use middle::freevars::freevar_entry; use middle::region; use metadata::tydecode; @@ -551,6 +552,15 @@ impl tr for freevar_entry { } } +impl tr for ty::UpvarBorrow { + fn tr(&self, xcx: &ExtendedDecodeContext) -> ty::UpvarBorrow { + ty::UpvarBorrow { + kind: self.kind, + region: self.region.tr(xcx) + } + } +} + // ______________________________________________________________________ // Encoding and decoding of MethodCallee @@ -1061,7 +1071,29 @@ fn encode_side_tables_for_id(ecx: &e::EncodeContext, Ok(encode_freevar_entry(rbml_w, fv_entry)) }); }) - }) + }); + + for freevar in fv.iter() { + match freevars::get_capture_mode(tcx, id) { + freevars::CaptureByRef => { + rbml_w.tag(c::tag_table_upvar_borrow_map, |rbml_w| { + rbml_w.id(id); + rbml_w.tag(c::tag_table_val, |rbml_w| { + let var_id = freevar.def.def_id().node; + let upvar_id = ty::UpvarId { + var_id: var_id, + closure_expr_id: id + }; + let upvar_borrow = tcx.upvar_borrow_map.borrow() + .get_copy(&upvar_id); + var_id.encode(rbml_w); + upvar_borrow.encode(rbml_w); + }) + }) + } + _ => {} + } + } } let lid = ast::DefId { krate: ast::LOCAL_CRATE, node: id }; @@ -1468,6 +1500,15 @@ fn decode_side_tables(xcx: &ExtendedDecodeContext, }).unwrap().move_iter().collect(); dcx.tcx.freevars.borrow_mut().insert(id, fv_info); } + c::tag_table_upvar_borrow_map => { + let var_id: ast::NodeId = Decodable::decode(val_dsr).unwrap(); + let upvar_id = ty::UpvarId { + var_id: xcx.tr_id(var_id), + closure_expr_id: id + }; + let ub: ty::UpvarBorrow = Decodable::decode(val_dsr).unwrap(); + dcx.tcx.upvar_borrow_map.borrow_mut().insert(upvar_id, ub.tr(xcx)); + } c::tag_table_tcache => { let pty = val_dsr.read_polytype(xcx); let lid = ast::DefId { krate: ast::LOCAL_CRATE, node: id }; diff --git a/src/librustc/middle/freevars.rs b/src/librustc/middle/freevars.rs index f6887718ec137..eda567f7d187c 100644 --- a/src/librustc/middle/freevars.rs +++ b/src/librustc/middle/freevars.rs @@ -14,6 +14,7 @@ #![allow(non_camel_case_types)] use middle::def; +use middle::mem_categorization::Typer; use middle::resolve; use middle::ty; use util::nodemap::{DefIdSet, NodeMap, NodeSet}; @@ -147,11 +148,8 @@ pub fn with_freevars(tcx: &ty::ctxt, fid: ast::NodeId, f: |&[freevar_entry]| } } -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); +pub fn get_capture_mode(tcx: &T, closure_expr_id: ast::NodeId) -> CaptureMode { + let fn_ty = tcx.node_ty(closure_expr_id).ok().expect("couldn't find closure ty?"); match ty::ty_closure_store(fn_ty) { ty::RegionTraitStore(..) => CaptureByRef, ty::UniqTraitStore => CaptureByValue diff --git a/src/librustc/middle/trans/_match.rs b/src/librustc/middle/trans/_match.rs index 5334205aa5234..85b6294ae34e6 100644 --- a/src/librustc/middle/trans/_match.rs +++ b/src/librustc/middle/trans/_match.rs @@ -189,7 +189,9 @@ #![allow(non_camel_case_types)] use back::abi; +use mc = middle::mem_categorization; use driver::config::FullDebugInfo; +use euv = middle::expr_use_visitor; use llvm; use llvm::{ValueRef, BasicBlockRef}; use middle::const_eval; @@ -1292,13 +1294,58 @@ pub fn trans_match<'a>( trans_match_inner(bcx, match_expr.id, discr_expr, arms, dest) } -fn create_bindings_map(bcx: &Block, pat: Gc) -> BindingsMap { +/// Checks whether the binding in `discr` is assigned to anywhere in the expression `body` +fn is_discr_reassigned(bcx: &Block, discr: &ast::Expr, body: &ast::Expr) -> bool { + match discr.node { + ast::ExprPath(..) => match bcx.def(discr.id) { + def::DefArg(vid, _) | def::DefBinding(vid, _) | + def::DefLocal(vid, _) | def::DefUpvar(vid, _, _, _) => { + let mut rc = ReassignmentChecker { + node: vid, + reassigned: false + }; + { + let mut visitor = euv::ExprUseVisitor::new(&mut rc, bcx); + visitor.walk_expr(body); + } + rc.reassigned + } + _ => false + }, + _ => false + } +} + +struct ReassignmentChecker { + node: ast::NodeId, + reassigned: bool +} + +impl euv::Delegate for ReassignmentChecker { + fn consume(&mut self, _: ast::NodeId, _: Span, _: mc::cmt, _: euv::ConsumeMode) {} + fn consume_pat(&mut self, _: &ast::Pat, _: mc::cmt, _: euv::ConsumeMode) {} + fn borrow(&mut self, _: ast::NodeId, _: Span, _: mc::cmt, _: ty::Region, + _: ty::BorrowKind, _: euv::LoanCause) {} + fn decl_without_init(&mut self, _: ast::NodeId, _: Span) {} + + fn mutate(&mut self, _: ast::NodeId, _: Span, cmt: mc::cmt, _: euv::MutateMode) { + match cmt.cat { + mc::cat_copied_upvar(mc::CopiedUpvar { upvar_id: vid, .. }) | + mc::cat_arg(vid) | mc::cat_local(vid) => self.reassigned = self.node == vid, + _ => {} + } + } +} + +fn create_bindings_map(bcx: &Block, pat: Gc, + discr: &ast::Expr, body: &ast::Expr) -> BindingsMap { // Create the bindings map, which is a mapping from each binding name // to an alloca() that will be the value for that local variable. // Note that we use the names because each binding will have many ids // from the various alternatives. let ccx = bcx.ccx(); let tcx = bcx.tcx(); + let reassigned = is_discr_reassigned(bcx, discr, body); let mut bindings_map = HashMap::new(); pat_bindings(&tcx.def_map, &*pat, |bm, p_id, span, path1| { let ident = path1.node; @@ -1310,7 +1357,7 @@ fn create_bindings_map(bcx: &Block, pat: Gc) -> BindingsMap { let trmode; match bm { ast::BindByValue(_) - if !ty::type_moves_by_default(tcx, variable_ty) => { + if !ty::type_moves_by_default(tcx, variable_ty) || reassigned => { llmatch = alloca_no_lifetime(bcx, llvariable_ty.ptr_to(), "__llmatch"); @@ -1371,7 +1418,7 @@ fn trans_match_inner<'a>(scope_cx: &'a Block<'a>, let arm_datas: Vec = arms.iter().map(|arm| ArmData { bodycx: fcx.new_id_block("case_body", arm.body.id), arm: arm, - bindings_map: create_bindings_map(bcx, *arm.pats.get(0)) + bindings_map: create_bindings_map(bcx, *arm.pats.get(0), discr_expr, &*arm.body) }).collect(); let mut static_inliner = StaticInliner { tcx: scope_cx.tcx() }; diff --git a/src/librustc/middle/trans/common.rs b/src/librustc/middle/trans/common.rs index f06415f26ad30..84f380e862a4d 100644 --- a/src/librustc/middle/trans/common.rs +++ b/src/librustc/middle/trans/common.rs @@ -16,6 +16,7 @@ use driver::session::Session; use llvm; use llvm::{ValueRef, BasicBlockRef, BuilderRef}; use llvm::{True, False, Bool}; +use mc = middle::mem_categorization; use middle::def; use middle::lang_items::LangItem; use middle::subst; @@ -481,6 +482,36 @@ impl<'a> Block<'a> { } } +impl<'a> mc::Typer for Block<'a> { + fn tcx<'a>(&'a self) -> &'a ty::ctxt { + self.tcx() + } + + fn node_ty(&self, id: ast::NodeId) -> mc::McResult { + Ok(node_id_type(self, id)) + } + + fn node_method_ty(&self, method_call: typeck::MethodCall) -> Option { + self.tcx().method_map.borrow().find(&method_call).map(|method| method.ty) + } + + fn adjustments<'a>(&'a self) -> &'a RefCell> { + &self.tcx().adjustments + } + + fn is_method_call(&self, id: ast::NodeId) -> bool { + self.tcx().method_map.borrow().contains_key(&typeck::MethodCall::expr(id)) + } + + fn temporary_scope(&self, rvalue_id: ast::NodeId) -> Option { + self.tcx().region_maps.temporary_scope(rvalue_id) + } + + fn upvar_borrow(&self, upvar_id: ty::UpvarId) -> ty::UpvarBorrow { + self.tcx().upvar_borrow_map.borrow().get_copy(&upvar_id) + } +} + pub struct Result<'a> { pub bcx: &'a Block<'a>, pub val: ValueRef diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index 0cc5486013aac..a4588da1bd7dd 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -539,7 +539,7 @@ pub struct UpvarId { pub closure_expr_id: ast::NodeId, } -#[deriving(Clone, PartialEq, Eq, Hash, Show)] +#[deriving(Clone, PartialEq, Eq, Hash, Show, Encodable, Decodable)] pub enum BorrowKind { /// Data must be immutable and is aliasable. ImmBorrow, @@ -634,7 +634,7 @@ pub enum BorrowKind { * the closure, so sometimes it is necessary for them to be larger * than the closure lifetime itself. */ -#[deriving(PartialEq, Clone)] +#[deriving(PartialEq, Clone, Encodable, Decodable)] pub struct UpvarBorrow { pub kind: BorrowKind, pub region: ty::Region, diff --git a/src/test/run-pass/issue-15571.rs b/src/test/run-pass/issue-15571.rs new file mode 100644 index 0000000000000..0ef0fc83c9456 --- /dev/null +++ b/src/test/run-pass/issue-15571.rs @@ -0,0 +1,64 @@ +// 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. + +fn match_on_local() { + let mut foo = Some(box 5i); + match foo { + None => {}, + Some(x) => { + foo = Some(x); + } + } + println!("'{}'", foo.unwrap()); +} + +fn match_on_arg(mut foo: Option>) { + match foo { + None => {} + Some(x) => { + foo = Some(x); + } + } + println!("'{}'", foo.unwrap()); +} + +fn match_on_binding() { + match Some(box 7i) { + mut foo => { + match foo { + None => {}, + Some(x) => { + foo = Some(x); + } + } + println!("'{}'", foo.unwrap()); + } + } +} + +fn match_on_upvar() { + let mut foo = Some(box 8i); + (proc() { + match foo { + None => {}, + Some(x) => { + foo = Some(x); + } + } + println!("'{}'", foo.unwrap()); + })(); +} + +fn main() { + match_on_local(); + match_on_arg(Some(box 6i)); + match_on_binding(); + match_on_upvar(); +} diff --git a/src/test/run-pass/issue-16151.rs b/src/test/run-pass/issue-16151.rs new file mode 100644 index 0000000000000..9cc571c7819cd --- /dev/null +++ b/src/test/run-pass/issue-16151.rs @@ -0,0 +1,38 @@ +// 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. + +use std::mem; + +static mut DROP_COUNT: uint = 0; + +struct Fragment; + +impl Drop for Fragment { + fn drop(&mut self) { + unsafe { + DROP_COUNT += 1; + } + } +} + +fn main() { + { + let mut fragments = vec![Fragment, Fragment, Fragment]; + let _new_fragments: Vec = mem::replace(&mut fragments, vec![]) + .move_iter() + .skip_while(|_fragment| { + true + }).collect(); + } + unsafe { + assert_eq!(DROP_COUNT, 3); + } +} +