rustc_trans: translate closures using the collector
Translate closures like normal functions, using the trans::collector interface.
This commit is contained in:
parent
b6828fd1ac
commit
57950982b2
10 changed files with 242 additions and 405 deletions
|
|
@ -1003,34 +1003,41 @@ impl<'blk, 'tcx> FunctionContext<'blk, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Builds an LLVM function out of a source function.
|
||||
///
|
||||
/// If the function closes over its environment a closure will be returned.
|
||||
pub fn trans_closure<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
|
||||
llfndecl: ValueRef,
|
||||
instance: Instance<'tcx>,
|
||||
sig: &ty::FnSig<'tcx>,
|
||||
abi: Abi) {
|
||||
ccx.stats().n_closures.set(ccx.stats().n_closures.get() + 1);
|
||||
|
||||
let _icx = push_ctxt("trans_closure");
|
||||
if !ccx.sess().no_landing_pads() {
|
||||
attributes::emit_uwtable(llfndecl, true);
|
||||
}
|
||||
|
||||
pub fn trans_instance<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, instance: Instance<'tcx>) {
|
||||
let _s = StatRecorder::new(ccx, ccx.tcx().item_path_str(instance.def));
|
||||
// this is an info! to allow collecting monomorphization statistics
|
||||
// and to allow finding the last function before LLVM aborts from
|
||||
// release builds.
|
||||
info!("trans_closure(..., {})", instance);
|
||||
info!("trans_instance({})", instance);
|
||||
|
||||
let fn_ty = FnType::new(ccx, abi, sig, &[]);
|
||||
let _icx = push_ctxt("trans_instance");
|
||||
|
||||
let fn_ty = ccx.tcx().item_type(instance.def);
|
||||
let fn_ty = ccx.tcx().erase_regions(&fn_ty);
|
||||
let fn_ty = monomorphize::apply_param_substs(ccx.shared(), instance.substs, &fn_ty);
|
||||
|
||||
let ty::BareFnTy { abi, ref sig, .. } = *common::ty_fn_ty(ccx, fn_ty);
|
||||
let sig = ccx.tcx().erase_late_bound_regions_and_normalize(sig);
|
||||
|
||||
let lldecl = match ccx.instances().borrow().get(&instance) {
|
||||
Some(&val) => val,
|
||||
None => bug!("Instance `{:?}` not already declared", instance)
|
||||
};
|
||||
|
||||
ccx.stats().n_closures.set(ccx.stats().n_closures.get() + 1);
|
||||
|
||||
if !ccx.sess().no_landing_pads() {
|
||||
attributes::emit_uwtable(lldecl, true);
|
||||
}
|
||||
|
||||
let fn_ty = FnType::new(ccx, abi, &sig, &[]);
|
||||
|
||||
let (arena, fcx): (TypedArena<_>, FunctionContext);
|
||||
arena = TypedArena::new();
|
||||
fcx = FunctionContext::new(ccx,
|
||||
llfndecl,
|
||||
lldecl,
|
||||
fn_ty,
|
||||
Some((instance, sig, abi)),
|
||||
Some((instance, &sig, abi)),
|
||||
&arena);
|
||||
|
||||
if fcx.mir.is_none() {
|
||||
|
|
@ -1040,26 +1047,6 @@ pub fn trans_closure<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
|
|||
mir::trans_mir(&fcx);
|
||||
}
|
||||
|
||||
pub fn trans_instance<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, instance: Instance<'tcx>) {
|
||||
let _s = StatRecorder::new(ccx, ccx.tcx().item_path_str(instance.def));
|
||||
debug!("trans_instance(instance={:?})", instance);
|
||||
let _icx = push_ctxt("trans_instance");
|
||||
|
||||
let fn_ty = ccx.tcx().item_type(instance.def);
|
||||
let fn_ty = ccx.tcx().erase_regions(&fn_ty);
|
||||
let fn_ty = monomorphize::apply_param_substs(ccx.shared(), instance.substs, &fn_ty);
|
||||
|
||||
let sig = ccx.tcx().erase_late_bound_regions_and_normalize(fn_ty.fn_sig());
|
||||
let abi = fn_ty.fn_abi();
|
||||
|
||||
let lldecl = match ccx.instances().borrow().get(&instance) {
|
||||
Some(&val) => val,
|
||||
None => bug!("Instance `{:?}` not already declared", instance)
|
||||
};
|
||||
|
||||
trans_closure(ccx, lldecl, instance, &sig, abi);
|
||||
}
|
||||
|
||||
pub fn trans_ctor_shim<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
|
||||
def_id: DefId,
|
||||
substs: &'tcx Substs<'tcx>,
|
||||
|
|
|
|||
|
|
@ -26,11 +26,11 @@ use attributes;
|
|||
use base;
|
||||
use base::*;
|
||||
use build::*;
|
||||
use closure;
|
||||
use common::{self, Block, Result, CrateContext, FunctionContext, SharedCrateContext};
|
||||
use consts;
|
||||
use debuginfo::DebugLoc;
|
||||
use declare;
|
||||
use value::Value;
|
||||
use meth;
|
||||
use monomorphize::{self, Instance};
|
||||
use trans_item::TransItem;
|
||||
|
|
@ -147,11 +147,12 @@ impl<'tcx> Callee<'tcx> {
|
|||
// after passing through fulfill_obligation
|
||||
let trait_closure_kind = tcx.lang_items.fn_trait_kind(trait_id).unwrap();
|
||||
let instance = Instance::new(def_id, substs);
|
||||
let llfn = closure::trans_closure_method(ccx,
|
||||
vtable_closure.closure_def_id,
|
||||
vtable_closure.substs,
|
||||
instance,
|
||||
trait_closure_kind);
|
||||
let llfn = trans_closure_method(
|
||||
ccx,
|
||||
vtable_closure.closure_def_id,
|
||||
vtable_closure.substs,
|
||||
instance,
|
||||
trait_closure_kind);
|
||||
|
||||
let method_ty = def_ty(ccx.shared(), def_id, substs);
|
||||
Callee::ptr(llfn, method_ty)
|
||||
|
|
@ -250,6 +251,170 @@ fn def_ty<'a, 'tcx>(shared: &SharedCrateContext<'a, 'tcx>,
|
|||
monomorphize::apply_param_substs(shared, substs, &ty)
|
||||
}
|
||||
|
||||
|
||||
fn trans_closure_method<'a, 'tcx>(ccx: &'a CrateContext<'a, 'tcx>,
|
||||
def_id: DefId,
|
||||
substs: ty::ClosureSubsts<'tcx>,
|
||||
method_instance: Instance<'tcx>,
|
||||
trait_closure_kind: ty::ClosureKind)
|
||||
-> ValueRef
|
||||
{
|
||||
// If this is a closure, redirect to it.
|
||||
let (llfn, _) = get_fn(ccx, def_id, substs.substs);
|
||||
|
||||
// If the closure is a Fn closure, but a FnOnce is needed (etc),
|
||||
// then adapt the self type
|
||||
let llfn_closure_kind = ccx.tcx().closure_kind(def_id);
|
||||
|
||||
let _icx = push_ctxt("trans_closure_adapter_shim");
|
||||
|
||||
debug!("trans_closure_adapter_shim(llfn_closure_kind={:?}, \
|
||||
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) |
|
||||
(ty::ClosureKind::FnMut, ty::ClosureKind::FnMut) |
|
||||
(ty::ClosureKind::FnOnce, ty::ClosureKind::FnOnce) => {
|
||||
// No adapter needed.
|
||||
llfn
|
||||
}
|
||||
(ty::ClosureKind::Fn, ty::ClosureKind::FnMut) => {
|
||||
// The closure fn `llfn` is a `fn(&self, ...)`. We want a
|
||||
// `fn(&mut self, ...)`. In fact, at trans time, these are
|
||||
// basically the same thing, so we can just return llfn.
|
||||
llfn
|
||||
}
|
||||
(ty::ClosureKind::Fn, ty::ClosureKind::FnOnce) |
|
||||
(ty::ClosureKind::FnMut, ty::ClosureKind::FnOnce) => {
|
||||
// The closure fn `llfn` is a `fn(&self, ...)` or `fn(&mut
|
||||
// self, ...)`. We want a `fn(self, ...)`. We can produce
|
||||
// this by doing something like:
|
||||
//
|
||||
// fn call_once(self, ...) { call_mut(&self, ...) }
|
||||
// fn call_once(mut self, ...) { call_mut(&mut self, ...) }
|
||||
//
|
||||
// These are both the same at trans time.
|
||||
trans_fn_once_adapter_shim(ccx, def_id, substs, method_instance, llfn)
|
||||
}
|
||||
_ => {
|
||||
bug!("trans_closure_adapter_shim: cannot convert {:?} to {:?}",
|
||||
llfn_closure_kind,
|
||||
trait_closure_kind);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 ty::ClosureTy { unsafety, abi, mut sig } = tcx.closure_type(def_id, substs);
|
||||
sig.0.inputs.insert(0, ref_closure_ty); // sig has no self type as of yet
|
||||
let llref_fn_ty = tcx.mk_fn_ptr(tcx.mk_bare_fn(ty::BareFnTy {
|
||||
unsafety: unsafety,
|
||||
abi: abi,
|
||||
sig: sig.clone()
|
||||
}));
|
||||
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.
|
||||
assert_eq!(abi, Abi::RustCall);
|
||||
sig.0.inputs[0] = closure_ty;
|
||||
|
||||
let sig = tcx.erase_late_bound_regions_and_normalize(&sig);
|
||||
let fn_ty = FnType::new(ccx, abi, &sig, &[]);
|
||||
|
||||
let llonce_fn_ty = tcx.mk_fn_ptr(tcx.mk_bare_fn(ty::BareFnTy {
|
||||
unsafety: unsafety,
|
||||
abi: abi,
|
||||
sig: ty::Binder(sig)
|
||||
}));
|
||||
|
||||
// Create the by-value helper.
|
||||
let function_name = method_instance.symbol_name(ccx.shared());
|
||||
let lloncefn = declare::define_internal_fn(ccx, &function_name, llonce_fn_ty);
|
||||
attributes::set_frame_pointer_elimination(ccx, lloncefn);
|
||||
|
||||
let (block_arena, fcx): (TypedArena<_>, FunctionContext);
|
||||
block_arena = TypedArena::new();
|
||||
fcx = FunctionContext::new(ccx, lloncefn, fn_ty, None, &block_arena);
|
||||
let mut bcx = fcx.init(false);
|
||||
|
||||
|
||||
// the first argument (`self`) will be the (by value) closure env.
|
||||
|
||||
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() {
|
||||
llargs[self_idx]
|
||||
} else {
|
||||
let scratch = alloc_ty(bcx, closure_ty, "self");
|
||||
let mut llarg_idx = self_idx;
|
||||
env_arg.store_fn_arg(&bcx.build(), &mut llarg_idx, scratch);
|
||||
scratch
|
||||
};
|
||||
|
||||
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();
|
||||
|
||||
let callee = Callee {
|
||||
data: Fn(llreffn),
|
||||
ty: llref_fn_ty
|
||||
};
|
||||
|
||||
// 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 = fcx.push_custom_cleanup_scope();
|
||||
fcx.schedule_drop_mem(self_scope, llenv, closure_ty);
|
||||
|
||||
bcx = callee.call(bcx, DebugLoc::None, &llargs[self_idx..], dest).bcx;
|
||||
|
||||
fcx.pop_and_trans_custom_cleanup_scope(bcx, self_scope);
|
||||
|
||||
fcx.finish(bcx, DebugLoc::None);
|
||||
|
||||
ccx.instances().borrow_mut().insert(method_instance, lloncefn);
|
||||
|
||||
lloncefn
|
||||
}
|
||||
|
||||
/// Translates an adapter that implements the `Fn` trait for a fn
|
||||
/// pointer. This is basically the equivalent of something like:
|
||||
///
|
||||
|
|
|
|||
|
|
@ -1,318 +0,0 @@
|
|||
// Copyright 2012-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 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use arena::TypedArena;
|
||||
use llvm::{self, ValueRef, get_params};
|
||||
use rustc::hir::def_id::DefId;
|
||||
use abi::{Abi, FnType};
|
||||
use attributes;
|
||||
use base::*;
|
||||
use callee::{self, Callee};
|
||||
use common::*;
|
||||
use debuginfo::{DebugLoc};
|
||||
use declare;
|
||||
use monomorphize::{Instance};
|
||||
use value::Value;
|
||||
use rustc::ty::{self, Ty, TyCtxt};
|
||||
|
||||
use rustc::hir;
|
||||
|
||||
fn get_self_type<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, '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::ReErased), fn_ty)
|
||||
}
|
||||
ty::ClosureKind::FnMut => {
|
||||
tcx.mk_mut_ref(tcx.mk_region(ty::ReErased), 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.
|
||||
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 tcx = ccx.tcx();
|
||||
let substs = tcx.erase_regions(&substs);
|
||||
let instance = Instance::new(closure_id, substs.substs);
|
||||
|
||||
if let Some(&llfn) = ccx.instances().borrow().get(&instance) {
|
||||
debug!("get_or_create_closure_declaration(): found closure {:?}: {:?}",
|
||||
instance, Value(llfn));
|
||||
return llfn;
|
||||
}
|
||||
|
||||
let symbol = instance.symbol_name(ccx.shared());
|
||||
|
||||
// Compute the rust-call form of the closure call method.
|
||||
let sig = &tcx.closure_type(closure_id, substs).sig;
|
||||
let sig = tcx.erase_late_bound_regions_and_normalize(sig);
|
||||
let closure_type = tcx.mk_closure_from_closure_substs(closure_id, substs);
|
||||
let function_type = tcx.mk_fn_ptr(tcx.mk_bare_fn(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::declare_fn(ccx, &symbol, function_type);
|
||||
|
||||
attributes::set_frame_pointer_elimination(ccx, llfn);
|
||||
|
||||
debug!("get_or_create_declaration_if_closure(): inserting new \
|
||||
closure {:?}: {:?}",
|
||||
instance, Value(llfn));
|
||||
|
||||
// NOTE: We do *not* store llfn in the ccx.instances() map here,
|
||||
// that is only done, when the closures body is translated.
|
||||
|
||||
llfn
|
||||
}
|
||||
|
||||
pub fn trans_closure_body_via_mir<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
|
||||
closure_def_id: DefId,
|
||||
closure_substs: ty::ClosureSubsts<'tcx>) {
|
||||
// (*) Note that in the case of inlined functions, the `closure_def_id` will be the
|
||||
// defid of the closure in its original crate, whereas `id` will be the id of the local
|
||||
// inlined copy.
|
||||
debug!("trans_closure_body_via_mir(closure_def_id={:?}, closure_substs={:?})",
|
||||
closure_def_id, closure_substs);
|
||||
|
||||
let tcx = ccx.tcx();
|
||||
let _icx = push_ctxt("closure::trans_closure_expr");
|
||||
|
||||
let instance = Instance::new(closure_def_id, closure_substs.substs);
|
||||
|
||||
// If we have not done so yet, translate this closure's body
|
||||
if !ccx.instances().borrow().contains_key(&instance) {
|
||||
let llfn = get_or_create_closure_declaration(ccx, closure_def_id, closure_substs);
|
||||
|
||||
unsafe {
|
||||
if ccx.sess().target.target.options.allows_weak_linkage {
|
||||
llvm::LLVMRustSetLinkage(llfn, llvm::Linkage::WeakODRLinkage);
|
||||
llvm::SetUniqueComdat(ccx.llmod(), llfn);
|
||||
} else {
|
||||
llvm::LLVMRustSetLinkage(llfn, llvm::Linkage::InternalLinkage);
|
||||
}
|
||||
}
|
||||
|
||||
// set an inline hint for all closures
|
||||
attributes::inline(llfn, attributes::InlineAttr::Hint);
|
||||
|
||||
// Get the type of this closure. Use the current `param_substs` as
|
||||
// the closure substitutions. This makes sense because the closure
|
||||
// takes the same set of type arguments as the enclosing fn, and
|
||||
// this function (`trans_closure`) is invoked at the point
|
||||
// of the closure expression.
|
||||
|
||||
let sig = &tcx.closure_type(closure_def_id, closure_substs).sig;
|
||||
let sig = tcx.erase_late_bound_regions_and_normalize(sig);
|
||||
|
||||
let closure_type = tcx.mk_closure_from_closure_substs(closure_def_id,
|
||||
closure_substs);
|
||||
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
|
||||
};
|
||||
|
||||
trans_closure(ccx,
|
||||
llfn,
|
||||
instance,
|
||||
&sig,
|
||||
Abi::RustCall);
|
||||
|
||||
ccx.instances().borrow_mut().insert(instance, llfn);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn trans_closure_method<'a, 'tcx>(ccx: &'a CrateContext<'a, 'tcx>,
|
||||
closure_def_id: DefId,
|
||||
substs: ty::ClosureSubsts<'tcx>,
|
||||
method_instance: Instance<'tcx>,
|
||||
trait_closure_kind: ty::ClosureKind)
|
||||
-> ValueRef
|
||||
{
|
||||
// If this is a closure, redirect to it.
|
||||
let llfn = get_or_create_closure_declaration(ccx, closure_def_id, substs);
|
||||
|
||||
// If weak linkage is not allowed, we have to make sure that a local,
|
||||
// private copy of the closure is available in this codegen unit
|
||||
if !ccx.sess().target.target.options.allows_weak_linkage &&
|
||||
!ccx.sess().opts.single_codegen_unit() {
|
||||
|
||||
trans_closure_body_via_mir(ccx, closure_def_id, substs);
|
||||
}
|
||||
|
||||
// If the closure is a Fn closure, but a FnOnce is needed (etc),
|
||||
// then adapt the self type
|
||||
let llfn_closure_kind = ccx.tcx().closure_kind(closure_def_id);
|
||||
|
||||
let _icx = push_ctxt("trans_closure_adapter_shim");
|
||||
|
||||
debug!("trans_closure_adapter_shim(llfn_closure_kind={:?}, \
|
||||
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) |
|
||||
(ty::ClosureKind::FnMut, ty::ClosureKind::FnMut) |
|
||||
(ty::ClosureKind::FnOnce, ty::ClosureKind::FnOnce) => {
|
||||
// No adapter needed.
|
||||
llfn
|
||||
}
|
||||
(ty::ClosureKind::Fn, ty::ClosureKind::FnMut) => {
|
||||
// The closure fn `llfn` is a `fn(&self, ...)`. We want a
|
||||
// `fn(&mut self, ...)`. In fact, at trans time, these are
|
||||
// basically the same thing, so we can just return llfn.
|
||||
llfn
|
||||
}
|
||||
(ty::ClosureKind::Fn, ty::ClosureKind::FnOnce) |
|
||||
(ty::ClosureKind::FnMut, ty::ClosureKind::FnOnce) => {
|
||||
// The closure fn `llfn` is a `fn(&self, ...)` or `fn(&mut
|
||||
// self, ...)`. We want a `fn(self, ...)`. We can produce
|
||||
// this by doing something like:
|
||||
//
|
||||
// fn call_once(self, ...) { call_mut(&self, ...) }
|
||||
// fn call_once(mut self, ...) { call_mut(&mut self, ...) }
|
||||
//
|
||||
// These are both the same at trans time.
|
||||
trans_fn_once_adapter_shim(ccx, closure_def_id, substs, method_instance, llfn)
|
||||
}
|
||||
_ => {
|
||||
bug!("trans_closure_adapter_shim: cannot convert {:?} to {:?}",
|
||||
llfn_closure_kind,
|
||||
trait_closure_kind);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn trans_fn_once_adapter_shim<'a, 'tcx>(
|
||||
ccx: &'a CrateContext<'a, 'tcx>,
|
||||
closure_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(closure_def_id={:?}, substs={:?}, llreffn={:?})",
|
||||
closure_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(closure_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 ty::ClosureTy { unsafety, abi, mut sig } =
|
||||
tcx.closure_type(closure_def_id, substs);
|
||||
sig.0.inputs.insert(0, ref_closure_ty); // sig has no self type as of yet
|
||||
let llref_fn_ty = tcx.mk_fn_ptr(tcx.mk_bare_fn(ty::BareFnTy {
|
||||
unsafety: unsafety,
|
||||
abi: abi,
|
||||
sig: sig.clone()
|
||||
}));
|
||||
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.
|
||||
assert_eq!(abi, Abi::RustCall);
|
||||
sig.0.inputs[0] = closure_ty;
|
||||
|
||||
let sig = tcx.erase_late_bound_regions_and_normalize(&sig);
|
||||
let fn_ty = FnType::new(ccx, abi, &sig, &[]);
|
||||
|
||||
let llonce_fn_ty = tcx.mk_fn_ptr(tcx.mk_bare_fn(ty::BareFnTy {
|
||||
unsafety: unsafety,
|
||||
abi: abi,
|
||||
sig: ty::Binder(sig)
|
||||
}));
|
||||
|
||||
// Create the by-value helper.
|
||||
let function_name = method_instance.symbol_name(ccx.shared());
|
||||
let lloncefn = declare::define_internal_fn(ccx, &function_name, llonce_fn_ty);
|
||||
attributes::set_frame_pointer_elimination(ccx, lloncefn);
|
||||
|
||||
let (block_arena, fcx): (TypedArena<_>, FunctionContext);
|
||||
block_arena = TypedArena::new();
|
||||
fcx = FunctionContext::new(ccx, lloncefn, fn_ty, None, &block_arena);
|
||||
let mut bcx = fcx.init(false);
|
||||
|
||||
|
||||
// the first argument (`self`) will be the (by value) closure env.
|
||||
|
||||
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() {
|
||||
llargs[self_idx]
|
||||
} else {
|
||||
let scratch = alloc_ty(bcx, closure_ty, "self");
|
||||
let mut llarg_idx = self_idx;
|
||||
env_arg.store_fn_arg(&bcx.build(), &mut llarg_idx, scratch);
|
||||
scratch
|
||||
};
|
||||
|
||||
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();
|
||||
|
||||
let callee = Callee {
|
||||
data: callee::Fn(llreffn),
|
||||
ty: llref_fn_ty
|
||||
};
|
||||
|
||||
// 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 = fcx.push_custom_cleanup_scope();
|
||||
fcx.schedule_drop_mem(self_scope, llenv, closure_ty);
|
||||
|
||||
bcx = callee.call(bcx, DebugLoc::None, &llargs[self_idx..], dest).bcx;
|
||||
|
||||
fcx.pop_and_trans_custom_cleanup_scope(bcx, self_scope);
|
||||
|
||||
fcx.finish(bcx, DebugLoc::None);
|
||||
|
||||
ccx.instances().borrow_mut().insert(method_instance, lloncefn);
|
||||
|
||||
lloncefn
|
||||
}
|
||||
|
|
@ -446,24 +446,6 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> {
|
|||
debug!("visiting rvalue {:?}", *rvalue);
|
||||
|
||||
match *rvalue {
|
||||
mir::Rvalue::Aggregate(mir::AggregateKind::Closure(def_id,
|
||||
ref substs), _) => {
|
||||
let mir = self.scx.tcx().item_mir(def_id);
|
||||
|
||||
let concrete_substs = monomorphize::apply_param_substs(self.scx,
|
||||
self.param_substs,
|
||||
&substs.substs);
|
||||
let concrete_substs = self.scx.tcx().erase_regions(&concrete_substs);
|
||||
|
||||
let visitor = MirNeighborCollector {
|
||||
scx: self.scx,
|
||||
mir: &mir,
|
||||
output: self.output,
|
||||
param_substs: concrete_substs
|
||||
};
|
||||
|
||||
visit_mir_and_promoted(visitor, &mir);
|
||||
}
|
||||
// When doing an cast from a regular pointer to a fat pointer, we
|
||||
// have to instantiate all methods of the trait being cast to, so we
|
||||
// can build the appropriate vtable.
|
||||
|
|
@ -888,10 +870,12 @@ fn do_static_trait_method_dispatch<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>,
|
|||
traits::VtableImpl(impl_data) => {
|
||||
Some(traits::find_method(tcx, trait_method.name, rcvr_substs, &impl_data))
|
||||
}
|
||||
// If we have a closure or a function pointer, we will also encounter
|
||||
// the concrete closure/function somewhere else (during closure or fn
|
||||
// pointer construction). That's where we track those things.
|
||||
traits::VtableClosure(..) |
|
||||
traits::VtableClosure(closure_data) => {
|
||||
Some((closure_data.closure_def_id, closure_data.substs.substs))
|
||||
}
|
||||
// Trait object and function pointer shims are always
|
||||
// instantiated in-place, and as they are just an ABI-adjusting
|
||||
// indirect call they do not have any dependencies.
|
||||
traits::VtableFnPointer(..) |
|
||||
traits::VtableObject(..) => {
|
||||
None
|
||||
|
|
|
|||
|
|
@ -44,6 +44,8 @@ use rustc::hir;
|
|||
|
||||
use arena::TypedArena;
|
||||
use libc::{c_uint, c_char};
|
||||
use std::borrow::Cow;
|
||||
use std::iter;
|
||||
use std::ops::Deref;
|
||||
use std::ffi::CString;
|
||||
use std::cell::{Cell, RefCell, Ref};
|
||||
|
|
@ -1069,3 +1071,32 @@ pub fn shift_mask_val<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
|
|||
_ => bug!("shift_mask_val: expected Integer or Vector, found {:?}", kind),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ty_fn_ty<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
|
||||
ty: Ty<'tcx>)
|
||||
-> Cow<'tcx, ty::BareFnTy<'tcx>>
|
||||
{
|
||||
match ty.sty {
|
||||
ty::TyFnDef(_, _, fty) => Cow::Borrowed(fty),
|
||||
// Shims currently have type TyFnPtr. Not sure this should remain.
|
||||
ty::TyFnPtr(fty) => Cow::Borrowed(fty),
|
||||
ty::TyClosure(def_id, substs) => {
|
||||
let tcx = ccx.tcx();
|
||||
let ty::ClosureTy { unsafety, abi, sig } = tcx.closure_type(def_id, substs);
|
||||
|
||||
let env_region = ty::ReLateBound(ty::DebruijnIndex::new(1), ty::BrEnv);
|
||||
let env_ty = match tcx.closure_kind(def_id) {
|
||||
ty::ClosureKind::Fn => tcx.mk_imm_ref(tcx.mk_region(env_region), ty),
|
||||
ty::ClosureKind::FnMut => tcx.mk_mut_ref(tcx.mk_region(env_region), ty),
|
||||
ty::ClosureKind::FnOnce => ty,
|
||||
};
|
||||
|
||||
let sig = sig.map_bound(|sig| ty::FnSig {
|
||||
inputs: iter::once(env_ty).chain(sig.inputs).collect(),
|
||||
..sig
|
||||
});
|
||||
Cow::Owned(ty::BareFnTy { unsafety: unsafety, abi: abi, sig: sig })
|
||||
}
|
||||
_ => bug!("unexpected type {:?} to ty_fn_sig", ty)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ use rustc::ty;
|
|||
use abi::{Abi, FnType};
|
||||
use attributes;
|
||||
use context::CrateContext;
|
||||
use common;
|
||||
use type_::Type;
|
||||
use value::Value;
|
||||
|
||||
|
|
@ -103,8 +104,8 @@ pub fn declare_cfn(ccx: &CrateContext, name: &str, fn_type: Type) -> ValueRef {
|
|||
pub fn declare_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, name: &str,
|
||||
fn_type: ty::Ty<'tcx>) -> ValueRef {
|
||||
debug!("declare_rust_fn(name={:?}, fn_type={:?})", name, fn_type);
|
||||
let abi = fn_type.fn_abi();
|
||||
let sig = ccx.tcx().erase_late_bound_regions_and_normalize(fn_type.fn_sig());
|
||||
let ty::BareFnTy { abi, ref sig, .. } = *common::ty_fn_ty(ccx, fn_type);
|
||||
let sig = ccx.tcx().erase_late_bound_regions_and_normalize(sig);
|
||||
debug!("declare_rust_fn (after region erasure) sig={:?}", sig);
|
||||
|
||||
let fty = FnType::new(ccx, abi, &sig, &[]);
|
||||
|
|
|
|||
|
|
@ -109,7 +109,6 @@ mod cabi_x86_64;
|
|||
mod cabi_x86_win64;
|
||||
mod callee;
|
||||
mod cleanup;
|
||||
mod closure;
|
||||
mod collector;
|
||||
mod common;
|
||||
mod consts;
|
||||
|
|
|
|||
|
|
@ -553,14 +553,6 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> {
|
|||
}
|
||||
failure?;
|
||||
|
||||
// FIXME Shouldn't need to manually trigger closure instantiations.
|
||||
if let mir::AggregateKind::Closure(def_id, substs) = *kind {
|
||||
use closure;
|
||||
closure::trans_closure_body_via_mir(self.ccx,
|
||||
def_id,
|
||||
self.monomorphize(&substs));
|
||||
}
|
||||
|
||||
match *kind {
|
||||
mir::AggregateKind::Array => {
|
||||
self.const_array(dest_ty, &fields)
|
||||
|
|
|
|||
|
|
@ -133,15 +133,6 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
|
|||
}
|
||||
},
|
||||
_ => {
|
||||
// FIXME Shouldn't need to manually trigger closure instantiations.
|
||||
if let mir::AggregateKind::Closure(def_id, substs) = *kind {
|
||||
use closure;
|
||||
|
||||
closure::trans_closure_body_via_mir(bcx.ccx(),
|
||||
def_id,
|
||||
bcx.monomorphize(&substs));
|
||||
}
|
||||
|
||||
for (i, operand) in operands.iter().enumerate() {
|
||||
let op = self.trans_operand(&bcx, operand);
|
||||
// Do not generate stores and GEPis for zero-sized fields.
|
||||
|
|
|
|||
|
|
@ -166,6 +166,11 @@ impl<'a, 'tcx> TransItem<'tcx> {
|
|||
llvm::SetUniqueComdat(ccx.llmod(), lldecl);
|
||||
}
|
||||
|
||||
if let ty::TyClosure(..) = mono_ty.sty {
|
||||
// set an inline hint for all closures
|
||||
attributes::inline(lldecl, attributes::InlineAttr::Hint);
|
||||
}
|
||||
|
||||
attributes::from_fn_attrs(ccx, &attrs, lldecl);
|
||||
|
||||
ccx.instances().borrow_mut().insert(instance, lldecl);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue