Skip to content

Commit 0576869

Browse files
committed
Auto merge of #38822 - michaelwoerister:collect-fn-once-adapter, r=eddyb
trans: Fix missing closure env drop-glue in trans-item collector. FnOnce adapters automatically generated by the compiler introduce a call to drop the closure environment. The collector didn't pick up on that because this drop call does not show up in MIR. That could lead to an assertion being triggered if the drop-glue for the environment wasn't instantiated via something else. Fixes #38810 cc @arielb1 r? @eddyb or @nikomatsakis
2 parents 05383b2 + 8b94267 commit 0576869

File tree

2 files changed

+123
-24
lines changed

2 files changed

+123
-24
lines changed

src/librustc_trans/callee.rs

+24-9
Original file line numberDiff line numberDiff line change
@@ -234,18 +234,37 @@ fn trans_closure_method<'a, 'tcx>(ccx: &'a CrateContext<'a, 'tcx>,
234234
trait_closure_kind={:?}, llfn={:?})",
235235
llfn_closure_kind, trait_closure_kind, Value(llfn));
236236

237-
match (llfn_closure_kind, trait_closure_kind) {
237+
match needs_fn_once_adapter_shim(llfn_closure_kind, trait_closure_kind) {
238+
Ok(true) => trans_fn_once_adapter_shim(ccx,
239+
def_id,
240+
substs,
241+
method_instance,
242+
llfn),
243+
Ok(false) => llfn,
244+
Err(()) => {
245+
bug!("trans_closure_adapter_shim: cannot convert {:?} to {:?}",
246+
llfn_closure_kind,
247+
trait_closure_kind);
248+
}
249+
}
250+
}
251+
252+
pub fn needs_fn_once_adapter_shim(actual_closure_kind: ty::ClosureKind,
253+
trait_closure_kind: ty::ClosureKind)
254+
-> Result<bool, ()>
255+
{
256+
match (actual_closure_kind, trait_closure_kind) {
238257
(ty::ClosureKind::Fn, ty::ClosureKind::Fn) |
239258
(ty::ClosureKind::FnMut, ty::ClosureKind::FnMut) |
240259
(ty::ClosureKind::FnOnce, ty::ClosureKind::FnOnce) => {
241260
// No adapter needed.
242-
llfn
261+
Ok(false)
243262
}
244263
(ty::ClosureKind::Fn, ty::ClosureKind::FnMut) => {
245264
// The closure fn `llfn` is a `fn(&self, ...)`. We want a
246265
// `fn(&mut self, ...)`. In fact, at trans time, these are
247266
// basically the same thing, so we can just return llfn.
248-
llfn
267+
Ok(false)
249268
}
250269
(ty::ClosureKind::Fn, ty::ClosureKind::FnOnce) |
251270
(ty::ClosureKind::FnMut, ty::ClosureKind::FnOnce) => {
@@ -257,13 +276,9 @@ fn trans_closure_method<'a, 'tcx>(ccx: &'a CrateContext<'a, 'tcx>,
257276
// fn call_once(mut self, ...) { call_mut(&mut self, ...) }
258277
//
259278
// These are both the same at trans time.
260-
trans_fn_once_adapter_shim(ccx, def_id, substs, method_instance, llfn)
261-
}
262-
_ => {
263-
bug!("trans_closure_adapter_shim: cannot convert {:?} to {:?}",
264-
llfn_closure_kind,
265-
trait_closure_kind);
279+
Ok(true)
266280
}
281+
_ => Err(()),
267282
}
268283
}
269284

src/librustc_trans/collector.rs

+99-15
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,7 @@ use rustc::mir::visit::Visitor as MirVisitor;
205205
use syntax::abi::Abi;
206206
use syntax_pos::DUMMY_SP;
207207
use base::custom_coerce_unsize_info;
208+
use callee::needs_fn_once_adapter_shim;
208209
use context::SharedCrateContext;
209210
use common::fulfill_obligation;
210211
use glue::{self, DropGlueKind};
@@ -568,7 +569,11 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> {
568569
callee_substs,
569570
self.param_substs);
570571

571-
if let Some((callee_def_id, callee_substs)) = dispatched {
572+
if let StaticDispatchResult::Dispatched {
573+
def_id: callee_def_id,
574+
substs: callee_substs,
575+
fn_once_adjustment,
576+
} = dispatched {
572577
// if we have a concrete impl (which we might not have
573578
// in the case of something compiler generated like an
574579
// object shim or a closure that is handled differently),
@@ -581,6 +586,17 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> {
581586
callee_substs,
582587
self.param_substs);
583588
self.output.push(trans_item);
589+
590+
// This call will instantiate an FnOnce adapter, which drops
591+
// the closure environment. Therefore we need to make sure
592+
// that we collect the drop-glue for the environment type.
593+
if let Some(env_ty) = fn_once_adjustment {
594+
let env_ty = glue::get_drop_glue_type(self.scx, env_ty);
595+
if self.scx.type_needs_drop(env_ty) {
596+
let dg = DropGlueKind::Ty(env_ty);
597+
self.output.push(TransItem::DropGlue(dg));
598+
}
599+
}
584600
}
585601
}
586602
}
@@ -793,15 +809,13 @@ fn find_drop_glue_neighbors<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>,
793809
bug!("encountered unexpected type");
794810
}
795811
}
796-
797-
798812
}
799813

800814
fn do_static_dispatch<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>,
801815
fn_def_id: DefId,
802816
fn_substs: &'tcx Substs<'tcx>,
803817
param_substs: &'tcx Substs<'tcx>)
804-
-> Option<(DefId, &'tcx Substs<'tcx>)> {
818+
-> StaticDispatchResult<'tcx> {
805819
debug!("do_static_dispatch(fn_def_id={}, fn_substs={:?}, param_substs={:?})",
806820
def_id_to_string(scx.tcx(), fn_def_id),
807821
fn_substs,
@@ -818,18 +832,38 @@ fn do_static_dispatch<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>,
818832
debug!(" => regular function");
819833
// The function is not part of an impl or trait, no dispatching
820834
// to be done
821-
Some((fn_def_id, fn_substs))
835+
StaticDispatchResult::Dispatched {
836+
def_id: fn_def_id,
837+
substs: fn_substs,
838+
fn_once_adjustment: None,
839+
}
822840
}
823841
}
824842

843+
enum StaticDispatchResult<'tcx> {
844+
// The call could be resolved statically as going to the method with
845+
// `def_id` and `substs`.
846+
Dispatched {
847+
def_id: DefId,
848+
substs: &'tcx Substs<'tcx>,
849+
850+
// If this is a call to a closure that needs an FnOnce adjustment,
851+
// this contains the new self type of the call (= type of the closure
852+
// environment)
853+
fn_once_adjustment: Option<ty::Ty<'tcx>>,
854+
},
855+
// This goes to somewhere that we don't know at compile-time
856+
Unknown
857+
}
858+
825859
// Given a trait-method and substitution information, find out the actual
826860
// implementation of the trait method.
827861
fn do_static_trait_method_dispatch<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>,
828862
trait_method: &ty::AssociatedItem,
829863
trait_id: DefId,
830864
callee_substs: &'tcx Substs<'tcx>,
831865
param_substs: &'tcx Substs<'tcx>)
832-
-> Option<(DefId, &'tcx Substs<'tcx>)> {
866+
-> StaticDispatchResult<'tcx> {
833867
let tcx = scx.tcx();
834868
debug!("do_static_trait_method_dispatch(trait_method={}, \
835869
trait_id={}, \
@@ -850,17 +884,56 @@ fn do_static_trait_method_dispatch<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>,
850884
// the actual function:
851885
match vtbl {
852886
traits::VtableImpl(impl_data) => {
853-
Some(traits::find_method(tcx, trait_method.name, rcvr_substs, &impl_data))
887+
let (def_id, substs) = traits::find_method(tcx,
888+
trait_method.name,
889+
rcvr_substs,
890+
&impl_data);
891+
StaticDispatchResult::Dispatched {
892+
def_id: def_id,
893+
substs: substs,
894+
fn_once_adjustment: None,
895+
}
854896
}
855897
traits::VtableClosure(closure_data) => {
856-
Some((closure_data.closure_def_id, closure_data.substs.substs))
898+
let closure_def_id = closure_data.closure_def_id;
899+
let trait_closure_kind = tcx.lang_items.fn_trait_kind(trait_id).unwrap();
900+
let actual_closure_kind = tcx.closure_kind(closure_def_id);
901+
902+
let needs_fn_once_adapter_shim =
903+
match needs_fn_once_adapter_shim(actual_closure_kind,
904+
trait_closure_kind) {
905+
Ok(true) => true,
906+
_ => false,
907+
};
908+
909+
let fn_once_adjustment = if needs_fn_once_adapter_shim {
910+
Some(tcx.mk_closure_from_closure_substs(closure_def_id,
911+
closure_data.substs))
912+
} else {
913+
None
914+
};
915+
916+
StaticDispatchResult::Dispatched {
917+
def_id: closure_def_id,
918+
substs: closure_data.substs.substs,
919+
fn_once_adjustment: fn_once_adjustment,
920+
}
857921
}
858-
// Trait object and function pointer shims are always
859-
// instantiated in-place, and as they are just an ABI-adjusting
860-
// indirect call they do not have any dependencies.
861-
traits::VtableFnPointer(..) |
922+
traits::VtableFnPointer(ref data) => {
923+
// If we know the destination of this fn-pointer, we'll have to make
924+
// sure that this destination actually gets instantiated.
925+
if let ty::TyFnDef(def_id, substs, _) = data.fn_ty.sty {
926+
// The destination of the pointer might be something that needs
927+
// further dispatching, such as a trait method, so we do that.
928+
do_static_dispatch(scx, def_id, substs, param_substs)
929+
} else {
930+
StaticDispatchResult::Unknown
931+
}
932+
}
933+
// Trait object shims are always instantiated in-place, and as they are
934+
// just an ABI-adjusting indirect call they do not have any dependencies.
862935
traits::VtableObject(..) => {
863-
None
936+
StaticDispatchResult::Unknown
864937
}
865938
_ => {
866939
bug!("static call to invalid vtable: {:?}", vtbl)
@@ -994,8 +1067,19 @@ fn create_trans_items_for_vtable_methods<'a, 'tcx>(scx: &SharedCrateContext<'a,
9941067
// Walk all methods of the trait, including those of its supertraits
9951068
let methods = traits::get_vtable_methods(scx.tcx(), poly_trait_ref);
9961069
let methods = methods.filter_map(|method| method)
997-
.filter_map(|(def_id, substs)| do_static_dispatch(scx, def_id, substs,
998-
param_substs))
1070+
.filter_map(|(def_id, substs)| {
1071+
if let StaticDispatchResult::Dispatched {
1072+
def_id,
1073+
substs,
1074+
// We already add the drop-glue for the closure env
1075+
// unconditionally below.
1076+
fn_once_adjustment: _ ,
1077+
} = do_static_dispatch(scx, def_id, substs, param_substs) {
1078+
Some((def_id, substs))
1079+
} else {
1080+
None
1081+
}
1082+
})
9991083
.filter(|&(def_id, _)| can_have_local_instance(scx.tcx(), def_id))
10001084
.map(|(def_id, substs)| create_fn_trans_item(scx, def_id, substs, param_substs));
10011085
output.extend(methods);

0 commit comments

Comments
 (0)