summaryrefslogtreecommitdiff
path: root/src/librustc_trans/trans/closure.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/librustc_trans/trans/closure.rs')
-rw-r--r--src/librustc_trans/trans/closure.rs323
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
}