Skip to content

trans: Fix missing closure env drop-glue in trans-item collector. #38822

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jan 8, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 24 additions & 9 deletions src/librustc_trans/callee.rs
Original file line number Diff line number Diff line change
Expand Up @@ -235,18 +235,37 @@ fn trans_closure_method<'a, 'tcx>(ccx: &'a CrateContext<'a, 'tcx>,
trait_closure_kind={:?}, llfn={:?})",
llfn_closure_kind, trait_closure_kind, Value(llfn));

match (llfn_closure_kind, trait_closure_kind) {
match needs_fn_once_adapter_shim(llfn_closure_kind, trait_closure_kind) {
Ok(true) => trans_fn_once_adapter_shim(ccx,
def_id,
substs,
method_instance,
llfn),
Ok(false) => llfn,
Err(()) => {
bug!("trans_closure_adapter_shim: cannot convert {:?} to {:?}",
llfn_closure_kind,
trait_closure_kind);
}
}
}

pub 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.
llfn
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.
llfn
Ok(false)
}
(ty::ClosureKind::Fn, ty::ClosureKind::FnOnce) |
(ty::ClosureKind::FnMut, ty::ClosureKind::FnOnce) => {
Expand All @@ -258,13 +277,9 @@ fn trans_closure_method<'a, 'tcx>(ccx: &'a CrateContext<'a, 'tcx>,
// 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);
Ok(true)
}
_ => Err(()),
}
}

Expand Down
114 changes: 99 additions & 15 deletions src/librustc_trans/collector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,7 @@ use rustc::mir::visit::Visitor as MirVisitor;
use syntax::abi::Abi;
use syntax_pos::DUMMY_SP;
use base::custom_coerce_unsize_info;
use callee::needs_fn_once_adapter_shim;
use context::SharedCrateContext;
use common::fulfill_obligation;
use glue::{self, DropGlueKind};
Expand Down Expand Up @@ -568,7 +569,11 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> {
callee_substs,
self.param_substs);

if let Some((callee_def_id, callee_substs)) = dispatched {
if let StaticDispatchResult::Dispatched {
def_id: callee_def_id,
substs: callee_substs,
fn_once_adjustment,
} = dispatched {
// if we have a concrete impl (which we might not have
// in the case of something compiler generated like an
// object shim or a closure that is handled differently),
Expand All @@ -581,6 +586,17 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> {
callee_substs,
self.param_substs);
self.output.push(trans_item);

// 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.
if let Some(env_ty) = fn_once_adjustment {
let env_ty = glue::get_drop_glue_type(self.scx, env_ty);
if self.scx.type_needs_drop(env_ty) {
let dg = DropGlueKind::Ty(env_ty);
self.output.push(TransItem::DropGlue(dg));
}
}
}
}
}
Expand Down Expand Up @@ -793,15 +809,13 @@ fn find_drop_glue_neighbors<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>,
bug!("encountered unexpected type");
}
}


}

fn do_static_dispatch<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>,
fn_def_id: DefId,
fn_substs: &'tcx Substs<'tcx>,
param_substs: &'tcx Substs<'tcx>)
-> Option<(DefId, &'tcx Substs<'tcx>)> {
-> StaticDispatchResult<'tcx> {
debug!("do_static_dispatch(fn_def_id={}, fn_substs={:?}, param_substs={:?})",
def_id_to_string(scx.tcx(), fn_def_id),
fn_substs,
Expand All @@ -818,18 +832,38 @@ fn do_static_dispatch<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>,
debug!(" => regular function");
// The function is not part of an impl or trait, no dispatching
// to be done
Some((fn_def_id, fn_substs))
StaticDispatchResult::Dispatched {
def_id: fn_def_id,
substs: fn_substs,
fn_once_adjustment: None,
}
}
}

enum StaticDispatchResult<'tcx> {
// The call could be resolved statically as going to the method with
// `def_id` and `substs`.
Dispatched {
def_id: DefId,
substs: &'tcx Substs<'tcx>,

// If this is a call to a closure that needs an FnOnce adjustment,
// this contains the new self type of the call (= type of the closure
// environment)
fn_once_adjustment: Option<ty::Ty<'tcx>>,
},
// This goes to somewhere that we don't know at compile-time
Unknown
}

// Given a trait-method and substitution information, find out the actual
// implementation of the trait method.
fn do_static_trait_method_dispatch<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>,
trait_method: &ty::AssociatedItem,
trait_id: DefId,
callee_substs: &'tcx Substs<'tcx>,
param_substs: &'tcx Substs<'tcx>)
-> Option<(DefId, &'tcx Substs<'tcx>)> {
-> StaticDispatchResult<'tcx> {
let tcx = scx.tcx();
debug!("do_static_trait_method_dispatch(trait_method={}, \
trait_id={}, \
Expand All @@ -850,17 +884,56 @@ fn do_static_trait_method_dispatch<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>,
// the actual function:
match vtbl {
traits::VtableImpl(impl_data) => {
Some(traits::find_method(tcx, trait_method.name, rcvr_substs, &impl_data))
let (def_id, substs) = traits::find_method(tcx,
trait_method.name,
rcvr_substs,
&impl_data);
StaticDispatchResult::Dispatched {
def_id: def_id,
substs: substs,
fn_once_adjustment: None,
}
}
traits::VtableClosure(closure_data) => {
Some((closure_data.closure_def_id, closure_data.substs.substs))
let closure_def_id = closure_data.closure_def_id;
let trait_closure_kind = tcx.lang_items.fn_trait_kind(trait_id).unwrap();
let actual_closure_kind = tcx.closure_kind(closure_def_id);

let needs_fn_once_adapter_shim =
match needs_fn_once_adapter_shim(actual_closure_kind,
trait_closure_kind) {
Ok(true) => true,
_ => false,
};

let fn_once_adjustment = if needs_fn_once_adapter_shim {
Some(tcx.mk_closure_from_closure_substs(closure_def_id,
closure_data.substs))
} else {
None
};

StaticDispatchResult::Dispatched {
def_id: closure_def_id,
substs: closure_data.substs.substs,
fn_once_adjustment: fn_once_adjustment,
}
}
// 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::VtableFnPointer(ref data) => {
// If we know the destination of this fn-pointer, we'll have to make
// sure that this destination actually gets instantiated.
if let ty::TyFnDef(def_id, substs, _) = data.fn_ty.sty {
// The destination of the pointer might be something that needs
// further dispatching, such as a trait method, so we do that.
do_static_dispatch(scx, def_id, substs, param_substs)
} else {
StaticDispatchResult::Unknown
}
}
// Trait object shims are always instantiated in-place, and as they are
// just an ABI-adjusting indirect call they do not have any dependencies.
traits::VtableObject(..) => {
None
StaticDispatchResult::Unknown
}
_ => {
bug!("static call to invalid vtable: {:?}", vtbl)
Expand Down Expand Up @@ -994,8 +1067,19 @@ fn create_trans_items_for_vtable_methods<'a, 'tcx>(scx: &SharedCrateContext<'a,
// Walk all methods of the trait, including those of its supertraits
let methods = traits::get_vtable_methods(scx.tcx(), poly_trait_ref);
let methods = methods.filter_map(|method| method)
.filter_map(|(def_id, substs)| do_static_dispatch(scx, def_id, substs,
param_substs))
.filter_map(|(def_id, substs)| {
if let StaticDispatchResult::Dispatched {
def_id,
substs,
// We already add the drop-glue for the closure env
// unconditionally below.
fn_once_adjustment: _ ,
} = do_static_dispatch(scx, def_id, substs, param_substs) {
Some((def_id, substs))
} else {
None
}
})
.filter(|&(def_id, _)| can_have_local_instance(scx.tcx(), def_id))
.map(|(def_id, substs)| create_fn_trans_item(scx, def_id, substs, param_substs));
output.extend(methods);
Expand Down