diff --git a/src/liballoc/lib.rs b/src/liballoc/lib.rs index f807d8d12a6b56d9dba2e8675e94dc28add38212..8960667fdfa861481f82f4d721c9a6ff5ee9f925 100644 --- a/src/liballoc/lib.rs +++ b/src/liballoc/lib.rs @@ -70,6 +70,7 @@ #![feature(lang_items, unsafe_destructor)] #![feature(box_syntax)] #![feature(optin_builtin_traits)] +#![feature(unboxed_closures)] #![allow(unknown_features)] #![feature(int_uint)] #![feature(core)] #![feature(hash)] diff --git a/src/libcore/ops.rs b/src/libcore/ops.rs index 9e020eeb8a9c18492a69ae7cf6f8711086a3aae6..0b9b865c13700af59ada7f8cb5828fe4678f9c46 100644 --- a/src/libcore/ops.rs +++ b/src/libcore/ops.rs @@ -1183,6 +1183,7 @@ extern "rust-call" fn call_once(mut self, args: A) -> R { #[unstable(feature = "core", reason = "uncertain about variadic generics, input versus associated types")] #[cfg(not(stage0))] +#[rustc_paren_sugar] pub trait Fn { type Output; @@ -1195,6 +1196,7 @@ pub trait Fn { #[unstable(feature = "core", reason = "uncertain about variadic generics, input versus associated types")] #[cfg(not(stage0))] +#[rustc_paren_sugar] pub trait FnMut { type Output; @@ -1207,6 +1209,7 @@ pub trait FnMut { #[unstable(feature = "core", reason = "uncertain about variadic generics, input versus associated types")] #[cfg(not(stage0))] +#[rustc_paren_sugar] pub trait FnOnce { type Output; diff --git a/src/librustc/lint/builtin.rs b/src/librustc/lint/builtin.rs index f13814527cdfd3a6ddb58b691839c85753a34814..ee7c0ee894fc7b927200ca7744e81ab010c0f4a5 100644 --- a/src/librustc/lint/builtin.rs +++ b/src/librustc/lint/builtin.rs @@ -670,6 +670,7 @@ fn check_attribute(&mut self, cx: &Context, attr: &ast::Attribute) { // FIXME: #19470 this shouldn't be needed forever "old_orphan_check", "old_impl_check", + "rustc_paren_sugar", // FIXME: #18101 temporary unboxed closure hack ]; static CRATE_ATTRS: &'static [&'static str] = &[ diff --git a/src/librustc/metadata/common.rs b/src/librustc/metadata/common.rs index aa2be7153ad456cf2b86468792c7077bb7bd88cd..242ab630a20ef424a8eaef8ebd506b9d12dc9da7 100644 --- a/src/librustc/metadata/common.rs +++ b/src/librustc/metadata/common.rs @@ -140,7 +140,7 @@ pub enum astencode_tag { // Reserves 0x40 -- 0x5f tag_table_moves_map = 0x52, tag_table_capture_map = 0x53, tag_table_closures = 0x54, - tag_table_upvar_borrow_map = 0x55, + tag_table_upvar_capture_map = 0x55, tag_table_capture_modes = 0x56, tag_table_object_cast_map = 0x57, } @@ -265,3 +265,5 @@ pub struct LinkMeta { pub const tag_macro_defs: uint = 0xb5; pub const tag_macro_def: uint = 0xb6; pub const tag_macro_def_body: uint = 0xb7; + +pub const tag_paren_sugar: uint = 0xb8; diff --git a/src/librustc/metadata/decoder.rs b/src/librustc/metadata/decoder.rs index 933fd873aeb7a06d4d3a6b9936c0ee06cc46cffa..93ca42e9a2872c41fb2d5b64ad946c39c49b8d8c 100644 --- a/src/librustc/metadata/decoder.rs +++ b/src/librustc/metadata/decoder.rs @@ -371,6 +371,11 @@ fn parse_unsafety(item_doc: rbml::Doc) -> ast::Unsafety { } } +fn parse_paren_sugar(item_doc: rbml::Doc) -> bool { + let paren_sugar_doc = reader::get_doc(item_doc, tag_paren_sugar); + reader::doc_as_u8(paren_sugar_doc) != 0 +} + fn parse_polarity(item_doc: rbml::Doc) -> ast::ImplPolarity { let polarity_doc = reader::get_doc(item_doc, tag_polarity); if reader::doc_as_u8(polarity_doc) != 0 { @@ -400,8 +405,10 @@ pub fn get_trait_def<'tcx>(cdata: Cmd, let bounds = trait_def_bounds(item_doc, tcx, cdata); let unsafety = parse_unsafety(item_doc); let associated_type_names = parse_associated_type_names(item_doc); + let paren_sugar = parse_paren_sugar(item_doc); ty::TraitDef { + paren_sugar: paren_sugar, unsafety: unsafety, generics: generics, bounds: bounds, diff --git a/src/librustc/metadata/encoder.rs b/src/librustc/metadata/encoder.rs index 13dc9397afc0f011d9f18fe98dbcc29d82f0b953..6767f77de84bb61ed017a3b2874f6ed500be4209 100644 --- a/src/librustc/metadata/encoder.rs +++ b/src/librustc/metadata/encoder.rs @@ -1317,6 +1317,7 @@ fn add_to_index(item: &ast::Item, rbml_w: &Encoder, encode_item_variances(rbml_w, ecx, item.id); let trait_def = ty::lookup_trait_def(tcx, def_id); encode_unsafety(rbml_w, trait_def.unsafety); + encode_paren_sugar(rbml_w, trait_def.paren_sugar); encode_associated_type_names(rbml_w, trait_def.associated_type_names.as_slice()); encode_generics(rbml_w, ecx, &trait_def.generics, tag_item_generics); encode_trait_ref(rbml_w, ecx, &*trait_def.trait_ref, tag_item_trait_ref); @@ -1697,6 +1698,11 @@ fn encode_unsafety(rbml_w: &mut Encoder, unsafety: ast::Unsafety) { rbml_w.wr_tagged_u8(tag_unsafety, byte); } +fn encode_paren_sugar(rbml_w: &mut Encoder, paren_sugar: bool) { + let byte: u8 = if paren_sugar {1} else {0}; + rbml_w.wr_tagged_u8(tag_paren_sugar, byte); +} + fn encode_associated_type_names(rbml_w: &mut Encoder, names: &[ast::Name]) { rbml_w.start_tag(tag_associated_type_names); for &name in names.iter() { diff --git a/src/librustc/middle/astencode.rs b/src/librustc/middle/astencode.rs index 52417c3e66c5cd613772f4e7259378ed8217b411..902c029fef4bf3907e82dcb61a0f109b292c0ad1 100644 --- a/src/librustc/middle/astencode.rs +++ b/src/librustc/middle/astencode.rs @@ -518,10 +518,6 @@ fn encode_freevar_entry(rbml_w: &mut Encoder, fv: &ty::Freevar) { (*fv).encode(rbml_w).unwrap(); } -fn encode_capture_mode(rbml_w: &mut Encoder, cm: ast::CaptureClause) { - cm.encode(rbml_w).unwrap(); -} - trait rbml_decoder_helper { fn read_freevar_entry(&mut self, dcx: &DecodeContext) -> ty::Freevar; @@ -559,6 +555,15 @@ fn tr(&self, dcx: &DecodeContext) -> ty::UpvarBorrow { } } +impl tr for ty::UpvarCapture { + fn tr(&self, dcx: &DecodeContext) -> ty::UpvarCapture { + match *self { + ty::UpvarCapture::ByValue => ty::UpvarCapture::ByValue, + ty::UpvarCapture::ByRef(ref data) => ty::UpvarCapture::ByRef(data.tr(dcx)), + } + } +} + // ______________________________________________________________________ // Encoding and decoding of MethodCallee @@ -1210,34 +1215,20 @@ fn encode_side_tables_for_id(ecx: &e::EncodeContext, }); for freevar in fv.iter() { - match tcx.capture_mode(id) { - ast::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()[upvar_id].clone(); - var_id.encode(rbml_w); - upvar_borrow.encode(rbml_w); - }) - }) - } - _ => {} - } - } - } - - for &cm in tcx.capture_modes.borrow().get(&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); + rbml_w.tag(c::tag_table_upvar_capture_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_capture = tcx.upvar_capture_map.borrow()[upvar_id].clone(); + var_id.encode(rbml_w); + upvar_capture.encode(rbml_w); + }) }) - }) + } } let lid = ast::DefId { krate: ast::LOCAL_CRATE, node: id }; @@ -1911,21 +1902,14 @@ fn decode_side_tables(dcx: &DecodeContext, }).unwrap().into_iter().collect(); dcx.tcx.freevars.borrow_mut().insert(id, fv_info); } - c::tag_table_upvar_borrow_map => { + c::tag_table_upvar_capture_map => { let var_id: ast::NodeId = Decodable::decode(val_dsr).unwrap(); let upvar_id = ty::UpvarId { var_id: dcx.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(dcx)); - } - c::tag_table_capture_modes => { - let capture_mode = val_dsr.read_capture_mode(); - dcx.tcx - .capture_modes - .borrow_mut() - .insert(id, capture_mode); + let ub: ty::UpvarCapture = Decodable::decode(val_dsr).unwrap(); + dcx.tcx.upvar_capture_map.borrow_mut().insert(upvar_id, ub.tr(dcx)); } c::tag_table_tcache => { let type_scheme = val_dsr.read_type_scheme(dcx); diff --git a/src/librustc/middle/expr_use_visitor.rs b/src/librustc/middle/expr_use_visitor.rs index 0d543ca7beb0cad0b75f40f343ba7768476cf8f5..9457a1a99f6d516bc12d4865de941459eef5e556 100644 --- a/src/librustc/middle/expr_use_visitor.rs +++ b/src/librustc/middle/expr_use_visitor.rs @@ -366,6 +366,9 @@ fn delegate_consume(&mut self, consume_id: ast::NodeId, consume_span: Span, cmt: mc::cmt<'tcx>) { + debug!("delegate_consume(consume_id={}, cmt={})", + consume_id, cmt.repr(self.tcx())); + let mode = copy_or_move(self.typer, &cmt, DirectRefMove); self.delegate.consume(consume_id, consume_span, cmt, mode); } @@ -1208,53 +1211,32 @@ fn walk_captures(&mut self, closure_expr: &ast::Expr) { debug!("walk_captures({})", closure_expr.repr(self.tcx())); ty::with_freevars(self.tcx(), closure_expr.id, |freevars| { - match self.tcx().capture_mode(closure_expr.id) { - ast::CaptureByRef => { - self.walk_by_ref_captures(closure_expr, freevars); - } - ast::CaptureByValue => { - self.walk_by_value_captures(closure_expr, freevars); + for freevar in freevars.iter() { + let id_var = freevar.def.def_id().node; + let upvar_id = ty::UpvarId { var_id: id_var, + closure_expr_id: closure_expr.id }; + let upvar_capture = self.typer.upvar_capture(upvar_id).unwrap(); + let cmt_var = return_if_err!(self.cat_captured_var(closure_expr.id, + closure_expr.span, + freevar.def)); + match upvar_capture { + ty::UpvarCapture::ByValue => { + let mode = copy_or_move(self.typer, &cmt_var, CaptureMove); + self.delegate.consume(closure_expr.id, freevar.span, cmt_var, mode); + } + ty::UpvarCapture::ByRef(upvar_borrow) => { + self.delegate.borrow(closure_expr.id, + closure_expr.span, + cmt_var, + upvar_borrow.region, + upvar_borrow.kind, + ClosureCapture(freevar.span)); + } } } }); } - fn walk_by_ref_captures(&mut self, - closure_expr: &ast::Expr, - freevars: &[ty::Freevar]) { - for freevar in freevars.iter() { - let id_var = freevar.def.def_id().node; - let cmt_var = return_if_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.typer.upvar_borrow(upvar_id).unwrap(); - - 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: &[ty::Freevar]) { - for freevar in freevars.iter() { - let cmt_var = return_if_err!(self.cat_captured_var(closure_expr.id, - closure_expr.span, - freevar.def)); - let mode = copy_or_move(self.typer, &cmt_var, CaptureMove); - self.delegate.consume(closure_expr.id, freevar.span, cmt_var, mode); - } - } - fn cat_captured_var(&mut self, closure_id: ast::NodeId, closure_span: Span, diff --git a/src/librustc/middle/mem_categorization.rs b/src/librustc/middle/mem_categorization.rs index 1be1bfa6730cdffa2eee0244d6081f13b79c403f..5fe5a4c7eb2cf7a4dbe38dda5e0af2fefa095095 100644 --- a/src/librustc/middle/mem_categorization.rs +++ b/src/librustc/middle/mem_categorization.rs @@ -277,9 +277,7 @@ fn node_method_origin(&self, method_call: ty::MethodCall) fn adjustments<'a>(&'a self) -> &'a RefCell>>; 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) -> Option; - fn capture_mode(&self, closure_expr_id: ast::NodeId) - -> ast::CaptureClause; + fn upvar_capture(&self, upvar_id: ty::UpvarId) -> Option; } impl MutabilityCategory { @@ -595,8 +593,7 @@ pub fn cat_def(&self, match ty.sty { ty::ty_closure(closure_id, _, _) => { let kind = self.typer.closure_kind(closure_id); - let mode = self.typer.capture_mode(fn_node_id); - self.cat_upvar(id, span, var_id, fn_node_id, kind, mode) + self.cat_upvar(id, span, var_id, fn_node_id, kind) } _ => { self.tcx().sess.span_bug( @@ -628,10 +625,13 @@ fn cat_upvar(&self, span: Span, var_id: ast::NodeId, fn_node_id: ast::NodeId, - kind: ty::ClosureKind, - mode: ast::CaptureClause) - -> McResult> { - // An upvar can have up to 3 components. The base is a + kind: ty::ClosureKind) + -> McResult> + { + // An upvar can have up to 3 components. We translate first to a + // `cat_upvar`, which is itself a fiction -- it represents the reference to the + // field from the environment. + // // `cat_upvar`. Next, we add a deref through the implicit // environment pointer with an anonymous free region 'env and // appropriate borrow kind for closure kinds that take self by @@ -650,135 +650,130 @@ fn cat_upvar(&self, // Fn | copied -> &'env | upvar -> &'env -> &'up bk // FnMut | copied -> &'env mut | upvar -> &'env mut -> &'up bk // FnOnce | copied | upvar -> &'up bk - let var_ty = try!(self.node_ty(var_id)); let upvar_id = ty::UpvarId { var_id: var_id, closure_expr_id: fn_node_id }; + let var_ty = try!(self.node_ty(var_id)); // Mutability of original variable itself let var_mutbl = MutabilityCategory::from_local(self.tcx(), var_id); - // Construct information about env pointer dereference, if any - let mutbl = match kind { - ty::FnOnceClosureKind => None, // None, env is by-value - ty::FnMutClosureKind => match mode { // Depends on capture type - ast::CaptureByValue => Some(var_mutbl), // Mutable if the original var is - ast::CaptureByRef => Some(McDeclared) // Mutable regardless - }, - ty::FnClosureKind => Some(McImmutable) // Never mutable + // Construct the upvar. This represents access to the field + // from the environment (perhaps we should eventually desugar + // this field further, but it will do for now). + let cmt_result = cmt_ { + id: id, + span: span, + cat: cat_upvar(Upvar {id: upvar_id, kind: kind}), + mutbl: var_mutbl, + ty: var_ty, + note: NoteNone }; - let env_info = mutbl.map(|env_mutbl| { - // Look up the node ID of the closure body so we can construct - // a free region within it - let fn_body_id = { - let fn_expr = match self.tcx().map.find(fn_node_id) { - Some(ast_map::NodeExpr(e)) => e, - _ => unreachable!() - }; - match fn_expr.node { - ast::ExprClosure(_, _, _, ref body) => body.id, - _ => unreachable!() - } - }; - - // Region of environment pointer - let env_region = ty::ReFree(ty::FreeRegion { - scope: region::CodeExtent::from_node_id(fn_body_id), - bound_region: ty::BrEnv - }); - - let env_ptr = BorrowedPtr(if env_mutbl.is_mutable() { - ty::MutBorrow - } else { - ty::ImmBorrow - }, env_region); - - (env_mutbl, env_ptr) - }); + // If this is a `FnMut` or `Fn` closure, then the above is + // conceptually a `&mut` or `&` reference, so we have to add a + // deref. + let cmt_result = match kind { + ty::FnOnceClosureKind => { + cmt_result + } + ty::FnMutClosureKind => { + self.env_deref(id, span, upvar_id, var_mutbl, ty::MutBorrow, cmt_result) + } + ty::FnClosureKind => { + self.env_deref(id, span, upvar_id, var_mutbl, ty::ImmBorrow, cmt_result) + } + }; - // First, switch by capture mode - Ok(match mode { - ast::CaptureByValue => { - let mut base = cmt_ { + // If this is a by-ref capture, then the upvar we loaded is + // actually a reference, so we have to add an implicit deref + // for that. + let upvar_id = ty::UpvarId { var_id: var_id, + closure_expr_id: fn_node_id }; + let upvar_capture = self.typer.upvar_capture(upvar_id).unwrap(); + let cmt_result = match upvar_capture { + ty::UpvarCapture::ByValue => { + cmt_result + } + ty::UpvarCapture::ByRef(upvar_borrow) => { + let ptr = BorrowedPtr(upvar_borrow.kind, upvar_borrow.region); + cmt_ { id: id, span: span, - cat: cat_upvar(Upvar { - id: upvar_id, - kind: kind - }), - mutbl: var_mutbl, + cat: cat_deref(Rc::new(cmt_result), 0, ptr), + mutbl: MutabilityCategory::from_borrow_kind(upvar_borrow.kind), ty: var_ty, - note: NoteNone - }; - - match env_info { - Some((env_mutbl, env_ptr)) => { - // We need to add the env deref. This means - // that the above is actually immutable and - // has a ref type. However, nothing should - // actually look at the type, so we can get - // away with stuffing a `ty_err` in there - // instead of bothering to construct a proper - // one. - base.mutbl = McImmutable; - base.ty = self.tcx().types.err; - Rc::new(cmt_ { - id: id, - span: span, - cat: cat_deref(Rc::new(base), 0, env_ptr), - mutbl: env_mutbl, - ty: var_ty, - note: NoteClosureEnv(upvar_id) - }) - } - None => Rc::new(base) + note: NoteUpvarRef(upvar_id) } - }, - ast::CaptureByRef => { - // The type here is actually a ref (or ref of a ref), - // but we can again get away with not constructing one - // properly since it will never be used. - let mut base = cmt_ { - id: id, - span: span, - cat: cat_upvar(Upvar { - id: upvar_id, - kind: kind - }), - mutbl: McImmutable, - ty: self.tcx().types.err, - note: NoteNone - }; + } + }; - match env_info { - Some((env_mutbl, env_ptr)) => { - base = cmt_ { - id: id, - span: span, - cat: cat_deref(Rc::new(base), 0, env_ptr), - mutbl: env_mutbl, - ty: self.tcx().types.err, - note: NoteClosureEnv(upvar_id) - }; - } - None => {} - } + Ok(Rc::new(cmt_result)) + } - // Look up upvar borrow so we can get its region - let upvar_borrow = self.typer.upvar_borrow(upvar_id).unwrap(); - let ptr = BorrowedPtr(upvar_borrow.kind, upvar_borrow.region); + fn env_deref(&self, + id: ast::NodeId, + span: Span, + upvar_id: ty::UpvarId, + upvar_mutbl: MutabilityCategory, + env_borrow_kind: ty::BorrowKind, + cmt_result: cmt_<'tcx>) + -> cmt_<'tcx> + { + // Look up the node ID of the closure body so we can construct + // a free region within it + let fn_body_id = { + let fn_expr = match self.tcx().map.find(upvar_id.closure_expr_id) { + Some(ast_map::NodeExpr(e)) => e, + _ => unreachable!() + }; - Rc::new(cmt_ { - id: id, - span: span, - cat: cat_deref(Rc::new(base), 0, ptr), - mutbl: MutabilityCategory::from_borrow_kind(upvar_borrow.kind), - ty: var_ty, - note: NoteUpvarRef(upvar_id) - }) + match fn_expr.node { + ast::ExprClosure(_, _, _, ref body) => body.id, + _ => unreachable!() } - }) + }; + + // Region of environment pointer + let env_region = ty::ReFree(ty::FreeRegion { + scope: region::CodeExtent::from_node_id(fn_body_id), + bound_region: ty::BrEnv + }); + + let env_ptr = BorrowedPtr(env_borrow_kind, env_region); + + let var_ty = cmt_result.ty; + + // We need to add the env deref. This means + // that the above is actually immutable and + // has a ref type. However, nothing should + // actually look at the type, so we can get + // away with stuffing a `ty_err` in there + // instead of bothering to construct a proper + // one. + let cmt_result = cmt_ { + mutbl: McImmutable, + ty: self.tcx().types.err, + ..cmt_result + }; + + let mut deref_mutbl = MutabilityCategory::from_borrow_kind(env_borrow_kind); + + // Issue #18335. If variable is declared as immutable, override the + // mutability from the environment and substitute an `&T` anyway. + match upvar_mutbl { + McImmutable => { deref_mutbl = McImmutable; } + McDeclared | McInherited => { } + } + + cmt_ { + id: id, + span: span, + cat: cat_deref(Rc::new(cmt_result), 0, env_ptr), + mutbl: deref_mutbl, + ty: var_ty, + note: NoteClosureEnv(upvar_id) + } } pub fn cat_rvalue_node(&self, diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index 44615f19d94aa7b9e6afc4c312bfbe97c6584190..425acbae483fe4824245056986c1bb80794e93d7 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -777,7 +777,7 @@ pub struct ctxt<'tcx> { pub populated_external_traits: RefCell, /// Borrows - pub upvar_borrow_map: RefCell, + pub upvar_capture_map: RefCell, /// These two caches are used by const_eval when decoding external statics /// and variants that are found. @@ -803,9 +803,6 @@ pub struct ctxt<'tcx> { /// Maps any item's def-id to its stability index. pub stability: RefCell, - /// Maps closures to their capture clauses. - pub capture_modes: RefCell, - /// Maps def IDs to true if and only if they're associated types. pub associated_types: RefCell>, @@ -1247,60 +1244,31 @@ pub enum BorrowKind { MutBorrow } -/// Information describing the borrowing of an upvar. This is computed -/// during `typeck`, specifically by `regionck`. The general idea is -/// that the compiler analyses treat closures like: -/// -/// let closure: &'e fn() = || { -/// x = 1; // upvar x is assigned to -/// use(y); // upvar y is read -/// foo(&z); // upvar z is borrowed immutably -/// }; -/// -/// as if they were "desugared" to something loosely like: -/// -/// struct Vars<'x,'y,'z> { x: &'x mut int, -/// y: &'y const int, -/// z: &'z int } -/// let closure: &'e fn() = { -/// fn f(env: &Vars) { -/// *env.x = 1; -/// use(*env.y); -/// foo(env.z); -/// } -/// let env: &'e mut Vars<'x,'y,'z> = &mut Vars { x: &'x mut x, -/// y: &'y const y, -/// z: &'z z }; -/// (env, f) -/// }; -/// -/// This is basically what happens at runtime. The closure is basically -/// an existentially quantified version of the `(env, f)` pair. -/// -/// This data structure indicates the region and mutability of a single -/// one of the `x...z` borrows. -/// -/// It may not be obvious why each borrowed variable gets its own -/// lifetime (in the desugared version of the example, these are indicated -/// by the lifetime parameters `'x`, `'y`, and `'z` in the `Vars` definition). -/// Each such lifetime must encompass the lifetime `'e` of the closure itself, -/// but need not be identical to it. The reason that this makes sense: -/// -/// - Callers are only permitted to invoke the closure, and hence to -/// use the pointers, within the lifetime `'e`, so clearly `'e` must -/// be a sublifetime of `'x...'z`. -/// - The closure creator knows which upvars were borrowed by the closure -/// and thus `x...z` will be reserved for `'x...'z` respectively. -/// - Through mutation, the borrowed upvars can actually escape -/// the closure, so sometimes it is necessary for them to be larger -/// than the closure lifetime itself. +/// Information describing the capture of an upvar. This is computed +/// during `typeck`, specifically by `regionck`. +#[derive(PartialEq, Clone, RustcEncodable, RustcDecodable, Debug, Copy)] +pub enum UpvarCapture { + /// Upvar is captured by value. This is always true when the + /// closure is labeled `move`, but can also be true in other cases + /// depending on inference. + ByValue, + + /// Upvar is captured by reference. + ByRef(UpvarBorrow), +} + #[derive(PartialEq, Clone, RustcEncodable, RustcDecodable, Debug, Copy)] pub struct UpvarBorrow { + /// The kind of borrow: by-ref upvars have access to shared + /// immutable borrows, which are not part of the normal language + /// syntax. pub kind: BorrowKind, + + /// Region of the resulting reference. pub region: ty::Region, } -pub type UpvarBorrowMap = FnvHashMap; +pub type UpvarCaptureMap = FnvHashMap; impl Region { pub fn is_bound(&self) -> bool { @@ -1320,7 +1288,7 @@ pub fn escapes_depth(&self, depth: u32) -> bool { } #[derive(Clone, PartialEq, PartialOrd, Eq, Ord, Hash, - RustcEncodable, RustcDecodable, Debug, Copy)] + RustcEncodable, RustcDecodable, Debug, Copy)] /// A "free" region `fr` can be interpreted as "some region /// at least as big as the scope `fr.scope`". pub struct FreeRegion { @@ -1329,7 +1297,7 @@ pub struct FreeRegion { } #[derive(Clone, PartialEq, PartialOrd, Eq, Ord, Hash, - RustcEncodable, RustcDecodable, Debug, Copy)] + RustcEncodable, RustcDecodable, Debug, Copy)] pub enum BoundRegion { /// An anonymous region parameter for a given fn (&T) BrAnon(u32), @@ -2253,6 +2221,12 @@ pub struct TypeScheme<'tcx> { pub struct TraitDef<'tcx> { pub unsafety: ast::Unsafety, + /// If `true`, then this trait had the `#[rustc_paren_sugar]` + /// attribute, indicating that it should be used with `Foo()` + /// sugar. This is a temporary thing -- eventually any trait wil + /// be usable with the sugar (or without it). + pub paren_sugar: bool, + /// Generic type definitions. Note that `Self` is listed in here /// as having a single bound, the trait itself (e.g., in the trait /// `Eq`, there is a single bound `Self : Eq`). This is so that @@ -2359,7 +2333,6 @@ pub fn mk_ctxt<'tcx>(s: Session, named_region_map: resolve_lifetime::NamedRegionMap, map: ast_map::Map<'tcx>, freevars: RefCell, - capture_modes: RefCell, region_maps: middle::region::RegionMaps, lang_items: middle::lang_items::LanguageItems, stability: stability::Index) -> ctxt<'tcx> @@ -2413,7 +2386,7 @@ pub fn mk_ctxt<'tcx>(s: Session, used_mut_nodes: RefCell::new(NodeSet()), populated_external_types: RefCell::new(DefIdSet()), populated_external_traits: RefCell::new(DefIdSet()), - upvar_borrow_map: RefCell::new(FnvHashMap()), + upvar_capture_map: RefCell::new(FnvHashMap()), extern_const_statics: RefCell::new(DefIdMap()), extern_const_variants: RefCell::new(DefIdMap()), method_map: RefCell::new(FnvHashMap()), @@ -2422,7 +2395,6 @@ pub fn mk_ctxt<'tcx>(s: Session, node_lint_levels: RefCell::new(FnvHashMap()), transmute_restrictions: RefCell::new(Vec::new()), stability: RefCell::new(stability), - capture_modes: capture_modes, associated_types: RefCell::new(DefIdMap()), selection_cache: traits::SelectionCache::new(), repr_hint_cache: RefCell::new(DefIdMap()), @@ -5646,7 +5618,6 @@ pub fn closure_upvars<'tcx>(typer: &mc::Typer<'tcx>, // implemented. assert!(closure_id.krate == ast::LOCAL_CRATE); let tcx = typer.tcx(); - let capture_mode = tcx.capture_modes.borrow()[closure_id.node].clone(); match tcx.freevars.borrow().get(&closure_id.node) { None => Some(vec![]), Some(ref freevars) => { @@ -5659,43 +5630,38 @@ pub fn closure_upvars<'tcx>(typer: &mc::Typer<'tcx>, }; let freevar_ty = freevar_ty.subst(tcx, substs); - match capture_mode { - ast::CaptureByValue => { - Some(ClosureUpvar { def: freevar.def, - span: freevar.span, - ty: freevar_ty }) + let upvar_id = ty::UpvarId { + var_id: freevar_def_id.node, + closure_expr_id: closure_id.node + }; + + let captured_freevar_ty = match typer.upvar_capture(upvar_id) { + Some(UpvarCapture::ByValue) => { + freevar_ty } - ast::CaptureByRef => { - let upvar_id = ty::UpvarId { - var_id: freevar_def_id.node, - closure_expr_id: closure_id.node - }; + Some(UpvarCapture::ByRef(borrow)) => { + mk_rptr(tcx, + tcx.mk_region(borrow.region), + ty::mt { + ty: freevar_ty, + mutbl: borrow.kind.to_mutbl_lossy(), + }) + } - // FIXME - let freevar_ref_ty = match typer.upvar_borrow(upvar_id) { - Some(borrow) => { - mk_rptr(tcx, - tcx.mk_region(borrow.region), - ty::mt { - ty: freevar_ty, - mutbl: borrow.kind.to_mutbl_lossy(), - }) - } - None => { - // FIXME(#16640) we should really return None here; - // but that requires better inference integration, - // for now gin up something. - freevar_ty - } - }; - Some(ClosureUpvar { - def: freevar.def, - span: freevar.span, - ty: freevar_ref_ty, - }) + None => { + // FIXME(#16640) we should really return None here; + // but that requires better inference integration, + // for now gin up something. + freevar_ty } - } + }; + + Some(ClosureUpvar { + def: freevar.def, + span: freevar.span, + ty: captured_freevar_ty, + }) }) .collect() } @@ -6449,14 +6415,13 @@ pub fn to_user_str(&self) -> &'static str { } impl<'tcx> ctxt<'tcx> { - pub fn capture_mode(&self, closure_expr_id: ast::NodeId) - -> ast::CaptureClause { - self.capture_modes.borrow()[closure_expr_id].clone() - } - pub fn is_method_call(&self, expr_id: ast::NodeId) -> bool { self.method_map.borrow().contains_key(&MethodCall::expr(expr_id)) } + + pub fn upvar_capture(&self, upvar_id: ty::UpvarId) -> Option { + Some(self.upvar_capture_map.borrow()[upvar_id].clone()) + } } impl<'a,'tcx> mc::Typer<'tcx> for ParameterEnvironment<'a,'tcx> { @@ -6494,13 +6459,8 @@ 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) -> Option { - Some(self.tcx.upvar_borrow_map.borrow()[upvar_id].clone()) - } - - fn capture_mode(&self, closure_expr_id: ast::NodeId) - -> ast::CaptureClause { - self.tcx.capture_mode(closure_expr_id) + fn upvar_capture(&self, upvar_id: ty::UpvarId) -> Option { + self.tcx.upvar_capture(upvar_id) } fn type_moves_by_default(&self, span: Span, ty: Ty<'tcx>) -> bool { diff --git a/src/librustc/util/ppaux.rs b/src/librustc/util/ppaux.rs index 1be99a8e569f3f55bb8b536b19b790c0998eee01..15a91fd0f8da08cb159f6a333588bf3e1c6f18fc 100644 --- a/src/librustc/util/ppaux.rs +++ b/src/librustc/util/ppaux.rs @@ -1292,6 +1292,15 @@ fn repr(&self, tcx: &ctxt) -> String { } } +impl<'tcx> Repr<'tcx> for ty::UpvarCapture { + fn repr(&self, tcx: &ctxt) -> String { + match *self { + ty::UpvarCapture::ByValue => format!("ByValue"), + ty::UpvarCapture::ByRef(ref data) => format!("ByRef({})", data.repr(tcx)), + } + } +} + impl<'tcx> Repr<'tcx> for ty::IntVid { fn repr(&self, _tcx: &ctxt) -> String { format!("{:?}", self) diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs index 6e76519ce23607fdbaf1b6567febd5f45b2e44c0..fc64cf9470ad7812136e592c5bc4e2e45a3a385f 100644 --- a/src/librustc_driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -560,7 +560,6 @@ pub fn phase_3_run_analysis_passes<'tcx>(sess: Session, let resolve::CrateMap { def_map, freevars, - capture_mode_map, export_map, trait_map, external_exports, @@ -606,7 +605,6 @@ pub fn phase_3_run_analysis_passes<'tcx>(sess: Session, named_region_map, ast_map, freevars, - capture_mode_map, region_map, lang_items, stability_index); diff --git a/src/librustc_driver/test.rs b/src/librustc_driver/test.rs index 52d7415a523df3467ea215b1a13f7a88327325b0..45ff1c4537cd5fdffe32e1de005c02a4ed94c4a7 100644 --- a/src/librustc_driver/test.rs +++ b/src/librustc_driver/test.rs @@ -121,7 +121,7 @@ fn test_env(source_string: &str, // run just enough stuff to build a tcx: let lang_items = lang_items::collect_language_items(krate, &sess); - let resolve::CrateMap { def_map, freevars, capture_mode_map, .. } = + let resolve::CrateMap { def_map, freevars, .. } = resolve::resolve_crate(&sess, &ast_map, &lang_items, krate, resolve::MakeGlobMap::No); let named_region_map = resolve_lifetime::krate(&sess, krate, &def_map); let region_map = region::resolve_crate(&sess, krate); @@ -132,7 +132,6 @@ fn test_env(source_string: &str, named_region_map, ast_map, freevars, - capture_mode_map, region_map, lang_items, stability_index); diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs index 000426771a81af14a6b78c2336d78d1d0697fe56..c099762036a969813e62d0620d29cff86d6812bd 100644 --- a/src/librustc_resolve/lib.rs +++ b/src/librustc_resolve/lib.rs @@ -62,7 +62,7 @@ use rustc::middle::pat_util::pat_bindings; use rustc::middle::privacy::*; use rustc::middle::subst::{ParamSpace, FnSpace, TypeSpace}; -use rustc::middle::ty::{CaptureModeMap, Freevar, FreevarMap, TraitMap, GlobMap}; +use rustc::middle::ty::{Freevar, FreevarMap, TraitMap, GlobMap}; use rustc::util::nodemap::{NodeMap, NodeSet, DefIdSet, FnvHashMap}; use rustc::util::lev_distance::lev_distance; @@ -900,7 +900,6 @@ struct Resolver<'a, 'tcx:'a> { def_map: DefMap, freevars: RefCell, freevars_seen: RefCell>, - capture_mode_map: CaptureModeMap, export_map: ExportMap, trait_map: TraitMap, external_exports: ExternalExports, @@ -974,7 +973,6 @@ fn new(session: &'a Session, def_map: RefCell::new(NodeMap()), freevars: RefCell::new(NodeMap()), freevars_seen: RefCell::new(NodeMap()), - capture_mode_map: NodeMap(), export_map: NodeMap(), trait_map: NodeMap(), used_imports: HashSet::new(), @@ -4523,8 +4521,7 @@ fn resolve_expr(&mut self, expr: &Expr) { visit::walk_expr(self, expr); } - ExprClosure(capture_clause, _, ref fn_decl, ref block) => { - self.capture_mode_map.insert(expr.id, capture_clause); + ExprClosure(_, _, ref fn_decl, ref block) => { self.resolve_function(ClosureRibKind(expr.id), Some(&**fn_decl), NoTypeParameters, &**block); @@ -4835,7 +4832,6 @@ fn dump_module(&mut self, module_: Rc) { pub struct CrateMap { pub def_map: DefMap, pub freevars: RefCell, - pub capture_mode_map: RefCell, pub export_map: ExportMap, pub trait_map: TraitMap, pub external_exports: ExternalExports, @@ -4875,7 +4871,6 @@ pub fn resolve_crate<'a, 'tcx>(session: &'a Session, CrateMap { def_map: resolver.def_map, freevars: resolver.freevars, - capture_mode_map: RefCell::new(resolver.capture_mode_map), export_map: resolver.export_map, trait_map: resolver.trait_map, external_exports: resolver.external_exports, diff --git a/src/librustc_trans/trans/base.rs b/src/librustc_trans/trans/base.rs index 1195b9f084b0f37595f7134fd5afab867ab80d49..7b4acfac4b3b71396a8f622b8c5ba84848f45c07 100644 --- a/src/librustc_trans/trans/base.rs +++ b/src/librustc_trans/trans/base.rs @@ -1775,7 +1775,7 @@ pub fn trans_closure<'a, 'b, 'tcx>(ccx: &CrateContext<'a, 'tcx>, _attributes: &[ast::Attribute], output_type: ty::FnOutput<'tcx>, abi: Abi, - closure_env: closure::ClosureEnv<'b, 'tcx>) { + closure_env: closure::ClosureEnv<'b>) { ccx.stats().n_closures.set(ccx.stats().n_closures.get() + 1); let _icx = push_ctxt("trans_closure"); @@ -1784,12 +1784,17 @@ pub fn trans_closure<'a, 'b, 'tcx>(ccx: &CrateContext<'a, 'tcx>, debug!("trans_closure(..., param_substs={})", param_substs.repr(ccx.tcx())); + let has_env = match closure_env { + closure::ClosureEnv::Closure(_) => true, + closure::ClosureEnv::NotClosure => false, + }; + let (arena, fcx): (TypedArena<_>, FunctionContext); arena = TypedArena::new(); fcx = new_fn_ctxt(ccx, llfndecl, fn_ast_id, - closure_env.kind != closure::NotClosure, + has_env, output_type, param_substs, Some(body.span), @@ -1808,13 +1813,13 @@ pub fn trans_closure<'a, 'b, 'tcx>(ccx: &CrateContext<'a, 'tcx>, decl.inputs.iter() .map(|arg| node_id_type(bcx, arg.id)) .collect::>(); - let monomorphized_arg_types = match closure_env.kind { - closure::NotClosure => { + let monomorphized_arg_types = match closure_env { + closure::ClosureEnv::NotClosure => { monomorphized_arg_types } // Tuple up closure argument types for the "rust-call" ABI. - closure::Closure(..) => { + closure::ClosureEnv::Closure(_) => { vec![ty::mk_tup(ccx.tcx(), monomorphized_arg_types)] } }; @@ -1835,14 +1840,14 @@ pub fn trans_closure<'a, 'b, 'tcx>(ccx: &CrateContext<'a, 'tcx>, &monomorphized_arg_types[]) }; - bcx = match closure_env.kind { - closure::NotClosure => { + bcx = match closure_env { + closure::ClosureEnv::NotClosure => { copy_args_to_allocas(bcx, arg_scope, &decl.inputs[], arg_datums) } - closure::Closure(..) => { + closure::ClosureEnv::Closure(_) => { copy_closure_args_to_allocas( bcx, arg_scope, @@ -1932,7 +1937,7 @@ pub fn trans_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, attrs, output_type, abi, - closure::ClosureEnv::new(&[], closure::NotClosure)); + closure::ClosureEnv::NotClosure); } pub fn trans_enum_variant<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, diff --git a/src/librustc_trans/trans/closure.rs b/src/librustc_trans/trans/closure.rs index c1914ce94aa629a03569b491663922c9e8b8f422..a15ede095a709be359a6e271f70af896bcbf05e8 100644 --- a/src/librustc_trans/trans/closure.rs +++ b/src/librustc_trans/trans/closure.rs @@ -8,8 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -pub use self::ClosureKind::*; - use back::link::mangle_internal_name_by_path_and_seq; use middle::mem_categorization::Typer; use trans::adt; @@ -33,9 +31,9 @@ fn load_closure_environment<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, arg_scope_id: ScopeId, - freevar_mode: ast::CaptureClause, freevars: &[ty::Freevar]) - -> Block<'blk, 'tcx> { + -> Block<'blk, 'tcx> +{ let _icx = push_ctxt("closure::load_closure_environment"); // Special case for small by-value selfs. @@ -65,18 +63,21 @@ fn load_closure_environment<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, }; for (i, freevar) in freevars.iter().enumerate() { + let upvar_id = ty::UpvarId { var_id: freevar.def.local_node_id(), + closure_expr_id: closure_id.node }; + let upvar_capture = bcx.tcx().upvar_capture(upvar_id).unwrap(); let mut upvar_ptr = GEPi(bcx, llenv, &[0, i]); - let captured_by_ref = match freevar_mode { - ast::CaptureByRef => { + let captured_by_ref = match upvar_capture { + ty::UpvarCapture::ByValue => false, + ty::UpvarCapture::ByRef(..) => { upvar_ptr = Load(bcx, upvar_ptr); true } - ast::CaptureByValue => false }; let def_id = freevar.def.def_id(); bcx.fcx.llupvars.borrow_mut().insert(def_id.node, upvar_ptr); - if kind == ty::FnOnceClosureKind && freevar_mode == ast::CaptureByValue { + if kind == ty::FnOnceClosureKind && !captured_by_ref { bcx.fcx.schedule_drop_mem(arg_scope_id, upvar_ptr, node_id_type(bcx, def_id.node)) @@ -96,38 +97,23 @@ fn load_closure_environment<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, bcx } -#[derive(PartialEq)] -pub enum ClosureKind<'tcx> { +pub enum ClosureEnv<'a> { NotClosure, - // See load_closure_environment. - Closure(ast::CaptureClause) + Closure(&'a [ty::Freevar]), } -pub struct ClosureEnv<'a, 'tcx> { - freevars: &'a [ty::Freevar], - pub kind: ClosureKind<'tcx> -} - -impl<'a, 'tcx> ClosureEnv<'a, 'tcx> { - pub fn new(freevars: &'a [ty::Freevar], kind: ClosureKind<'tcx>) - -> ClosureEnv<'a, 'tcx> { - ClosureEnv { - freevars: freevars, - kind: kind - } - } - - pub fn load<'blk>(self, bcx: Block<'blk, 'tcx>, arg_scope: ScopeId) - -> Block<'blk, 'tcx> { - // Don't bother to create the block if there's nothing to load - if self.freevars.is_empty() { - return bcx; - } - - match self.kind { - NotClosure => bcx, - Closure(freevar_mode) => { - load_closure_environment(bcx, arg_scope, freevar_mode, self.freevars) +impl<'a> ClosureEnv<'a> { + pub fn load<'blk,'tcx>(self, bcx: Block<'blk, 'tcx>, arg_scope: ScopeId) + -> Block<'blk, 'tcx> + { + match self { + ClosureEnv::NotClosure => bcx, + ClosureEnv::Closure(freevars) => { + if freevars.is_empty() { + bcx + } else { + load_closure_environment(bcx, arg_scope, freevars) + } } } } @@ -212,7 +198,6 @@ pub fn trans_closure_expr<'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>, let freevars: Vec = ty::with_freevars(bcx.tcx(), id, |fv| fv.iter().map(|&fv| fv).collect()); - let freevar_mode = bcx.tcx().capture_mode(id); let sig = ty::erase_late_bound_regions(bcx.tcx(), &function_type.sig); @@ -225,8 +210,7 @@ pub fn trans_closure_expr<'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>, &[], sig.output, function_type.abi, - ClosureEnv::new(&freevars[], - Closure(freevar_mode))); + ClosureEnv::Closure(&freevars[])); // Don't hoist this to the top of the function. It's perfectly legitimate // to have a zero-size closure (in which case dest will be `Ignore`) and @@ -249,11 +233,14 @@ pub fn trans_closure_expr<'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>, dest_addr, 0, i); - match freevar_mode { - ast::CaptureByValue => { + let upvar_id = ty::UpvarId { var_id: freevar.def.local_node_id(), + closure_expr_id: id }; + let upvar_capture = bcx.tcx().upvar_capture(upvar_id).unwrap(); + match upvar_capture { + ty::UpvarCapture::ByValue => { bcx = datum.store_to(bcx, upvar_slot_dest); } - ast::CaptureByRef => { + ty::UpvarCapture::ByRef(..) => { Store(bcx, datum.to_llref(), upvar_slot_dest); } } diff --git a/src/librustc_trans/trans/common.rs b/src/librustc_trans/trans/common.rs index 7d4e6aed876a547d6cfb760c39c03d51036907b6..a0dbc9c40a68d535473c744552c58136e04970aa 100644 --- a/src/librustc_trans/trans/common.rs +++ b/src/librustc_trans/trans/common.rs @@ -679,13 +679,8 @@ 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) -> Option { - Some(self.tcx().upvar_borrow_map.borrow()[upvar_id].clone()) - } - - fn capture_mode(&self, closure_expr_id: ast::NodeId) - -> ast::CaptureClause { - self.tcx().capture_modes.borrow()[closure_expr_id].clone() + fn upvar_capture(&self, upvar_id: ty::UpvarId) -> Option { + Some(self.tcx().upvar_capture_map.borrow()[upvar_id].clone()) } fn type_moves_by_default(&self, span: Span, ty: Ty<'tcx>) -> bool { diff --git a/src/librustc_typeck/astconv.rs b/src/librustc_typeck/astconv.rs index a382cfca0bd899e73b25c1e185dfb984e78dc774..350227c6662433757bbede36c59a63a9c0a06d24 100644 --- a/src/librustc_typeck/astconv.rs +++ b/src/librustc_typeck/astconv.rs @@ -614,11 +614,9 @@ fn ast_path_to_trait_ref<'a,'tcx>( let (regions, types, assoc_bindings) = match path.segments.last().unwrap().parameters { ast::AngleBracketedParameters(ref data) => { - // For now, require that parenthetical notation be used + // For now, require that parenthetical5D notation be used // only with `Fn()` etc. - if !this.tcx().sess.features.borrow().unboxed_closures && - this.tcx().lang_items.fn_trait_kind(trait_def_id).is_some() - { + if !this.tcx().sess.features.borrow().unboxed_closures && trait_def.paren_sugar { span_err!(this.tcx().sess, path.span, E0215, "angle-bracket notation is not stable when \ used with the `Fn` family of traits, use parentheses"); @@ -632,9 +630,7 @@ fn ast_path_to_trait_ref<'a,'tcx>( ast::ParenthesizedParameters(ref data) => { // For now, require that parenthetical notation be used // only with `Fn()` etc. - if !this.tcx().sess.features.borrow().unboxed_closures && - this.tcx().lang_items.fn_trait_kind(trait_def_id).is_none() - { + if !this.tcx().sess.features.borrow().unboxed_closures && !trait_def.paren_sugar { span_err!(this.tcx().sess, path.span, E0216, "parenthetical notation is only stable when \ used with the `Fn` family of traits"); diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index d78b819065a99ddf8aec9801fedd5d269db84611..640c8d8d4bed992d44cf4c057db9d84abfd18f93 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -160,7 +160,7 @@ pub struct Inherited<'a, 'tcx: 'a> { item_substs: RefCell>>, adjustments: RefCell>>, method_map: MethodMap<'tcx>, - upvar_borrow_map: RefCell, + upvar_capture_map: RefCell, closures: RefCell>>, object_cast_map: ObjectCastMap<'tcx>, @@ -305,7 +305,7 @@ fn expr_ty_adjusted(&self, expr: &ast::Expr) -> McResult> { } fn type_moves_by_default(&self, span: Span, ty: Ty<'tcx>) -> bool { let ty = self.infcx().resolve_type_vars_if_possible(&ty); - traits::type_known_to_meet_builtin_bound(self.infcx(), self, ty, ty::BoundCopy, span) + !traits::type_known_to_meet_builtin_bound(self.infcx(), self, ty, ty::BoundCopy, span) } fn node_method_ty(&self, method_call: ty::MethodCall) -> Option> { @@ -330,12 +330,8 @@ fn is_method_call(&self, id: ast::NodeId) -> bool { fn temporary_scope(&self, rvalue_id: ast::NodeId) -> Option { self.param_env().temporary_scope(rvalue_id) } - fn upvar_borrow(&self, upvar_id: ty::UpvarId) -> Option { - self.inh.upvar_borrow_map.borrow().get(&upvar_id).cloned() - } - fn capture_mode(&self, closure_expr_id: ast::NodeId) - -> ast::CaptureClause { - self.ccx.tcx.capture_mode(closure_expr_id) + fn upvar_capture(&self, upvar_id: ty::UpvarId) -> Option { + self.inh.upvar_capture_map.borrow().get(&upvar_id).cloned() } } @@ -378,7 +374,7 @@ fn new(tcx: &'a ty::ctxt<'tcx>, adjustments: RefCell::new(NodeMap()), method_map: RefCell::new(FnvHashMap()), object_cast_map: RefCell::new(NodeMap()), - upvar_borrow_map: RefCell::new(FnvHashMap()), + upvar_capture_map: RefCell::new(FnvHashMap()), closures: RefCell::new(DefIdMap()), fn_sig_map: RefCell::new(NodeMap()), fulfillment_cx: RefCell::new(traits::FulfillmentContext::new()), diff --git a/src/librustc_typeck/check/regionck.rs b/src/librustc_typeck/check/regionck.rs index 15602505d90d4e4bf3e67553adfa2e40a67ef790..d1fee578218280230fad008bbd56eabb96ff9f0e 100644 --- a/src/librustc_typeck/check/regionck.rs +++ b/src/librustc_typeck/check/regionck.rs @@ -742,16 +742,9 @@ fn check_expr_fn_block(rcx: &mut Rcx, match function_type.sty { ty::ty_closure(_, region, _) => { - if tcx.capture_modes.borrow()[expr.id].clone() == ast::CaptureByRef { - ty::with_freevars(tcx, expr.id, |freevars| { - if !freevars.is_empty() { - // Variables being referenced must be constrained and registered - // in the upvar borrow map - constrain_free_variables_in_by_ref_closure( - rcx, *region, expr, freevars); - } - }) - } + ty::with_freevars(tcx, expr.id, |freevars| { + constrain_captured_variables(rcx, *region, expr, freevars); + }) } _ => { } } @@ -799,14 +792,14 @@ fn ensure_free_variable_types_outlive_closure_bound( let raw_var_ty = rcx.resolve_node_type(var_node_id); let upvar_id = ty::UpvarId { var_id: var_node_id, closure_expr_id: expr.id }; - let var_ty = match rcx.fcx.inh.upvar_borrow_map.borrow().get(&upvar_id) { - Some(upvar_borrow) => { + let var_ty = match rcx.fcx.inh.upvar_capture_map.borrow()[upvar_id] { + ty::UpvarCapture::ByRef(ref upvar_borrow) => { ty::mk_rptr(rcx.tcx(), rcx.tcx().mk_region(upvar_borrow.region), ty::mt { mutbl: upvar_borrow.kind.to_mutbl_lossy(), ty: raw_var_ty }) } - None => raw_var_ty + ty::UpvarCapture::ByValue => raw_var_ty, }; // Check that the type meets the criteria of the existential bounds: @@ -824,17 +817,17 @@ fn ensure_free_variable_types_outlive_closure_bound( /// Make sure that all free variables referenced inside the closure outlive the closure's /// lifetime bound. Also, create an entry in the upvar_borrows map with a region. - fn constrain_free_variables_in_by_ref_closure( + fn constrain_captured_variables( rcx: &mut Rcx, region_bound: ty::Region, expr: &ast::Expr, freevars: &[ty::Freevar]) { let tcx = rcx.fcx.ccx.tcx; - debug!("constrain_free_variables({}, {})", + debug!("constrain_captured_variables({}, {})", region_bound.repr(tcx), expr.repr(tcx)); for freevar in freevars.iter() { - debug!("freevar def is {:?}", freevar.def); + debug!("constrain_captured_variables: freevar.def={:?}", freevar.def); // Identify the variable being closed over and its node-id. let def = freevar.def; @@ -846,16 +839,20 @@ fn constrain_free_variables_in_by_ref_closure( let upvar_id = ty::UpvarId { var_id: var_node_id, closure_expr_id: expr.id }; - let upvar_borrow = rcx.fcx.inh.upvar_borrow_map.borrow()[upvar_id]; - - rcx.fcx.mk_subr(infer::FreeVariable(freevar.span, var_node_id), - region_bound, upvar_borrow.region); - - // Guarantee that the closure does not outlive the variable itself. - let enclosing_region = region_of_def(rcx.fcx, def); - debug!("enclosing_region = {}", enclosing_region.repr(tcx)); - rcx.fcx.mk_subr(infer::FreeVariable(freevar.span, var_node_id), - region_bound, enclosing_region); + match rcx.fcx.inh.upvar_capture_map.borrow()[upvar_id] { + ty::UpvarCapture::ByValue => { } + ty::UpvarCapture::ByRef(upvar_borrow) => { + rcx.fcx.mk_subr(infer::FreeVariable(freevar.span, var_node_id), + region_bound, upvar_borrow.region); + + // Guarantee that the closure does not outlive the variable itself. + let enclosing_region = region_of_def(rcx.fcx, def); + debug!("constrain_captured_variables: enclosing_region = {}", + enclosing_region.repr(tcx)); + rcx.fcx.mk_subr(infer::FreeVariable(freevar.span, var_node_id), + region_bound, enclosing_region); + } + } } } } @@ -1333,22 +1330,20 @@ fn link_reborrowed_region<'a, 'tcx>(rcx: &Rcx<'a, 'tcx>, // Detect by-ref upvar `x`: let cause = match note { mc::NoteUpvarRef(ref upvar_id) => { - let mut upvar_borrow_map = - rcx.fcx.inh.upvar_borrow_map.borrow_mut(); - match upvar_borrow_map.get_mut(upvar_id) { - Some(upvar_borrow) => { + let upvar_capture_map = rcx.fcx.inh.upvar_capture_map.borrow_mut(); + match upvar_capture_map.get(upvar_id) { + Some(&ty::UpvarCapture::ByRef(ref upvar_borrow)) => { // The mutability of the upvar may have been modified // by the above adjustment, so update our local variable. ref_kind = upvar_borrow.kind; infer::ReborrowUpvar(span, *upvar_id) } - None => { + _ => { rcx.tcx().sess.span_bug( span, &format!("Illegal upvar id: {}", - upvar_id.repr( - rcx.tcx()))[]); + upvar_id.repr(rcx.tcx()))[]); } } } diff --git a/src/librustc_typeck/check/upvar.rs b/src/librustc_typeck/check/upvar.rs index 7fc0a83e34ac591765e935a6928b33fe4014de88..449220b1c85285c4b56fc79a76a0b65d60484b3a 100644 --- a/src/librustc_typeck/check/upvar.rs +++ b/src/librustc_typeck/check/upvar.rs @@ -121,25 +121,29 @@ fn check_closure(&mut self, capture_clause: ast::CaptureClause, _body: &ast::Block) { - match capture_clause { - ast::CaptureByValue => {} - _ => { - ty::with_freevars(self.tcx(), expr.id, |freevars| { - for freevar in freevars.iter() { - let var_node_id = freevar.def.local_node_id(); - let upvar_id = ty::UpvarId { var_id: var_node_id, - closure_expr_id: expr.id }; - debug!("seed upvar_id {:?}", upvar_id); + ty::with_freevars(self.tcx(), expr.id, |freevars| { + for freevar in freevars.iter() { + let var_node_id = freevar.def.local_node_id(); + let upvar_id = ty::UpvarId { var_id: var_node_id, + closure_expr_id: expr.id }; + debug!("seed upvar_id {:?}", upvar_id); + + let capture_kind = match capture_clause { + ast::CaptureByValue => { + ty::UpvarCapture::ByValue + } + ast::CaptureByRef => { let origin = UpvarRegion(upvar_id, expr.span); let freevar_region = self.infcx().next_region_var(origin); let upvar_borrow = ty::UpvarBorrow { kind: ty::ImmBorrow, region: freevar_region }; - self.fcx.inh.upvar_borrow_map.borrow_mut().insert(upvar_id, - upvar_borrow); + ty::UpvarCapture::ByRef(upvar_borrow) } - }); + }; + + self.fcx.inh.upvar_capture_map.borrow_mut().insert(upvar_id, capture_kind); } - } + }); } } @@ -150,7 +154,7 @@ struct AdjustBorrowKind<'a,'tcx:'a> { fcx: &'a FnCtxt<'a,'tcx> } -impl<'a,'tcx> AdjustBorrowKind<'a,'tcx>{ +impl<'a,'tcx> AdjustBorrowKind<'a,'tcx> { fn new(fcx: &'a FnCtxt<'a,'tcx>) -> AdjustBorrowKind<'a,'tcx> { AdjustBorrowKind { fcx: fcx } } @@ -172,6 +176,41 @@ fn analyze_fn(&mut self, decl: &ast::FnDecl, body: &ast::Block) { euv.walk_fn(decl, body); } + fn adjust_upvar_borrow_kind_for_consume(&self, + cmt: mc::cmt<'tcx>, + mode: euv::ConsumeMode) + { + debug!("adjust_upvar_borrow_kind_for_consume(cmt={}, mode={:?})", + cmt.repr(self.tcx()), mode); + + // we only care about moves + match mode { + euv::Copy => { return; } + euv::Move(_) => { } + } + + // watch out for a move of the deref of a borrowed pointer; + // for that to be legal, the upvar would have to be borrowed + // by value instead + let guarantor = cmt.guarantor(); + debug!("adjust_upvar_borrow_kind_for_consume: guarantor={}", + guarantor.repr(self.tcx())); + match guarantor.cat { + mc::cat_deref(_, _, mc::BorrowedPtr(..)) | + mc::cat_deref(_, _, mc::Implicit(..)) => { + if let mc::NoteUpvarRef(upvar_id) = cmt.note { + debug!("adjust_upvar_borrow_kind_for_consume: \ + setting upvar_id={:?} to by value", + upvar_id); + + let mut upvar_capture_map = self.fcx.inh.upvar_capture_map.borrow_mut(); + upvar_capture_map.insert(upvar_id, ty::UpvarCapture::ByValue); + } + } + _ => { } + } + } + /// Indicates that `cmt` is being directly mutated (e.g., assigned /// to). If cmt contains any by-ref upvars, this implies that /// those upvars must be borrowed using an `&mut` borow. @@ -195,8 +234,8 @@ fn adjust_upvar_borrow_kind_for_mut(&mut self, cmt: mc::cmt<'tcx>) { // upvar, then we need to modify the // borrow_kind of the upvar to make sure it // is inferred to mutable if necessary - let mut upvar_borrow_map = self.fcx.inh.upvar_borrow_map.borrow_mut(); - let ub = &mut upvar_borrow_map[upvar_id]; + let mut upvar_capture_map = self.fcx.inh.upvar_capture_map.borrow_mut(); + let ub = &mut upvar_capture_map[upvar_id]; self.adjust_upvar_borrow_kind(upvar_id, ub, ty::MutBorrow); } else { // assignment to deref of an `&mut` @@ -237,7 +276,7 @@ fn adjust_upvar_borrow_kind_for_unique(&self, cmt: mc::cmt<'tcx>) { // upvar, then we need to modify the // borrow_kind of the upvar to make sure it // is inferred to unique if necessary - let mut ub = self.fcx.inh.upvar_borrow_map.borrow_mut(); + let mut ub = self.fcx.inh.upvar_capture_map.borrow_mut(); let ub = &mut ub[upvar_id]; self.adjust_upvar_borrow_kind(upvar_id, ub, ty::UniqueImmBorrow); } else { @@ -262,23 +301,30 @@ fn adjust_upvar_borrow_kind_for_unique(&self, cmt: mc::cmt<'tcx>) { /// some particular use. fn adjust_upvar_borrow_kind(&self, upvar_id: ty::UpvarId, - upvar_borrow: &mut ty::UpvarBorrow, + upvar_capture: &mut ty::UpvarCapture, kind: ty::BorrowKind) { - debug!("adjust_upvar_borrow_kind: id={:?} kind=({:?} -> {:?})", - upvar_id, upvar_borrow.kind, kind); - - match (upvar_borrow.kind, kind) { - // Take RHS: - (ty::ImmBorrow, ty::UniqueImmBorrow) | - (ty::ImmBorrow, ty::MutBorrow) | - (ty::UniqueImmBorrow, ty::MutBorrow) => { - upvar_borrow.kind = kind; + debug!("adjust_upvar_borrow_kind(upvar_id={:?}, upvar_capture={:?}, kind={:?})", + upvar_id, upvar_capture, kind); + + match *upvar_capture { + ty::UpvarCapture::ByValue => { + // Upvar is already by-value, the strongest criteria. } - // Take LHS: - (ty::ImmBorrow, ty::ImmBorrow) | - (ty::UniqueImmBorrow, ty::ImmBorrow) | - (ty::UniqueImmBorrow, ty::UniqueImmBorrow) | - (ty::MutBorrow, _) => { + ty::UpvarCapture::ByRef(ref mut upvar_borrow) => { + match (upvar_borrow.kind, kind) { + // Take RHS: + (ty::ImmBorrow, ty::UniqueImmBorrow) | + (ty::ImmBorrow, ty::MutBorrow) | + (ty::UniqueImmBorrow, ty::MutBorrow) => { + upvar_borrow.kind = kind; + } + // Take LHS: + (ty::ImmBorrow, ty::ImmBorrow) | + (ty::UniqueImmBorrow, ty::ImmBorrow) | + (ty::UniqueImmBorrow, ty::UniqueImmBorrow) | + (ty::MutBorrow, _) => { + } + } } } } @@ -308,9 +354,12 @@ impl<'a,'tcx> euv::Delegate<'tcx> for AdjustBorrowKind<'a,'tcx> { fn consume(&mut self, _consume_id: ast::NodeId, _consume_span: Span, - _cmt: mc::cmt<'tcx>, - _mode: euv::ConsumeMode) - {} + cmt: mc::cmt<'tcx>, + mode: euv::ConsumeMode) + { + debug!("consume(cmt={},mode={:?})", cmt.repr(self.tcx()), mode); + self.adjust_upvar_borrow_kind_for_consume(cmt, mode); + } fn matched_pat(&mut self, _matched_pat: &ast::Pat, @@ -320,9 +369,12 @@ fn matched_pat(&mut self, fn consume_pat(&mut self, _consume_pat: &ast::Pat, - _cmt: mc::cmt<'tcx>, - _mode: euv::ConsumeMode) - {} + cmt: mc::cmt<'tcx>, + mode: euv::ConsumeMode) + { + debug!("consume_pat(cmt={},mode={:?})", cmt.repr(self.tcx()), mode); + self.adjust_upvar_borrow_kind_for_consume(cmt, mode); + } fn borrow(&mut self, borrow_id: ast::NodeId, diff --git a/src/librustc_typeck/check/writeback.rs b/src/librustc_typeck/check/writeback.rs index a5a600c5748b32a6fe8d588c2fa280a30a9ac3ae..f7e1afed8fc53f219d1ea8d25d771ab447acd7c6 100644 --- a/src/librustc_typeck/check/writeback.rs +++ b/src/librustc_typeck/check/writeback.rs @@ -182,16 +182,20 @@ fn visit_upvar_borrow_map(&self) { return; } - for (upvar_id, upvar_borrow) in self.fcx.inh.upvar_borrow_map.borrow().iter() { - let r = upvar_borrow.region; - let r = self.resolve(&r, ResolvingUpvar(*upvar_id)); - let new_upvar_borrow = ty::UpvarBorrow { kind: upvar_borrow.kind, - region: r }; - debug!("Upvar borrow for {} resolved to {}", + for (upvar_id, upvar_capture) in self.fcx.inh.upvar_capture_map.borrow().iter() { + let new_upvar_capture = match *upvar_capture { + ty::UpvarCapture::ByValue => ty::UpvarCapture::ByValue, + ty::UpvarCapture::ByRef(ref upvar_borrow) => { + let r = upvar_borrow.region; + let r = self.resolve(&r, ResolvingUpvar(*upvar_id)); + ty::UpvarCapture::ByRef( + ty::UpvarBorrow { kind: upvar_borrow.kind, region: r }) + } + }; + debug!("Upvar capture for {} resolved to {}", upvar_id.repr(self.tcx()), - new_upvar_borrow.repr(self.tcx())); - self.fcx.tcx().upvar_borrow_map.borrow_mut().insert( - *upvar_id, new_upvar_borrow); + new_upvar_capture.repr(self.tcx())); + self.fcx.tcx().upvar_capture_map.borrow_mut().insert(*upvar_id, new_upvar_capture); } } diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs index 8158b8da86dfd6f149da7c5b76c95dae73faa269..ed33ddd458a54223de475475d659c74b577454e6 100644 --- a/src/librustc_typeck/collect.rs +++ b/src/librustc_typeck/collect.rs @@ -855,6 +855,17 @@ fn trait_def_of_item<'a, 'tcx>(ccx: &CollectCtxt<'a, 'tcx>, } }; + let paren_sugar = ty::has_attr(tcx, def_id, "rustc_paren_sugar"); + if paren_sugar && !ccx.tcx.sess.features.borrow().unboxed_closures { + ccx.tcx.sess.span_err( + it.span, + "the `#[rustc_paren_sugar]` attribute is a temporary means of controlling \ + which traits can use parenthetical notation"); + span_help!(ccx.tcx.sess, it.span, + "add `#![feature(unboxed_closures)]` to \ + the crate attributes to use it"); + } + let substs = ccx.tcx.mk_substs(mk_trait_substs(ccx, generics)); let ty_generics = ty_generics_for_trait(ccx, @@ -887,6 +898,7 @@ fn trait_def_of_item<'a, 'tcx>(ccx: &CollectCtxt<'a, 'tcx>, }); let trait_def = Rc::new(ty::TraitDef { + paren_sugar: paren_sugar, unsafety: unsafety, generics: ty_generics, bounds: bounds, diff --git a/src/test/compile-fail/borrowck-call-is-borrow-issue-12224.rs b/src/test/compile-fail/borrowck-call-is-borrow-issue-12224.rs index 010ddb792ccc6f18fc954cdc737c339a52590b64..9b9edce243bb77ce9a9c5e94b4ff5356cbe4b752 100644 --- a/src/test/compile-fail/borrowck-call-is-borrow-issue-12224.rs +++ b/src/test/compile-fail/borrowck-call-is-borrow-issue-12224.rs @@ -58,8 +58,10 @@ fn test6() { fn test7() { fn foo(_: F) where F: FnMut(Box, isize) {} let mut f = |&mut: g: Box, b: isize| {}; - f(box |a| { //~ ERROR: cannot borrow `f` as immutable because it is also borrowed as mutable - foo(f); //~ ERROR: cannot move out of captured outer variable + f(box |a| { + foo(f); + //~^ ERROR cannot move `f` into closure because it is borrowed + //~| ERROR cannot move out of captured outer variable in an `FnMut` closure }, 3); } diff --git a/src/test/compile-fail/unboxed-closure-illegal-move.rs b/src/test/compile-fail/unboxed-closure-illegal-move.rs index d489c3a64fabd96308e892733e28bddd030a53c6..1312b42fb82d4ccdeaf33e2cb2a738bc415fa182 100644 --- a/src/test/compile-fail/unboxed-closure-illegal-move.rs +++ b/src/test/compile-fail/unboxed-closure-illegal-move.rs @@ -19,24 +19,24 @@ fn main() { // By-ref cases { let x = box 0us; - let f = |&:| drop(x); //~ cannot move + let f = |&:| drop(x); //~ ERROR cannot move } { let x = box 0us; - let f = |&mut:| drop(x); //~ cannot move + let f = |&mut:| drop(x); //~ ERROR cannot move } { let x = box 0us; - let f = |:| drop(x); //~ cannot move + let f = |:| drop(x); // OK -- FnOnce } // By-value cases { let x = box 0us; - let f = move |&:| drop(x); //~ cannot move + let f = move |&:| drop(x); //~ ERROR cannot move } { let x = box 0us; - let f = move |&mut:| drop(x); //~ cannot move + let f = move |&mut:| drop(x); //~ ERROR cannot move } { let x = box 0us; diff --git a/src/test/compile-fail/unboxed-closures-move-upvar-from-non-once-ref-closure.rs b/src/test/compile-fail/unboxed-closures-move-upvar-from-non-once-ref-closure.rs new file mode 100644 index 0000000000000000000000000000000000000000..e66610c14960757e852c51c5110f9749948a2993 --- /dev/null +++ b/src/test/compile-fail/unboxed-closures-move-upvar-from-non-once-ref-closure.rs @@ -0,0 +1,24 @@ +// Copyright 2015 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. + +// Test that a by-ref `FnMut` closure gets an error when it tries to +// consume a value. + +fn call(f: F) where F : Fn() { + f(); +} + +fn main() { + let y = vec!(format!("World")); + call(|| { + y.into_iter(); + //~^ ERROR cannot move out of captured outer variable in an `Fn` closure + }); +} diff --git a/src/test/compile-fail/unboxed-closures-mutate-upvar.rs b/src/test/compile-fail/unboxed-closures-mutate-upvar.rs new file mode 100644 index 0000000000000000000000000000000000000000..96c7948dcb0469253d781a411a6e85ab3bfda608 --- /dev/null +++ b/src/test/compile-fail/unboxed-closures-mutate-upvar.rs @@ -0,0 +1,62 @@ +// Copyright 2015 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. + +// Test that we cannot mutate an outer variable that is not declared +// as `mut` through a closure. Also test that we CAN mutate a moved copy, +// unless this is a `Fn` closure. Issue #16749. + +use std::mem; + +fn a() { + let n = 0u8; + let mut f = |&mut:| { //~ ERROR closure cannot assign + n += 1; + }; +} + +fn b() { + let mut n = 0u8; + let mut f = |&mut:| { + n += 1; // OK + }; +} + +fn c() { + let n = 0u8; + let mut f = move |&mut:| { + // If we just did a straight-forward desugaring, this would + // compile, but we do something a bit more subtle, and hence + // we get an error. + n += 1; //~ ERROR cannot assign + }; +} + +fn d() { + let mut n = 0u8; + let mut f = move |&mut:| { + n += 1; // OK + }; +} + +fn e() { + let n = 0u8; + let mut f = move |&:| { + n += 1; //~ ERROR cannot assign + }; +} + +fn f() { + let mut n = 0u8; + let mut f = move |&:| { + n += 1; //~ ERROR cannot assign + }; +} + +fn main() { } diff --git a/src/test/compile-fail/unboxed-closures-mutated-upvar-from-fn-closure.rs b/src/test/compile-fail/unboxed-closures-mutated-upvar-from-fn-closure.rs new file mode 100644 index 0000000000000000000000000000000000000000..2345a86595e2da0223b60d29f68147c5964c3bdc --- /dev/null +++ b/src/test/compile-fail/unboxed-closures-mutated-upvar-from-fn-closure.rs @@ -0,0 +1,24 @@ +// Copyright 2015 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. + +// Test that a by-ref `FnMut` closure gets an error when it tries to +// mutate a value. + +fn call(f: F) where F : Fn() { + f(); +} + +fn main() { + let mut counter = 0_u32; + call(|| { + counter += 1; + //~^ ERROR cannot assign to data in a captured outer variable in an `Fn` closure + }); +} diff --git a/src/test/run-pass/unboxed-closures-counter-not-moved.rs b/src/test/run-pass/unboxed-closures-counter-not-moved.rs new file mode 100644 index 0000000000000000000000000000000000000000..e921f0c723ee5a6a1f2d3e2d4d6ba3076bd89cbb --- /dev/null +++ b/src/test/run-pass/unboxed-closures-counter-not-moved.rs @@ -0,0 +1,36 @@ +// Copyright 2015 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. + +// Test that we mutate a counter on the stack only when we expect to. + +fn call(f: F) where F : FnOnce() { + f(); +} + +fn main() { + let y = vec!(format!("Hello"), format!("World")); + let mut counter = 22_u32; + + call(|| { + // Move `y`, but do not move `counter`, even though it is read + // by value (note that it is also mutated). + for item in y.into_iter() { + let v = counter; + counter += v; + } + }); + assert_eq!(counter, 88); + + call(move || { + // this mutates a moved copy, and hence doesn't affect original + counter += 1; + }); + assert_eq!(counter, 88); +} diff --git a/src/test/run-pass/unboxed-closures-move-some-upvars-in-by-ref-closure.rs b/src/test/run-pass/unboxed-closures-move-some-upvars-in-by-ref-closure.rs new file mode 100644 index 0000000000000000000000000000000000000000..9534ee6fa12619449e6568bc11f46b2847751e92 --- /dev/null +++ b/src/test/run-pass/unboxed-closures-move-some-upvars-in-by-ref-closure.rs @@ -0,0 +1,32 @@ +// Copyright 2015 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. + +// Test that in a by-ref once closure we move some variables even as +// we capture others by mutable reference. + +fn call(f: F) where F : FnOnce() { + f(); +} + +fn main() { + let mut x = vec!(format!("Hello")); + let y = vec!(format!("World")); + call(|| { + // Here: `x` must be captured with a mutable reference in + // order for us to append on it, and `y` must be captured by + // value. + for item in y.into_iter() { + x.push(item); + } + }); + assert_eq!(x.len(), 2); + assert_eq!(&*x[0], "Hello"); + assert_eq!(&*x[1], "World"); +}