From a63003fe1aac487d3c0c527c4c984375c998de99 Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Wed, 23 Jul 2014 12:43:29 -0700 Subject: [PATCH] librustc: Parse, but do not fully turn on, the `ref` keyword for by-reference upvars. This partially implements RFC 38. A snapshot will be needed to turn this on, because stage0 cannot yet parse the keyword. Part of #12381. --- src/librustc/driver/driver.rs | 16 +++-- src/librustc/metadata/common.rs | 3 +- src/librustc/middle/astencode.rs | 28 +++++++- src/librustc/middle/borrowck/mod.rs | 2 +- src/librustc/middle/check_loop.rs | 4 +- src/librustc/middle/freevars.rs | 67 +++++++++++++------ src/librustc/middle/liveness.rs | 4 +- src/librustc/middle/mem_categorization.rs | 3 + src/librustc/middle/resolve.rs | 4 +- src/librustc/middle/save/mod.rs | 2 +- src/librustc/middle/trans/base.rs | 4 +- src/librustc/middle/trans/common.rs | 6 ++ src/librustc/middle/trans/debuginfo.rs | 8 +-- src/librustc/middle/trans/expr.rs | 4 +- src/librustc/middle/ty.rs | 19 ++++-- src/librustc/middle/typeck/check/mod.rs | 4 +- src/librustc/middle/typeck/check/regionck.rs | 9 ++- src/librustc/middle/typeck/check/writeback.rs | 4 +- src/librustc/middle/typeck/infer/test.rs | 14 +++- src/libsyntax/ast.rs | 10 ++- src/libsyntax/ast_map/blocks.rs | 2 +- src/libsyntax/ext/build.rs | 4 +- src/libsyntax/ext/expand.rs | 6 +- src/libsyntax/fold.rs | 10 +-- src/libsyntax/parse/parser.rs | 17 +++-- src/libsyntax/print/pprust.rs | 16 ++++- src/libsyntax/visit.rs | 4 +- .../capture-clauses-boxed-closures.rs | 23 +++++++ .../capture-clauses-unboxed-closures.rs | 29 ++++++++ 29 files changed, 254 insertions(+), 72 deletions(-) create mode 100644 src/test/run-pass/capture-clauses-boxed-closures.rs create mode 100644 src/test/run-pass/capture-clauses-unboxed-closures.rs diff --git a/src/librustc/driver/driver.rs b/src/librustc/driver/driver.rs index 477fc5e1c0f30..5f86745f3f221 100644 --- a/src/librustc/driver/driver.rs +++ b/src/librustc/driver/driver.rs @@ -360,8 +360,9 @@ pub fn phase_3_run_analysis_passes(sess: Session, plugin::build::find_plugin_registrar( sess.diagnostic(), krate))); - let freevars = time(time_passes, "freevar finding", (), |_| - freevars::annotate_freevars(&def_map, krate)); + let (freevars, capture_modes) = + time(time_passes, "freevar finding", (), |_| + freevars::annotate_freevars(&def_map, krate)); let region_map = time(time_passes, "region resolution", (), |_| middle::region::resolve_crate(&sess, krate)); @@ -372,8 +373,15 @@ pub fn phase_3_run_analysis_passes(sess: Session, let stability_index = time(time_passes, "stability index", (), |_| stability::Index::build(krate)); - let ty_cx = ty::mk_ctxt(sess, def_map, named_region_map, ast_map, - freevars, region_map, lang_items, stability_index); + let ty_cx = ty::mk_ctxt(sess, + def_map, + named_region_map, + ast_map, + freevars, + capture_modes, + region_map, + lang_items, + stability_index); // passes are timed inside typeck typeck::check_crate(&ty_cx, trait_map, krate); diff --git a/src/librustc/metadata/common.rs b/src/librustc/metadata/common.rs index 8ed471ec58a5e..633ad8293491b 100644 --- a/src/librustc/metadata/common.rs +++ b/src/librustc/metadata/common.rs @@ -141,9 +141,10 @@ pub enum astencode_tag { // Reserves 0x40 -- 0x5f tag_table_capture_map = 0x53, tag_table_unboxed_closure_type = 0x54, tag_table_upvar_borrow_map = 0x55, + tag_table_capture_modes = 0x56, } static first_astencode_tag: uint = tag_ast as uint; -static last_astencode_tag: uint = tag_table_upvar_borrow_map as uint; +static last_astencode_tag: uint = tag_table_capture_modes 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 33b663dea1557..e31af8b58b9f1 100644 --- a/src/librustc/middle/astencode.rs +++ b/src/librustc/middle/astencode.rs @@ -18,8 +18,8 @@ use driver::session::Session; use metadata::decoder; use middle::def; use e = metadata::encoder; +use middle::freevars::{CaptureMode, freevar_entry}; use middle::freevars; -use middle::freevars::freevar_entry; use middle::region; use metadata::tydecode; use metadata::tydecode::{DefIdSource, NominalType, TypeWithId, TypeParameter, @@ -530,9 +530,14 @@ fn encode_freevar_entry(rbml_w: &mut Encoder, fv: &freevar_entry) { (*fv).encode(rbml_w).unwrap(); } +fn encode_capture_mode(rbml_w: &mut Encoder, cm: CaptureMode) { + cm.encode(rbml_w).unwrap(); +} + trait rbml_decoder_helper { fn read_freevar_entry(&mut self, xcx: &ExtendedDecodeContext) -> freevar_entry; + fn read_capture_mode(&mut self) -> CaptureMode; } impl<'a> rbml_decoder_helper for reader::Decoder<'a> { @@ -541,6 +546,11 @@ impl<'a> rbml_decoder_helper for reader::Decoder<'a> { let fv: freevar_entry = Decodable::decode(self).unwrap(); fv.tr(xcx) } + + fn read_capture_mode(&mut self) -> CaptureMode { + let cm: CaptureMode = Decodable::decode(self).unwrap(); + cm + } } impl tr for freevar_entry { @@ -1096,6 +1106,15 @@ fn encode_side_tables_for_id(ecx: &e::EncodeContext, } } + for &cm in tcx.capture_modes.borrow().find(&id).iter() { + rbml_w.tag(c::tag_table_capture_modes, |rbml_w| { + rbml_w.id(id); + rbml_w.tag(c::tag_table_val, |rbml_w| { + encode_capture_mode(rbml_w, *cm); + }) + }) + } + let lid = ast::DefId { krate: ast::LOCAL_CRATE, node: id }; for &pty in tcx.tcache.borrow().find(&lid).iter() { rbml_w.tag(c::tag_table_tcache, |rbml_w| { @@ -1509,6 +1528,13 @@ fn decode_side_tables(xcx: &ExtendedDecodeContext, 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_capture_modes => { + let capture_mode = val_dsr.read_capture_mode(); + dcx.tcx + .capture_modes + .borrow_mut() + .insert(id, capture_mode); + } 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/borrowck/mod.rs b/src/librustc/middle/borrowck/mod.rs index 6c5352ed796d6..fd1369439c9a4 100644 --- a/src/librustc/middle/borrowck/mod.rs +++ b/src/librustc/middle/borrowck/mod.rs @@ -291,7 +291,7 @@ pub fn closure_to_block(closure_id: ast::NodeId, match tcx.map.get(closure_id) { ast_map::NodeExpr(expr) => match expr.node { ast::ExprProc(_decl, block) | - ast::ExprFnBlock(_decl, block) => { block.id } + ast::ExprFnBlock(_, _decl, block) => { block.id } _ => fail!("encountered non-closure id: {}", closure_id) }, _ => fail!("encountered non-expr id: {}", closure_id) diff --git a/src/librustc/middle/check_loop.rs b/src/librustc/middle/check_loop.rs index 61a2e8407303b..f973b33ef2c86 100644 --- a/src/librustc/middle/check_loop.rs +++ b/src/librustc/middle/check_loop.rs @@ -46,9 +46,9 @@ impl<'a> Visitor for CheckLoopVisitor<'a> { self.visit_expr(&**e, cx); self.visit_block(&**b, Loop); } - ast::ExprFnBlock(_, ref b) | + ast::ExprFnBlock(_, _, ref b) | ast::ExprProc(_, ref b) | - ast::ExprUnboxedFn(_, ref b) => { + ast::ExprUnboxedFn(_, _, ref b) => { self.visit_block(&**b, Closure); } ast::ExprBreak(_) => self.require_loop("break", cx, e.span), diff --git a/src/librustc/middle/freevars.rs b/src/librustc/middle/freevars.rs index eda567f7d187c..b092d45e0396a 100644 --- a/src/librustc/middle/freevars.rs +++ b/src/librustc/middle/freevars.rs @@ -17,14 +17,14 @@ use middle::def; use middle::mem_categorization::Typer; use middle::resolve; use middle::ty; -use util::nodemap::{DefIdSet, NodeMap, NodeSet}; +use util::nodemap::{NodeMap, NodeSet}; +use syntax::ast; use syntax::codemap::Span; -use syntax::{ast}; -use syntax::visit; use syntax::visit::Visitor; +use syntax::visit; -#[deriving(Show)] +#[deriving(Clone, Decodable, Encodable, Show)] pub enum CaptureMode { /// Copy/move the value from this llvm ValueRef into the environment. CaptureByValue, @@ -43,12 +43,13 @@ pub struct freevar_entry { pub type freevar_map = NodeMap>; -pub type UnboxedClosureList = DefIdSet; +pub type CaptureModeMap = NodeMap; struct CollectFreevarsVisitor<'a> { seen: NodeSet, refs: Vec, def_map: &'a resolve::DefMap, + capture_mode_map: &'a mut CaptureModeMap, } impl<'a> Visitor for CollectFreevarsVisitor<'a> { @@ -58,8 +59,27 @@ impl<'a> Visitor for CollectFreevarsVisitor<'a> { fn visit_expr(&mut self, expr: &ast::Expr, depth: int) { match expr.node { - ast::ExprFnBlock(..) | ast::ExprProc(..) | - ast::ExprUnboxedFn(..) => { + ast::ExprProc(..) => { + self.capture_mode_map.insert(expr.id, CaptureByValue); + visit::walk_expr(self, expr, depth + 1) + } + ast::ExprFnBlock(_, _, _) => { + // NOTE(stage0): After snapshot, change to: + // + //let capture_mode = match capture_clause { + // ast::CaptureByValue => CaptureByValue, + // ast::CaptureByRef => CaptureByRef, + //}; + let capture_mode = CaptureByRef; + self.capture_mode_map.insert(expr.id, capture_mode); + visit::walk_expr(self, expr, depth + 1) + } + ast::ExprUnboxedFn(capture_clause, _, _) => { + let capture_mode = match capture_clause { + ast::CaptureByValue => CaptureByValue, + ast::CaptureByRef => CaptureByRef, + }; + self.capture_mode_map.insert(expr.id, capture_mode); visit::walk_expr(self, expr, depth + 1) } ast::ExprPath(..) => { @@ -91,8 +111,6 @@ impl<'a> Visitor for CollectFreevarsVisitor<'a> { _ => visit::walk_expr(self, expr, depth) } } - - } // Searches through part of the AST for all references to locals or @@ -100,26 +118,34 @@ impl<'a> Visitor for CollectFreevarsVisitor<'a> { // Since we want to be able to collect upvars in some arbitrary piece // of the AST, we take a walker function that we invoke with a visitor // in order to start the search. -fn collect_freevars(def_map: &resolve::DefMap, blk: &ast::Block) -> Vec { +fn collect_freevars(def_map: &resolve::DefMap, + blk: &ast::Block, + capture_mode_map: &mut CaptureModeMap) + -> Vec { let mut v = CollectFreevarsVisitor { seen: NodeSet::new(), refs: Vec::new(), def_map: def_map, + capture_mode_map: &mut *capture_mode_map, }; v.visit_block(blk, 1); + v.refs } struct AnnotateFreevarsVisitor<'a> { def_map: &'a resolve::DefMap, freevars: freevar_map, + capture_mode_map: CaptureModeMap, } impl<'a> Visitor<()> for AnnotateFreevarsVisitor<'a> { fn visit_fn(&mut self, fk: &visit::FnKind, fd: &ast::FnDecl, blk: &ast::Block, s: Span, nid: ast::NodeId, _: ()) { - let vars = collect_freevars(self.def_map, blk); + let vars = collect_freevars(self.def_map, + blk, + &mut self.capture_mode_map); self.freevars.insert(nid, vars); visit::walk_fn(self, fk, fd, blk, s, ()); } @@ -131,14 +157,20 @@ impl<'a> Visitor<()> for AnnotateFreevarsVisitor<'a> { // node of interest rather than building up the free variables in // one pass. This could be improved upon if it turns out to matter. pub fn annotate_freevars(def_map: &resolve::DefMap, krate: &ast::Crate) - -> freevar_map { + -> (freevar_map, CaptureModeMap) { let mut visitor = AnnotateFreevarsVisitor { def_map: def_map, freevars: NodeMap::new(), + capture_mode_map: NodeMap::new(), }; visit::walk_crate(&mut visitor, krate, ()); - visitor.freevars + let AnnotateFreevarsVisitor { + freevars, + capture_mode_map, + .. + } = visitor; + (freevars, capture_mode_map) } pub fn with_freevars(tcx: &ty::ctxt, fid: ast::NodeId, f: |&[freevar_entry]| -> T) -> T { @@ -148,10 +180,7 @@ pub fn with_freevars(tcx: &ty::ctxt, fid: ast::NodeId, f: |&[freevar_entry]| } } -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 - } +pub fn get_capture_mode(tcx: &T, closure_expr_id: ast::NodeId) + -> CaptureMode { + tcx.capture_mode(closure_expr_id) } diff --git a/src/librustc/middle/liveness.rs b/src/librustc/middle/liveness.rs index 1c31b671a947b..1e48417878834 100644 --- a/src/librustc/middle/liveness.rs +++ b/src/librustc/middle/liveness.rs @@ -965,9 +965,9 @@ impl<'a> Liveness<'a> { self.propagate_through_expr(&**e, succ) } - ExprFnBlock(_, ref blk) | + ExprFnBlock(_, _, ref blk) | ExprProc(_, ref blk) | - ExprUnboxedFn(_, ref blk) => { + ExprUnboxedFn(_, _, ref blk) => { debug!("{} is an ExprFnBlock, ExprProc, or ExprUnboxedFn", expr_to_string(expr)); diff --git a/src/librustc/middle/mem_categorization.rs b/src/librustc/middle/mem_categorization.rs index 39a7b4aa3d68e..6ad8bc0c1e9b1 100644 --- a/src/librustc/middle/mem_categorization.rs +++ b/src/librustc/middle/mem_categorization.rs @@ -63,6 +63,7 @@ #![allow(non_camel_case_types)] use middle::def; +use middle::freevars; use middle::ty; use middle::typeck; use util::nodemap::NodeMap; @@ -270,6 +271,8 @@ pub trait Typer { fn is_method_call(&self, id: ast::NodeId) -> bool; fn temporary_scope(&self, rvalue_id: ast::NodeId) -> Option; fn upvar_borrow(&self, upvar_id: ty::UpvarId) -> ty::UpvarBorrow; + fn capture_mode(&self, closure_expr_id: ast::NodeId) + -> freevars::CaptureMode; } impl MutabilityCategory { diff --git a/src/librustc/middle/resolve.rs b/src/librustc/middle/resolve.rs index 95c04ad6607d6..1e4cbdbdb6e5a 100644 --- a/src/librustc/middle/resolve.rs +++ b/src/librustc/middle/resolve.rs @@ -5287,9 +5287,9 @@ impl<'a> Resolver<'a> { visit::walk_expr(self, expr, ()); } - ExprFnBlock(fn_decl, block) | + ExprFnBlock(_, fn_decl, block) | ExprProc(fn_decl, block) | - ExprUnboxedFn(fn_decl, block) => { + ExprUnboxedFn(_, fn_decl, block) => { self.resolve_function(FunctionRibKind(expr.id, block.id), Some(fn_decl), NoTypeParameters, block); diff --git a/src/librustc/middle/save/mod.rs b/src/librustc/middle/save/mod.rs index c4373a023ccb2..4a58a8bba99c8 100644 --- a/src/librustc/middle/save/mod.rs +++ b/src/librustc/middle/save/mod.rs @@ -1237,7 +1237,7 @@ impl<'l> Visitor for DxrVisitor<'l> { "Expected struct type, but not ty_struct"), } }, - ast::ExprFnBlock(decl, body) => { + ast::ExprFnBlock(_, decl, body) => { if generated_code(body.span) { return } diff --git a/src/librustc/middle/trans/base.rs b/src/librustc/middle/trans/base.rs index 35d32d9952506..b7bb383ad3cb2 100644 --- a/src/librustc/middle/trans/base.rs +++ b/src/librustc/middle/trans/base.rs @@ -1306,7 +1306,9 @@ fn has_nested_returns(tcx: &ty::ctxt, id: ast::NodeId) -> bool { } Some(ast_map::NodeExpr(e)) => { match e.node { - ast::ExprFnBlock(_, blk) | ast::ExprProc(_, blk) | ast::ExprUnboxedFn(_, blk) => { + ast::ExprFnBlock(_, _, blk) | + ast::ExprProc(_, blk) | + ast::ExprUnboxedFn(_, _, blk) => { let mut explicit = CheckForNestedReturnsVisitor { found: false }; let mut implicit = CheckForNestedReturnsVisitor { found: false }; visit::walk_expr(&mut explicit, &*e, false); diff --git a/src/librustc/middle/trans/common.rs b/src/librustc/middle/trans/common.rs index 3b89c73b31da8..8b36270ee5402 100644 --- a/src/librustc/middle/trans/common.rs +++ b/src/librustc/middle/trans/common.rs @@ -18,6 +18,7 @@ use llvm::{ValueRef, BasicBlockRef, BuilderRef}; use llvm::{True, False, Bool}; use mc = middle::mem_categorization; use middle::def; +use middle::freevars; use middle::lang_items::LangItem; use middle::subst; use middle::subst::Subst; @@ -516,6 +517,11 @@ impl<'a> mc::Typer for Block<'a> { fn upvar_borrow(&self, upvar_id: ty::UpvarId) -> ty::UpvarBorrow { self.tcx().upvar_borrow_map.borrow().get_copy(&upvar_id) } + + fn capture_mode(&self, closure_expr_id: ast::NodeId) + -> freevars::CaptureMode { + self.tcx().capture_modes.borrow().get_copy(&closure_expr_id) + } } pub struct Result<'a> { diff --git a/src/librustc/middle/trans/debuginfo.rs b/src/librustc/middle/trans/debuginfo.rs index c27b0bb8cb1eb..ad1c5bf6ef133 100644 --- a/src/librustc/middle/trans/debuginfo.rs +++ b/src/librustc/middle/trans/debuginfo.rs @@ -1150,9 +1150,9 @@ pub fn create_function_debug_context(cx: &CrateContext, } ast_map::NodeExpr(ref expr) => { match expr.node { - ast::ExprFnBlock(fn_decl, top_level_block) | + ast::ExprFnBlock(_, fn_decl, top_level_block) | ast::ExprProc(fn_decl, top_level_block) | - ast::ExprUnboxedFn(fn_decl, top_level_block) => { + ast::ExprUnboxedFn(_, fn_decl, top_level_block) => { let name = format!("fn{}", token::gensym("fn")); let name = token::str_to_ident(name.as_slice()); (name, fn_decl, @@ -3618,9 +3618,9 @@ fn populate_scope_map(cx: &CrateContext, }) } - ast::ExprFnBlock(ref decl, ref block) | + ast::ExprFnBlock(_, ref decl, ref block) | ast::ExprProc(ref decl, ref block) | - ast::ExprUnboxedFn(ref decl, ref block) => { + ast::ExprUnboxedFn(_, ref decl, ref block) => { with_new_scope(cx, block.span, scope_stack, diff --git a/src/librustc/middle/trans/expr.rs b/src/librustc/middle/trans/expr.rs index 1dad6e3cb1843..7cd2bd631f091 100644 --- a/src/librustc/middle/trans/expr.rs +++ b/src/librustc/middle/trans/expr.rs @@ -782,7 +782,7 @@ fn trans_rvalue_dps_unadjusted<'a>(bcx: &'a Block<'a>, ast::ExprVec(..) | ast::ExprRepeat(..) => { tvec::trans_fixed_vstore(bcx, expr, expr, dest) } - ast::ExprFnBlock(ref decl, ref body) | + ast::ExprFnBlock(_, ref decl, ref body) | ast::ExprProc(ref decl, ref body) => { let expr_ty = expr_ty(bcx, expr); let store = ty::ty_closure_store(expr_ty); @@ -790,7 +790,7 @@ fn trans_rvalue_dps_unadjusted<'a>(bcx: &'a Block<'a>, expr_to_string(expr), expr_ty.repr(tcx)); closure::trans_expr_fn(bcx, store, &**decl, &**body, expr.id, dest) } - ast::ExprUnboxedFn(decl, body) => { + ast::ExprUnboxedFn(_, decl, body) => { closure::trans_unboxed_closure(bcx, &*decl, &*body, expr.id, dest) } ast::ExprCall(ref f, ref args) => { diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index a4588da1bd7dd..3346b475267b9 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -18,14 +18,15 @@ use lint; use middle::const_eval; use middle::def; use middle::dependency_format; +use middle::freevars::CaptureModeMap; +use middle::freevars; use middle::lang_items::{FnMutTraitLangItem, OpaqueStructLangItem}; use middle::lang_items::{TyDescStructLangItem, TyVisitorTraitLangItem}; -use middle::freevars; use middle::resolve; use middle::resolve_lifetime; -use middle::subst; -use middle::subst::{Subst, Substs, VecPerParamSpace}; use middle::stability; +use middle::subst::{Subst, Substs, VecPerParamSpace}; +use middle::subst; use middle::ty; use middle::typeck; use middle::typeck::MethodCall; @@ -384,6 +385,9 @@ pub struct ctxt { /// Maps any item's def-id to its stability index. pub stability: RefCell, + + /// Maps closures to their capture clauses. + pub capture_modes: RefCell, } pub enum tbox_flag { @@ -1057,6 +1061,7 @@ pub fn mk_ctxt(s: Session, named_region_map: resolve_lifetime::NamedRegionMap, map: ast_map::Map, freevars: freevars::freevar_map, + capture_modes: freevars::CaptureModeMap, region_maps: middle::region::RegionMaps, lang_items: middle::lang_items::LanguageItems, stability: stability::Index) @@ -1115,7 +1120,8 @@ pub fn mk_ctxt(s: Session, unboxed_closure_types: RefCell::new(DefIdMap::new()), node_lint_levels: RefCell::new(HashMap::new()), transmute_restrictions: RefCell::new(Vec::new()), - stability: RefCell::new(stability) + stability: RefCell::new(stability), + capture_modes: RefCell::new(capture_modes), } } @@ -4862,6 +4868,11 @@ impl mc::Typer for ty::ctxt { fn upvar_borrow(&self, upvar_id: ty::UpvarId) -> ty::UpvarBorrow { self.upvar_borrow_map.borrow().get_copy(&upvar_id) } + + fn capture_mode(&self, closure_expr_id: ast::NodeId) + -> freevars::CaptureMode { + self.capture_modes.borrow().get_copy(&closure_expr_id) + } } /// The category of explicit self. diff --git a/src/librustc/middle/typeck/check/mod.rs b/src/librustc/middle/typeck/check/mod.rs index aa38ff68f24e6..33acb52178008 100644 --- a/src/librustc/middle/typeck/check/mod.rs +++ b/src/librustc/middle/typeck/check/mod.rs @@ -3390,7 +3390,7 @@ fn check_expr_with_unifier(fcx: &FnCtxt, ast::ExprMatch(ref discrim, ref arms) => { _match::check_match(fcx, expr, &**discrim, arms.as_slice()); } - ast::ExprFnBlock(ref decl, ref body) => { + ast::ExprFnBlock(_, ref decl, ref body) => { let region = astconv::opt_ast_region_to_region(fcx, fcx.infcx(), expr.span, @@ -3402,7 +3402,7 @@ fn check_expr_with_unifier(fcx: &FnCtxt, body.clone(), expected); } - ast::ExprUnboxedFn(ref decl, ref body) => { + ast::ExprUnboxedFn(_, ref decl, ref body) => { check_unboxed_closure(fcx, expr, &**decl, diff --git a/src/librustc/middle/typeck/check/regionck.rs b/src/librustc/middle/typeck/check/regionck.rs index d0431de81a359..47f5c45dde248 100644 --- a/src/librustc/middle/typeck/check/regionck.rs +++ b/src/librustc/middle/typeck/check/regionck.rs @@ -290,6 +290,11 @@ impl<'fcx> mc::Typer for Rcx<'fcx> { fn upvar_borrow(&self, id: ty::UpvarId) -> ty::UpvarBorrow { self.fcx.inh.upvar_borrow_map.borrow().get_copy(&id) } + + fn capture_mode(&self, closure_expr_id: ast::NodeId) + -> freevars::CaptureMode { + self.tcx().capture_modes.borrow().get_copy(&closure_expr_id) + } } pub fn regionck_expr(fcx: &FnCtxt, e: &ast::Expr) { @@ -587,9 +592,9 @@ fn visit_expr(rcx: &mut Rcx, expr: &ast::Expr) { visit::walk_expr(rcx, expr, ()); } - ast::ExprFnBlock(_, ref body) | + ast::ExprFnBlock(_, _, ref body) | ast::ExprProc(_, ref body) | - ast::ExprUnboxedFn(_, ref body) => { + ast::ExprUnboxedFn(_, _, ref body) => { check_expr_fn_block(rcx, expr, &**body); } diff --git a/src/librustc/middle/typeck/check/writeback.rs b/src/librustc/middle/typeck/check/writeback.rs index d94ac103ceb73..6e6ebd181fbc9 100644 --- a/src/librustc/middle/typeck/check/writeback.rs +++ b/src/librustc/middle/typeck/check/writeback.rs @@ -132,9 +132,9 @@ impl<'cx> Visitor<()> for WritebackCx<'cx> { MethodCall::expr(e.id)); match e.node { - ast::ExprFnBlock(ref decl, _) | + ast::ExprFnBlock(_, ref decl, _) | ast::ExprProc(ref decl, _) | - ast::ExprUnboxedFn(ref decl, _) => { + ast::ExprUnboxedFn(_, ref decl, _) => { for input in decl.inputs.iter() { let _ = self.visit_node_id(ResolvingExpr(e.span), input.id); diff --git a/src/librustc/middle/typeck/infer/test.rs b/src/librustc/middle/typeck/infer/test.rs index 637af96b6321a..dd00fc6207981 100644 --- a/src/librustc/middle/typeck/infer/test.rs +++ b/src/librustc/middle/typeck/infer/test.rs @@ -124,12 +124,20 @@ fn test_env(_test_name: &str, let lang_items = lang_items::collect_language_items(&krate, &sess); let resolve::CrateMap { def_map: def_map, .. } = resolve::resolve_crate(&sess, &lang_items, &krate); - let freevars_map = freevars::annotate_freevars(&def_map, &krate); + let (freevars_map, captures_map) = freevars::annotate_freevars(&def_map, + &krate); let named_region_map = resolve_lifetime::krate(&sess, &krate); let region_map = region::resolve_crate(&sess, &krate); let stability_index = stability::Index::build(&krate); - let tcx = ty::mk_ctxt(sess, def_map, named_region_map, ast_map, - freevars_map, region_map, lang_items, stability_index); + let tcx = ty::mk_ctxt(sess, + def_map, + named_region_map, + ast_map, + freevars_map, + captures_map, + region_map, + lang_items, + stability_index); let infcx = infer::new_infer_ctxt(&tcx); let env = Env {krate: krate, tcx: &tcx, diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index 629b21875c9b6..2f2347d8db80e 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -520,9 +520,9 @@ pub enum Expr_ { // FIXME #6993: change to Option ... or not, if these are hygienic. ExprLoop(P, Option), ExprMatch(Gc, Vec), - ExprFnBlock(P, P), + ExprFnBlock(CaptureClause, P, P), ExprProc(P, P), - ExprUnboxedFn(P, P), + ExprUnboxedFn(CaptureClause, P, P), ExprBlock(P), ExprAssign(Gc, Gc), @@ -553,6 +553,12 @@ pub enum Expr_ { ExprParen(Gc) } +#[deriving(Clone, PartialEq, Eq, Encodable, Decodable, Hash, Show)] +pub enum CaptureClause { + CaptureByValue, + CaptureByRef, +} + /// When the main rust parser encounters a syntax-extension invocation, it /// parses the arguments to the invocation as a token-tree. This is a very /// loose structure, such that all sorts of different AST-fragments can diff --git a/src/libsyntax/ast_map/blocks.rs b/src/libsyntax/ast_map/blocks.rs index a522f80554327..83af390c4edc3 100644 --- a/src/libsyntax/ast_map/blocks.rs +++ b/src/libsyntax/ast_map/blocks.rs @@ -206,7 +206,7 @@ impl FnLikeNode { }, ast_map::NodeMethod(ref m) => method(&**m), ast_map::NodeExpr(ref e) => match e.node { - ast::ExprFnBlock(ref decl, ref block) => + ast::ExprFnBlock(_, ref decl, ref block) => closure(ClosureParts::new(*decl, *block, e.id, e.span)), ast::ExprProc(ref decl, ref block) => closure(ClosureParts::new(*decl, *block, e.id, e.span)), diff --git a/src/libsyntax/ext/build.rs b/src/libsyntax/ext/build.rs index 0e687c02c1daf..f7eddca4b7aed 100644 --- a/src/libsyntax/ext/build.rs +++ b/src/libsyntax/ext/build.rs @@ -876,14 +876,14 @@ impl<'a> AstBuilder for ExtCtxt<'a> { fn lambda_fn_decl(&self, span: Span, fn_decl: P, blk: P) -> Gc { - self.expr(span, ast::ExprFnBlock(fn_decl, blk)) + self.expr(span, ast::ExprFnBlock(ast::CaptureByRef, fn_decl, blk)) } fn lambda(&self, span: Span, ids: Vec , blk: P) -> Gc { let fn_decl = self.fn_decl( ids.iter().map(|id| self.arg(span, *id, self.ty_infer(span))).collect(), self.ty_infer(span)); - self.expr(span, ast::ExprFnBlock(fn_decl, blk)) + self.expr(span, ast::ExprFnBlock(ast::CaptureByRef, fn_decl, blk)) } fn lambda0(&self, span: Span, blk: P) -> Gc { self.lambda(span, Vec::new(), blk) diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 808532d55bb2e..d918b28d4dc8f 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -74,10 +74,12 @@ fn expand_expr(e: Gc, fld: &mut MacroExpander) -> Gc { fld.cx.expr(e.span, ast::ExprForLoop(pat, head, body, opt_ident)) } - ast::ExprFnBlock(fn_decl, block) => { + ast::ExprFnBlock(capture_clause, fn_decl, block) => { let (rewritten_fn_decl, rewritten_block) = expand_and_rename_fn_decl_and_block(&*fn_decl, block, fld); - let new_node = ast::ExprFnBlock(rewritten_fn_decl, rewritten_block); + let new_node = ast::ExprFnBlock(capture_clause, + rewritten_fn_decl, + rewritten_block); box(GC) ast::Expr{id:e.id, node: new_node, span: fld.new_span(e.span)} } diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs index 80325c64349e8..02f1f4646a466 100644 --- a/src/libsyntax/fold.rs +++ b/src/libsyntax/fold.rs @@ -1094,16 +1094,18 @@ pub fn noop_fold_expr(e: Gc, folder: &mut T) -> Gc { ExprMatch(folder.fold_expr(expr), arms.iter().map(|x| folder.fold_arm(x)).collect()) } - ExprFnBlock(ref decl, ref body) => { - ExprFnBlock(folder.fold_fn_decl(&**decl), + ExprFnBlock(capture_clause, ref decl, ref body) => { + ExprFnBlock(capture_clause, + folder.fold_fn_decl(&**decl), folder.fold_block(body.clone())) } ExprProc(ref decl, ref body) => { ExprProc(folder.fold_fn_decl(&**decl), folder.fold_block(body.clone())) } - ExprUnboxedFn(ref decl, ref body) => { - ExprUnboxedFn(folder.fold_fn_decl(&**decl), + ExprUnboxedFn(capture_clause, ref decl, ref body) => { + ExprUnboxedFn(capture_clause, + folder.fold_fn_decl(&**decl), folder.fold_block(*body)) } ExprBlock(ref blk) => ExprBlock(folder.fold_block(*blk)), diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 08d96f5b0086d..f0920603ad12e 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -17,6 +17,7 @@ use ast::{Provided, Public, FnStyle}; use ast::{Mod, BiAdd, Arg, Arm, Attribute, BindByRef, BindByValue}; use ast::{BiBitAnd, BiBitOr, BiBitXor, Block}; use ast::{BlockCheckMode, UnBox}; +use ast::{CaptureByRef, CaptureByValue, CaptureClause}; use ast::{Crate, CrateConfig, Decl, DeclItem}; use ast::{DeclLocal, DefaultBlock, UnDeref, BiDiv, EMPTY_CTXT, EnumDef, ExplicitSelf}; use ast::{Expr, Expr_, ExprAddrOf, ExprMatch, ExprAgain}; @@ -1985,7 +1986,7 @@ impl<'a> Parser<'a> { ExprBlock(blk)); }, token::BINOP(token::OR) | token::OROR => { - return self.parse_lambda_expr(); + return self.parse_lambda_expr(CaptureByValue); }, // FIXME #13626: Should be able to stick in // token::SELF_KEYWORD_NAME @@ -2036,6 +2037,9 @@ impl<'a> Parser<'a> { hi = self.last_span.hi; }, _ => { + if self.eat_keyword(keywords::Ref) { + return self.parse_lambda_expr(CaptureByRef); + } if self.eat_keyword(keywords::Proc) { let decl = self.parse_proc_decl(); let body = self.parse_expr(); @@ -2696,7 +2700,8 @@ impl<'a> Parser<'a> { } // `|args| expr` - pub fn parse_lambda_expr(&mut self) -> Gc { + pub fn parse_lambda_expr(&mut self, capture_clause: CaptureClause) + -> Gc { let lo = self.span.lo; let (decl, is_unboxed) = self.parse_fn_block_decl(); let body = self.parse_expr(); @@ -2710,9 +2715,13 @@ impl<'a> Parser<'a> { }); if is_unboxed { - self.mk_expr(lo, body.span.hi, ExprUnboxedFn(decl, fakeblock)) + self.mk_expr(lo, + body.span.hi, + ExprUnboxedFn(capture_clause, decl, fakeblock)) } else { - self.mk_expr(lo, body.span.hi, ExprFnBlock(decl, fakeblock)) + self.mk_expr(lo, + body.span.hi, + ExprFnBlock(capture_clause, decl, fakeblock)) } } diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index 9d4b7343c8a15..3ef9d96e3b690 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -1437,7 +1437,9 @@ impl<'a> State<'a> { } try!(self.bclose_(expr.span, indent_unit)); } - ast::ExprFnBlock(ref decl, ref body) => { + ast::ExprFnBlock(capture_clause, ref decl, ref body) => { + try!(self.print_capture_clause(capture_clause)); + // in do/for blocks we don't want to show an empty // argument list, but at this point we don't know which // we are inside. @@ -1467,7 +1469,9 @@ impl<'a> State<'a> { // empty box to satisfy the close. try!(self.ibox(0)); } - ast::ExprUnboxedFn(ref decl, ref body) => { + ast::ExprUnboxedFn(capture_clause, ref decl, ref body) => { + try!(self.print_capture_clause(capture_clause)); + // in do/for blocks we don't want to show an empty // argument list, but at this point we don't know which // we are inside. @@ -2030,6 +2034,14 @@ impl<'a> State<'a> { self.maybe_print_comment(decl.output.span.lo) } + pub fn print_capture_clause(&mut self, capture_clause: ast::CaptureClause) + -> IoResult<()> { + match capture_clause { + ast::CaptureByValue => Ok(()), + ast::CaptureByRef => self.word_space("ref"), + } + } + pub fn print_proc_args(&mut self, decl: &ast::FnDecl) -> IoResult<()> { try!(word(&mut self.s, "proc")); try!(word(&mut self.s, "(")); diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs index 647e81db1f1c0..372cee9ad0988 100644 --- a/src/libsyntax/visit.rs +++ b/src/libsyntax/visit.rs @@ -787,7 +787,7 @@ pub fn walk_expr>(visitor: &mut V, expression: &Expr, en visitor.visit_arm(arm, env.clone()) } } - ExprFnBlock(ref function_declaration, ref body) => { + ExprFnBlock(_, ref function_declaration, ref body) => { visitor.visit_fn(&FkFnBlock, &**function_declaration, &**body, @@ -795,7 +795,7 @@ pub fn walk_expr>(visitor: &mut V, expression: &Expr, en expression.id, env.clone()) } - ExprUnboxedFn(ref function_declaration, ref body) => { + ExprUnboxedFn(_, ref function_declaration, ref body) => { visitor.visit_fn(&FkFnBlock, &**function_declaration, &**body, diff --git a/src/test/run-pass/capture-clauses-boxed-closures.rs b/src/test/run-pass/capture-clauses-boxed-closures.rs new file mode 100644 index 0000000000000..c88b44925d06a --- /dev/null +++ b/src/test/run-pass/capture-clauses-boxed-closures.rs @@ -0,0 +1,23 @@ +// 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 each(x: &[T], f: |&T|) { + for val in x.iter() { + f(val) + } +} + +fn main() { + let mut sum = 0u; + let elems = [ 1u, 2, 3, 4, 5 ]; + each(elems, ref |val| sum += *val); + assert_eq!(sum, 15); +} + diff --git a/src/test/run-pass/capture-clauses-unboxed-closures.rs b/src/test/run-pass/capture-clauses-unboxed-closures.rs new file mode 100644 index 0000000000000..99e6d6e7a4ffa --- /dev/null +++ b/src/test/run-pass/capture-clauses-unboxed-closures.rs @@ -0,0 +1,29 @@ +// 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. + +// ignore-test +// +// This is ignored because it depends on #16122. + +#![feature(overloaded_calls, unboxed_closures)] + +fn each<'a,T,F:|&mut: &'a T|>(x: &'a [T], mut f: F) { + for val in x.iter() { + f(val) + } +} + +fn main() { + let mut sum = 0u; + let elems = [ 1u, 2, 3, 4, 5 ]; + each(elems, ref |&mut: val: &uint| sum += *val); + assert_eq!(sum, 15); +} +