-use rustc::ty::{self, TypeFoldable};
-use std::iter;
-
-use mir::lvalue::Alignment;
-
-fn trans_fn_once_adapter_shim<'a, 'tcx>(
- ccx: &'a CrateContext<'a, 'tcx>,
- def_id: DefId,
- substs: ty::ClosureSubsts<'tcx>,
- method_instance: Instance<'tcx>,
- llreffn: ValueRef)
- -> ValueRef
-{
- if let Some(&llfn) = ccx.instances().borrow().get(&method_instance) {
- return llfn;
- }
-
- debug!("trans_fn_once_adapter_shim(def_id={:?}, substs={:?}, llreffn={:?})",
- def_id, substs, Value(llreffn));
-
- let tcx = ccx.tcx();
-
- // Find a version of the closure type. Substitute static for the
- // region since it doesn't really matter.
- let closure_ty = tcx.mk_closure_from_closure_substs(def_id, substs);
- let ref_closure_ty = tcx.mk_imm_ref(tcx.mk_region(ty::ReErased), closure_ty);
-
- // Make a version with the type of by-ref closure.
- let sig = tcx.closure_type(def_id).subst(tcx, substs.substs);
- let sig = tcx.erase_late_bound_regions_and_normalize(&sig);
- assert_eq!(sig.abi, Abi::RustCall);
- let llref_fn_sig = tcx.mk_fn_sig(
- iter::once(ref_closure_ty).chain(sig.inputs().iter().cloned()),
- sig.output(),
- sig.variadic,
- sig.unsafety,
- Abi::RustCall
- );
- let llref_fn_ty = tcx.mk_fn_ptr(ty::Binder(llref_fn_sig));
- debug!("trans_fn_once_adapter_shim: llref_fn_ty={:?}",
- llref_fn_ty);
-
-
- // Make a version of the closure type with the same arguments, but
- // with argument #0 being by value.
- let sig = tcx.mk_fn_sig(
- iter::once(closure_ty).chain(sig.inputs().iter().cloned()),
- sig.output(),
- sig.variadic,
- sig.unsafety,
- Abi::RustCall
- );
-
- let fn_ty = FnType::new(ccx, sig, &[]);
- let llonce_fn_ty = tcx.mk_fn_ptr(ty::Binder(sig));
-
- // Create the by-value helper.
- let function_name = symbol_name(method_instance, ccx.shared());
- let lloncefn = declare::define_internal_fn(ccx, &function_name, llonce_fn_ty);
- attributes::set_frame_pointer_elimination(ccx, lloncefn);
-
- let orig_fn_ty = fn_ty;
- let mut bcx = Builder::new_block(ccx, lloncefn, "entry-block");
-
- // the first argument (`self`) will be the (by value) closure env.
-
- let mut llargs = get_params(lloncefn);
- let fn_ty = FnType::new(ccx, llref_fn_sig, &[]);
- let self_idx = fn_ty.ret.is_indirect() as usize;
- let env_arg = &orig_fn_ty.args[0];
- let env = if env_arg.is_indirect() {
- LvalueRef::new_sized_ty(llargs[self_idx], closure_ty, Alignment::AbiAligned)
- } else {
- let scratch = LvalueRef::alloca(&bcx, closure_ty, "self");
- let mut llarg_idx = self_idx;
- env_arg.store_fn_arg(&bcx, &mut llarg_idx, scratch.llval);
- scratch
- };
-
- debug!("trans_fn_once_adapter_shim: env={:?}", env);
- // 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() {
- llargs.insert(self_idx, env.llval);
- } else {
- llargs[self_idx] = env.llval;
- }
-
- // Call the by-ref closure body with `self` in a cleanup scope,
- // to drop `self` when the body returns, or in case it unwinds.
- let self_scope = CleanupScope::schedule_drop_mem(&bcx, env);
-
- let llret;
- if let Some(landing_pad) = self_scope.landing_pad {
- let normal_bcx = bcx.build_sibling_block("normal-return");
- llret = bcx.invoke(llreffn, &llargs[..], normal_bcx.llbb(), landing_pad, None);
- bcx = normal_bcx;
- } else {
- llret = bcx.call(llreffn, &llargs[..], None);
- }
- fn_ty.apply_attrs_callsite(llret);
-
- if sig.output().is_never() {
- bcx.unreachable();
- } else {
- self_scope.trans(&bcx);
-
- if fn_ty.ret.is_indirect() || fn_ty.ret.is_ignore() {
- bcx.ret_void();
- } else {
- bcx.ret(llret);
- }
- }
-
- ccx.instances().borrow_mut().insert(method_instance, lloncefn);
-
- lloncefn
-}
-