diff options
Diffstat (limited to 'src/librustc_trans/trans/closure.rs')
-rw-r--r-- | src/librustc_trans/trans/closure.rs | 323 |
1 files changed, 179 insertions, 144 deletions
diff --git a/src/librustc_trans/trans/closure.rs b/src/librustc_trans/trans/closure.rs index 11c03fe7a7d..2036feb31a2 100644 --- a/src/librustc_trans/trans/closure.rs +++ b/src/librustc_trans/trans/closure.rs @@ -10,10 +10,11 @@ use arena::TypedArena; use back::link::{self, mangle_internal_name_by_path_and_seq}; -use llvm::{ValueRef, get_params}; +use llvm::{ValueRef, get_param, get_params}; use middle::def_id::DefId; use middle::infer; use middle::traits::ProjectionMode; +use trans::abi::{Abi, FnType}; use trans::adt; use trans::attributes; use trans::base::*; @@ -21,44 +22,41 @@ use trans::build::*; use trans::callee::{self, ArgVals, Callee}; use trans::cleanup::{CleanupMethods, CustomScope, ScopeId}; use trans::common::*; -use trans::datum::{self, Datum, rvalue_scratch_datum, Rvalue}; +use trans::datum::{ByRef, Datum, lvalue_scratch_datum}; +use trans::datum::{rvalue_scratch_datum, Rvalue}; use trans::debuginfo::{self, DebugLoc}; use trans::declare; use trans::expr; -use trans::monomorphize::{MonoId}; -use trans::type_of::*; +use trans::monomorphize::{Instance}; +use trans::value::Value; use trans::Disr; -use middle::ty; +use middle::ty::{self, Ty, TyCtxt}; use session::config::FullDebugInfo; -use syntax::abi::Abi::RustCall; use syntax::ast; -use syntax::attr::{ThinAttributes, ThinAttributesExt}; use rustc_front::hir; +use libc::c_uint; fn load_closure_environment<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, closure_def_id: DefId, arg_scope_id: ScopeId, - freevars: &[ty::Freevar]) - -> Block<'blk, 'tcx> -{ + id: ast::NodeId) { let _icx = push_ctxt("closure::load_closure_environment"); + let kind = kind_for_closure(bcx.ccx(), closure_def_id); + + let env_arg = &bcx.fcx.fn_ty.args[0]; + let mut env_idx = bcx.fcx.fn_ty.ret.is_indirect() as usize; // Special case for small by-value selfs. - let closure_ty = node_id_type(bcx, bcx.fcx.id); - let self_type = self_type_for_closure(bcx.ccx(), closure_def_id, closure_ty); - let kind = kind_for_closure(bcx.ccx(), closure_def_id); - let llenv = if kind == ty::ClosureKind::FnOnce && - !arg_is_indirect(bcx.ccx(), self_type) { - let datum = rvalue_scratch_datum(bcx, - self_type, - "closure_env"); - store_ty(bcx, bcx.fcx.llenv.unwrap(), datum.val, self_type); - datum.val + let llenv = if kind == ty::ClosureKind::FnOnce && !env_arg.is_indirect() { + let closure_ty = node_id_type(bcx, id); + let llenv = rvalue_scratch_datum(bcx, closure_ty, "closure_env").val; + env_arg.store_fn_arg(&bcx.build(), &mut env_idx, llenv); + llenv } else { - bcx.fcx.llenv.unwrap() + get_param(bcx.fcx.llfn, env_idx as c_uint) }; // Store the pointer to closure data in an alloca for debug info because that's what the @@ -71,100 +69,120 @@ fn load_closure_environment<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, None }; - for (i, freevar) in freevars.iter().enumerate() { - let upvar_id = ty::UpvarId { var_id: freevar.def.var_id(), - closure_expr_id: bcx.fcx.id }; - let upvar_capture = bcx.tcx().upvar_capture(upvar_id).unwrap(); - let mut upvar_ptr = StructGEP(bcx, llenv, i); - let captured_by_ref = match upvar_capture { - ty::UpvarCapture::ByValue => false, - ty::UpvarCapture::ByRef(..) => { - upvar_ptr = Load(bcx, upvar_ptr); - true + bcx.tcx().with_freevars(id, |fv| { + for (i, freevar) in fv.iter().enumerate() { + let upvar_id = ty::UpvarId { var_id: freevar.def.var_id(), + closure_expr_id: id }; + let upvar_capture = bcx.tcx().upvar_capture(upvar_id).unwrap(); + let mut upvar_ptr = StructGEP(bcx, llenv, i); + let captured_by_ref = match upvar_capture { + ty::UpvarCapture::ByValue => false, + ty::UpvarCapture::ByRef(..) => { + upvar_ptr = Load(bcx, upvar_ptr); + true + } + }; + let node_id = freevar.def.var_id(); + bcx.fcx.llupvars.borrow_mut().insert(node_id, upvar_ptr); + + if kind == ty::ClosureKind::FnOnce && !captured_by_ref { + let hint = bcx.fcx.lldropflag_hints.borrow().hint_datum(upvar_id.var_id); + bcx.fcx.schedule_drop_mem(arg_scope_id, + upvar_ptr, + node_id_type(bcx, node_id), + hint) } - }; - let node_id = freevar.def.var_id(); - bcx.fcx.llupvars.borrow_mut().insert(node_id, upvar_ptr); - - if kind == ty::ClosureKind::FnOnce && !captured_by_ref { - let hint = bcx.fcx.lldropflag_hints.borrow().hint_datum(upvar_id.var_id); - bcx.fcx.schedule_drop_mem(arg_scope_id, - upvar_ptr, - node_id_type(bcx, node_id), - hint) - } - if let Some(env_pointer_alloca) = env_pointer_alloca { - debuginfo::create_captured_var_metadata( - bcx, - node_id, - env_pointer_alloca, - i, - captured_by_ref, - freevar.span); + if let Some(env_pointer_alloca) = env_pointer_alloca { + debuginfo::create_captured_var_metadata( + bcx, + node_id, + env_pointer_alloca, + i, + captured_by_ref, + freevar.span); + } } - } - - bcx + }) } -pub enum ClosureEnv<'a> { +pub enum ClosureEnv { NotClosure, - Closure(DefId, &'a [ty::Freevar]), + Closure(DefId, ast::NodeId), } -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(def_id, freevars) => { - if freevars.is_empty() { - bcx - } else { - load_closure_environment(bcx, def_id, arg_scope, freevars) - } - } +impl ClosureEnv { + pub fn load<'blk,'tcx>(self, bcx: Block<'blk, 'tcx>, arg_scope: ScopeId) { + if let ClosureEnv::Closure(def_id, id) = self { + load_closure_environment(bcx, def_id, arg_scope, id); } } } +fn get_self_type<'tcx>(tcx: &TyCtxt<'tcx>, + closure_id: DefId, + fn_ty: Ty<'tcx>) + -> Ty<'tcx> { + match tcx.closure_kind(closure_id) { + ty::ClosureKind::Fn => { + tcx.mk_imm_ref(tcx.mk_region(ty::ReStatic), fn_ty) + } + ty::ClosureKind::FnMut => { + tcx.mk_mut_ref(tcx.mk_region(ty::ReStatic), fn_ty) + } + ty::ClosureKind::FnOnce => fn_ty, + } +} + /// Returns the LLVM function declaration for a closure, creating it if /// necessary. If the ID does not correspond to a closure ID, returns None. -pub fn get_or_create_closure_declaration<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, - closure_id: DefId, - substs: &ty::ClosureSubsts<'tcx>) - -> ValueRef { +fn get_or_create_closure_declaration<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, + closure_id: DefId, + substs: &ty::ClosureSubsts<'tcx>) + -> ValueRef { // Normalize type so differences in regions and typedefs don't cause // duplicate declarations - let substs = ccx.tcx().erase_regions(substs); - let mono_id = MonoId { + let tcx = ccx.tcx(); + let substs = tcx.erase_regions(substs); + let instance = Instance { def: closure_id, params: &substs.func_substs.types }; - if let Some(&llfn) = ccx.closure_vals().borrow().get(&mono_id) { + if let Some(&llfn) = ccx.instances().borrow().get(&instance) { debug!("get_or_create_closure_declaration(): found closure {:?}: {:?}", - mono_id, ccx.tn().val_to_string(llfn)); + instance, Value(llfn)); return llfn; } - let path = ccx.tcx().def_path(closure_id); + let path = tcx.def_path(closure_id); let symbol = mangle_internal_name_by_path_and_seq(path, "closure"); - let function_type = ccx.tcx().mk_closure_from_closure_substs(closure_id, Box::new(substs)); - let llfn = declare::define_internal_rust_fn(ccx, &symbol[..], function_type); + // Compute the rust-call form of the closure call method. + let infcx = infer::normalizing_infer_ctxt(tcx, &tcx.tables, ProjectionMode::Any); + let sig = &infcx.closure_type(closure_id, &substs).sig; + let sig = tcx.erase_late_bound_regions(sig); + let sig = infer::normalize_associated_type(tcx, &sig); + let closure_type = tcx.mk_closure_from_closure_substs(closure_id, Box::new(substs)); + let function_type = tcx.mk_fn_ptr(ty::BareFnTy { + unsafety: hir::Unsafety::Normal, + abi: Abi::RustCall, + sig: ty::Binder(ty::FnSig { + inputs: Some(get_self_type(tcx, closure_id, closure_type)) + .into_iter().chain(sig.inputs).collect(), + output: sig.output, + variadic: false + }) + }); + let llfn = declare::define_internal_fn(ccx, &symbol, function_type); // set an inline hint for all closures attributes::inline(llfn, attributes::InlineAttr::Hint); debug!("get_or_create_declaration_if_closure(): inserting new \ - closure {:?} (type {}): {:?}", - mono_id, - ccx.tn().type_to_string(val_ty(llfn)), - ccx.tn().val_to_string(llfn)); - ccx.closure_vals().borrow_mut().insert(mono_id, llfn); + closure {:?}: {:?}", + instance, Value(llfn)); + ccx.instances().borrow_mut().insert(instance, llfn); llfn } @@ -179,8 +197,7 @@ pub fn trans_closure_expr<'a, 'tcx>(dest: Dest<'a, 'tcx>, body: &hir::Block, id: ast::NodeId, closure_def_id: DefId, // (*) - closure_substs: &'tcx ty::ClosureSubsts<'tcx>, - closure_expr_attrs: &ThinAttributes) + closure_substs: &ty::ClosureSubsts<'tcx>) -> Option<Block<'a, 'tcx>> { // (*) Note that in the case of inlined functions, the `closure_def_id` will be the @@ -210,22 +227,29 @@ pub fn trans_closure_expr<'a, 'tcx>(dest: Dest<'a, 'tcx>, let infcx = infer::normalizing_infer_ctxt(ccx.tcx(), &ccx.tcx().tables, ProjectionMode::Any); let function_type = infcx.closure_type(closure_def_id, closure_substs); - let freevars: Vec<ty::Freevar> = - tcx.with_freevars(id, |fv| fv.iter().cloned().collect()); - let sig = tcx.erase_late_bound_regions(&function_type.sig); let sig = infer::normalize_associated_type(ccx.tcx(), &sig); + let closure_type = tcx.mk_closure_from_closure_substs(closure_def_id, + Box::new(closure_substs.clone())); + let sig = ty::FnSig { + inputs: Some(get_self_type(tcx, closure_def_id, closure_type)) + .into_iter().chain(sig.inputs).collect(), + output: sig.output, + variadic: false + }; + let fn_ty = FnType::new(ccx, Abi::RustCall, &sig, &[]); + trans_closure(ccx, decl, body, llfn, param_substs, + closure_def_id, id, - closure_expr_attrs.as_attr_slice(), - sig.output, - function_type.abi, - ClosureEnv::Closure(closure_def_id, &freevars)); + fn_ty, + Abi::RustCall, + ClosureEnv::Closure(closure_def_id, id)); // 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 @@ -241,21 +265,23 @@ pub fn trans_closure_expr<'a, 'tcx>(dest: Dest<'a, 'tcx>, let repr = adt::represent_type(ccx, node_id_type(bcx, id)); // Create the closure. - for (i, freevar) in freevars.iter().enumerate() { - let datum = expr::trans_local_var(bcx, freevar.def); - let upvar_slot_dest = adt::trans_field_ptr( - bcx, &repr, adt::MaybeSizedValue::sized(dest_addr), Disr(0), i); - let upvar_id = ty::UpvarId { var_id: freevar.def.var_id(), - closure_expr_id: id }; - match tcx.upvar_capture(upvar_id).unwrap() { - ty::UpvarCapture::ByValue => { - bcx = datum.store_to(bcx, upvar_slot_dest); - } - ty::UpvarCapture::ByRef(..) => { - Store(bcx, datum.to_llref(), upvar_slot_dest); + tcx.with_freevars(id, |fv| { + for (i, freevar) in fv.iter().enumerate() { + let datum = expr::trans_var(bcx, freevar.def); + let upvar_slot_dest = adt::trans_field_ptr( + bcx, &repr, adt::MaybeSizedValue::sized(dest_addr), Disr(0), i); + let upvar_id = ty::UpvarId { var_id: freevar.def.var_id(), + closure_expr_id: id }; + match tcx.upvar_capture(upvar_id).unwrap() { + ty::UpvarCapture::ByValue => { + bcx = datum.store_to(bcx, upvar_slot_dest); + } + ty::UpvarCapture::ByRef(..) => { + Store(bcx, datum.to_llref(), upvar_slot_dest); + } } } - } + }); adt::trans_set_discr(bcx, &repr, dest_addr, Disr(0)); Some(bcx) @@ -278,11 +304,8 @@ pub fn trans_closure_method<'a, 'tcx>(ccx: &'a CrateContext<'a, 'tcx>, let tcx = ccx.tcx(); debug!("trans_closure_adapter_shim(llfn_closure_kind={:?}, \ - trait_closure_kind={:?}, \ - llfn={})", - llfn_closure_kind, - trait_closure_kind, - ccx.tn().val_to_string(llfn)); + trait_closure_kind={:?}, llfn={:?})", + llfn_closure_kind, trait_closure_kind, Value(llfn)); match (llfn_closure_kind, trait_closure_kind) { (ty::ClosureKind::Fn, ty::ClosureKind::Fn) | @@ -324,10 +347,8 @@ fn trans_fn_once_adapter_shim<'a, 'tcx>( llreffn: ValueRef) -> ValueRef { - debug!("trans_fn_once_adapter_shim(closure_def_id={:?}, substs={:?}, llreffn={})", - closure_def_id, - substs, - ccx.tn().val_to_string(llreffn)); + debug!("trans_fn_once_adapter_shim(closure_def_id={:?}, substs={:?}, llreffn={:?})", + closure_def_id, substs, Value(llreffn)); let tcx = ccx.tcx(); let infcx = infer::normalizing_infer_ctxt(ccx.tcx(), &ccx.tcx().tables, ProjectionMode::Any); @@ -348,56 +369,70 @@ fn trans_fn_once_adapter_shim<'a, 'tcx>( debug!("trans_fn_once_adapter_shim: llref_fn_ty={:?}", llref_fn_ty); - let ret_ty = tcx.erase_late_bound_regions(&sig.output()); - let ret_ty = infer::normalize_associated_type(ccx.tcx(), &ret_ty); // Make a version of the closure type with the same arguments, but // with argument #0 being by value. - assert_eq!(abi, RustCall); + assert_eq!(abi, Abi::RustCall); sig.0.inputs[0] = closure_ty; + + let sig = tcx.erase_late_bound_regions(&sig); + let sig = infer::normalize_associated_type(ccx.tcx(), &sig); + let fn_ty = FnType::new(ccx, abi, &sig, &[]); + let llonce_fn_ty = tcx.mk_fn_ptr(ty::BareFnTy { unsafety: unsafety, abi: abi, - sig: sig + sig: ty::Binder(sig) }); // Create the by-value helper. let function_name = link::mangle_internal_name_by_type_and_seq(ccx, llonce_fn_ty, "once_shim"); - let lloncefn = declare::define_internal_rust_fn(ccx, &function_name, - llonce_fn_ty); + let lloncefn = declare::define_internal_fn(ccx, &function_name, llonce_fn_ty); let (block_arena, fcx): (TypedArena<_>, FunctionContext); block_arena = TypedArena::new(); - fcx = new_fn_ctxt(ccx, - lloncefn, - ast::DUMMY_NODE_ID, - false, - ret_ty, - substs.func_substs, - None, - &block_arena); - let mut bcx = init_function(&fcx, false, ret_ty); + fcx = FunctionContext::new(ccx, lloncefn, fn_ty, None, substs.func_substs, &block_arena); + let mut bcx = fcx.init(false, None); - let mut llargs = get_params(fcx.llfn); // the first argument (`self`) will be the (by value) closure env. let self_scope = fcx.push_custom_cleanup_scope(); let self_scope_id = CustomScope(self_scope); - let rvalue_mode = datum::appropriate_rvalue_mode(ccx, closure_ty); - let self_idx = fcx.arg_offset(); - let llself = llargs[self_idx]; - let env_datum = Datum::new(llself, closure_ty, Rvalue::new(rvalue_mode)); - let env_datum = unpack_datum!(bcx, - env_datum.to_lvalue_datum_in_scope(bcx, "self", - self_scope_id)); - - debug!("trans_fn_once_adapter_shim: env_datum={}", - bcx.val_to_string(env_datum.val)); - llargs[self_idx] = env_datum.val; + + let mut llargs = get_params(fcx.llfn); + let mut self_idx = fcx.fn_ty.ret.is_indirect() as usize; + let env_arg = &fcx.fn_ty.args[0]; + let llenv = if env_arg.is_indirect() { + Datum::new(llargs[self_idx], closure_ty, Rvalue::new(ByRef)) + .add_clean(&fcx, self_scope_id) + } else { + unpack_datum!(bcx, lvalue_scratch_datum(bcx, closure_ty, "self", + InitAlloca::Dropped, + self_scope_id, |bcx, llval| { + let mut llarg_idx = self_idx; + env_arg.store_fn_arg(&bcx.build(), &mut llarg_idx, llval); + bcx.fcx.schedule_lifetime_end(self_scope_id, llval); + bcx + })).val + }; + + debug!("trans_fn_once_adapter_shim: env={:?}", Value(llenv)); + // Adjust llargs such that llargs[self_idx..] has the call arguments. + // For zero-sized closures that means sneaking in a new argument. + if env_arg.is_ignore() { + if self_idx > 0 { + self_idx -= 1; + llargs[self_idx] = llenv; + } else { + llargs.insert(0, llenv); + } + } else { + llargs[self_idx] = llenv; + } let dest = fcx.llretslotptr.get().map( - |_| expr::SaveIn(fcx.get_ret_slot(bcx, ret_ty, "ret_slot"))); + |_| expr::SaveIn(fcx.get_ret_slot(bcx, "ret_slot"))); let callee = Callee { data: callee::Fn(llreffn), @@ -407,7 +442,7 @@ fn trans_fn_once_adapter_shim<'a, 'tcx>( fcx.pop_and_trans_custom_cleanup_scope(bcx, self_scope); - finish_fn(&fcx, bcx, ret_ty, DebugLoc::None); + fcx.finish(bcx, DebugLoc::None); lloncefn } |