From 8d272321417df3a954802e42a66adda87ade5a49 Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Tue, 29 Jul 2014 22:08:39 -0700 Subject: [PATCH] librustc: Tie up loose ends in unboxed closures. This patch primarily does two things: (1) it prevents lifetimes from leaking out of unboxed closures; (2) it allows unboxed closure type notation, call notation, and construction notation to construct closures matching any of the three traits. This breaks code that looked like: let mut f; { let x = &5i; f = |&mut:| *x + 10; } Change this code to avoid having a reference escape. For example: { let x = &5i; let mut f; // <-- move here to avoid dangling reference f = |&mut:| *x + 10; } I believe this is enough to consider unboxed closures essentially implemented. Further issues (for example, higher-rank lifetimes) should be filed as followups. Closes #14449. [breaking-change] --- src/librustc/front/feature_gate.rs | 2 +- src/librustc/metadata/common.rs | 12 +- src/librustc/metadata/encoder.rs | 21 ++- src/librustc/metadata/tydecode.rs | 3 +- src/librustc/metadata/tyencode.rs | 3 +- src/librustc/middle/astencode.rs | 81 +++++++-- src/librustc/middle/borrowck/mod.rs | 5 +- src/librustc/middle/check_loop.rs | 2 +- src/librustc/middle/freevars.rs | 2 +- src/librustc/middle/kind.rs | 2 +- src/librustc/middle/liveness.rs | 2 +- src/librustc/middle/mem_categorization.rs | 17 +- src/librustc/middle/resolve.rs | 2 +- src/librustc/middle/trans/adt.rs | 2 +- src/librustc/middle/trans/base.rs | 62 +++++-- src/librustc/middle/trans/closure.rs | 63 ++++--- src/librustc/middle/trans/common.rs | 7 +- src/librustc/middle/trans/debuginfo.rs | 6 +- src/librustc/middle/trans/expr.rs | 2 +- src/librustc/middle/trans/meth.rs | 2 +- src/librustc/middle/trans/type_of.rs | 2 +- src/librustc/middle/ty.rs | 62 +++++-- src/librustc/middle/ty_fold.rs | 4 +- src/librustc/middle/typeck/astconv.rs | 28 +-- src/librustc/middle/typeck/check/method.rs | 68 +++---- src/librustc/middle/typeck/check/mod.rs | 37 +++- src/librustc/middle/typeck/check/regionck.rs | 20 ++- src/librustc/middle/typeck/check/vtable.rs | 49 +++-- src/librustc/middle/typeck/check/writeback.rs | 28 +-- src/librustc/middle/typeck/coherence.rs | 6 +- src/librustc/middle/typeck/infer/combine.rs | 6 +- src/librustc/middle/typeck/variance.rs | 7 +- src/libsyntax/ast.rs | 10 +- src/libsyntax/fold.rs | 5 +- src/libsyntax/parse/parser.rs | 167 ++++++++++-------- src/libsyntax/print/pprust.rs | 67 ++++--- src/libsyntax/visit.rs | 2 +- .../compile-fail/borrowck-unboxed-closures.rs | 29 +++ .../regionck-unboxed-closure-lifetimes.rs | 24 +++ .../unboxed-closures-wrong-trait.rs | 22 +++ .../run-pass/unboxed-closures-all-traits.rs | 31 ++++ src/test/run-pass/unboxed-closures-drop.rs | 127 +++++++++++++ .../unboxed-closures-single-word-env.rs | 34 ++++ 43 files changed, 835 insertions(+), 298 deletions(-) create mode 100644 src/test/compile-fail/borrowck-unboxed-closures.rs create mode 100644 src/test/compile-fail/regionck-unboxed-closure-lifetimes.rs create mode 100644 src/test/compile-fail/unboxed-closures-wrong-trait.rs create mode 100644 src/test/run-pass/unboxed-closures-all-traits.rs create mode 100644 src/test/run-pass/unboxed-closures-drop.rs create mode 100644 src/test/run-pass/unboxed-closures-single-word-env.rs diff --git a/src/librustc/front/feature_gate.rs b/src/librustc/front/feature_gate.rs index 99855c7345cb4..fdfbdea245bb2 100644 --- a/src/librustc/front/feature_gate.rs +++ b/src/librustc/front/feature_gate.rs @@ -314,7 +314,7 @@ impl<'a> Visitor<()> for Context<'a> { }, ast::TyBox(_) => { self.gate_box(t.span); } - ast::TyUnboxedFn(_) => { + ast::TyUnboxedFn(..) => { self.gate_feature("unboxed_closure_sugar", t.span, "unboxed closure trait sugar is experimental"); diff --git a/src/librustc/metadata/common.rs b/src/librustc/metadata/common.rs index 633ad8293491b..7d0a21b2f8e42 100644 --- a/src/librustc/metadata/common.rs +++ b/src/librustc/metadata/common.rs @@ -139,7 +139,7 @@ pub enum astencode_tag { // Reserves 0x40 -- 0x5f tag_table_adjustments = 0x51, tag_table_moves_map = 0x52, tag_table_capture_map = 0x53, - tag_table_unboxed_closure_type = 0x54, + tag_table_unboxed_closures = 0x54, tag_table_upvar_borrow_map = 0x55, tag_table_capture_modes = 0x56, } @@ -229,9 +229,11 @@ pub static tag_region_param_def_index: uint = 0x94; pub static tag_unboxed_closures: uint = 0x95; pub static tag_unboxed_closure: uint = 0x96; pub static tag_unboxed_closure_type: uint = 0x97; +pub static tag_unboxed_closure_kind: uint = 0x98; -pub static tag_struct_fields: uint = 0x98; -pub static tag_struct_field: uint = 0x99; -pub static tag_struct_field_id: uint = 0x9a; +pub static tag_struct_fields: uint = 0x99; +pub static tag_struct_field: uint = 0x9a; +pub static tag_struct_field_id: uint = 0x9b; + +pub static tag_attribute_is_sugared_doc: uint = 0x9c; -pub static tag_attribute_is_sugared_doc: uint = 0x9b; diff --git a/src/librustc/metadata/encoder.rs b/src/librustc/metadata/encoder.rs index 9b31d3a1b6460..1b47227fea403 100644 --- a/src/librustc/metadata/encoder.rs +++ b/src/librustc/metadata/encoder.rs @@ -630,6 +630,18 @@ fn encode_visibility(rbml_w: &mut Encoder, visibility: Visibility) { rbml_w.end_tag(); } +fn encode_unboxed_closure_kind(rbml_w: &mut Encoder, + kind: ty::UnboxedClosureKind) { + rbml_w.start_tag(tag_unboxed_closure_kind); + let ch = match kind { + ty::FnUnboxedClosureKind => 'f', + ty::FnMutUnboxedClosureKind => 'm', + ty::FnOnceUnboxedClosureKind => 'o', + }; + rbml_w.wr_str(ch.to_string().as_slice()); + rbml_w.end_tag(); +} + fn encode_explicit_self(rbml_w: &mut Encoder, explicit_self: &ty::ExplicitSelfCategory) { rbml_w.start_tag(tag_item_trait_method_explicit_self); @@ -1629,8 +1641,10 @@ fn encode_unboxed_closures<'a>( ecx: &'a EncodeContext, rbml_w: &'a mut Encoder) { rbml_w.start_tag(tag_unboxed_closures); - for (unboxed_closure_id, unboxed_closure_type) in - ecx.tcx.unboxed_closure_types.borrow().iter() { + for (unboxed_closure_id, unboxed_closure) in ecx.tcx + .unboxed_closures + .borrow() + .iter() { if unboxed_closure_id.krate != LOCAL_CRATE { continue } @@ -1638,8 +1652,9 @@ fn encode_unboxed_closures<'a>( rbml_w.start_tag(tag_unboxed_closure); encode_def_id(rbml_w, *unboxed_closure_id); rbml_w.start_tag(tag_unboxed_closure_type); - write_closure_type(ecx, rbml_w, unboxed_closure_type); + write_closure_type(ecx, rbml_w, &unboxed_closure.closure_type); rbml_w.end_tag(); + encode_unboxed_closure_kind(rbml_w, unboxed_closure.kind); rbml_w.end_tag(); } rbml_w.end_tag(); diff --git a/src/librustc/metadata/tydecode.rs b/src/librustc/metadata/tydecode.rs index fed23185c5d27..e6e7d8bf8d1a6 100644 --- a/src/librustc/metadata/tydecode.rs +++ b/src/librustc/metadata/tydecode.rs @@ -432,7 +432,8 @@ fn parse_ty(st: &mut PState, conv: conv_did) -> ty::t { } 'k' => { let did = parse_def(st, NominalType, |x,y| conv(x,y)); - return ty::mk_unboxed_closure(st.tcx, did); + let region = parse_region(st, conv); + return ty::mk_unboxed_closure(st.tcx, did, region); } 'e' => { return ty::mk_err(); diff --git a/src/librustc/metadata/tyencode.rs b/src/librustc/metadata/tyencode.rs index ba865c7ab04d0..fc5e267aa9050 100644 --- a/src/librustc/metadata/tyencode.rs +++ b/src/librustc/metadata/tyencode.rs @@ -285,8 +285,9 @@ fn enc_sty(w: &mut SeekableMemWriter, cx: &ctxt, st: &ty::sty) { enc_substs(w, cx, substs); mywrite!(w, "]"); } - ty::ty_unboxed_closure(def) => { + ty::ty_unboxed_closure(def, region) => { mywrite!(w, "k{}", (cx.ds)(def)); + enc_region(w, cx, region); } ty::ty_err => { mywrite!(w, "e"); diff --git a/src/librustc/middle/astencode.rs b/src/librustc/middle/astencode.rs index e31af8b58b9f1..b1ce2ece4afe8 100644 --- a/src/librustc/middle/astencode.rs +++ b/src/librustc/middle/astencode.rs @@ -689,10 +689,35 @@ pub fn encode_vtable_param_res(ecx: &e::EncodeContext, }).unwrap() } +pub fn encode_unboxed_closure_kind(ebml_w: &mut Encoder, + kind: ty::UnboxedClosureKind) { + ebml_w.emit_enum("UnboxedClosureKind", |ebml_w| { + match kind { + ty::FnUnboxedClosureKind => { + ebml_w.emit_enum_variant("FnUnboxedClosureKind", 0, 3, |_| { + Ok(()) + }) + } + ty::FnMutUnboxedClosureKind => { + ebml_w.emit_enum_variant("FnMutUnboxedClosureKind", 1, 3, |_| { + Ok(()) + }) + } + ty::FnOnceUnboxedClosureKind => { + ebml_w.emit_enum_variant("FnOnceUnboxedClosureKind", + 2, + 3, + |_| { + Ok(()) + }) + } + } + }).unwrap() +} pub fn encode_vtable_origin(ecx: &e::EncodeContext, - rbml_w: &mut Encoder, - vtable_origin: &typeck::vtable_origin) { + rbml_w: &mut Encoder, + vtable_origin: &typeck::vtable_origin) { rbml_w.emit_enum("vtable_origin", |rbml_w| { match *vtable_origin { typeck::vtable_static(def_id, ref substs, ref vtable_res) => { @@ -1210,14 +1235,15 @@ fn encode_side_tables_for_id(ecx: &e::EncodeContext, }) } - for unboxed_closure_type in tcx.unboxed_closure_types - .borrow() - .find(&ast_util::local_def(id)) - .iter() { - rbml_w.tag(c::tag_table_unboxed_closure_type, |rbml_w| { + for unboxed_closure in tcx.unboxed_closures + .borrow() + .find(&ast_util::local_def(id)) + .iter() { + rbml_w.tag(c::tag_table_unboxed_closures, |rbml_w| { rbml_w.id(id); rbml_w.tag(c::tag_table_val, |rbml_w| { - rbml_w.emit_closure_type(ecx, *unboxed_closure_type) + rbml_w.emit_closure_type(ecx, &unboxed_closure.closure_type); + encode_unboxed_closure_kind(rbml_w, unboxed_closure.kind) }) }) } @@ -1244,8 +1270,8 @@ trait rbml_decoder_decoder_helpers { -> ty::Polytype; fn read_substs(&mut self, xcx: &ExtendedDecodeContext) -> subst::Substs; fn read_auto_adjustment(&mut self, xcx: &ExtendedDecodeContext) -> ty::AutoAdjustment; - fn read_unboxed_closure_type(&mut self, xcx: &ExtendedDecodeContext) - -> ty::ClosureTy; + fn read_unboxed_closure(&mut self, xcx: &ExtendedDecodeContext) + -> ty::UnboxedClosure; fn convert_def_id(&mut self, xcx: &ExtendedDecodeContext, source: DefIdSource, @@ -1418,16 +1444,33 @@ impl<'a> rbml_decoder_decoder_helpers for reader::Decoder<'a> { }).unwrap() } - fn read_unboxed_closure_type(&mut self, xcx: &ExtendedDecodeContext) - -> ty::ClosureTy { - self.read_opaque(|this, doc| { + fn read_unboxed_closure(&mut self, xcx: &ExtendedDecodeContext) + -> ty::UnboxedClosure { + let closure_type = self.read_opaque(|this, doc| { Ok(tydecode::parse_ty_closure_data( doc.data, xcx.dcx.cdata.cnum, doc.start, xcx.dcx.tcx, |s, a| this.convert_def_id(xcx, s, a))) - }).unwrap() + }).unwrap(); + let variants = [ + "FnUnboxedClosureKind", + "FnMutUnboxedClosureKind", + "FnOnceUnboxedClosureKind" + ]; + let kind = self.read_enum_variant(variants, |_, i| { + Ok(match i { + 0 => ty::FnUnboxedClosureKind, + 1 => ty::FnMutUnboxedClosureKind, + 2 => ty::FnOnceUnboxedClosureKind, + _ => fail!("bad enum variant for ty::UnboxedClosureKind"), + }) + }).unwrap(); + ty::UnboxedClosure { + closure_type: closure_type, + kind: kind, + } } fn convert_def_id(&mut self, @@ -1566,14 +1609,14 @@ fn decode_side_tables(xcx: &ExtendedDecodeContext, let adj: ty::AutoAdjustment = val_dsr.read_auto_adjustment(xcx); dcx.tcx.adjustments.borrow_mut().insert(id, adj); } - c::tag_table_unboxed_closure_type => { - let unboxed_closure_type = - val_dsr.read_unboxed_closure_type(xcx); + c::tag_table_unboxed_closures => { + let unboxed_closure = + val_dsr.read_unboxed_closure(xcx); dcx.tcx - .unboxed_closure_types + .unboxed_closures .borrow_mut() .insert(ast_util::local_def(id), - unboxed_closure_type); + unboxed_closure); } _ => { xcx.dcx.tcx.sess.bug( diff --git a/src/librustc/middle/borrowck/mod.rs b/src/librustc/middle/borrowck/mod.rs index fd1369439c9a4..77dd7ed1c4204 100644 --- a/src/librustc/middle/borrowck/mod.rs +++ b/src/librustc/middle/borrowck/mod.rs @@ -290,8 +290,9 @@ pub fn closure_to_block(closure_id: ast::NodeId, tcx: &ty::ctxt) -> 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::ExprProc(_, block) | + ast::ExprFnBlock(_, _, block) | + ast::ExprUnboxedFn(_, _, _, 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 f973b33ef2c86..12841fb20d26f 100644 --- a/src/librustc/middle/check_loop.rs +++ b/src/librustc/middle/check_loop.rs @@ -48,7 +48,7 @@ impl<'a> Visitor for CheckLoopVisitor<'a> { } 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 b092d45e0396a..1a2d21baa210a 100644 --- a/src/librustc/middle/freevars.rs +++ b/src/librustc/middle/freevars.rs @@ -74,7 +74,7 @@ impl<'a> Visitor for CollectFreevarsVisitor<'a> { self.capture_mode_map.insert(expr.id, capture_mode); visit::walk_expr(self, expr, depth + 1) } - ast::ExprUnboxedFn(capture_clause, _, _) => { + ast::ExprUnboxedFn(capture_clause, _, _, _) => { let capture_mode = match capture_clause { ast::CaptureByValue => CaptureByValue, ast::CaptureByRef => CaptureByRef, diff --git a/src/librustc/middle/kind.rs b/src/librustc/middle/kind.rs index 83f4fbaed6792..17b2c69f45316 100644 --- a/src/librustc/middle/kind.rs +++ b/src/librustc/middle/kind.rs @@ -225,7 +225,7 @@ fn with_appropriate_checker(cx: &Context, b(check_for_bare) } - ty::ty_unboxed_closure(_) => {} + ty::ty_unboxed_closure(..) => {} ref s => { cx.tcx.sess.bug(format!("expect fn type in kind checker, not \ diff --git a/src/librustc/middle/liveness.rs b/src/librustc/middle/liveness.rs index 1e48417878834..baac68904d553 100644 --- a/src/librustc/middle/liveness.rs +++ b/src/librustc/middle/liveness.rs @@ -967,7 +967,7 @@ impl<'a> Liveness<'a> { 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 6ad8bc0c1e9b1..ef1e0515156c0 100644 --- a/src/librustc/middle/mem_categorization.rs +++ b/src/librustc/middle/mem_categorization.rs @@ -66,7 +66,7 @@ use middle::def; use middle::freevars; use middle::ty; use middle::typeck; -use util::nodemap::NodeMap; +use util::nodemap::{DefIdMap, NodeMap}; use util::ppaux::{ty_to_string, Repr}; use syntax::ast::{MutImmutable, MutMutable}; @@ -273,6 +273,8 @@ pub trait Typer { fn upvar_borrow(&self, upvar_id: ty::UpvarId) -> ty::UpvarBorrow; fn capture_mode(&self, closure_expr_id: ast::NodeId) -> freevars::CaptureMode; + fn unboxed_closures<'a>(&'a self) + -> &'a RefCell>; } impl MutabilityCategory { @@ -598,13 +600,22 @@ impl<'t,TYPER:Typer> MemCategorizationContext<'t,TYPER> { })) } } - ty::ty_unboxed_closure(_) => { + ty::ty_unboxed_closure(closure_id, _) => { + let unboxed_closures = self.typer + .unboxed_closures() + .borrow(); + let kind = unboxed_closures.get(&closure_id).kind; + let onceness = match kind { + ty::FnUnboxedClosureKind | + ty::FnMutUnboxedClosureKind => ast::Many, + ty::FnOnceUnboxedClosureKind => ast::Once, + }; Ok(Rc::new(cmt_ { id: id, span: span, cat: cat_copied_upvar(CopiedUpvar { upvar_id: var_id, - onceness: ast::Many, + onceness: onceness, capturing_proc: fn_node_id, }), mutbl: MutabilityCategory::from_def(&def), diff --git a/src/librustc/middle/resolve.rs b/src/librustc/middle/resolve.rs index 1e4cbdbdb6e5a..13a29842fcbb3 100644 --- a/src/librustc/middle/resolve.rs +++ b/src/librustc/middle/resolve.rs @@ -5289,7 +5289,7 @@ impl<'a> Resolver<'a> { 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/trans/adt.rs b/src/librustc/middle/trans/adt.rs index 9ec0407b5c3fc..9c68631baa44c 100644 --- a/src/librustc/middle/trans/adt.rs +++ b/src/librustc/middle/trans/adt.rs @@ -170,7 +170,7 @@ fn represent_type_uncached(cx: &CrateContext, t: ty::t) -> Repr { return Univariant(mk_struct(cx, ftys.as_slice(), packed), dtor) } - ty::ty_unboxed_closure(def_id) => { + ty::ty_unboxed_closure(def_id, _) => { let upvars = ty::unboxed_closure_upvars(cx.tcx(), def_id); let upvar_types = upvars.iter().map(|u| u.ty).collect::>(); return Univariant(mk_struct(cx, upvar_types.as_slice(), false), diff --git a/src/librustc/middle/trans/base.rs b/src/librustc/middle/trans/base.rs index b7bb383ad3cb2..16ba3ddbd7076 100644 --- a/src/librustc/middle/trans/base.rs +++ b/src/librustc/middle/trans/base.rs @@ -45,8 +45,8 @@ use middle::trans::adt; use middle::trans::build::*; use middle::trans::builder::{Builder, noname}; use middle::trans::callee; +use middle::trans::cleanup::{CleanupMethods, ScopeId}; use middle::trans::cleanup; -use middle::trans::cleanup::CleanupMethods; use middle::trans::common::*; use middle::trans::consts; use middle::trans::controlflow; @@ -252,6 +252,31 @@ fn get_extern_rust_fn(ccx: &CrateContext, fn_ty: ty::t, name: &str, did: ast::De f } +pub fn self_type_for_unboxed_closure(ccx: &CrateContext, + closure_id: ast::DefId) + -> ty::t { + let unboxed_closure_type = ty::mk_unboxed_closure(ccx.tcx(), + closure_id, + ty::ReStatic); + let unboxed_closures = ccx.tcx.unboxed_closures.borrow(); + let unboxed_closure = unboxed_closures.get(&closure_id); + match unboxed_closure.kind { + ty::FnUnboxedClosureKind => { + ty::mk_imm_rptr(&ccx.tcx, ty::ReStatic, unboxed_closure_type) + } + ty::FnMutUnboxedClosureKind => { + ty::mk_mut_rptr(&ccx.tcx, ty::ReStatic, unboxed_closure_type) + } + ty::FnOnceUnboxedClosureKind => unboxed_closure_type, + } +} + +pub fn kind_for_unboxed_closure(ccx: &CrateContext, closure_id: ast::DefId) + -> ty::UnboxedClosureKind { + let unboxed_closures = ccx.tcx.unboxed_closures.borrow(); + unboxed_closures.get(&closure_id).kind +} + pub fn decl_rust_fn(ccx: &CrateContext, fn_ty: ty::t, name: &str) -> ValueRef { let (inputs, output, abi, env) = match ty::get(fn_ty).sty { ty::ty_bare_fn(ref f) => { @@ -260,12 +285,12 @@ pub fn decl_rust_fn(ccx: &CrateContext, fn_ty: ty::t, name: &str) -> ValueRef { ty::ty_closure(ref f) => { (f.sig.inputs.clone(), f.sig.output, f.abi, Some(Type::i8p(ccx))) } - ty::ty_unboxed_closure(closure_did) => { - let unboxed_closure_types = ccx.tcx - .unboxed_closure_types - .borrow(); - let function_type = unboxed_closure_types.get(&closure_did); - let llenvironment_type = type_of(ccx, fn_ty).ptr_to(); + ty::ty_unboxed_closure(closure_did, _) => { + let unboxed_closures = ccx.tcx.unboxed_closures.borrow(); + let unboxed_closure = unboxed_closures.get(&closure_did); + let function_type = unboxed_closure.closure_type.clone(); + let self_type = self_type_for_unboxed_closure(ccx, closure_did); + let llenvironment_type = type_of_explicit_arg(ccx, self_type); (function_type.sig.inputs.clone(), function_type.sig.output, RustCall, @@ -691,7 +716,7 @@ pub fn iter_structural_ty<'r, } }) } - ty::ty_unboxed_closure(def_id) => { + ty::ty_unboxed_closure(def_id, _) => { let repr = adt::represent_type(cx.ccx(), t); let upvars = ty::unboxed_closure_upvars(cx.tcx(), def_id); for (i, upvar) in upvars.iter().enumerate() { @@ -1308,7 +1333,7 @@ fn has_nested_returns(tcx: &ty::ctxt, id: ast::NodeId) -> bool { match e.node { ast::ExprFnBlock(_, _, blk) | ast::ExprProc(_, blk) | - ast::ExprUnboxedFn(_, _, blk) => { + ast::ExprUnboxedFn(_, _, _, blk) => { let mut explicit = CheckForNestedReturnsVisitor { found: false }; let mut implicit = CheckForNestedReturnsVisitor { found: false }; visit::walk_expr(&mut explicit, &*e, false); @@ -1460,6 +1485,8 @@ pub fn create_datums_for_fn_args(fcx: &FunctionContext, /// Creates rvalue datums for each of the incoming function arguments and /// tuples the arguments. These will later be stored into appropriate lvalue /// datums. +/// +/// FIXME(pcwalton): Reduce the amount of code bloat this is responsible for. fn create_datums_for_fn_args_under_call_abi< 'a>( mut bcx: &'a Block<'a>, @@ -1708,7 +1735,8 @@ pub fn trans_closure(ccx: &CrateContext, abi: Abi, has_env: bool, is_unboxed_closure: IsUnboxedClosureFlag, - maybe_load_env: <'a> |&'a Block<'a>| -> &'a Block<'a>) { + maybe_load_env: <'a>|&'a Block<'a>, ScopeId| + -> &'a Block<'a>) { ccx.stats.n_closures.set(ccx.stats.n_closures.get() + 1); let _icx = push_ctxt("trans_closure"); @@ -1773,7 +1801,7 @@ pub fn trans_closure(ccx: &CrateContext, } }; - bcx = maybe_load_env(bcx); + bcx = maybe_load_env(bcx, cleanup::CustomScope(arg_scope)); // Up until here, IR instructions for this function have explicitly not been annotated with // source code location, so we don't step into call setup code. From here on, source location @@ -1854,7 +1882,7 @@ pub fn trans_fn(ccx: &CrateContext, abi, false, NotUnboxedClosure, - |bcx| bcx); + |bcx, _| bcx); } pub fn trans_enum_variant(ccx: &CrateContext, @@ -2188,11 +2216,11 @@ pub fn get_fn_llvm_attributes(ccx: &CrateContext, fn_ty: ty::t) let (fn_sig, abi, has_env) = match ty::get(fn_ty).sty { ty::ty_closure(ref f) => (f.sig.clone(), f.abi, true), ty::ty_bare_fn(ref f) => (f.sig.clone(), f.abi, false), - ty::ty_unboxed_closure(closure_did) => { - let unboxed_closure_types = ccx.tcx - .unboxed_closure_types - .borrow(); - let function_type = unboxed_closure_types.get(&closure_did); + ty::ty_unboxed_closure(closure_did, _) => { + let unboxed_closures = ccx.tcx.unboxed_closures.borrow(); + let function_type = unboxed_closures.get(&closure_did) + .closure_type + .clone(); (function_type.sig.clone(), RustCall, true) } _ => fail!("expected closure or function.") diff --git a/src/librustc/middle/trans/closure.rs b/src/librustc/middle/trans/closure.rs index 44c52a6739ab4..17f1b6ca52669 100644 --- a/src/librustc/middle/trans/closure.rs +++ b/src/librustc/middle/trans/closure.rs @@ -19,6 +19,7 @@ use middle::lang_items::ClosureExchangeMallocFnLangItem; use middle::trans::adt; use middle::trans::base::*; use middle::trans::build::*; +use middle::trans::cleanup::{CleanupMethods, ScopeId}; use middle::trans::common::*; use middle::trans::datum::{Datum, DatumBlock, Expr, Lvalue, rvalue_scratch_datum}; use middle::trans::debuginfo; @@ -306,7 +307,9 @@ fn load_environment<'a>(bcx: &'a Block<'a>, fn load_unboxed_closure_environment<'a>( bcx: &'a Block<'a>, - freevars: &Vec) + arg_scope_id: ScopeId, + freevars: &Vec, + closure_id: ast::DefId) -> &'a Block<'a> { let _icx = push_ctxt("closure::load_environment"); @@ -314,11 +317,31 @@ fn load_unboxed_closure_environment<'a>( return bcx } - let llenv = bcx.fcx.llenv.unwrap(); + // Special case for small by-value selfs. + let self_type = self_type_for_unboxed_closure(bcx.ccx(), closure_id); + let kind = kind_for_unboxed_closure(bcx.ccx(), closure_id); + let llenv = if kind == ty::FnOnceUnboxedClosureKind && + !arg_is_indirect(bcx.ccx(), self_type) { + let datum = rvalue_scratch_datum(bcx, + self_type, + "unboxed_closure_env"); + store_ty(bcx, bcx.fcx.llenv.unwrap(), datum.val, self_type); + assert!(freevars.len() <= 1); + datum.val + } else { + bcx.fcx.llenv.unwrap() + }; + for (i, freevar) in freevars.iter().enumerate() { let upvar_ptr = GEPi(bcx, llenv, [0, i]); let def_id = freevar.def.def_id(); bcx.fcx.llupvars.borrow_mut().insert(def_id.node, upvar_ptr); + + if kind == ty::FnOnceUnboxedClosureKind { + bcx.fcx.schedule_drop_mem(arg_scope_id, + upvar_ptr, + node_id_type(bcx, def_id.node)) + } } bcx @@ -394,7 +417,7 @@ pub fn trans_expr_fn<'a>( ty::ty_fn_abi(fty), true, NotUnboxedClosure, - |bcx| load_environment(bcx, cdata_ty, &freevars, store)); + |bcx, _| load_environment(bcx, cdata_ty, &freevars, store)); fill_fn_pair(bcx, dest_addr, llfn, llbox); bcx } @@ -404,7 +427,7 @@ pub fn trans_expr_fn<'a>( pub fn get_or_create_declaration_if_unboxed_closure(ccx: &CrateContext, closure_id: ast::DefId) -> Option { - if !ccx.tcx.unboxed_closure_types.borrow().contains_key(&closure_id) { + if !ccx.tcx.unboxed_closures.borrow().contains_key(&closure_id) { // Not an unboxed closure. return None } @@ -418,7 +441,9 @@ pub fn get_or_create_declaration_if_unboxed_closure(ccx: &CrateContext, None => {} } - let function_type = ty::mk_unboxed_closure(&ccx.tcx, closure_id); + let function_type = ty::mk_unboxed_closure(&ccx.tcx, + closure_id, + ty::ReStatic); let symbol = ccx.tcx.map.with_path(closure_id.node, |path| { mangle_internal_name_by_path_and_seq(path, "unboxed_closure") }); @@ -453,19 +478,10 @@ pub fn trans_unboxed_closure<'a>( bcx.ccx(), closure_id).unwrap(); - // Untuple the arguments. - let unboxed_closure_types = bcx.tcx().unboxed_closure_types.borrow(); - let /*mut*/ function_type = (*unboxed_closure_types.get(&closure_id)).clone(); - /*function_type.sig.inputs = - match ty::get(*function_type.sig.inputs.get(0)).sty { - ty::ty_tup(ref tuple_types) => { - tuple_types.iter().map(|x| (*x).clone()).collect() - } - _ => { - bcx.tcx().sess.span_bug(body.span, - "unboxed closure wasn't a tuple?!") - } - };*/ + let unboxed_closures = bcx.tcx().unboxed_closures.borrow(); + let function_type = unboxed_closures.get(&closure_id) + .closure_type + .clone(); let function_type = ty::mk_closure(bcx.tcx(), function_type); let freevars: Vec = @@ -486,7 +502,12 @@ pub fn trans_unboxed_closure<'a>( ty::ty_fn_abi(function_type), true, IsUnboxedClosure, - |bcx| load_unboxed_closure_environment(bcx, freevars_ptr)); + |bcx, arg_scope| { + load_unboxed_closure_environment(bcx, + arg_scope, + freevars_ptr, + closure_id) + }); // Don't hoist this to the top of the function. It's perfectly legitimate // to have a zero-size unboxed closure (in which case dest will be @@ -502,13 +523,13 @@ pub fn trans_unboxed_closure<'a>( let repr = adt::represent_type(bcx.ccx(), node_id_type(bcx, id)); // Create the closure. - for freevar in freevars_ptr.iter() { + for (i, freevar) in freevars_ptr.iter().enumerate() { let datum = expr::trans_local_var(bcx, freevar.def); let upvar_slot_dest = adt::trans_field_ptr(bcx, &*repr, dest_addr, 0, - 0); + i); bcx = datum.store_to(bcx, upvar_slot_dest); } adt::trans_set_discr(bcx, &*repr, dest_addr, 0); diff --git a/src/librustc/middle/trans/common.rs b/src/librustc/middle/trans/common.rs index 8b36270ee5402..300e7ecf81f43 100644 --- a/src/librustc/middle/trans/common.rs +++ b/src/librustc/middle/trans/common.rs @@ -32,7 +32,7 @@ use middle::trans::type_of; use middle::ty; use middle::typeck; use util::ppaux::Repr; -use util::nodemap::NodeMap; +use util::nodemap::{DefIdMap, NodeMap}; use arena::TypedArena; use std::collections::HashMap; @@ -514,6 +514,11 @@ impl<'a> mc::Typer for Block<'a> { self.tcx().region_maps.temporary_scope(rvalue_id) } + fn unboxed_closures<'a>(&'a self) + -> &'a RefCell> { + &self.tcx().unboxed_closures + } + fn upvar_borrow(&self, upvar_id: ty::UpvarId) -> ty::UpvarBorrow { self.tcx().upvar_borrow_map.borrow().get_copy(&upvar_id) } diff --git a/src/librustc/middle/trans/debuginfo.rs b/src/librustc/middle/trans/debuginfo.rs index ad1c5bf6ef133..96b8acbfb404a 100644 --- a/src/librustc/middle/trans/debuginfo.rs +++ b/src/librustc/middle/trans/debuginfo.rs @@ -1152,7 +1152,7 @@ pub fn create_function_debug_context(cx: &CrateContext, match expr.node { 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, @@ -3620,7 +3620,7 @@ fn populate_scope_map(cx: &CrateContext, 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, @@ -3895,7 +3895,7 @@ fn push_debuginfo_type_name(cx: &CrateContext, push_debuginfo_type_name(cx, sig.output, true, output); } }, - ty::ty_unboxed_closure(_) => { + ty::ty_unboxed_closure(..) => { output.push_str("closure"); } ty::ty_err | diff --git a/src/librustc/middle/trans/expr.rs b/src/librustc/middle/trans/expr.rs index 7cd2bd631f091..576bdb8b8c0b8 100644 --- a/src/librustc/middle/trans/expr.rs +++ b/src/librustc/middle/trans/expr.rs @@ -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/trans/meth.rs b/src/librustc/middle/trans/meth.rs index 3578b25c83957..3ae4c552b84e8 100644 --- a/src/librustc/middle/trans/meth.rs +++ b/src/librustc/middle/trans/meth.rs @@ -446,7 +446,7 @@ pub fn trans_trait_callee_from_llval<'a>(bcx: &'a Block<'a>, fn get_callee_substitutions_for_unboxed_closure(bcx: &Block, def_id: ast::DefId) -> subst::Substs { - let self_ty = ty::mk_unboxed_closure(bcx.tcx(), def_id); + let self_ty = ty::mk_unboxed_closure(bcx.tcx(), def_id, ty::ReStatic); subst::Substs::erased( VecPerParamSpace::new(Vec::new(), vec![ diff --git a/src/librustc/middle/trans/type_of.rs b/src/librustc/middle/trans/type_of.rs index 94c376c09c86a..8a445fc48398e 100644 --- a/src/librustc/middle/trans/type_of.rs +++ b/src/librustc/middle/trans/type_of.rs @@ -273,7 +273,7 @@ pub fn type_of(cx: &CrateContext, t: ty::t) -> Type { let name = llvm_type_name(cx, an_enum, did, tps); adt::incomplete_type_of(cx, &*repr, name.as_slice()) } - ty::ty_unboxed_closure(did) => { + ty::ty_unboxed_closure(did, _) => { // Only create the named struct, but don't fill it in. We // fill it in *after* placing it into the type cache. let repr = adt::represent_type(cx, t); diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index 3346b475267b9..f03c982bb083f 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -373,7 +373,7 @@ pub struct ctxt { /// Records the type of each unboxed closure. The def ID is the ID of the /// expression defining the unboxed closure. - pub unboxed_closure_types: RefCell>, + pub unboxed_closures: RefCell>, pub node_lint_levels: RefCell>, @@ -745,7 +745,7 @@ pub enum sty { ty_closure(Box), ty_trait(Box), ty_struct(DefId, Substs), - ty_unboxed_closure(DefId), + ty_unboxed_closure(DefId, Region), ty_tup(Vec), ty_param(ParamTy), // type parameter @@ -1056,6 +1056,21 @@ pub type type_cache = RefCell>; pub type node_type_table = RefCell>; +/// Records information about each unboxed closure. +pub struct UnboxedClosure { + /// The type of the unboxed closure. + pub closure_type: ClosureTy, + /// The kind of unboxed closure this is. + pub kind: UnboxedClosureKind, +} + +#[deriving(PartialEq, Eq)] +pub enum UnboxedClosureKind { + FnUnboxedClosureKind, + FnMutUnboxedClosureKind, + FnOnceUnboxedClosureKind, +} + pub fn mk_ctxt(s: Session, dm: resolve::DefMap, named_region_map: resolve_lifetime::NamedRegionMap, @@ -1117,7 +1132,7 @@ pub fn mk_ctxt(s: Session, method_map: RefCell::new(FnvHashMap::new()), vtable_map: RefCell::new(FnvHashMap::new()), dependency_formats: RefCell::new(HashMap::new()), - unboxed_closure_types: RefCell::new(DefIdMap::new()), + unboxed_closures: RefCell::new(DefIdMap::new()), node_lint_levels: RefCell::new(HashMap::new()), transmute_restrictions: RefCell::new(Vec::new()), stability: RefCell::new(stability), @@ -1177,7 +1192,7 @@ pub fn mk_t(cx: &ctxt, st: sty) -> t { } match &st { &ty_nil | &ty_bool | &ty_char | &ty_int(_) | &ty_float(_) | &ty_uint(_) | - &ty_str | &ty_unboxed_closure(_) => {} + &ty_str => {} // You might think that we could just return ty_err for // any type containing ty_err as a component, and get // rid of the has_ty_err flag -- likewise for ty_bot (with @@ -1194,6 +1209,7 @@ pub fn mk_t(cx: &ctxt, st: sty) -> t { flags |= has_params as uint; } } + &ty_unboxed_closure(_, ref region) => flags |= rflags(*region), &ty_infer(_) => flags |= needs_infer as uint, &ty_enum(_, ref substs) | &ty_struct(_, ref substs) => { flags |= sflags(substs); @@ -1442,8 +1458,9 @@ pub fn mk_struct(cx: &ctxt, struct_id: ast::DefId, substs: Substs) -> t { mk_t(cx, ty_struct(struct_id, substs)) } -pub fn mk_unboxed_closure(cx: &ctxt, closure_id: ast::DefId) -> t { - mk_t(cx, ty_unboxed_closure(closure_id)) +pub fn mk_unboxed_closure(cx: &ctxt, closure_id: ast::DefId, region: Region) + -> t { + mk_t(cx, ty_unboxed_closure(closure_id, region)) } pub fn mk_var(cx: &ctxt, v: TyVid) -> t { mk_infer(cx, TyVar(v)) } @@ -1476,8 +1493,8 @@ pub fn maybe_walk_ty(ty: t, f: |t| -> bool) { } match get(ty).sty { ty_nil | ty_bot | ty_bool | ty_char | ty_int(_) | ty_uint(_) | ty_float(_) | - ty_str | ty_infer(_) | ty_param(_) | ty_unboxed_closure(_) | ty_err => { - } + ty_str | ty_infer(_) | ty_param(_) | ty_unboxed_closure(..) | + ty_err => {} ty_box(ty) | ty_uniq(ty) => maybe_walk_ty(ty, f), ty_ptr(ref tm) | ty_rptr(_, ref tm) | ty_vec(ref tm, _) => { maybe_walk_ty(tm.ty, f); @@ -1584,7 +1601,7 @@ pub fn type_is_vec(ty: t) -> bool { pub fn type_is_structural(ty: t) -> bool { match get(ty).sty { ty_struct(..) | ty_tup(_) | ty_enum(..) | ty_closure(_) | - ty_vec(_, Some(_)) | ty_unboxed_closure(_) => true, + ty_vec(_, Some(_)) | ty_unboxed_closure(..) => true, _ => type_is_slice(ty) | type_is_trait(ty) } } @@ -2099,10 +2116,13 @@ pub fn type_contents(cx: &ctxt, ty: t) -> TypeContents { apply_lang_items(cx, did, res) } - ty_unboxed_closure(did) => { + ty_unboxed_closure(did, r) => { + // FIXME(#14449): `borrowed_contents` below assumes `&mut` + // unboxed closure. let upvars = unboxed_closure_upvars(cx, did); TypeContents::union(upvars.as_slice(), - |f| tc_ty(cx, f.ty, cache)) + |f| tc_ty(cx, f.ty, cache)) | + borrowed_contents(r, MutMutable) } ty_tup(ref tys) => { @@ -2349,7 +2369,7 @@ pub fn is_instantiable(cx: &ctxt, r_ty: t) -> bool { r } - ty_unboxed_closure(did) => { + ty_unboxed_closure(did, _) => { let upvars = unboxed_closure_upvars(cx, did); upvars.iter().any(|f| type_requires(cx, seen, r_ty, f.ty)) } @@ -2477,7 +2497,7 @@ pub fn is_type_representable(cx: &ctxt, sp: Span, ty: t) -> Representability { r } - ty_unboxed_closure(did) => { + ty_unboxed_closure(did, _) => { let upvars = unboxed_closure_upvars(cx, did); find_nonrepresentable(cx, sp, @@ -2718,7 +2738,7 @@ pub fn ty_fn_args(fty: t) -> Vec { pub fn ty_closure_store(fty: t) -> TraitStore { match get(fty).sty { ty_closure(ref f) => f.store, - ty_unboxed_closure(_) => { + ty_unboxed_closure(..) => { // Close enough for the purposes of all the callers of this // function (which is soon to be deprecated anyhow). UniqTraitStore @@ -3318,7 +3338,7 @@ pub fn ty_sort_string(cx: &ctxt, t: t) -> String { ty_struct(id, _) => { format!("struct {}", item_path_str(cx, id)) } - ty_unboxed_closure(_) => "closure".to_string(), + ty_unboxed_closure(..) => "closure".to_string(), ty_tup(_) => "tuple".to_string(), ty_infer(TyVar(_)) => "inferred type".to_string(), ty_infer(IntVar(_)) => "integral variable".to_string(), @@ -3687,7 +3707,7 @@ pub fn ty_to_def_id(ty: t) -> Option { ty_trait(box TyTrait { def_id: id, .. }) | ty_struct(id, _) | ty_enum(id, _) | - ty_unboxed_closure(id) => Some(id), + ty_unboxed_closure(id, _) => Some(id), _ => None } } @@ -4717,9 +4737,10 @@ pub fn hash_crate_independent(tcx: &ctxt, t: t, svh: &Svh) -> u64 { } ty_infer(_) => unreachable!(), ty_err => byte!(23), - ty_unboxed_closure(d) => { + ty_unboxed_closure(d, r) => { byte!(24); did(&mut state, d); + region(&mut state, r); } } }); @@ -4873,6 +4894,11 @@ impl mc::Typer for ty::ctxt { -> freevars::CaptureMode { self.capture_modes.borrow().get_copy(&closure_expr_id) } + + fn unboxed_closures<'a>(&'a self) + -> &'a RefCell> { + &self.unboxed_closures + } } /// The category of explicit self. @@ -4914,6 +4940,7 @@ pub fn accumulate_lifetimes_in_type(accumulator: &mut Vec, UniqTraitStore => {} } } + ty_unboxed_closure(_, ref region) => accumulator.push(*region), ty_nil | ty_bot | ty_bool | @@ -4930,7 +4957,6 @@ pub fn accumulate_lifetimes_in_type(accumulator: &mut Vec, ty_tup(_) | ty_param(_) | ty_infer(_) | - ty_unboxed_closure(_) | ty_err => {} } }) diff --git a/src/librustc/middle/ty_fold.rs b/src/librustc/middle/ty_fold.rs index e2b984959060c..9f475bfd9d5d0 100644 --- a/src/librustc/middle/ty_fold.rs +++ b/src/librustc/middle/ty_fold.rs @@ -392,8 +392,8 @@ pub fn super_fold_sty(this: &mut T, ty::ty_struct(did, ref substs) => { ty::ty_struct(did, substs.fold_with(this)) } - ty::ty_unboxed_closure(did) => { - ty::ty_unboxed_closure(did) + ty::ty_unboxed_closure(did, ref region) => { + ty::ty_unboxed_closure(did, region.fold_with(this)) } ty::ty_nil | ty::ty_bot | ty::ty_bool | ty::ty_char | ty::ty_str | ty::ty_int(_) | ty::ty_uint(_) | ty::ty_float(_) | diff --git a/src/librustc/middle/typeck/astconv.rs b/src/librustc/middle/typeck/astconv.rs index 5e7426f3ae749..c317f98a25afe 100644 --- a/src/librustc/middle/typeck/astconv.rs +++ b/src/librustc/middle/typeck/astconv.rs @@ -51,7 +51,8 @@ use middle::const_eval; use middle::def; -use middle::lang_items::FnMutTraitLangItem; +use middle::lang_items::{FnTraitLangItem, FnMutTraitLangItem}; +use middle::lang_items::{FnOnceTraitLangItem}; use middle::subst::{FnSpace, TypeSpace, SelfSpace, Subst, Substs}; use middle::subst::{VecPerParamSpace}; use middle::ty; @@ -544,16 +545,17 @@ fn ast_ty_to_mt(this: &AC, pub fn trait_ref_for_unboxed_function( - this: &AC, - rscope: &RS, - unboxed_function: &ast::UnboxedFnTy, - self_ty: Option) - -> ty::TraitRef -{ - let fn_mut_trait_did = this.tcx() - .lang_items - .require(FnMutTraitLangItem) - .unwrap(); + this: &AC, + rscope: &RS, + unboxed_function: &ast::UnboxedFnTy, + self_ty: Option) + -> ty::TraitRef { + let lang_item = match unboxed_function.kind { + ast::FnUnboxedClosureKind => FnTraitLangItem, + ast::FnMutUnboxedClosureKind => FnMutTraitLangItem, + ast::FnOnceUnboxedClosureKind => FnOnceTraitLangItem, + }; + let trait_did = this.tcx().lang_items.require(lang_item).unwrap(); let input_types = unboxed_function.decl .inputs @@ -574,7 +576,7 @@ pub fn trait_ref_for_unboxed_function( None); ty::mk_closure(tcx, fn_decl) } - ast::TyUnboxedFn(_) => { + ast::TyUnboxedFn(..) => { tcx.sess.span_err(ast_ty.span, "cannot use unboxed functions here"); ty::mk_err() diff --git a/src/librustc/middle/typeck/check/method.rs b/src/librustc/middle/typeck/check/method.rs index 1805c18eaf10c..dfcde8ca9b9bc 100644 --- a/src/librustc/middle/typeck/check/method.rs +++ b/src/librustc/middle/typeck/check/method.rs @@ -444,7 +444,7 @@ impl<'a> LookupContext<'a> { }, ty_enum(did, _) | ty_struct(did, _) | - ty_unboxed_closure(did) => { + ty_unboxed_closure(did, _) => { if self.check_traits == CheckTraitsAndInherentMethods { self.push_inherent_impl_candidates_for_type(did); } @@ -468,7 +468,7 @@ impl<'a> LookupContext<'a> { ty_param(p) => { self.push_inherent_candidates_from_param(self_ty, restrict_to, p); } - ty_unboxed_closure(closure_did) => { + ty_unboxed_closure(closure_did, _) => { self.push_unboxed_closure_call_candidates_if_applicable( closure_did); } @@ -531,8 +531,11 @@ impl<'a> LookupContext<'a> { let arguments_type = *closure_function_type.sig.inputs.get(0); let return_type = closure_function_type.sig.output; + let closure_region = + vcx.infcx.next_region_var(MiscVariable(self.span)); let unboxed_closure_type = ty::mk_unboxed_closure(self.tcx(), - closure_did); + closure_did, + closure_region); self.extension_candidates.push(Candidate { rcvr_match_condition: RcvrMatchesIfSubtype(unboxed_closure_type), @@ -548,39 +551,38 @@ impl<'a> LookupContext<'a> { fn push_unboxed_closure_call_candidates_if_applicable( &mut self, closure_did: DefId) { - // FIXME(pcwalton): Try `Fn` and `FnOnce` too. - let trait_did = match self.tcx().lang_items.fn_mut_trait() { - Some(trait_did) => trait_did, - None => return, - }; - - match self.tcx() - .unboxed_closure_types - .borrow() - .find(&closure_did) { - None => {} // Fall through to try inherited. - Some(closure_function_type) => { - self.push_unboxed_closure_call_candidate_if_applicable( - trait_did, - closure_did, - closure_function_type); - return + let trait_dids = [ + self.tcx().lang_items.fn_trait(), + self.tcx().lang_items.fn_mut_trait(), + self.tcx().lang_items.fn_once_trait() + ]; + for optional_trait_did in trait_dids.iter() { + let trait_did = match *optional_trait_did { + Some(trait_did) => trait_did, + None => continue, + }; + + match self.tcx().unboxed_closures.borrow().find(&closure_did) { + None => {} // Fall through to try inherited. + Some(closure) => { + self.push_unboxed_closure_call_candidate_if_applicable( + trait_did, + closure_did, + &closure.closure_type); + return + } } - } - match self.fcx - .inh - .unboxed_closure_types - .borrow() - .find(&closure_did) { - Some(closure_function_type) => { - self.push_unboxed_closure_call_candidate_if_applicable( - trait_did, - closure_did, - closure_function_type); - return + match self.fcx.inh.unboxed_closures.borrow().find(&closure_did) { + Some(closure) => { + self.push_unboxed_closure_call_candidate_if_applicable( + trait_did, + closure_did, + &closure.closure_type); + return + } + None => {} } - None => {} } self.tcx().sess.bug("didn't find unboxed closure type in tcx map or \ diff --git a/src/librustc/middle/typeck/check/mod.rs b/src/librustc/middle/typeck/check/mod.rs index 33acb52178008..aa5a358515885 100644 --- a/src/librustc/middle/typeck/check/mod.rs +++ b/src/librustc/middle/typeck/check/mod.rs @@ -168,7 +168,7 @@ pub struct Inherited<'a> { method_map: MethodMap, vtable_map: vtable_map, upvar_borrow_map: RefCell, - unboxed_closure_types: RefCell>, + unboxed_closures: RefCell>, } /// When type-checking an expression, we propagate downward @@ -275,7 +275,7 @@ impl<'a> Inherited<'a> { method_map: RefCell::new(FnvHashMap::new()), vtable_map: RefCell::new(FnvHashMap::new()), upvar_borrow_map: RefCell::new(HashMap::new()), - unboxed_closure_types: RefCell::new(DefIdMap::new()), + unboxed_closures: RefCell::new(DefIdMap::new()), } } } @@ -1271,7 +1271,7 @@ impl<'a> FnCtxt<'a> { VtableContext { infcx: self.infcx(), param_env: &self.inh.param_env, - unboxed_closure_types: &self.inh.unboxed_closure_types, + unboxed_closures: &self.inh.unboxed_closures, } } } @@ -2618,6 +2618,7 @@ fn check_expr_with_unifier(fcx: &FnCtxt, fn check_unboxed_closure(fcx: &FnCtxt, expr: &ast::Expr, + kind: ast::UnboxedClosureKind, decl: &ast::FnDecl, body: ast::P) { // The `RegionTraitStore` is a lie, but we ignore it so it doesn't @@ -2635,8 +2636,16 @@ fn check_expr_with_unifier(fcx: &FnCtxt, abi::RustCall, None); + let region = match fcx.infcx().anon_regions(expr.span, 1) { + Err(_) => { + fcx.ccx.tcx.sess.span_bug(expr.span, + "can't make anon regions here?!") + } + Ok(regions) => *regions.get(0), + }; let closure_type = ty::mk_unboxed_closure(fcx.ccx.tcx, - local_def(expr.id)); + local_def(expr.id), + region); fcx.write_ty(expr.id, closure_type); check_fn(fcx.ccx, @@ -2648,13 +2657,24 @@ fn check_expr_with_unifier(fcx: &FnCtxt, fcx.inh); // Tuple up the arguments and insert the resulting function type into - // the `unboxed_closure_types` table. + // the `unboxed_closures` table. fn_ty.sig.inputs = vec![ty::mk_tup(fcx.tcx(), fn_ty.sig.inputs)]; + let kind = match kind { + ast::FnUnboxedClosureKind => ty::FnUnboxedClosureKind, + ast::FnMutUnboxedClosureKind => ty::FnMutUnboxedClosureKind, + ast::FnOnceUnboxedClosureKind => ty::FnOnceUnboxedClosureKind, + }; + + let unboxed_closure = ty::UnboxedClosure { + closure_type: fn_ty, + kind: kind, + }; + fcx.inh - .unboxed_closure_types + .unboxed_closures .borrow_mut() - .insert(local_def(expr.id), fn_ty); + .insert(local_def(expr.id), unboxed_closure); } fn check_expr_fn(fcx: &FnCtxt, @@ -3402,9 +3422,10 @@ fn check_expr_with_unifier(fcx: &FnCtxt, body.clone(), expected); } - ast::ExprUnboxedFn(_, ref decl, ref body) => { + ast::ExprUnboxedFn(_, kind, ref decl, ref body) => { check_unboxed_closure(fcx, expr, + kind, &**decl, *body); } diff --git a/src/librustc/middle/typeck/check/regionck.rs b/src/librustc/middle/typeck/check/regionck.rs index 47f5c45dde248..0acf6e9183164 100644 --- a/src/librustc/middle/typeck/check/regionck.rs +++ b/src/librustc/middle/typeck/check/regionck.rs @@ -132,7 +132,7 @@ use middle::typeck::infer::resolve_type; use middle::typeck::infer; use middle::typeck::MethodCall; use middle::pat_util; -use util::nodemap::NodeMap; +use util::nodemap::{DefIdMap, NodeMap}; use util::ppaux::{ty_to_string, region_to_string, Repr}; use syntax::ast; @@ -295,6 +295,11 @@ impl<'fcx> mc::Typer for Rcx<'fcx> { -> freevars::CaptureMode { self.tcx().capture_modes.borrow().get_copy(&closure_expr_id) } + + fn unboxed_closures<'a>(&'a self) + -> &'a RefCell> { + &self.fcx.inh.unboxed_closures + } } pub fn regionck_expr(fcx: &FnCtxt, e: &ast::Expr) { @@ -594,7 +599,7 @@ fn visit_expr(rcx: &mut Rcx, expr: &ast::Expr) { ast::ExprFnBlock(_, _, ref body) | ast::ExprProc(_, ref body) | - ast::ExprUnboxedFn(_, _, ref body) => { + ast::ExprUnboxedFn(_, _, _, ref body) => { check_expr_fn_block(rcx, expr, &**body); } @@ -660,6 +665,17 @@ fn check_expr_fn_block(rcx: &mut Rcx, } }); } + ty::ty_unboxed_closure(_, region) => { + freevars::with_freevars(tcx, expr.id, |freevars| { + // No free variables means that there is no environment and + // hence the closure has static lifetime. Otherwise, the + // closure must not outlive the variables it closes over + // by-reference. + if !freevars.is_empty() { + constrain_free_variables(rcx, region, expr, freevars); + } + }) + } _ => () } diff --git a/src/librustc/middle/typeck/check/vtable.rs b/src/librustc/middle/typeck/check/vtable.rs index 565b88b74939b..850d5c5a8f07d 100644 --- a/src/librustc/middle/typeck/check/vtable.rs +++ b/src/librustc/middle/typeck/check/vtable.rs @@ -71,7 +71,7 @@ use syntax::visit::Visitor; pub struct VtableContext<'a> { pub infcx: &'a infer::InferCtxt<'a>, pub param_env: &'a ty::ParameterEnvironment, - pub unboxed_closure_types: &'a RefCell>, + pub unboxed_closures: &'a RefCell>, } impl<'a> VtableContext<'a> { @@ -309,31 +309,42 @@ fn search_for_unboxed_closure_vtable(vcx: &VtableContext, -> Option { let tcx = vcx.tcx(); let closure_def_id = match ty::get(ty).sty { - ty::ty_unboxed_closure(closure_def_id) => closure_def_id, + ty::ty_unboxed_closure(closure_def_id, _) => closure_def_id, _ => return None, }; let fn_traits = [ - tcx.lang_items.fn_trait(), - tcx.lang_items.fn_mut_trait(), - tcx.lang_items.fn_once_trait() + (ty::FnUnboxedClosureKind, tcx.lang_items.fn_trait()), + (ty::FnMutUnboxedClosureKind, tcx.lang_items.fn_mut_trait()), + (ty::FnOnceUnboxedClosureKind, tcx.lang_items.fn_once_trait()), ]; - for fn_trait in fn_traits.iter() { - match *fn_trait { - Some(ref fn_trait) if *fn_trait == trait_ref.def_id => {} + for tuple in fn_traits.iter() { + let kind = match tuple { + &(kind, Some(ref fn_trait)) if *fn_trait == trait_ref.def_id => { + kind + } _ => continue, }; // Check to see whether the argument and return types match. - let unboxed_closure_types = tcx.unboxed_closure_types.borrow(); - let closure_type = match unboxed_closure_types.find(&closure_def_id) { - Some(closure_type) => (*closure_type).clone(), + let unboxed_closures = tcx.unboxed_closures.borrow(); + let closure_type = match unboxed_closures.find(&closure_def_id) { + Some(closure) => { + if closure.kind != kind { + continue + } + closure.closure_type.clone() + } None => { // Try the inherited unboxed closure type map. - let unboxed_closure_types = vcx.unboxed_closure_types - .borrow(); - match unboxed_closure_types.find(&closure_def_id) { - Some(closure_type) => (*closure_type).clone(), + let unboxed_closures = vcx.unboxed_closures.borrow(); + match unboxed_closures.find(&closure_def_id) { + Some(closure) => { + if closure.kind != kind { + continue + } + closure.closure_type.clone() + } None => { tcx.sess.span_bug(span, "didn't find unboxed closure type \ @@ -881,11 +892,11 @@ pub fn resolve_impl(tcx: &ty::ctxt, debug!("impl_trait_ref={}", impl_trait_ref.repr(tcx)); let infcx = &infer::new_infer_ctxt(tcx); - let unboxed_closure_types = RefCell::new(DefIdMap::new()); + let unboxed_closures = RefCell::new(DefIdMap::new()); let vcx = VtableContext { infcx: infcx, param_env: ¶m_env, - unboxed_closure_types: &unboxed_closure_types, + unboxed_closures: &unboxed_closures, }; // Resolve the vtables for the trait reference on the impl. This @@ -934,11 +945,11 @@ pub fn resolve_impl(tcx: &ty::ctxt, pub fn trans_resolve_method(tcx: &ty::ctxt, id: ast::NodeId, substs: &subst::Substs) -> vtable_res { let generics = ty::lookup_item_type(tcx, ast_util::local_def(id)).generics; - let unboxed_closure_types = RefCell::new(DefIdMap::new()); + let unboxed_closures = RefCell::new(DefIdMap::new()); let vcx = VtableContext { infcx: &infer::new_infer_ctxt(tcx), param_env: &ty::construct_parameter_environment(tcx, &ty::Generics::empty(), id), - unboxed_closure_types: &unboxed_closure_types, + unboxed_closures: &unboxed_closures, }; lookup_vtables(&vcx, diff --git a/src/librustc/middle/typeck/check/writeback.rs b/src/librustc/middle/typeck/check/writeback.rs index 6e6ebd181fbc9..892a62249ac3b 100644 --- a/src/librustc/middle/typeck/check/writeback.rs +++ b/src/librustc/middle/typeck/check/writeback.rs @@ -43,7 +43,7 @@ pub fn resolve_type_vars_in_expr(fcx: &FnCtxt, e: &ast::Expr) { let mut wbcx = WritebackCx::new(fcx); wbcx.visit_expr(e, ()); wbcx.visit_upvar_borrow_map(); - wbcx.visit_unboxed_closure_types(); + wbcx.visit_unboxed_closures(); } pub fn resolve_type_vars_in_fn(fcx: &FnCtxt, @@ -62,7 +62,7 @@ pub fn resolve_type_vars_in_fn(fcx: &FnCtxt, } } wbcx.visit_upvar_borrow_map(); - wbcx.visit_unboxed_closure_types(); + wbcx.visit_unboxed_closures(); } pub fn resolve_impl_res(infcx: &infer::InferCtxt, @@ -134,7 +134,7 @@ impl<'cx> Visitor<()> for WritebackCx<'cx> { match e.node { 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); @@ -211,23 +211,27 @@ impl<'cx> WritebackCx<'cx> { } } - fn visit_unboxed_closure_types(&self) { + fn visit_unboxed_closures(&self) { if self.fcx.writeback_errors.get() { return } - for (def_id, closure_ty) in self.fcx - .inh - .unboxed_closure_types - .borrow() - .iter() { - let closure_ty = self.resolve(closure_ty, + for (def_id, unboxed_closure) in self.fcx + .inh + .unboxed_closures + .borrow() + .iter() { + let closure_ty = self.resolve(&unboxed_closure.closure_type, ResolvingUnboxedClosure(*def_id)); + let unboxed_closure = ty::UnboxedClosure { + closure_type: closure_ty, + kind: unboxed_closure.kind, + }; self.fcx .tcx() - .unboxed_closure_types + .unboxed_closures .borrow_mut() - .insert(*def_id, closure_ty); + .insert(*def_id, unboxed_closure); } } diff --git a/src/librustc/middle/typeck/coherence.rs b/src/librustc/middle/typeck/coherence.rs index fdf9125e6e14d..76af54ba4d15c 100644 --- a/src/librustc/middle/typeck/coherence.rs +++ b/src/librustc/middle/typeck/coherence.rs @@ -109,7 +109,7 @@ fn type_is_defined_in_local_crate(tcx: &ty::ctxt, original_type: t) -> bool { match get(t).sty { ty_enum(def_id, _) | ty_struct(def_id, _) | - ty_unboxed_closure(def_id) => { + ty_unboxed_closure(def_id, _) => { if def_id.krate == ast::LOCAL_CRATE { found_nominal = true; } @@ -153,7 +153,7 @@ fn get_base_type_def_id(inference_context: &InferCtxt, match get(base_type).sty { ty_enum(def_id, _) | ty_struct(def_id, _) | - ty_unboxed_closure(def_id) => { + ty_unboxed_closure(def_id, _) => { Some(def_id) } ty_rptr(_, ty::mt {ty, ..}) | ty_uniq(ty) => match ty::get(ty).sty { @@ -687,7 +687,7 @@ impl<'a> CoherenceChecker<'a> { match ty::get(self_type.ty).sty { ty::ty_enum(type_def_id, _) | ty::ty_struct(type_def_id, _) | - ty::ty_unboxed_closure(type_def_id) => { + ty::ty_unboxed_closure(type_def_id, _) => { tcx.destructor_for_type.borrow_mut().insert(type_def_id, method_def_id); tcx.destructors.borrow_mut().insert(method_def_id); diff --git a/src/librustc/middle/typeck/infer/combine.rs b/src/librustc/middle/typeck/infer/combine.rs index 1aae97d3d83e9..d99d55d4d873d 100644 --- a/src/librustc/middle/typeck/infer/combine.rs +++ b/src/librustc/middle/typeck/infer/combine.rs @@ -492,9 +492,11 @@ pub fn super_tys(this: &C, a: ty::t, b: ty::t) -> cres { Ok(ty::mk_struct(tcx, a_id, substs)) } - (&ty::ty_unboxed_closure(a_id), &ty::ty_unboxed_closure(b_id)) + (&ty::ty_unboxed_closure(a_id, a_region), + &ty::ty_unboxed_closure(b_id, b_region)) if a_id == b_id => { - Ok(ty::mk_unboxed_closure(tcx, a_id)) + let region = if_ok!(this.regions(a_region, b_region)); + Ok(ty::mk_unboxed_closure(tcx, a_id, region)) } (&ty::ty_box(a_inner), &ty::ty_box(b_inner)) => { diff --git a/src/librustc/middle/typeck/variance.rs b/src/librustc/middle/typeck/variance.rs index be9ae0cc719f2..54b9ce035348d 100644 --- a/src/librustc/middle/typeck/variance.rs +++ b/src/librustc/middle/typeck/variance.rs @@ -722,10 +722,15 @@ impl<'a> ConstraintContext<'a> { match ty::get(ty).sty { ty::ty_nil | ty::ty_bot | ty::ty_bool | ty::ty_char | ty::ty_int(_) | ty::ty_uint(_) | - ty::ty_float(_) | ty::ty_str | ty::ty_unboxed_closure(..) => { + ty::ty_float(_) | ty::ty_str => { /* leaf type -- noop */ } + ty::ty_unboxed_closure(_, region) => { + let contra = self.contravariant(variance); + self.add_constraints_from_region(region, contra); + } + ty::ty_rptr(region, ref mt) => { let contra = self.contravariant(variance); self.add_constraints_from_region(region, contra); diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index 46039a70fb21d..7d9a23e91bae9 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -528,7 +528,7 @@ pub enum Expr_ { ExprMatch(Gc, Vec), ExprFnBlock(CaptureClause, P, P), ExprProc(P, P), - ExprUnboxedFn(CaptureClause, P, P), + ExprUnboxedFn(CaptureClause, UnboxedClosureKind, P, P), ExprBlock(P), ExprAssign(Gc, Gc), @@ -900,6 +900,7 @@ pub struct BareFnTy { #[deriving(Clone, PartialEq, Eq, Encodable, Decodable, Hash, Show)] pub struct UnboxedFnTy { + pub kind: UnboxedClosureKind, pub decl: P, } @@ -1297,6 +1298,13 @@ pub enum ForeignItem_ { ForeignItemStatic(P, /* is_mutbl */ bool), } +#[deriving(Clone, PartialEq, Eq, Encodable, Decodable, Hash, Show)] +pub enum UnboxedClosureKind { + FnUnboxedClosureKind, + FnMutUnboxedClosureKind, + FnOnceUnboxedClosureKind, +} + /// The data we save and restore about an inlined item or method. This is not /// part of the AST that we parse from a file, but it becomes part of the tree /// that we trans. diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs index 02f1f4646a466..cebe186252825 100644 --- a/src/libsyntax/fold.rs +++ b/src/libsyntax/fold.rs @@ -368,6 +368,7 @@ pub fn noop_fold_ty(t: P, fld: &mut T) -> P { TyUnboxedFn(ref f) => { TyUnboxedFn(box(GC) UnboxedFnTy { decl: fld.fold_fn_decl(&*f.decl), + kind: f.kind, }) } TyTup(ref tys) => TyTup(tys.iter().map(|&ty| fld.fold_ty(ty)).collect()), @@ -641,6 +642,7 @@ pub fn noop_fold_ty_param_bound(tpb: &TyParamBound, fld: &mut T) UnboxedFnTyParamBound(ref unboxed_function_type) => { UnboxedFnTyParamBound(UnboxedFnTy { decl: fld.fold_fn_decl(&*unboxed_function_type.decl), + kind: unboxed_function_type.kind, }) } OtherRegionTyParamBound(s) => OtherRegionTyParamBound(s) @@ -1103,8 +1105,9 @@ pub fn noop_fold_expr(e: Gc, folder: &mut T) -> Gc { ExprProc(folder.fold_fn_decl(&**decl), folder.fold_block(body.clone())) } - ExprUnboxedFn(capture_clause, ref decl, ref body) => { + ExprUnboxedFn(capture_clause, kind, ref decl, ref body) => { ExprUnboxedFn(capture_clause, + kind, folder.fold_fn_decl(&**decl), folder.fold_block(*body)) } diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index f0920603ad12e..f6db577a00453 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -30,6 +30,8 @@ use ast::{ExprRepeat, ExprRet, ExprStruct, ExprTup, ExprUnary, ExprUnboxedFn}; use ast::{ExprVec, ExprVstore, ExprVstoreSlice}; use ast::{ExprVstoreMutSlice, ExprWhile, ExprForLoop, Field, FnDecl}; use ast::{ExprVstoreUniq, Once, Many}; +use ast::{FnUnboxedClosureKind, FnMutUnboxedClosureKind}; +use ast::{FnOnceUnboxedClosureKind}; use ast::{ForeignItem, ForeignItemStatic, ForeignItemFn, ForeignMod}; use ast::{Ident, NormalFn, Inherited, Item, Item_, ItemStatic}; use ast::{ItemEnum, ItemFn, ItemForeignMod, ItemImpl}; @@ -53,7 +55,8 @@ use ast::{TypeField, TyFixedLengthVec, TyClosure, TyProc, TyBareFn}; use ast::{TyTypeof, TyInfer, TypeMethod}; use ast::{TyNil, TyParam, TyParamBound, TyParen, TyPath, TyPtr, TyRptr}; use ast::{TyTup, TyU32, TyUnboxedFn, TyUniq, TyVec, UnUniq}; -use ast::{UnboxedFnTy, UnboxedFnTyParamBound, UnnamedField, UnsafeBlock}; +use ast::{UnboxedClosureKind, UnboxedFnTy, UnboxedFnTyParamBound}; +use ast::{UnnamedField, UnsafeBlock}; use ast::{UnsafeFn, ViewItem, ViewItem_, ViewItemExternCrate, ViewItemUse}; use ast::{ViewPath, ViewPathGlob, ViewPathList, ViewPathSimple}; use ast::Visibility; @@ -1087,6 +1090,34 @@ impl<'a> Parser<'a> { }) } + /// Parses an optional unboxed closure kind (`&:`, `&mut:`, or `:`). + pub fn parse_optional_unboxed_closure_kind(&mut self) + -> Option { + if self.token == token::BINOP(token::AND) && + self.look_ahead(1, |t| { + token::is_keyword(keywords::Mut, t) + }) && + self.look_ahead(2, |t| *t == token::COLON) { + self.bump(); + self.bump(); + self.bump(); + return Some(FnMutUnboxedClosureKind) + } + + if self.token == token::BINOP(token::AND) && + self.look_ahead(1, |t| *t == token::COLON) { + self.bump(); + self.bump(); + return Some(FnUnboxedClosureKind) + } + + if self.eat(&token::COLON) { + return Some(FnOnceUnboxedClosureKind) + } + + return None + } + /// Parse a TyClosure type pub fn parse_ty_closure(&mut self) -> Ty_ { /* @@ -1115,27 +1146,19 @@ impl<'a> Parser<'a> { Vec::new() }; - let (is_unboxed, inputs) = if self.eat(&token::OROR) { - (false, Vec::new()) + let (optional_unboxed_closure_kind, inputs) = if self.eat(&token::OROR) { + (None, Vec::new()) } else { self.expect_or(); - let is_unboxed = self.token == token::BINOP(token::AND) && - self.look_ahead(1, |t| { - token::is_keyword(keywords::Mut, t) - }) && - self.look_ahead(2, |t| *t == token::COLON); - if is_unboxed { - self.bump(); - self.bump(); - self.bump(); - } + let optional_unboxed_closure_kind = + self.parse_optional_unboxed_closure_kind(); let inputs = self.parse_seq_to_before_or( &token::COMMA, |p| p.parse_arg_general(false)); self.expect_or(); - (is_unboxed, inputs) + (optional_unboxed_closure_kind, inputs) }; let (region, bounds) = { @@ -1155,18 +1178,22 @@ impl<'a> Parser<'a> { variadic: false }); - if is_unboxed { - TyUnboxedFn(box(GC) UnboxedFnTy { - decl: decl, - }) - } else { - TyClosure(box(GC) ClosureTy { - fn_style: fn_style, - onceness: onceness, - bounds: bounds, - decl: decl, - lifetimes: lifetime_defs, - }, region) + match optional_unboxed_closure_kind { + Some(unboxed_closure_kind) => { + TyUnboxedFn(box(GC) UnboxedFnTy { + kind: unboxed_closure_kind, + decl: decl, + }) + } + None => { + TyClosure(box(GC) ClosureTy { + fn_style: fn_style, + onceness: onceness, + bounds: bounds, + decl: decl, + lifetimes: lifetime_defs, + }, region) + } } } @@ -2703,7 +2730,8 @@ impl<'a> Parser<'a> { 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 (decl, optional_unboxed_closure_kind) = + self.parse_fn_block_decl(); let body = self.parse_expr(); let fakeblock = P(ast::Block { view_items: Vec::new(), @@ -2714,14 +2742,20 @@ impl<'a> Parser<'a> { span: body.span, }); - if is_unboxed { - self.mk_expr(lo, - body.span.hi, - ExprUnboxedFn(capture_clause, decl, fakeblock)) - } else { - self.mk_expr(lo, - body.span.hi, - ExprFnBlock(capture_clause, decl, fakeblock)) + match optional_unboxed_closure_kind { + Some(unboxed_closure_kind) => { + self.mk_expr(lo, + body.span.hi, + ExprUnboxedFn(capture_clause, + unboxed_closure_kind, + decl, + fakeblock)) + } + None => { + self.mk_expr(lo, + body.span.hi, + ExprFnBlock(capture_clause, decl, fakeblock)) + } } } @@ -3553,28 +3587,22 @@ impl<'a> Parser<'a> { } fn parse_unboxed_function_type(&mut self) -> UnboxedFnTy { - let inputs = if self.eat(&token::OROR) { - Vec::new() - } else { - self.expect_or(); + let (optional_unboxed_closure_kind, inputs) = + if self.eat(&token::OROR) { + (None, Vec::new()) + } else { + self.expect_or(); - if self.token == token::BINOP(token::AND) && - self.look_ahead(1, |t| { - token::is_keyword(keywords::Mut, t) - }) && - self.look_ahead(2, |t| *t == token::COLON) { - self.bump(); - self.bump(); - self.bump(); - } + let optional_unboxed_closure_kind = + self.parse_optional_unboxed_closure_kind(); - let inputs = self.parse_seq_to_before_or(&token::COMMA, - |p| { - p.parse_arg_general(false) - }); - self.expect_or(); - inputs - }; + let inputs = self.parse_seq_to_before_or(&token::COMMA, + |p| { + p.parse_arg_general(false) + }); + self.expect_or(); + (optional_unboxed_closure_kind, inputs) + }; let (return_style, output) = self.parse_ret_ty(); UnboxedFnTy { @@ -3583,7 +3611,11 @@ impl<'a> Parser<'a> { output: output, cf: return_style, variadic: false, - }) + }), + kind: match optional_unboxed_closure_kind { + Some(kind) => kind, + None => FnMutUnboxedClosureKind, + }, } } @@ -4026,29 +4058,22 @@ impl<'a> Parser<'a> { } // parse the |arg, arg| header on a lambda - fn parse_fn_block_decl(&mut self) -> (P, bool) { - let (is_unboxed, inputs_captures) = { + fn parse_fn_block_decl(&mut self) + -> (P, Option) { + let (optional_unboxed_closure_kind, inputs_captures) = { if self.eat(&token::OROR) { - (false, Vec::new()) + (None, Vec::new()) } else { self.expect(&token::BINOP(token::OR)); - let is_unboxed = self.token == token::BINOP(token::AND) && - self.look_ahead(1, |t| { - token::is_keyword(keywords::Mut, t) - }) && - self.look_ahead(2, |t| *t == token::COLON); - if is_unboxed { - self.bump(); - self.bump(); - self.bump(); - } + let optional_unboxed_closure_kind = + self.parse_optional_unboxed_closure_kind(); let args = self.parse_seq_to_before_end( &token::BINOP(token::OR), seq_sep_trailing_disallowed(token::COMMA), |p| p.parse_fn_block_arg() ); self.bump(); - (is_unboxed, args) + (optional_unboxed_closure_kind, args) } }; let output = if self.eat(&token::RARROW) { @@ -4066,7 +4091,7 @@ impl<'a> Parser<'a> { output: output, cf: Return, variadic: false - }), is_unboxed) + }), optional_unboxed_closure_kind) } /// Parses the `(arg, arg) -> return_type` header on a procedure. diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index ed285e2aa443a..4ee73406f0340 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -9,8 +9,10 @@ // except according to those terms. use abi; -use ast::{P, StaticRegionTyParamBound, OtherRegionTyParamBound}; -use ast::{TraitTyParamBound, UnboxedFnTyParamBound, Required, Provided}; +use ast::{FnMutUnboxedClosureKind, FnOnceUnboxedClosureKind}; +use ast::{FnUnboxedClosureKind, P, OtherRegionTyParamBound}; +use ast::{StaticRegionTyParamBound, TraitTyParamBound, UnboxedClosureKind}; +use ast::{UnboxedFnTyParamBound, Required, Provided}; use ast; use ast_util; use owned_slice::OwnedSlice; @@ -228,7 +230,7 @@ pub fn method_to_string(p: &ast::Method) -> String { } pub fn fn_block_to_string(p: &ast::FnDecl) -> String { - $to_string(|s| s.print_fn_block_args(p, false)) + $to_string(|s| s.print_fn_block_args(p, None)) } pub fn path_to_string(p: &ast::Path) -> String { @@ -594,7 +596,7 @@ impl<'a> State<'a> { &None, Some(&generics), None, - false)); + None)); } ast::TyClosure(f, ref region) => { let generics = ast::Generics { @@ -611,7 +613,7 @@ impl<'a> State<'a> { &f.bounds, Some(&generics), None, - false)); + None)); } ast::TyProc(ref f) => { let generics = ast::Generics { @@ -628,7 +630,7 @@ impl<'a> State<'a> { &f.bounds, Some(&generics), None, - false)); + None)); } ast::TyUnboxedFn(f) => { try!(self.print_ty_fn(None, @@ -641,7 +643,7 @@ impl<'a> State<'a> { &None, None, None, - true)); + Some(f.kind))); } ast::TyPath(ref path, ref bounds, _) => { try!(self.print_bounded_path(path, bounds)); @@ -1054,7 +1056,7 @@ impl<'a> State<'a> { &None, Some(&m.generics), Some(m.explicit_self.node), - false)); + None)); word(&mut self.s, ";") } @@ -1481,7 +1483,7 @@ impl<'a> State<'a> { // we are inside. // // if !decl.inputs.is_empty() { - try!(self.print_fn_block_args(&**decl, false)); + try!(self.print_fn_block_args(&**decl, None)); try!(space(&mut self.s)); // } @@ -1505,7 +1507,7 @@ impl<'a> State<'a> { // empty box to satisfy the close. try!(self.ibox(0)); } - ast::ExprUnboxedFn(capture_clause, ref decl, ref body) => { + ast::ExprUnboxedFn(capture_clause, kind, ref decl, ref body) => { try!(self.print_capture_clause(capture_clause)); // in do/for blocks we don't want to show an empty @@ -1513,7 +1515,7 @@ impl<'a> State<'a> { // we are inside. // // if !decl.inputs.is_empty() { - try!(self.print_fn_block_args(&**decl, true)); + try!(self.print_fn_block_args(&**decl, Some(kind))); try!(space(&mut self.s)); // } @@ -2052,13 +2054,17 @@ impl<'a> State<'a> { } } - pub fn print_fn_block_args(&mut self, - decl: &ast::FnDecl, - is_unboxed: bool) - -> IoResult<()> { + pub fn print_fn_block_args( + &mut self, + decl: &ast::FnDecl, + unboxed_closure_kind: Option) + -> IoResult<()> { try!(word(&mut self.s, "|")); - if is_unboxed { - try!(self.word_space("&mut:")); + match unboxed_closure_kind { + None => {} + Some(FnUnboxedClosureKind) => try!(self.word_space("&:")), + Some(FnMutUnboxedClosureKind) => try!(self.word_space("&mut:")), + Some(FnOnceUnboxedClosureKind) => try!(self.word_space(":")), } try!(self.print_fn_args(decl, None)); try!(word(&mut self.s, "|")); @@ -2148,7 +2154,7 @@ impl<'a> State<'a> { &None, None, None, - true) + Some(unboxed_function_type.kind)) } OtherRegionTyParamBound(_) => Ok(()) }) @@ -2366,7 +2372,8 @@ impl<'a> State<'a> { opt_bounds: &Option>, generics: Option<&ast::Generics>, opt_explicit_self: Option, - is_unboxed: bool) + opt_unboxed_closure_kind: + Option) -> IoResult<()> { try!(self.ibox(indent_unit)); @@ -2383,7 +2390,7 @@ impl<'a> State<'a> { try!(self.print_fn_style(fn_style)); try!(self.print_opt_abi_and_extern_if_nondefault(opt_abi)); try!(self.print_onceness(onceness)); - if !is_unboxed { + if opt_unboxed_closure_kind.is_none() { try!(word(&mut self.s, "fn")); } } @@ -2399,20 +2406,30 @@ impl<'a> State<'a> { match generics { Some(g) => try!(self.print_generics(g)), _ => () } try!(zerobreak(&mut self.s)); - if is_unboxed || opt_sigil == Some('&') { + if opt_unboxed_closure_kind.is_some() || opt_sigil == Some('&') { try!(word(&mut self.s, "|")); } else { try!(self.popen()); } - if is_unboxed { - try!(word(&mut self.s, "&mut")); - try!(self.word_space(":")); + match opt_unboxed_closure_kind { + Some(ast::FnUnboxedClosureKind) => { + try!(word(&mut self.s, "&")); + try!(self.word_space(":")); + } + Some(ast::FnMutUnboxedClosureKind) => { + try!(word(&mut self.s, "&mut")); + try!(self.word_space(":")); + } + Some(ast::FnOnceUnboxedClosureKind) => { + try!(self.word_space(":")); + } + None => {} } try!(self.print_fn_args(decl, opt_explicit_self)); - if is_unboxed || opt_sigil == Some('&') { + if opt_unboxed_closure_kind.is_some() || opt_sigil == Some('&') { try!(word(&mut self.s, "|")); } else { if decl.variadic { diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs index 372cee9ad0988..79a870422a68b 100644 --- a/src/libsyntax/visit.rs +++ b/src/libsyntax/visit.rs @@ -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/compile-fail/borrowck-unboxed-closures.rs b/src/test/compile-fail/borrowck-unboxed-closures.rs new file mode 100644 index 0000000000000..d822bb22e2a72 --- /dev/null +++ b/src/test/compile-fail/borrowck-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. + +#![feature(overloaded_calls)] + +fn a int>(mut f: F) { + let g = &mut f; + f(1, 2); //~ ERROR cannot borrow `f` as immutable + //~^ ERROR cannot borrow `f` as immutable +} + +fn b int>(f: F) { + f(1, 2); //~ ERROR cannot borrow immutable argument +} + +fn c int>(f: F) { + f(1, 2); + f(1, 2); //~ ERROR use of moved value +} + +fn main() {} + diff --git a/src/test/compile-fail/regionck-unboxed-closure-lifetimes.rs b/src/test/compile-fail/regionck-unboxed-closure-lifetimes.rs new file mode 100644 index 0000000000000..1c590db11e399 --- /dev/null +++ b/src/test/compile-fail/regionck-unboxed-closure-lifetimes.rs @@ -0,0 +1,24 @@ +// 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. + +#![feature(unboxed_closure_sugar, unboxed_closures, overloaded_calls)] + +use std::ops::FnMut; + +fn main() { + let mut f; + { + let c = 1; + let c_ref = &c; + f = |&mut: a: int, b: int| { a + b + *c_ref }; + //~^ ERROR cannot infer an appropriate lifetime + } +} + diff --git a/src/test/compile-fail/unboxed-closures-wrong-trait.rs b/src/test/compile-fail/unboxed-closures-wrong-trait.rs new file mode 100644 index 0000000000000..50d90c6200e12 --- /dev/null +++ b/src/test/compile-fail/unboxed-closures-wrong-trait.rs @@ -0,0 +1,22 @@ +// 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. + +#![feature(lang_items, overloaded_calls, unboxed_closures)] + +fn c int>(f: F) -> int { + f(5, 6) +} + +fn main() { + let z: int = 7; + assert_eq!(c(|&: x: int, y| x + y + z), 10); + //~^ ERROR failed to find an implementation +} + diff --git a/src/test/run-pass/unboxed-closures-all-traits.rs b/src/test/run-pass/unboxed-closures-all-traits.rs new file mode 100644 index 0000000000000..c362a83e60c4a --- /dev/null +++ b/src/test/run-pass/unboxed-closures-all-traits.rs @@ -0,0 +1,31 @@ +// 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. + +#![feature(lang_items, overloaded_calls, unboxed_closures)] + +fn a int>(f: F) -> int { + f(1, 2) +} + +fn b int>(mut f: F) -> int { + f(3, 4) +} + +fn c int>(f: F) -> int { + f(5, 6) +} + +fn main() { + let z: int = 7; + assert_eq!(a(|&: x: int, y| x + y + z), 10); + assert_eq!(b(|&mut: x: int, y| x + y + z), 14); + assert_eq!(c(|: x: int, y| x + y + z), 18); +} + diff --git a/src/test/run-pass/unboxed-closures-drop.rs b/src/test/run-pass/unboxed-closures-drop.rs new file mode 100644 index 0000000000000..f20dddcae54b3 --- /dev/null +++ b/src/test/run-pass/unboxed-closures-drop.rs @@ -0,0 +1,127 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// A battery of tests to ensure destructors of unboxed closure environments +// run at the right times. + +#![feature(overloaded_calls, unboxed_closures)] + +static mut DROP_COUNT: uint = 0; + +fn drop_count() -> uint { + unsafe { + DROP_COUNT + } +} + +struct Droppable { + x: int, +} + +impl Droppable { + fn new() -> Droppable { + Droppable { + x: 1 + } + } +} + +impl Drop for Droppable { + fn drop(&mut self) { + unsafe { + DROP_COUNT += 1 + } + } +} + +fn a int>(f: F) -> int { + f(1, 2) +} + +fn b int>(mut f: F) -> int { + f(3, 4) +} + +fn c int>(f: F) -> int { + f(5, 6) +} + +fn test_fn() { + { + a(|&: a: int, b| { a + b }); + } + assert_eq!(drop_count(), 0); + + { + let z = &Droppable::new(); + a(|&: a: int, b| { z; a + b }); + assert_eq!(drop_count(), 0); + } + assert_eq!(drop_count(), 1); + + { + let z = &Droppable::new(); + let zz = &Droppable::new(); + a(|&: a: int, b| { z; zz; a + b }); + assert_eq!(drop_count(), 1); + } + assert_eq!(drop_count(), 3); +} + +fn test_fn_mut() { + { + b(|&mut: a: int, b| { a + b }); + } + assert_eq!(drop_count(), 3); + + { + let z = &Droppable::new(); + b(|&mut: a: int, b| { z; a + b }); + assert_eq!(drop_count(), 3); + } + assert_eq!(drop_count(), 4); + + { + let z = &Droppable::new(); + let zz = &Droppable::new(); + b(|&mut: a: int, b| { z; zz; a + b }); + assert_eq!(drop_count(), 4); + } + assert_eq!(drop_count(), 6); +} + +fn test_fn_once() { + { + c(|: a: int, b| { a + b }); + } + assert_eq!(drop_count(), 6); + + { + let z = Droppable::new(); + c(|: a: int, b| { z; a + b }); + assert_eq!(drop_count(), 7); + } + assert_eq!(drop_count(), 7); + + { + let z = Droppable::new(); + let zz = Droppable::new(); + c(|: a: int, b| { z; zz; a + b }); + assert_eq!(drop_count(), 9); + } + assert_eq!(drop_count(), 9); +} + +fn main() { + test_fn(); + test_fn_mut(); + test_fn_once(); +} + diff --git a/src/test/run-pass/unboxed-closures-single-word-env.rs b/src/test/run-pass/unboxed-closures-single-word-env.rs new file mode 100644 index 0000000000000..754b1f706444b --- /dev/null +++ b/src/test/run-pass/unboxed-closures-single-word-env.rs @@ -0,0 +1,34 @@ +// 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. + +// Ensures that single-word environments work right in unboxed closures. +// These take a different path in codegen. + +#![feature(overloaded_calls, unboxed_closures)] + +fn a int>(f: F) -> int { + f(1, 2) +} + +fn b int>(mut f: F) -> int { + f(3, 4) +} + +fn c int>(f: F) -> int { + f(5, 6) +} + +fn main() { + let z = 10; + assert_eq!(a(|&: x: int, y| x + y + z), 13); + assert_eq!(b(|&mut: x: int, y| x + y + z), 17); + assert_eq!(c(|: x: int, y| x + y + z), 21); +} +