Skip to content

Commit 5795098

Browse files
committed
rustc_trans: translate closures using the collector
Translate closures like normal functions, using the trans::collector interface.
1 parent b6828fd commit 5795098

File tree

10 files changed

+241
-404
lines changed

10 files changed

+241
-404
lines changed

src/librustc_trans/base.rs

+25-38
Original file line numberDiff line numberDiff line change
@@ -1003,34 +1003,41 @@ impl<'blk, 'tcx> FunctionContext<'blk, 'tcx> {
10031003
}
10041004
}
10051005

1006-
/// Builds an LLVM function out of a source function.
1007-
///
1008-
/// If the function closes over its environment a closure will be returned.
1009-
pub fn trans_closure<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
1010-
llfndecl: ValueRef,
1011-
instance: Instance<'tcx>,
1012-
sig: &ty::FnSig<'tcx>,
1013-
abi: Abi) {
1006+
pub fn trans_instance<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, instance: Instance<'tcx>) {
1007+
let _s = StatRecorder::new(ccx, ccx.tcx().item_path_str(instance.def));
1008+
// this is an info! to allow collecting monomorphization statistics
1009+
// and to allow finding the last function before LLVM aborts from
1010+
// release builds.
1011+
info!("trans_instance({})", instance);
1012+
1013+
let _icx = push_ctxt("trans_instance");
1014+
1015+
let fn_ty = ccx.tcx().item_type(instance.def);
1016+
let fn_ty = ccx.tcx().erase_regions(&fn_ty);
1017+
let fn_ty = monomorphize::apply_param_substs(ccx.shared(), instance.substs, &fn_ty);
1018+
1019+
let ty::BareFnTy { abi, ref sig, .. } = *common::ty_fn_ty(ccx, fn_ty);
1020+
let sig = ccx.tcx().erase_late_bound_regions_and_normalize(sig);
1021+
1022+
let lldecl = match ccx.instances().borrow().get(&instance) {
1023+
Some(&val) => val,
1024+
None => bug!("Instance `{:?}` not already declared", instance)
1025+
};
1026+
10141027
ccx.stats().n_closures.set(ccx.stats().n_closures.get() + 1);
10151028

1016-
let _icx = push_ctxt("trans_closure");
10171029
if !ccx.sess().no_landing_pads() {
1018-
attributes::emit_uwtable(llfndecl, true);
1030+
attributes::emit_uwtable(lldecl, true);
10191031
}
10201032

1021-
// this is an info! to allow collecting monomorphization statistics
1022-
// and to allow finding the last function before LLVM aborts from
1023-
// release builds.
1024-
info!("trans_closure(..., {})", instance);
1025-
1026-
let fn_ty = FnType::new(ccx, abi, sig, &[]);
1033+
let fn_ty = FnType::new(ccx, abi, &sig, &[]);
10271034

10281035
let (arena, fcx): (TypedArena<_>, FunctionContext);
10291036
arena = TypedArena::new();
10301037
fcx = FunctionContext::new(ccx,
1031-
llfndecl,
1038+
lldecl,
10321039
fn_ty,
1033-
Some((instance, sig, abi)),
1040+
Some((instance, &sig, abi)),
10341041
&arena);
10351042

10361043
if fcx.mir.is_none() {
@@ -1040,26 +1047,6 @@ pub fn trans_closure<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
10401047
mir::trans_mir(&fcx);
10411048
}
10421049

1043-
pub fn trans_instance<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, instance: Instance<'tcx>) {
1044-
let _s = StatRecorder::new(ccx, ccx.tcx().item_path_str(instance.def));
1045-
debug!("trans_instance(instance={:?})", instance);
1046-
let _icx = push_ctxt("trans_instance");
1047-
1048-
let fn_ty = ccx.tcx().item_type(instance.def);
1049-
let fn_ty = ccx.tcx().erase_regions(&fn_ty);
1050-
let fn_ty = monomorphize::apply_param_substs(ccx.shared(), instance.substs, &fn_ty);
1051-
1052-
let sig = ccx.tcx().erase_late_bound_regions_and_normalize(fn_ty.fn_sig());
1053-
let abi = fn_ty.fn_abi();
1054-
1055-
let lldecl = match ccx.instances().borrow().get(&instance) {
1056-
Some(&val) => val,
1057-
None => bug!("Instance `{:?}` not already declared", instance)
1058-
};
1059-
1060-
trans_closure(ccx, lldecl, instance, &sig, abi);
1061-
}
1062-
10631050
pub fn trans_ctor_shim<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
10641051
def_id: DefId,
10651052
substs: &'tcx Substs<'tcx>,

src/librustc_trans/callee.rs

+171-6
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,11 @@ use attributes;
2626
use base;
2727
use base::*;
2828
use build::*;
29-
use closure;
3029
use common::{self, Block, Result, CrateContext, FunctionContext, SharedCrateContext};
3130
use consts;
3231
use debuginfo::DebugLoc;
3332
use declare;
33+
use value::Value;
3434
use meth;
3535
use monomorphize::{self, Instance};
3636
use trans_item::TransItem;
@@ -147,11 +147,12 @@ impl<'tcx> Callee<'tcx> {
147147
// after passing through fulfill_obligation
148148
let trait_closure_kind = tcx.lang_items.fn_trait_kind(trait_id).unwrap();
149149
let instance = Instance::new(def_id, substs);
150-
let llfn = closure::trans_closure_method(ccx,
151-
vtable_closure.closure_def_id,
152-
vtable_closure.substs,
153-
instance,
154-
trait_closure_kind);
150+
let llfn = trans_closure_method(
151+
ccx,
152+
vtable_closure.closure_def_id,
153+
vtable_closure.substs,
154+
instance,
155+
trait_closure_kind);
155156

156157
let method_ty = def_ty(ccx.shared(), def_id, substs);
157158
Callee::ptr(llfn, method_ty)
@@ -250,6 +251,170 @@ fn def_ty<'a, 'tcx>(shared: &SharedCrateContext<'a, 'tcx>,
250251
monomorphize::apply_param_substs(shared, substs, &ty)
251252
}
252253

254+
255+
fn trans_closure_method<'a, 'tcx>(ccx: &'a CrateContext<'a, 'tcx>,
256+
def_id: DefId,
257+
substs: ty::ClosureSubsts<'tcx>,
258+
method_instance: Instance<'tcx>,
259+
trait_closure_kind: ty::ClosureKind)
260+
-> ValueRef
261+
{
262+
// If this is a closure, redirect to it.
263+
let (llfn, _) = get_fn(ccx, def_id, substs.substs);
264+
265+
// If the closure is a Fn closure, but a FnOnce is needed (etc),
266+
// then adapt the self type
267+
let llfn_closure_kind = ccx.tcx().closure_kind(def_id);
268+
269+
let _icx = push_ctxt("trans_closure_adapter_shim");
270+
271+
debug!("trans_closure_adapter_shim(llfn_closure_kind={:?}, \
272+
trait_closure_kind={:?}, llfn={:?})",
273+
llfn_closure_kind, trait_closure_kind, Value(llfn));
274+
275+
match (llfn_closure_kind, trait_closure_kind) {
276+
(ty::ClosureKind::Fn, ty::ClosureKind::Fn) |
277+
(ty::ClosureKind::FnMut, ty::ClosureKind::FnMut) |
278+
(ty::ClosureKind::FnOnce, ty::ClosureKind::FnOnce) => {
279+
// No adapter needed.
280+
llfn
281+
}
282+
(ty::ClosureKind::Fn, ty::ClosureKind::FnMut) => {
283+
// The closure fn `llfn` is a `fn(&self, ...)`. We want a
284+
// `fn(&mut self, ...)`. In fact, at trans time, these are
285+
// basically the same thing, so we can just return llfn.
286+
llfn
287+
}
288+
(ty::ClosureKind::Fn, ty::ClosureKind::FnOnce) |
289+
(ty::ClosureKind::FnMut, ty::ClosureKind::FnOnce) => {
290+
// The closure fn `llfn` is a `fn(&self, ...)` or `fn(&mut
291+
// self, ...)`. We want a `fn(self, ...)`. We can produce
292+
// this by doing something like:
293+
//
294+
// fn call_once(self, ...) { call_mut(&self, ...) }
295+
// fn call_once(mut self, ...) { call_mut(&mut self, ...) }
296+
//
297+
// These are both the same at trans time.
298+
trans_fn_once_adapter_shim(ccx, def_id, substs, method_instance, llfn)
299+
}
300+
_ => {
301+
bug!("trans_closure_adapter_shim: cannot convert {:?} to {:?}",
302+
llfn_closure_kind,
303+
trait_closure_kind);
304+
}
305+
}
306+
}
307+
308+
fn trans_fn_once_adapter_shim<'a, 'tcx>(
309+
ccx: &'a CrateContext<'a, 'tcx>,
310+
def_id: DefId,
311+
substs: ty::ClosureSubsts<'tcx>,
312+
method_instance: Instance<'tcx>,
313+
llreffn: ValueRef)
314+
-> ValueRef
315+
{
316+
if let Some(&llfn) = ccx.instances().borrow().get(&method_instance) {
317+
return llfn;
318+
}
319+
320+
debug!("trans_fn_once_adapter_shim(def_id={:?}, substs={:?}, llreffn={:?})",
321+
def_id, substs, Value(llreffn));
322+
323+
let tcx = ccx.tcx();
324+
325+
// Find a version of the closure type. Substitute static for the
326+
// region since it doesn't really matter.
327+
let closure_ty = tcx.mk_closure_from_closure_substs(def_id, substs);
328+
let ref_closure_ty = tcx.mk_imm_ref(tcx.mk_region(ty::ReErased), closure_ty);
329+
330+
// Make a version with the type of by-ref closure.
331+
let ty::ClosureTy { unsafety, abi, mut sig } = tcx.closure_type(def_id, substs);
332+
sig.0.inputs.insert(0, ref_closure_ty); // sig has no self type as of yet
333+
let llref_fn_ty = tcx.mk_fn_ptr(tcx.mk_bare_fn(ty::BareFnTy {
334+
unsafety: unsafety,
335+
abi: abi,
336+
sig: sig.clone()
337+
}));
338+
debug!("trans_fn_once_adapter_shim: llref_fn_ty={:?}",
339+
llref_fn_ty);
340+
341+
342+
// Make a version of the closure type with the same arguments, but
343+
// with argument #0 being by value.
344+
assert_eq!(abi, Abi::RustCall);
345+
sig.0.inputs[0] = closure_ty;
346+
347+
let sig = tcx.erase_late_bound_regions_and_normalize(&sig);
348+
let fn_ty = FnType::new(ccx, abi, &sig, &[]);
349+
350+
let llonce_fn_ty = tcx.mk_fn_ptr(tcx.mk_bare_fn(ty::BareFnTy {
351+
unsafety: unsafety,
352+
abi: abi,
353+
sig: ty::Binder(sig)
354+
}));
355+
356+
// Create the by-value helper.
357+
let function_name = method_instance.symbol_name(ccx.shared());
358+
let lloncefn = declare::define_internal_fn(ccx, &function_name, llonce_fn_ty);
359+
attributes::set_frame_pointer_elimination(ccx, lloncefn);
360+
361+
let (block_arena, fcx): (TypedArena<_>, FunctionContext);
362+
block_arena = TypedArena::new();
363+
fcx = FunctionContext::new(ccx, lloncefn, fn_ty, None, &block_arena);
364+
let mut bcx = fcx.init(false);
365+
366+
367+
// the first argument (`self`) will be the (by value) closure env.
368+
369+
let mut llargs = get_params(fcx.llfn);
370+
let mut self_idx = fcx.fn_ty.ret.is_indirect() as usize;
371+
let env_arg = &fcx.fn_ty.args[0];
372+
let llenv = if env_arg.is_indirect() {
373+
llargs[self_idx]
374+
} else {
375+
let scratch = alloc_ty(bcx, closure_ty, "self");
376+
let mut llarg_idx = self_idx;
377+
env_arg.store_fn_arg(&bcx.build(), &mut llarg_idx, scratch);
378+
scratch
379+
};
380+
381+
debug!("trans_fn_once_adapter_shim: env={:?}", Value(llenv));
382+
// Adjust llargs such that llargs[self_idx..] has the call arguments.
383+
// For zero-sized closures that means sneaking in a new argument.
384+
if env_arg.is_ignore() {
385+
if self_idx > 0 {
386+
self_idx -= 1;
387+
llargs[self_idx] = llenv;
388+
} else {
389+
llargs.insert(0, llenv);
390+
}
391+
} else {
392+
llargs[self_idx] = llenv;
393+
}
394+
395+
let dest = fcx.llretslotptr.get();
396+
397+
let callee = Callee {
398+
data: Fn(llreffn),
399+
ty: llref_fn_ty
400+
};
401+
402+
// Call the by-ref closure body with `self` in a cleanup scope,
403+
// to drop `self` when the body returns, or in case it unwinds.
404+
let self_scope = fcx.push_custom_cleanup_scope();
405+
fcx.schedule_drop_mem(self_scope, llenv, closure_ty);
406+
407+
bcx = callee.call(bcx, DebugLoc::None, &llargs[self_idx..], dest).bcx;
408+
409+
fcx.pop_and_trans_custom_cleanup_scope(bcx, self_scope);
410+
411+
fcx.finish(bcx, DebugLoc::None);
412+
413+
ccx.instances().borrow_mut().insert(method_instance, lloncefn);
414+
415+
lloncefn
416+
}
417+
253418
/// Translates an adapter that implements the `Fn` trait for a fn
254419
/// pointer. This is basically the equivalent of something like:
255420
///

0 commit comments

Comments
 (0)