Skip to content
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

Move monomorphize::resolve() to librustc #44896

Merged
merged 12 commits into from
Oct 3, 2017
211 changes: 211 additions & 0 deletions src/librustc/ty/instance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@

use hir::def_id::DefId;
use ty::{self, Ty, TypeFoldable, Substs, TyCtxt};
use ty::subst::{Kind, Subst};
use traits;
use syntax::abi::Abi;
use syntax::codemap::DUMMY_SP;
use util::ppaux;

use std::fmt;
Expand Down Expand Up @@ -111,4 +115,211 @@ impl<'a, 'b, 'tcx> Instance<'tcx> {
pub fn def_id(&self) -> DefId {
self.def.def_id()
}

/// Resolve a (def_id, substs) pair to an (optional) instance -- most commonly,
/// this is used to find the precise code that will run for a trait method invocation,
/// if known.
///
/// Returns `None` if we cannot resolve `Instance` to a specific instance.
/// For example, in a context like this,
///
/// ```
/// fn foo<T: Debug>(t: T) { ... }
/// ```
///
/// trying to resolve `Debug::fmt` applied to `T` will yield `None`, because we do not
/// know what code ought to run. (Note that this setting is also affected by the
/// `RevealMode` in the parameter environment.)
///
/// Presuming that coherence and type-check have succeeded, if this method is invoked
/// in a monomorphic context (i.e., like during trans), then it is guaranteed to return
/// `Some`.
pub fn resolve(tcx: TyCtxt<'a, 'tcx, 'tcx>,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if this should be a query. Seems like a handy thing to cache.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we did make it a query, I'd prefer to see the provider moved into traits/instance or something.

param_env: ty::ParamEnv<'tcx>,
def_id: DefId,
substs: &'tcx Substs<'tcx>) -> Option<Instance<'tcx>> {
debug!("resolve(def_id={:?}, substs={:?})", def_id, substs);
let result = if let Some(trait_def_id) = tcx.trait_of_item(def_id) {
debug!(" => associated item, attempting to find impl");
let item = tcx.associated_item(def_id);
resolve_associated_item(tcx, &item, param_env, trait_def_id, substs)
} else {
let ty = tcx.type_of(def_id);
let item_type = tcx.trans_apply_param_substs(substs, &ty);

let def = match item_type.sty {
ty::TyFnDef(..) if {
let f = item_type.fn_sig(tcx);
f.abi() == Abi::RustIntrinsic ||
f.abi() == Abi::PlatformIntrinsic
} =>
{
debug!(" => intrinsic");
ty::InstanceDef::Intrinsic(def_id)
}
_ => {
if Some(def_id) == tcx.lang_items().drop_in_place_fn() {
let ty = substs.type_at(0);
if ty.needs_drop(tcx, ty::ParamEnv::empty(traits::Reveal::All)) {
debug!(" => nontrivial drop glue");
ty::InstanceDef::DropGlue(def_id, Some(ty))
} else {
debug!(" => trivial drop glue");
ty::InstanceDef::DropGlue(def_id, None)
}
} else {
debug!(" => free item");
ty::InstanceDef::Item(def_id)
}
}
};
Some(Instance {
def: def,
substs: substs
})
};
debug!("resolve(def_id={:?}, substs={:?}) = {:?}", def_id, substs, result);
result
}
}

fn resolve_closure<'a, 'tcx>(
tcx: TyCtxt<'a, 'tcx, 'tcx>,
def_id: DefId,
substs: ty::ClosureSubsts<'tcx>,
requested_kind: ty::ClosureKind)
-> Instance<'tcx>
{
let actual_kind = tcx.closure_kind(def_id);

match needs_fn_once_adapter_shim(actual_kind, requested_kind) {
Ok(true) => fn_once_adapter_instance(tcx, def_id, substs),
_ => Instance::new(def_id, substs.substs)
}
}

fn resolve_associated_item<'a, 'tcx>(
tcx: TyCtxt<'a, 'tcx, 'tcx>,
trait_item: &ty::AssociatedItem,
param_env: ty::ParamEnv<'tcx>,
trait_id: DefId,
rcvr_substs: &'tcx Substs<'tcx>
) -> Option<Instance<'tcx>> {
let def_id = trait_item.def_id;
debug!("resolve_associated_item(trait_item={:?}, \
trait_id={:?}, \
rcvr_substs={:?})",
def_id, trait_id, rcvr_substs);

let trait_ref = ty::TraitRef::from_method(tcx, trait_id, rcvr_substs);
let vtbl = tcx.trans_fulfill_obligation(DUMMY_SP, param_env, ty::Binder(trait_ref));

// Now that we know which impl is being used, we can dispatch to
// the actual function:
match vtbl {
traits::VtableImpl(impl_data) => {
let (def_id, substs) = traits::find_associated_item(
tcx, trait_item, rcvr_substs, &impl_data);
let substs = tcx.erase_regions(&substs);
Some(ty::Instance::new(def_id, substs))
}
traits::VtableGenerator(closure_data) => {
Some(Instance {
def: ty::InstanceDef::Item(closure_data.closure_def_id),
substs: closure_data.substs.substs
})
}
traits::VtableClosure(closure_data) => {
let trait_closure_kind = tcx.lang_items().fn_trait_kind(trait_id).unwrap();
Some(resolve_closure(tcx, closure_data.closure_def_id, closure_data.substs,
trait_closure_kind))
}
traits::VtableFnPointer(ref data) => {
Some(Instance {
def: ty::InstanceDef::FnPtrShim(trait_item.def_id, data.fn_ty),
substs: rcvr_substs
})
}
traits::VtableObject(ref data) => {
let index = tcx.get_vtable_index_of_object_method(data, def_id);
Some(Instance {
def: ty::InstanceDef::Virtual(def_id, index),
substs: rcvr_substs
})
}
traits::VtableBuiltin(..) => {
if let Some(_) = tcx.lang_items().clone_trait() {
Some(Instance {
def: ty::InstanceDef::CloneShim(def_id, trait_ref.self_ty()),
substs: rcvr_substs
})
} else {
None
}
}
traits::VtableDefaultImpl(..) | traits::VtableParam(..) => None
}
}

fn needs_fn_once_adapter_shim<'a, 'tcx>(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)
}
(ty::ClosureKind::FnMut, _) |
(ty::ClosureKind::FnOnce, _) => Err(())
}
}

fn fn_once_adapter_instance<'a, 'tcx>(
tcx: TyCtxt<'a, 'tcx, 'tcx>,
closure_did: DefId,
substs: ty::ClosureSubsts<'tcx>,
) -> Instance<'tcx> {
debug!("fn_once_adapter_shim({:?}, {:?})",
closure_did,
substs);
let fn_once = tcx.lang_items().fn_once_trait().unwrap();
let call_once = tcx.associated_items(fn_once)
.find(|it| it.kind == ty::AssociatedKind::Method)
.unwrap().def_id;
let def = ty::InstanceDef::ClosureOnceShim { call_once };

let self_ty = tcx.mk_closure_from_closure_substs(
closure_did, substs);

let sig = tcx.fn_sig(closure_did).subst(tcx, substs.substs);
let sig = tcx.erase_late_bound_regions_and_normalize(&sig);
assert_eq!(sig.inputs().len(), 1);
let substs = tcx.mk_substs([
Kind::from(self_ty),
Kind::from(sig.inputs()[0]),
].iter().cloned());

debug!("fn_once_adapter_shim: self_ty={:?} sig={:?}", self_ty, sig);
Instance { def, substs }
}
28 changes: 17 additions & 11 deletions src/librustc_mir/transform/inline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use rustc_data_structures::indexed_vec::{Idx, IndexVec};
use rustc::mir::*;
use rustc::mir::transform::{MirPass, MirSource};
use rustc::mir::visit::*;
use rustc::ty::{self, Ty, TyCtxt};
use rustc::ty::{self, Ty, TyCtxt, Instance};
use rustc::ty::subst::{Subst,Substs};

use std::collections::VecDeque;
Expand Down Expand Up @@ -78,7 +78,7 @@ impl<'a, 'tcx> Inliner<'a, 'tcx> {
let mut callsites = VecDeque::new();

// Only do inlining into fn bodies.
if let MirSource::Fn(_) = self.source {
if let MirSource::Fn(caller_id) = self.source {
for (bb, bb_data) in caller_mir.basic_blocks().iter_enumerated() {
// Don't inline calls that are in cleanup blocks.
if bb_data.is_cleanup { continue; }
Expand All @@ -87,17 +87,23 @@ impl<'a, 'tcx> Inliner<'a, 'tcx> {
let terminator = bb_data.terminator();
if let TerminatorKind::Call {
func: Operand::Constant(ref f), .. } = terminator.kind {
if let ty::TyFnDef(callee_def_id, substs) = f.ty.sty {
if self.tcx.trait_of_item(callee_def_id).is_none() {
callsites.push_back(CallSite {
callee: callee_def_id,
substs,
bb,
location: terminator.source_info
});
if let ty::TyFnDef(callee_def_id, substs) = f.ty.sty {
let caller_def_id = self.tcx.hir.local_def_id(caller_id);
let param_env = self.tcx.param_env(caller_def_id);

if let Some(instance) = Instance::resolve(self.tcx,
param_env,
callee_def_id,
substs) {
callsites.push_back(CallSite {
callee: instance.def_id(),
substs: instance.substs,
bb,
location: terminator.source_info
});
}
}
}
}
}
}

Expand Down
15 changes: 12 additions & 3 deletions src/librustc_trans/callee.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,10 @@ use common::{self, CrateContext};
use consts;
use declare;
use llvm::{self, ValueRef};
use monomorphize::{self, Instance};
use monomorphize::Instance;
use rustc::hir::def_id::DefId;
use rustc::ty::TypeFoldable;
use rustc::ty::{self, TypeFoldable};
use rustc::traits;
use rustc::ty::subst::Substs;
use type_of;

Expand Down Expand Up @@ -179,5 +180,13 @@ pub fn resolve_and_get_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
substs: &'tcx Substs<'tcx>)
-> ValueRef
{
get_fn(ccx, monomorphize::resolve(ccx.tcx(), def_id, substs))
get_fn(
ccx,
ty::Instance::resolve(
ccx.tcx(),
ty::ParamEnv::empty(traits::Reveal::All),
def_id,
substs,)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: extra comma

.unwrap()
)
}
28 changes: 22 additions & 6 deletions src/librustc_trans/collector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -566,7 +566,10 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> {
if let ConstVal::Unevaluated(def_id, substs) = constant.val {
let substs = self.tcx.trans_apply_param_substs(self.param_substs,
&substs);
let instance = monomorphize::resolve(self.tcx, def_id, substs);
let instance = ty::Instance::resolve(self.tcx,
ty::ParamEnv::empty(traits::Reveal::All),
def_id,
substs).unwrap();
collect_neighbours(self.tcx, instance, true, self.output);
}

Expand All @@ -587,7 +590,11 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> {

let constness = match (self.const_context, &callee_ty.sty) {
(true, &ty::TyFnDef(def_id, substs)) if self.tcx.is_const_fn(def_id) => {
let instance = monomorphize::resolve(self.tcx, def_id, substs);
let instance =
ty::Instance::resolve(self.tcx,
ty::ParamEnv::empty(traits::Reveal::All),
def_id,
substs).unwrap();
Some(instance)
}
_ => None
Expand Down Expand Up @@ -657,7 +664,10 @@ fn visit_fn_use<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
output: &mut Vec<TransItem<'tcx>>)
{
if let ty::TyFnDef(def_id, substs) = ty.sty {
let instance = monomorphize::resolve(tcx, def_id, substs);
let instance = ty::Instance::resolve(tcx,
ty::ParamEnv::empty(traits::Reveal::All),
def_id,
substs).unwrap();
visit_instance_use(tcx, instance, is_direct_call, output);
}
}
Expand Down Expand Up @@ -845,7 +855,11 @@ fn create_trans_items_for_vtable_methods<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
// Walk all methods of the trait, including those of its supertraits
let methods = traits::get_vtable_methods(tcx, poly_trait_ref);
let methods = methods.filter_map(|method| method)
.map(|(def_id, substs)| monomorphize::resolve(tcx, def_id, substs))
.map(|(def_id, substs)| ty::Instance::resolve(
tcx,
ty::ParamEnv::empty(traits::Reveal::All),
def_id,
substs).unwrap())
.filter(|&instance| should_trans_locally(tcx, &instance))
.map(|instance| create_fn_trans_item(instance));
output.extend(methods);
Expand Down Expand Up @@ -1000,8 +1014,10 @@ fn create_trans_items_for_default_impls<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
continue;
}

let instance =
monomorphize::resolve(tcx, method.def_id, callee_substs);
let instance = ty::Instance::resolve(tcx,
ty::ParamEnv::empty(traits::Reveal::All),
method.def_id,
callee_substs).unwrap();

let trans_item = create_fn_trans_item(instance);
if trans_item.is_instantiable(tcx) && should_trans_locally(tcx, &instance) {
Expand Down
6 changes: 5 additions & 1 deletion src/librustc_trans/mir/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ use rustc::middle::lang_items;
use rustc::middle::const_val::{ConstEvalErr, ConstInt, ErrKind};
use rustc::ty::{self, Ty, TypeFoldable};
use rustc::ty::layout::{self, LayoutTyper};
use rustc::traits;
use rustc::mir;
use abi::{Abi, FnType, ArgType};
use adt;
Expand Down Expand Up @@ -429,7 +430,10 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> {

let (instance, mut llfn) = match callee.ty.sty {
ty::TyFnDef(def_id, substs) => {
(Some(monomorphize::resolve(bcx.ccx.tcx(), def_id, substs)),
(Some(ty::Instance::resolve(bcx.ccx.tcx(),
ty::ParamEnv::empty(traits::Reveal::All),
def_id,
substs).unwrap()),
None)
}
ty::TyFnPtr(_) => {
Expand Down
Loading