translate closure shims using MIR
This commit is contained in:
parent
a5e3c3d5b8
commit
e1f3c67cb4
8 changed files with 183 additions and 321 deletions
|
@ -34,10 +34,7 @@ pub enum InstanceDef<'tcx> {
|
||||||
// <Trait as Trait>::fn
|
// <Trait as Trait>::fn
|
||||||
Virtual(DefId, usize),
|
Virtual(DefId, usize),
|
||||||
// <[mut closure] as FnOnce>::call_once
|
// <[mut closure] as FnOnce>::call_once
|
||||||
ClosureOnceShim {
|
ClosureOnceShim { call_once: DefId },
|
||||||
call_once: DefId,
|
|
||||||
closure_did: DefId
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'tcx> InstanceDef<'tcx> {
|
impl<'tcx> InstanceDef<'tcx> {
|
||||||
|
@ -48,9 +45,8 @@ impl<'tcx> InstanceDef<'tcx> {
|
||||||
InstanceDef::FnPtrShim(def_id, _) |
|
InstanceDef::FnPtrShim(def_id, _) |
|
||||||
InstanceDef::Virtual(def_id, _) |
|
InstanceDef::Virtual(def_id, _) |
|
||||||
InstanceDef::Intrinsic(def_id, ) |
|
InstanceDef::Intrinsic(def_id, ) |
|
||||||
InstanceDef::ClosureOnceShim {
|
InstanceDef::ClosureOnceShim { call_once: def_id }
|
||||||
call_once: def_id, closure_did: _
|
=> def_id
|
||||||
} => def_id
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,10 +94,8 @@ impl<'tcx> fmt::Display for Instance<'tcx> {
|
||||||
InstanceDef::FnPtrShim(_, ty) => {
|
InstanceDef::FnPtrShim(_, ty) => {
|
||||||
write!(f, " - shim({:?})", ty)
|
write!(f, " - shim({:?})", ty)
|
||||||
}
|
}
|
||||||
InstanceDef::ClosureOnceShim {
|
InstanceDef::ClosureOnceShim { .. } => {
|
||||||
call_once: _, closure_did
|
write!(f, " - shim")
|
||||||
} => {
|
|
||||||
write!(f, " - shim({:?})", closure_did)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -83,7 +83,25 @@ fn make_shim<'a, 'tcx>(tcx: ty::TyCtxt<'a, 'tcx, 'tcx>,
|
||||||
None
|
None
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
_ => bug!("unknown shim kind")
|
ty::InstanceDef::ClosureOnceShim { call_once } => {
|
||||||
|
let fn_mut = tcx.lang_items.fn_mut_trait().unwrap();
|
||||||
|
let call_mut = tcx.global_tcx()
|
||||||
|
.associated_items(fn_mut)
|
||||||
|
.find(|it| it.kind == ty::AssociatedKind::Method)
|
||||||
|
.unwrap().def_id;
|
||||||
|
|
||||||
|
build_call_shim(
|
||||||
|
tcx,
|
||||||
|
¶m_env,
|
||||||
|
call_once,
|
||||||
|
Adjustment::RefMut,
|
||||||
|
CallKind::Direct(call_mut),
|
||||||
|
None
|
||||||
|
)
|
||||||
|
}
|
||||||
|
ty::InstanceDef::Intrinsic(_) => {
|
||||||
|
bug!("creating shims from intrinsics ({:?}) is unsupported", instance)
|
||||||
|
}
|
||||||
};
|
};
|
||||||
debug!("make_shim({:?}) = {:?}", instance, result);
|
debug!("make_shim({:?}) = {:?}", instance, result);
|
||||||
|
|
||||||
|
@ -97,6 +115,7 @@ fn make_shim<'a, 'tcx>(tcx: ty::TyCtxt<'a, 'tcx, 'tcx>,
|
||||||
enum Adjustment {
|
enum Adjustment {
|
||||||
Identity,
|
Identity,
|
||||||
Deref,
|
Deref,
|
||||||
|
RefMut,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||||
|
@ -143,18 +162,37 @@ fn build_call_shim<'a, 'tcx>(tcx: ty::TyCtxt<'a, 'tcx, 'tcx>,
|
||||||
|
|
||||||
debug!("build_call_shim: sig={:?}", sig);
|
debug!("build_call_shim: sig={:?}", sig);
|
||||||
|
|
||||||
let local_decls = local_decls_for_sig(&sig);
|
let mut local_decls = local_decls_for_sig(&sig);
|
||||||
let source_info = SourceInfo { span, scope: ARGUMENT_VISIBILITY_SCOPE };
|
let source_info = SourceInfo { span, scope: ARGUMENT_VISIBILITY_SCOPE };
|
||||||
|
|
||||||
let rcvr_l = Lvalue::Local(Local::new(1+0));
|
let rcvr_arg = Local::new(1+0);
|
||||||
|
let rcvr_l = Lvalue::Local(rcvr_arg);
|
||||||
let return_block_id = BasicBlock::new(1);
|
let mut statements = vec![];
|
||||||
|
|
||||||
let rcvr = match rcvr_adjustment {
|
let rcvr = match rcvr_adjustment {
|
||||||
Adjustment::Identity => Operand::Consume(rcvr_l),
|
Adjustment::Identity => Operand::Consume(rcvr_l),
|
||||||
Adjustment::Deref => Operand::Consume(Lvalue::Projection(
|
Adjustment::Deref => Operand::Consume(Lvalue::Projection(
|
||||||
box Projection { base: rcvr_l, elem: ProjectionElem::Deref }
|
box Projection { base: rcvr_l, elem: ProjectionElem::Deref }
|
||||||
))
|
)),
|
||||||
|
Adjustment::RefMut => {
|
||||||
|
// let rcvr = &mut rcvr;
|
||||||
|
let re_erased = tcx.mk_region(ty::ReErased);
|
||||||
|
let ref_rcvr = local_decls.push(temp_decl(
|
||||||
|
Mutability::Not,
|
||||||
|
tcx.mk_ref(re_erased, ty::TypeAndMut {
|
||||||
|
ty: sig.inputs()[0],
|
||||||
|
mutbl: hir::Mutability::MutMutable
|
||||||
|
})
|
||||||
|
));
|
||||||
|
statements.push(Statement {
|
||||||
|
source_info: source_info,
|
||||||
|
kind: StatementKind::Assign(
|
||||||
|
Lvalue::Local(ref_rcvr),
|
||||||
|
Rvalue::Ref(re_erased, BorrowKind::Mut, rcvr_l)
|
||||||
|
)
|
||||||
|
});
|
||||||
|
Operand::Consume(Lvalue::Local(ref_rcvr))
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let (callee, mut args) = match call_kind {
|
let (callee, mut args) = match call_kind {
|
||||||
|
@ -184,28 +222,57 @@ fn build_call_shim<'a, 'tcx>(tcx: ty::TyCtxt<'a, 'tcx, 'tcx>,
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut blocks = IndexVec::new();
|
let mut blocks = IndexVec::new();
|
||||||
blocks.push(BasicBlockData {
|
let block = |blocks: &mut IndexVec<_, _>, statements, kind, is_cleanup| {
|
||||||
statements: vec![],
|
blocks.push(BasicBlockData {
|
||||||
terminator: Some(Terminator {
|
statements,
|
||||||
source_info: source_info,
|
terminator: Some(Terminator { source_info, kind }),
|
||||||
kind: TerminatorKind::Call {
|
is_cleanup
|
||||||
func: callee,
|
})
|
||||||
args: args,
|
};
|
||||||
destination: Some((Lvalue::Local(RETURN_POINTER),
|
|
||||||
return_block_id)),
|
let have_unwind = match (rcvr_adjustment, tcx.sess.no_landing_pads()) {
|
||||||
cleanup: None
|
(Adjustment::RefMut, false) => true,
|
||||||
|
_ => false
|
||||||
|
};
|
||||||
|
|
||||||
|
// BB #0
|
||||||
|
block(&mut blocks, statements, TerminatorKind::Call {
|
||||||
|
func: callee,
|
||||||
|
args: args,
|
||||||
|
destination: Some((Lvalue::Local(RETURN_POINTER),
|
||||||
|
BasicBlock::new(1))),
|
||||||
|
cleanup: if have_unwind {
|
||||||
|
Some(BasicBlock::new(3))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}, false);
|
||||||
|
|
||||||
|
if let Adjustment::RefMut = rcvr_adjustment {
|
||||||
|
// BB #1 - drop for Self
|
||||||
|
block(&mut blocks, vec![], TerminatorKind::Drop {
|
||||||
|
location: Lvalue::Local(rcvr_arg),
|
||||||
|
target: BasicBlock::new(2),
|
||||||
|
unwind: if have_unwind {
|
||||||
|
Some(BasicBlock::new(4))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
}
|
}
|
||||||
}),
|
}, false);
|
||||||
is_cleanup: false
|
}
|
||||||
});
|
// BB #1/#2 - return
|
||||||
blocks.push(BasicBlockData {
|
block(&mut blocks, vec![], TerminatorKind::Return, false);
|
||||||
statements: vec![],
|
if have_unwind {
|
||||||
terminator: Some(Terminator {
|
// BB #3 - drop if closure panics
|
||||||
source_info: source_info,
|
block(&mut blocks, vec![], TerminatorKind::Drop {
|
||||||
kind: TerminatorKind::Return
|
location: Lvalue::Local(rcvr_arg),
|
||||||
}),
|
target: BasicBlock::new(4),
|
||||||
is_cleanup: false
|
unwind: None
|
||||||
});
|
}, true);
|
||||||
|
|
||||||
|
// BB #4 - resume
|
||||||
|
block(&mut blocks, vec![], TerminatorKind::Resume, true);
|
||||||
|
}
|
||||||
|
|
||||||
let mut mir = Mir::new(
|
let mut mir = Mir::new(
|
||||||
blocks,
|
blocks,
|
||||||
|
|
|
@ -14,142 +14,18 @@
|
||||||
//! and methods are represented as just a fn ptr and not a full
|
//! and methods are represented as just a fn ptr and not a full
|
||||||
//! closure.
|
//! closure.
|
||||||
|
|
||||||
use llvm::{self, ValueRef, get_params};
|
use llvm::{self, ValueRef};
|
||||||
use rustc::hir::def_id::DefId;
|
use rustc::hir::def_id::DefId;
|
||||||
use rustc::ty::subst::{Substs, Subst};
|
use rustc::ty::subst::Substs;
|
||||||
use abi::{Abi, FnType};
|
|
||||||
use attributes;
|
use attributes;
|
||||||
use builder::Builder;
|
|
||||||
use common::{self, CrateContext};
|
use common::{self, CrateContext};
|
||||||
use cleanup::CleanupScope;
|
|
||||||
use mir::lvalue::LvalueRef;
|
|
||||||
use monomorphize;
|
use monomorphize;
|
||||||
use consts;
|
use consts;
|
||||||
use declare;
|
use declare;
|
||||||
use value::Value;
|
|
||||||
use monomorphize::Instance;
|
use monomorphize::Instance;
|
||||||
use back::symbol_names::symbol_name;
|
|
||||||
use trans_item::TransItem;
|
use trans_item::TransItem;
|
||||||
use type_of;
|
use type_of;
|
||||||
use rustc::ty::{self, TypeFoldable};
|
use rustc::ty::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
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// Translates a reference to a fn/method item, monomorphizing and
|
/// Translates a reference to a fn/method item, monomorphizing and
|
||||||
/// inlining as it goes.
|
/// inlining as it goes.
|
||||||
|
@ -157,11 +33,10 @@ fn trans_fn_once_adapter_shim<'a, 'tcx>(
|
||||||
/// # Parameters
|
/// # Parameters
|
||||||
///
|
///
|
||||||
/// - `ccx`: the crate context
|
/// - `ccx`: the crate context
|
||||||
/// - `def_id`: def id of the fn or method item being referenced
|
/// - `instance`: the instance to be instantiated
|
||||||
/// - `substs`: values for each of the fn/method's parameters
|
pub fn get_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
|
||||||
fn do_get_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
|
instance: Instance<'tcx>)
|
||||||
instance: Instance<'tcx>)
|
-> ValueRef
|
||||||
-> ValueRef
|
|
||||||
{
|
{
|
||||||
let tcx = ccx.tcx();
|
let tcx = ccx.tcx();
|
||||||
|
|
||||||
|
@ -248,40 +123,6 @@ fn do_get_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
|
||||||
llfn
|
llfn
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
|
|
||||||
instance: Instance<'tcx>)
|
|
||||||
-> ValueRef
|
|
||||||
{
|
|
||||||
match instance.def {
|
|
||||||
ty::InstanceDef::Intrinsic(_) => {
|
|
||||||
bug!("intrinsic {} getting reified", instance)
|
|
||||||
}
|
|
||||||
ty::InstanceDef::ClosureOnceShim { .. } => {
|
|
||||||
let closure_ty = instance.substs.type_at(0);
|
|
||||||
let (closure_def_id, closure_substs) = match closure_ty.sty {
|
|
||||||
ty::TyClosure(def_id, substs) => (def_id, substs),
|
|
||||||
_ => bug!("bad closure instance {:?}", instance)
|
|
||||||
};
|
|
||||||
|
|
||||||
trans_fn_once_adapter_shim(
|
|
||||||
ccx,
|
|
||||||
closure_def_id,
|
|
||||||
closure_substs,
|
|
||||||
instance,
|
|
||||||
do_get_fn(
|
|
||||||
ccx,
|
|
||||||
Instance::new(closure_def_id, closure_substs.substs)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
ty::InstanceDef::FnPtrShim(..) |
|
|
||||||
ty::InstanceDef::Item(..) |
|
|
||||||
ty::InstanceDef::Virtual(..) => {
|
|
||||||
do_get_fn(ccx, instance)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn resolve_and_get_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
|
pub fn resolve_and_get_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
|
||||||
def_id: DefId,
|
def_id: DefId,
|
||||||
substs: &'tcx Substs<'tcx>)
|
substs: &'tcx Substs<'tcx>)
|
||||||
|
|
|
@ -93,24 +93,6 @@ impl<'tcx> DropValue<'tcx> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'tcx> CleanupScope<'tcx> {
|
impl<'a, 'tcx> CleanupScope<'tcx> {
|
||||||
/// Schedules a (deep) drop of `val`, which is a pointer to an instance of `ty`
|
|
||||||
pub fn schedule_drop_mem(
|
|
||||||
bcx: &Builder<'a, 'tcx>, val: LvalueRef<'tcx>
|
|
||||||
) -> CleanupScope<'tcx> {
|
|
||||||
if let LvalueTy::Downcast { .. } = val.ty {
|
|
||||||
bug!("Cannot drop downcast ty yet");
|
|
||||||
}
|
|
||||||
if !bcx.ccx.shared().type_needs_drop(val.ty.to_ty(bcx.tcx())) {
|
|
||||||
return CleanupScope::noop();
|
|
||||||
}
|
|
||||||
let drop = DropValue {
|
|
||||||
val: val,
|
|
||||||
skip_dtor: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
CleanupScope::new(bcx, drop)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Issue #23611: Schedules a (deep) drop of the contents of
|
/// Issue #23611: Schedules a (deep) drop of the contents of
|
||||||
/// `val`, which is a pointer to an instance of struct/enum type
|
/// `val`, which is a pointer to an instance of struct/enum type
|
||||||
/// `ty`. The scheduled code handles extracting the discriminant
|
/// `ty`. The scheduled code handles extracting the discriminant
|
||||||
|
|
|
@ -497,11 +497,9 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> {
|
||||||
let source_ty = operand.ty(self.mir, self.scx.tcx());
|
let source_ty = operand.ty(self.mir, self.scx.tcx());
|
||||||
match source_ty.sty {
|
match source_ty.sty {
|
||||||
ty::TyClosure(def_id, substs) => {
|
ty::TyClosure(def_id, substs) => {
|
||||||
let substs = monomorphize::apply_param_substs(
|
let instance = monomorphize::resolve_closure(
|
||||||
self.scx, self.param_substs, &substs.substs);
|
self.scx, def_id, substs, ty::ClosureKind::FnOnce);
|
||||||
self.output.push(create_fn_trans_item(
|
self.output.push(create_fn_trans_item(instance));
|
||||||
Instance::new(def_id, substs)
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
_ => bug!(),
|
_ => bug!(),
|
||||||
}
|
}
|
||||||
|
@ -601,20 +599,6 @@ fn visit_fn_use<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>,
|
||||||
}
|
}
|
||||||
|
|
||||||
match instance.def {
|
match instance.def {
|
||||||
ty::InstanceDef::ClosureOnceShim { .. } => {
|
|
||||||
// This call will instantiate an FnOnce adapter, which
|
|
||||||
// drops the closure environment. Therefore we need to
|
|
||||||
// make sure that we collect the drop-glue for the
|
|
||||||
// environment type along with the instance.
|
|
||||||
|
|
||||||
let env_ty = instance.substs.type_at(0);
|
|
||||||
let env_ty = glue::get_drop_glue_type(scx, env_ty);
|
|
||||||
if scx.type_needs_drop(env_ty) {
|
|
||||||
let dg = DropGlueKind::Ty(env_ty);
|
|
||||||
output.push(TransItem::DropGlue(dg));
|
|
||||||
}
|
|
||||||
output.push(create_fn_trans_item(instance));
|
|
||||||
}
|
|
||||||
ty::InstanceDef::Intrinsic(..) => {
|
ty::InstanceDef::Intrinsic(..) => {
|
||||||
if !is_direct_call {
|
if !is_direct_call {
|
||||||
bug!("intrinsic {:?} being reified", ty);
|
bug!("intrinsic {:?} being reified", ty);
|
||||||
|
@ -632,6 +616,7 @@ fn visit_fn_use<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>,
|
||||||
output.push(create_fn_trans_item(instance));
|
output.push(create_fn_trans_item(instance));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ty::InstanceDef::ClosureOnceShim { .. } |
|
||||||
ty::InstanceDef::Item(..) |
|
ty::InstanceDef::Item(..) |
|
||||||
ty::InstanceDef::FnPtrShim(..) => {
|
ty::InstanceDef::FnPtrShim(..) => {
|
||||||
output.push(create_fn_trans_item(instance));
|
output.push(create_fn_trans_item(instance));
|
||||||
|
@ -645,10 +630,8 @@ fn visit_fn_use<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>,
|
||||||
fn should_trans_locally<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, instance: &Instance<'tcx>)
|
fn should_trans_locally<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, instance: &Instance<'tcx>)
|
||||||
-> bool {
|
-> bool {
|
||||||
let def_id = match instance.def {
|
let def_id = match instance.def {
|
||||||
ty::InstanceDef::Item(def_id) |
|
ty::InstanceDef::Item(def_id) => def_id,
|
||||||
ty::InstanceDef::ClosureOnceShim {
|
ty::InstanceDef::ClosureOnceShim { .. } |
|
||||||
call_once: _, closure_did: def_id
|
|
||||||
} => def_id,
|
|
||||||
ty::InstanceDef::Virtual(..) |
|
ty::InstanceDef::Virtual(..) |
|
||||||
ty::InstanceDef::FnPtrShim(..) |
|
ty::InstanceDef::FnPtrShim(..) |
|
||||||
ty::InstanceDef::Intrinsic(_) => return true
|
ty::InstanceDef::Intrinsic(_) => return true
|
||||||
|
@ -885,20 +868,6 @@ fn find_vtable_types_for_unsizing<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>,
|
||||||
|
|
||||||
fn create_fn_trans_item<'a, 'tcx>(instance: Instance<'tcx>) -> TransItem<'tcx> {
|
fn create_fn_trans_item<'a, 'tcx>(instance: Instance<'tcx>) -> TransItem<'tcx> {
|
||||||
debug!("create_fn_trans_item(instance={})", instance);
|
debug!("create_fn_trans_item(instance={})", instance);
|
||||||
let instance = match instance.def {
|
|
||||||
ty::InstanceDef::ClosureOnceShim { .. } => {
|
|
||||||
// HACK: don't create ClosureOnce trans items for now
|
|
||||||
// have someone else generate the drop glue
|
|
||||||
let closure_ty = instance.substs.type_at(0);
|
|
||||||
match closure_ty.sty {
|
|
||||||
ty::TyClosure(def_id, substs) => {
|
|
||||||
Instance::new(def_id, substs.substs)
|
|
||||||
}
|
|
||||||
_ => bug!("bad closure instance {:?}", instance)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => instance
|
|
||||||
};
|
|
||||||
TransItem::Fn(instance)
|
TransItem::Fn(instance)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,6 @@ use llvm::{self, ValueRef};
|
||||||
use rustc::ty::{self, Ty};
|
use rustc::ty::{self, Ty};
|
||||||
use rustc::ty::cast::{CastTy, IntTy};
|
use rustc::ty::cast::{CastTy, IntTy};
|
||||||
use rustc::ty::layout::Layout;
|
use rustc::ty::layout::Layout;
|
||||||
use rustc::ty::subst::{Kind, Subst};
|
|
||||||
use rustc::mir::tcx::LvalueTy;
|
use rustc::mir::tcx::LvalueTy;
|
||||||
use rustc::mir;
|
use rustc::mir;
|
||||||
use middle::lang_items::ExchangeMallocFnLangItem;
|
use middle::lang_items::ExchangeMallocFnLangItem;
|
||||||
|
@ -24,6 +23,7 @@ use common::{self, val_ty, C_bool, C_null, C_uint};
|
||||||
use common::{C_integral};
|
use common::{C_integral};
|
||||||
use adt;
|
use adt;
|
||||||
use machine;
|
use machine;
|
||||||
|
use monomorphize;
|
||||||
use type_::Type;
|
use type_::Type;
|
||||||
use type_of;
|
use type_of;
|
||||||
use tvec;
|
use tvec;
|
||||||
|
@ -193,22 +193,9 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> {
|
||||||
mir::CastKind::ClosureFnPointer => {
|
mir::CastKind::ClosureFnPointer => {
|
||||||
match operand.ty.sty {
|
match operand.ty.sty {
|
||||||
ty::TyClosure(def_id, substs) => {
|
ty::TyClosure(def_id, substs) => {
|
||||||
// Get the def_id for FnOnce::call_once
|
let instance = monomorphize::resolve_closure(
|
||||||
let fn_once = bcx.tcx().lang_items.fn_once_trait().unwrap();
|
bcx.ccx.shared(), def_id, substs, ty::ClosureKind::FnOnce);
|
||||||
let call_once = bcx.tcx()
|
OperandValue::Immediate(callee::get_fn(bcx.ccx, instance))
|
||||||
.global_tcx().associated_items(fn_once)
|
|
||||||
.find(|it| it.kind == ty::AssociatedKind::Method)
|
|
||||||
.unwrap().def_id;
|
|
||||||
// Now create its substs [Closure, Tuple]
|
|
||||||
let input = bcx.tcx().closure_type(def_id)
|
|
||||||
.subst(bcx.tcx(), substs.substs).input(0);
|
|
||||||
let input =
|
|
||||||
bcx.tcx().erase_late_bound_regions_and_normalize(&input);
|
|
||||||
let substs = bcx.tcx().mk_substs([operand.ty, input]
|
|
||||||
.iter().cloned().map(Kind::from));
|
|
||||||
OperandValue::Immediate(
|
|
||||||
callee::resolve_and_get_fn(bcx.ccx, call_once, substs)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
bug!("{} cannot be cast to a fn ptr", operand.ty)
|
bug!("{} cannot be cast to a fn ptr", operand.ty)
|
||||||
|
|
|
@ -37,7 +37,7 @@ fn fn_once_adapter_instance<'a, 'tcx>(
|
||||||
let call_once = tcx.associated_items(fn_once)
|
let call_once = tcx.associated_items(fn_once)
|
||||||
.find(|it| it.kind == ty::AssociatedKind::Method)
|
.find(|it| it.kind == ty::AssociatedKind::Method)
|
||||||
.unwrap().def_id;
|
.unwrap().def_id;
|
||||||
let def = ty::InstanceDef::ClosureOnceShim { call_once, closure_did };
|
let def = ty::InstanceDef::ClosureOnceShim { call_once };
|
||||||
|
|
||||||
let self_ty = tcx.mk_closure_from_closure_substs(
|
let self_ty = tcx.mk_closure_from_closure_substs(
|
||||||
closure_did, substs);
|
closure_did, substs);
|
||||||
|
@ -54,6 +54,54 @@ fn fn_once_adapter_instance<'a, 'tcx>(
|
||||||
Instance { def, substs }
|
Instance { def, substs }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn needs_fn_once_adapter_shim(actual_closure_kind: ty::ClosureKind,
|
||||||
|
trait_closure_kind: ty::ClosureKind)
|
||||||
|
-> Result<bool, ()>
|
||||||
|
{
|
||||||
|
match (actual_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.
|
||||||
|
Ok(false)
|
||||||
|
}
|
||||||
|
(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.
|
||||||
|
Ok(false)
|
||||||
|
}
|
||||||
|
(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.
|
||||||
|
Ok(true)
|
||||||
|
}
|
||||||
|
_ => Err(()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resolve_closure<'a, 'tcx> (
|
||||||
|
scx: &SharedCrateContext<'a, 'tcx>,
|
||||||
|
def_id: DefId,
|
||||||
|
substs: ty::ClosureSubsts<'tcx>,
|
||||||
|
requested_kind: ty::ClosureKind)
|
||||||
|
-> Instance<'tcx>
|
||||||
|
{
|
||||||
|
let actual_kind = scx.tcx().closure_kind(def_id);
|
||||||
|
|
||||||
|
match needs_fn_once_adapter_shim(actual_kind, requested_kind) {
|
||||||
|
Ok(true) => fn_once_adapter_instance(scx.tcx(), def_id, substs),
|
||||||
|
_ => Instance::new(def_id, substs.substs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Attempts to resolve an obligation. The result is a shallow vtable resolution -- meaning that we
|
/// Attempts to resolve an obligation. The result is a shallow vtable resolution -- meaning that we
|
||||||
/// do not (necessarily) resolve all nested obligations on the impl. Note that type check should
|
/// do not (necessarily) resolve all nested obligations on the impl. Note that type check should
|
||||||
/// guarantee to us that all nested obligations *could be* resolved if we wanted to.
|
/// guarantee to us that all nested obligations *could be* resolved if we wanted to.
|
||||||
|
@ -121,39 +169,6 @@ fn fulfill_obligation<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn needs_fn_once_adapter_shim(actual_closure_kind: ty::ClosureKind,
|
|
||||||
trait_closure_kind: ty::ClosureKind)
|
|
||||||
-> Result<bool, ()>
|
|
||||||
{
|
|
||||||
match (actual_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.
|
|
||||||
Ok(false)
|
|
||||||
}
|
|
||||||
(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.
|
|
||||||
Ok(false)
|
|
||||||
}
|
|
||||||
(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.
|
|
||||||
Ok(true)
|
|
||||||
}
|
|
||||||
_ => Err(()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn resolve_associated_item<'a, 'tcx>(
|
fn resolve_associated_item<'a, 'tcx>(
|
||||||
scx: &SharedCrateContext<'a, 'tcx>,
|
scx: &SharedCrateContext<'a, 'tcx>,
|
||||||
trait_item: &ty::AssociatedItem,
|
trait_item: &ty::AssociatedItem,
|
||||||
|
@ -180,16 +195,9 @@ fn resolve_associated_item<'a, 'tcx>(
|
||||||
ty::Instance::new(def_id, substs)
|
ty::Instance::new(def_id, substs)
|
||||||
}
|
}
|
||||||
traits::VtableClosure(closure_data) => {
|
traits::VtableClosure(closure_data) => {
|
||||||
let closure_def_id = closure_data.closure_def_id;
|
|
||||||
let trait_closure_kind = tcx.lang_items.fn_trait_kind(trait_id).unwrap();
|
let trait_closure_kind = tcx.lang_items.fn_trait_kind(trait_id).unwrap();
|
||||||
let actual_closure_kind = tcx.closure_kind(closure_def_id);
|
resolve_closure(scx, closure_data.closure_def_id, closure_data.substs,
|
||||||
|
trait_closure_kind)
|
||||||
match needs_fn_once_adapter_shim(actual_closure_kind,
|
|
||||||
trait_closure_kind) {
|
|
||||||
Ok(true) => fn_once_adapter_instance(
|
|
||||||
tcx, closure_def_id, closure_data.substs),
|
|
||||||
_ => Instance::new(closure_def_id, closure_data.substs.substs),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
traits::VtableFnPointer(ref data) => {
|
traits::VtableFnPointer(ref data) => {
|
||||||
Instance {
|
Instance {
|
||||||
|
@ -279,7 +287,6 @@ pub fn apply_param_substs<'a, 'tcx, T>(scx: &SharedCrateContext<'a, 'tcx>,
|
||||||
AssociatedTypeNormalizer::new(scx).fold(&substituted)
|
AssociatedTypeNormalizer::new(scx).fold(&substituted)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Returns the normalized type of a struct field
|
/// Returns the normalized type of a struct field
|
||||||
pub fn field_ty<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
pub fn field_ty<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||||
param_substs: &Substs<'tcx>,
|
param_substs: &Substs<'tcx>,
|
||||||
|
|
|
@ -8,6 +8,11 @@
|
||||||
// option. This file may not be copied, modified, or distributed
|
// option. This file may not be copied, modified, or distributed
|
||||||
// except according to those terms.
|
// except according to those terms.
|
||||||
|
|
||||||
|
use std::panic;
|
||||||
|
|
||||||
|
impl<'a> panic::UnwindSafe for Foo<'a> {}
|
||||||
|
impl<'a> panic::RefUnwindSafe for Foo<'a> {}
|
||||||
|
|
||||||
struct Foo<'a>(&'a mut bool);
|
struct Foo<'a>(&'a mut bool);
|
||||||
|
|
||||||
impl<'a> Drop for Foo<'a> {
|
impl<'a> Drop for Foo<'a> {
|
||||||
|
@ -28,5 +33,15 @@ fn main() {
|
||||||
f(x);
|
f(x);
|
||||||
}
|
}
|
||||||
assert!(ran_drop);
|
assert!(ran_drop);
|
||||||
}
|
|
||||||
|
|
||||||
|
let mut ran_drop = false;
|
||||||
|
{
|
||||||
|
let x = Foo(&mut ran_drop);
|
||||||
|
let result = panic::catch_unwind(move || {
|
||||||
|
let x = move || { let _ = x; panic!() };
|
||||||
|
f(x);
|
||||||
|
});
|
||||||
|
assert!(result.is_err());
|
||||||
|
}
|
||||||
|
assert!(ran_drop);
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue