862911df9a
The purpose of the translation item collector is to find all monomorphic instances of functions, methods and statics that need to be translated into LLVM IR in order to compile the current crate. So far these instances have been discovered lazily during the trans path. For incremental compilation we want to know the set of these instances in advance, and that is what the trans::collect module provides. In the future, incremental and regular translation will be driven by the collector implemented here.
659 lines
26 KiB
Rust
659 lines
26 KiB
Rust
// Copyright 2012 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 back::link;
|
|
use llvm::{ValueRef, get_params};
|
|
use middle::def_id::DefId;
|
|
use middle::infer;
|
|
use middle::subst::{Subst, Substs};
|
|
use middle::subst;
|
|
use middle::traits;
|
|
use trans::base::*;
|
|
use trans::build::*;
|
|
use trans::callee::*;
|
|
use trans::callee;
|
|
use trans::cleanup;
|
|
use trans::closure;
|
|
use trans::common::*;
|
|
use trans::consts;
|
|
use trans::datum::*;
|
|
use trans::debuginfo::DebugLoc;
|
|
use trans::declare;
|
|
use trans::expr;
|
|
use trans::glue;
|
|
use trans::machine;
|
|
use trans::monomorphize;
|
|
use trans::type_::Type;
|
|
use trans::type_of::*;
|
|
use middle::ty::{self, Ty};
|
|
use middle::ty::MethodCall;
|
|
|
|
use syntax::ast;
|
|
use syntax::attr;
|
|
use syntax::codemap::DUMMY_SP;
|
|
|
|
use rustc_front::hir;
|
|
|
|
// drop_glue pointer, size, align.
|
|
const VTABLE_OFFSET: usize = 3;
|
|
|
|
/// The main "translation" pass for methods. Generates code
|
|
/// for non-monomorphized methods only. Other methods will
|
|
/// be generated once they are invoked with specific type parameters,
|
|
/// see `trans::base::lval_static_fn()` or `trans::base::monomorphic_fn()`.
|
|
pub fn trans_impl(ccx: &CrateContext,
|
|
name: ast::Name,
|
|
impl_items: &[hir::ImplItem],
|
|
generics: &hir::Generics,
|
|
id: ast::NodeId) {
|
|
let _icx = push_ctxt("meth::trans_impl");
|
|
let tcx = ccx.tcx();
|
|
|
|
debug!("trans_impl(name={}, id={})", name, id);
|
|
|
|
// Both here and below with generic methods, be sure to recurse and look for
|
|
// items that we need to translate.
|
|
if !generics.ty_params.is_empty() {
|
|
return;
|
|
}
|
|
|
|
for impl_item in impl_items {
|
|
match impl_item.node {
|
|
hir::ImplItemKind::Method(ref sig, ref body) => {
|
|
if sig.generics.ty_params.is_empty() {
|
|
let trans_everywhere = attr::requests_inline(&impl_item.attrs);
|
|
for (ref ccx, is_origin) in ccx.maybe_iter(trans_everywhere) {
|
|
let llfn = get_item_val(ccx, impl_item.id);
|
|
let empty_substs = tcx.mk_substs(Substs::trans_empty());
|
|
trans_fn(ccx,
|
|
&sig.decl,
|
|
body,
|
|
llfn,
|
|
empty_substs,
|
|
impl_item.id,
|
|
&impl_item.attrs);
|
|
update_linkage(ccx,
|
|
llfn,
|
|
Some(impl_item.id),
|
|
if is_origin { OriginalTranslation } else { InlinedCopy });
|
|
}
|
|
}
|
|
}
|
|
_ => {}
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn trans_method_callee<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
|
|
method_call: MethodCall,
|
|
self_expr: Option<&hir::Expr>,
|
|
arg_cleanup_scope: cleanup::ScopeId)
|
|
-> Callee<'blk, 'tcx> {
|
|
let _icx = push_ctxt("meth::trans_method_callee");
|
|
|
|
let method = bcx.tcx().tables.borrow().method_map[&method_call];
|
|
|
|
match bcx.tcx().impl_or_trait_item(method.def_id).container() {
|
|
ty::ImplContainer(_) => {
|
|
debug!("trans_method_callee: static, {:?}", method.def_id);
|
|
let datum = callee::trans_fn_ref(bcx.ccx(),
|
|
method.def_id,
|
|
MethodCallKey(method_call),
|
|
bcx.fcx.param_substs);
|
|
Callee {
|
|
bcx: bcx,
|
|
data: Fn(datum.val),
|
|
ty: datum.ty
|
|
}
|
|
}
|
|
|
|
ty::TraitContainer(trait_def_id) => {
|
|
let trait_ref = method.substs.to_trait_ref(bcx.tcx(), trait_def_id);
|
|
let trait_ref = ty::Binder(bcx.monomorphize(&trait_ref));
|
|
let span = bcx.tcx().map.span(method_call.expr_id);
|
|
debug!("method_call={:?} trait_ref={:?} trait_ref id={:?} substs={:?}",
|
|
method_call,
|
|
trait_ref,
|
|
trait_ref.0.def_id,
|
|
trait_ref.0.substs);
|
|
let origin = fulfill_obligation(bcx.ccx(), span, trait_ref);
|
|
debug!("origin = {:?}", origin);
|
|
trans_monomorphized_callee(bcx,
|
|
method_call,
|
|
self_expr,
|
|
trait_def_id,
|
|
method.def_id,
|
|
method.ty,
|
|
origin,
|
|
arg_cleanup_scope)
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn trans_static_method_callee<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
|
|
method_id: DefId,
|
|
trait_id: DefId,
|
|
expr_id: ast::NodeId,
|
|
param_substs: &'tcx subst::Substs<'tcx>)
|
|
-> Datum<'tcx, Rvalue>
|
|
{
|
|
let _icx = push_ctxt("meth::trans_static_method_callee");
|
|
let tcx = ccx.tcx();
|
|
|
|
debug!("trans_static_method_callee(method_id={:?}, trait_id={}, \
|
|
expr_id={})",
|
|
method_id,
|
|
tcx.item_path_str(trait_id),
|
|
expr_id);
|
|
|
|
let mname = tcx.item_name(method_id);
|
|
|
|
debug!("trans_static_method_callee: method_id={:?}, expr_id={}, \
|
|
name={}", method_id, expr_id, mname);
|
|
|
|
// Find the substitutions for the fn itself. This includes
|
|
// type parameters that belong to the trait but also some that
|
|
// belong to the method:
|
|
let rcvr_substs = node_id_substs(ccx, ExprId(expr_id), param_substs);
|
|
debug!("rcvr_substs={:?}", rcvr_substs);
|
|
let trait_ref = ty::Binder(rcvr_substs.to_trait_ref(tcx, trait_id));
|
|
let vtbl = fulfill_obligation(ccx, DUMMY_SP, trait_ref);
|
|
|
|
// Now that we know which impl is being used, we can dispatch to
|
|
// the actual function:
|
|
match vtbl {
|
|
traits::VtableImpl(traits::VtableImplData {
|
|
impl_def_id: impl_did,
|
|
substs: impl_substs,
|
|
nested: _ }) =>
|
|
{
|
|
let callee_substs = impl_substs.with_method_from(&rcvr_substs);
|
|
let mth = tcx.get_impl_method(impl_did, callee_substs, mname);
|
|
trans_fn_ref_with_substs(ccx, mth.method.def_id, ExprId(expr_id),
|
|
param_substs,
|
|
mth.substs)
|
|
}
|
|
traits::VtableObject(ref data) => {
|
|
let idx = traits::get_vtable_index_of_object_method(tcx, data, method_id);
|
|
trans_object_shim(ccx,
|
|
data.upcast_trait_ref.clone(),
|
|
method_id,
|
|
idx)
|
|
}
|
|
_ => {
|
|
// FIXME(#20847): handle at least VtableFnPointer
|
|
tcx.sess.bug(&format!("static call to invalid vtable: {:?}",
|
|
vtbl));
|
|
}
|
|
}
|
|
}
|
|
|
|
fn trans_monomorphized_callee<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
|
|
method_call: MethodCall,
|
|
self_expr: Option<&hir::Expr>,
|
|
trait_id: DefId,
|
|
method_id: DefId,
|
|
method_ty: Ty<'tcx>,
|
|
vtable: traits::Vtable<'tcx, ()>,
|
|
arg_cleanup_scope: cleanup::ScopeId)
|
|
-> Callee<'blk, 'tcx> {
|
|
let _icx = push_ctxt("meth::trans_monomorphized_callee");
|
|
match vtable {
|
|
traits::VtableImpl(vtable_impl) => {
|
|
let ccx = bcx.ccx();
|
|
let impl_did = vtable_impl.impl_def_id;
|
|
let mname = match ccx.tcx().impl_or_trait_item(method_id) {
|
|
ty::MethodTraitItem(method) => method.name,
|
|
_ => {
|
|
bcx.tcx().sess.bug("can't monomorphize a non-method trait \
|
|
item")
|
|
}
|
|
};
|
|
// create a concatenated set of substitutions which includes
|
|
// those from the impl and those from the method:
|
|
let meth_substs = node_id_substs(ccx,
|
|
MethodCallKey(method_call),
|
|
bcx.fcx.param_substs);
|
|
let impl_substs = vtable_impl.substs.with_method_from(&meth_substs);
|
|
let mth = bcx.tcx().get_impl_method(impl_did, impl_substs, mname);
|
|
// translate the function
|
|
let datum = trans_fn_ref_with_substs(bcx.ccx(),
|
|
mth.method.def_id,
|
|
MethodCallKey(method_call),
|
|
bcx.fcx.param_substs,
|
|
mth.substs);
|
|
|
|
Callee { bcx: bcx, data: Fn(datum.val), ty: datum.ty }
|
|
}
|
|
traits::VtableClosure(vtable_closure) => {
|
|
// The substitutions should have no type parameters remaining
|
|
// after passing through fulfill_obligation
|
|
let trait_closure_kind = bcx.tcx().lang_items.fn_trait_kind(trait_id).unwrap();
|
|
let llfn = closure::trans_closure_method(bcx.ccx(),
|
|
vtable_closure.closure_def_id,
|
|
vtable_closure.substs,
|
|
trait_closure_kind);
|
|
Callee {
|
|
bcx: bcx,
|
|
data: Fn(llfn),
|
|
ty: monomorphize_type(bcx, method_ty)
|
|
}
|
|
}
|
|
traits::VtableFnPointer(fn_ty) => {
|
|
let trait_closure_kind = bcx.tcx().lang_items.fn_trait_kind(trait_id).unwrap();
|
|
let llfn = trans_fn_pointer_shim(bcx.ccx(), trait_closure_kind, fn_ty);
|
|
Callee {
|
|
bcx: bcx,
|
|
data: Fn(llfn),
|
|
ty: monomorphize_type(bcx, method_ty)
|
|
}
|
|
}
|
|
traits::VtableObject(ref data) => {
|
|
let idx = traits::get_vtable_index_of_object_method(bcx.tcx(), data, method_id);
|
|
if let Some(self_expr) = self_expr {
|
|
if let ty::TyBareFn(_, ref fty) = monomorphize_type(bcx, method_ty).sty {
|
|
let ty = bcx.tcx().mk_fn(None, opaque_method_ty(bcx.tcx(), fty));
|
|
return trans_trait_callee(bcx, ty, idx, self_expr, arg_cleanup_scope);
|
|
}
|
|
}
|
|
let datum = trans_object_shim(bcx.ccx(),
|
|
data.upcast_trait_ref.clone(),
|
|
method_id,
|
|
idx);
|
|
Callee { bcx: bcx, data: Fn(datum.val), ty: datum.ty }
|
|
}
|
|
traits::VtableBuiltin(..) |
|
|
traits::VtableDefaultImpl(..) |
|
|
traits::VtableParam(..) => {
|
|
bcx.sess().bug(
|
|
&format!("resolved vtable bad vtable {:?} in trans",
|
|
vtable));
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Create a method callee where the method is coming from a trait object (e.g., Box<Trait> type).
|
|
/// In this case, we must pull the fn pointer out of the vtable that is packaged up with the
|
|
/// object. Objects are represented as a pair, so we first evaluate the self expression and then
|
|
/// extract the self data and vtable out of the pair.
|
|
fn trans_trait_callee<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
|
|
opaque_fn_ty: Ty<'tcx>,
|
|
vtable_index: usize,
|
|
self_expr: &hir::Expr,
|
|
arg_cleanup_scope: cleanup::ScopeId)
|
|
-> Callee<'blk, 'tcx> {
|
|
let _icx = push_ctxt("meth::trans_trait_callee");
|
|
let mut bcx = bcx;
|
|
|
|
// Translate self_datum and take ownership of the value by
|
|
// converting to an rvalue.
|
|
let self_datum = unpack_datum!(
|
|
bcx, expr::trans(bcx, self_expr));
|
|
|
|
let llval = if bcx.fcx.type_needs_drop(self_datum.ty) {
|
|
let self_datum = unpack_datum!(
|
|
bcx, self_datum.to_rvalue_datum(bcx, "trait_callee"));
|
|
|
|
// Convert to by-ref since `trans_trait_callee_from_llval` wants it
|
|
// that way.
|
|
let self_datum = unpack_datum!(
|
|
bcx, self_datum.to_ref_datum(bcx));
|
|
|
|
// Arrange cleanup in case something should go wrong before the
|
|
// actual call occurs.
|
|
self_datum.add_clean(bcx.fcx, arg_cleanup_scope)
|
|
} else {
|
|
// We don't have to do anything about cleanups for &Trait and &mut Trait.
|
|
assert!(self_datum.kind.is_by_ref());
|
|
self_datum.val
|
|
};
|
|
|
|
let llself = Load(bcx, expr::get_dataptr(bcx, llval));
|
|
let llvtable = Load(bcx, expr::get_meta(bcx, llval));
|
|
trans_trait_callee_from_llval(bcx, opaque_fn_ty, vtable_index, llself, llvtable)
|
|
}
|
|
|
|
/// Same as `trans_trait_callee()` above, except that it is given a by-ref pointer to the object
|
|
/// pair.
|
|
fn trans_trait_callee_from_llval<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
|
|
opaque_fn_ty: Ty<'tcx>,
|
|
vtable_index: usize,
|
|
llself: ValueRef,
|
|
llvtable: ValueRef)
|
|
-> Callee<'blk, 'tcx> {
|
|
let _icx = push_ctxt("meth::trans_trait_callee");
|
|
let ccx = bcx.ccx();
|
|
|
|
// Load the data pointer from the object.
|
|
debug!("trans_trait_callee_from_llval(callee_ty={}, vtable_index={}, llself={}, llvtable={})",
|
|
opaque_fn_ty,
|
|
vtable_index,
|
|
bcx.val_to_string(llself),
|
|
bcx.val_to_string(llvtable));
|
|
|
|
// Replace the self type (&Self or Box<Self>) with an opaque pointer.
|
|
let mptr = Load(bcx, GEPi(bcx, llvtable, &[vtable_index + VTABLE_OFFSET]));
|
|
let llcallee_ty = type_of_fn_from_ty(ccx, opaque_fn_ty);
|
|
|
|
Callee {
|
|
bcx: bcx,
|
|
data: TraitItem(MethodData {
|
|
llfn: PointerCast(bcx, mptr, llcallee_ty.ptr_to()),
|
|
llself: PointerCast(bcx, llself, Type::i8p(ccx)),
|
|
}),
|
|
ty: opaque_fn_ty
|
|
}
|
|
}
|
|
|
|
/// Generate a shim function that allows an object type like `SomeTrait` to
|
|
/// implement the type `SomeTrait`. Imagine a trait definition:
|
|
///
|
|
/// trait SomeTrait { fn get(&self) -> i32; ... }
|
|
///
|
|
/// And a generic bit of code:
|
|
///
|
|
/// fn foo<T:SomeTrait>(t: &T) {
|
|
/// let x = SomeTrait::get;
|
|
/// x(t)
|
|
/// }
|
|
///
|
|
/// What is the value of `x` when `foo` is invoked with `T=SomeTrait`?
|
|
/// The answer is that it is a shim function generated by this routine:
|
|
///
|
|
/// fn shim(t: &SomeTrait) -> i32 {
|
|
/// // ... call t.get() virtually ...
|
|
/// }
|
|
///
|
|
/// In fact, all virtual calls can be thought of as normal trait calls
|
|
/// that go through this shim function.
|
|
pub fn trans_object_shim<'a, 'tcx>(
|
|
ccx: &'a CrateContext<'a, 'tcx>,
|
|
upcast_trait_ref: ty::PolyTraitRef<'tcx>,
|
|
method_id: DefId,
|
|
vtable_index: usize)
|
|
-> Datum<'tcx, Rvalue>
|
|
{
|
|
let _icx = push_ctxt("trans_object_shim");
|
|
let tcx = ccx.tcx();
|
|
|
|
debug!("trans_object_shim(upcast_trait_ref={:?}, method_id={:?})",
|
|
upcast_trait_ref,
|
|
method_id);
|
|
|
|
// Upcast to the trait in question and extract out the substitutions.
|
|
let upcast_trait_ref = tcx.erase_late_bound_regions(&upcast_trait_ref);
|
|
let object_substs = upcast_trait_ref.substs.clone().erase_regions();
|
|
debug!("trans_object_shim: object_substs={:?}", object_substs);
|
|
|
|
// Lookup the type of this method as declared in the trait and apply substitutions.
|
|
let method_ty = match tcx.impl_or_trait_item(method_id) {
|
|
ty::MethodTraitItem(method) => method,
|
|
_ => {
|
|
tcx.sess.bug("can't create a method shim for a non-method item")
|
|
}
|
|
};
|
|
let fty = monomorphize::apply_param_substs(tcx, &object_substs, &method_ty.fty);
|
|
let fty = tcx.mk_bare_fn(fty);
|
|
let method_ty = opaque_method_ty(tcx, fty);
|
|
debug!("trans_object_shim: fty={:?} method_ty={:?}", fty, method_ty);
|
|
|
|
//
|
|
let shim_fn_ty = tcx.mk_fn(None, fty);
|
|
let method_bare_fn_ty = tcx.mk_fn(None, method_ty);
|
|
let function_name = link::mangle_internal_name_by_type_and_seq(ccx, shim_fn_ty, "object_shim");
|
|
let llfn = declare::define_internal_rust_fn(ccx, &function_name, shim_fn_ty);
|
|
|
|
let sig = ccx.tcx().erase_late_bound_regions(&fty.sig);
|
|
let sig = infer::normalize_associated_type(ccx.tcx(), &sig);
|
|
|
|
let empty_substs = tcx.mk_substs(Substs::trans_empty());
|
|
let (block_arena, fcx): (TypedArena<_>, FunctionContext);
|
|
block_arena = TypedArena::new();
|
|
fcx = new_fn_ctxt(ccx,
|
|
llfn,
|
|
ast::DUMMY_NODE_ID,
|
|
false,
|
|
sig.output,
|
|
empty_substs,
|
|
None,
|
|
&block_arena);
|
|
let mut bcx = init_function(&fcx, false, sig.output);
|
|
|
|
let llargs = get_params(fcx.llfn);
|
|
|
|
let self_idx = fcx.arg_offset();
|
|
let llself = llargs[self_idx];
|
|
let llvtable = llargs[self_idx + 1];
|
|
|
|
debug!("trans_object_shim: llself={}, llvtable={}",
|
|
bcx.val_to_string(llself), bcx.val_to_string(llvtable));
|
|
|
|
assert!(!fcx.needs_ret_allocas);
|
|
|
|
let dest =
|
|
fcx.llretslotptr.get().map(
|
|
|_| expr::SaveIn(fcx.get_ret_slot(bcx, sig.output, "ret_slot")));
|
|
|
|
debug!("trans_object_shim: method_offset_in_vtable={}",
|
|
vtable_index);
|
|
|
|
bcx = trans_call_inner(bcx,
|
|
DebugLoc::None,
|
|
|bcx, _| trans_trait_callee_from_llval(bcx,
|
|
method_bare_fn_ty,
|
|
vtable_index,
|
|
llself, llvtable),
|
|
ArgVals(&llargs[(self_idx + 2)..]),
|
|
dest).bcx;
|
|
|
|
finish_fn(&fcx, bcx, sig.output, DebugLoc::None);
|
|
|
|
immediate_rvalue(llfn, shim_fn_ty)
|
|
}
|
|
|
|
/// Creates a returns a dynamic vtable for the given type and vtable origin.
|
|
/// This is used only for objects.
|
|
///
|
|
/// The `trait_ref` encodes the erased self type. Hence if we are
|
|
/// making an object `Foo<Trait>` from a value of type `Foo<T>`, then
|
|
/// `trait_ref` would map `T:Trait`.
|
|
pub fn get_vtable<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
|
|
trait_ref: ty::PolyTraitRef<'tcx>,
|
|
param_substs: &'tcx subst::Substs<'tcx>)
|
|
-> ValueRef
|
|
{
|
|
let tcx = ccx.tcx();
|
|
let _icx = push_ctxt("meth::get_vtable");
|
|
|
|
debug!("get_vtable(trait_ref={:?})", trait_ref);
|
|
|
|
// Check the cache.
|
|
match ccx.vtables().borrow().get(&trait_ref) {
|
|
Some(&val) => { return val }
|
|
None => { }
|
|
}
|
|
|
|
// Not in the cache. Build it.
|
|
let methods = traits::supertraits(tcx, trait_ref.clone()).flat_map(|trait_ref| {
|
|
let vtable = fulfill_obligation(ccx, DUMMY_SP, trait_ref.clone());
|
|
match vtable {
|
|
// Should default trait error here?
|
|
traits::VtableDefaultImpl(_) |
|
|
traits::VtableBuiltin(_) => {
|
|
Vec::new().into_iter()
|
|
}
|
|
traits::VtableImpl(
|
|
traits::VtableImplData {
|
|
impl_def_id: id,
|
|
substs,
|
|
nested: _ }) => {
|
|
let nullptr = C_null(Type::nil(ccx).ptr_to());
|
|
get_vtable_methods(ccx, id, substs)
|
|
.into_iter()
|
|
.map(|opt_mth| {
|
|
match opt_mth {
|
|
Some(mth) => {
|
|
trans_fn_ref_with_substs(ccx,
|
|
mth.method.def_id,
|
|
ExprId(0),
|
|
param_substs,
|
|
mth.substs).val
|
|
}
|
|
None => nullptr
|
|
}
|
|
})
|
|
.collect::<Vec<_>>()
|
|
.into_iter()
|
|
}
|
|
traits::VtableClosure(
|
|
traits::VtableClosureData {
|
|
closure_def_id,
|
|
substs,
|
|
nested: _ }) => {
|
|
let trait_closure_kind = tcx.lang_items.fn_trait_kind(trait_ref.def_id()).unwrap();
|
|
let llfn = closure::trans_closure_method(ccx,
|
|
closure_def_id,
|
|
substs,
|
|
trait_closure_kind);
|
|
vec![llfn].into_iter()
|
|
}
|
|
traits::VtableFnPointer(bare_fn_ty) => {
|
|
let trait_closure_kind = tcx.lang_items.fn_trait_kind(trait_ref.def_id()).unwrap();
|
|
vec![trans_fn_pointer_shim(ccx, trait_closure_kind, bare_fn_ty)].into_iter()
|
|
}
|
|
traits::VtableObject(ref data) => {
|
|
// this would imply that the Self type being erased is
|
|
// an object type; this cannot happen because we
|
|
// cannot cast an unsized type into a trait object
|
|
tcx.sess.bug(
|
|
&format!("cannot get vtable for an object type: {:?}",
|
|
data));
|
|
}
|
|
traits::VtableParam(..) => {
|
|
tcx.sess.bug(
|
|
&format!("resolved vtable for {:?} to bad vtable {:?} in trans",
|
|
trait_ref,
|
|
vtable));
|
|
}
|
|
}
|
|
});
|
|
|
|
let size_ty = sizing_type_of(ccx, trait_ref.self_ty());
|
|
let size = machine::llsize_of_alloc(ccx, size_ty);
|
|
let align = align_of(ccx, trait_ref.self_ty());
|
|
|
|
let components: Vec<_> = vec![
|
|
// Generate a destructor for the vtable.
|
|
glue::get_drop_glue(ccx, trait_ref.self_ty()),
|
|
C_uint(ccx, size),
|
|
C_uint(ccx, align)
|
|
].into_iter().chain(methods).collect();
|
|
|
|
let vtable_const = C_struct(ccx, &components, false);
|
|
let align = machine::llalign_of_pref(ccx, val_ty(vtable_const));
|
|
let vtable = consts::addr_of(ccx, vtable_const, align, "vtable");
|
|
|
|
ccx.vtables().borrow_mut().insert(trait_ref, vtable);
|
|
vtable
|
|
}
|
|
|
|
pub fn get_vtable_methods<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
|
|
impl_id: DefId,
|
|
substs: subst::Substs<'tcx>)
|
|
-> Vec<Option<ty::util::ImplMethod<'tcx>>>
|
|
{
|
|
let tcx = ccx.tcx();
|
|
|
|
debug!("get_vtable_methods(impl_id={:?}, substs={:?}", impl_id, substs);
|
|
|
|
let trt_id = match tcx.impl_trait_ref(impl_id) {
|
|
Some(t_id) => t_id.def_id,
|
|
None => ccx.sess().bug("make_impl_vtable: don't know how to \
|
|
make a vtable for a type impl!")
|
|
};
|
|
|
|
tcx.populate_implementations_for_trait_if_necessary(trt_id);
|
|
|
|
let trait_item_def_ids = tcx.trait_item_def_ids(trt_id);
|
|
trait_item_def_ids
|
|
.iter()
|
|
|
|
// Filter out non-method items.
|
|
.filter_map(|item_def_id| {
|
|
match *item_def_id {
|
|
ty::MethodTraitItemId(def_id) => Some(def_id),
|
|
_ => None,
|
|
}
|
|
})
|
|
|
|
// Now produce pointers for each remaining method. If the
|
|
// method could never be called from this object, just supply
|
|
// null.
|
|
.map(|trait_method_def_id| {
|
|
debug!("get_vtable_methods: trait_method_def_id={:?}",
|
|
trait_method_def_id);
|
|
|
|
let trait_method_type = match tcx.impl_or_trait_item(trait_method_def_id) {
|
|
ty::MethodTraitItem(m) => m,
|
|
_ => ccx.sess().bug("should be a method, not other assoc item"),
|
|
};
|
|
let name = trait_method_type.name;
|
|
|
|
// Some methods cannot be called on an object; skip those.
|
|
if !traits::is_vtable_safe_method(tcx, trt_id, &trait_method_type) {
|
|
debug!("get_vtable_methods: not vtable safe");
|
|
return None;
|
|
}
|
|
|
|
debug!("get_vtable_methods: trait_method_type={:?}",
|
|
trait_method_type);
|
|
|
|
// The substitutions we have are on the impl, so we grab
|
|
// the method type from the impl to substitute into.
|
|
let mth = tcx.get_impl_method(impl_id, substs.clone(), name);
|
|
|
|
debug!("get_vtable_methods: mth={:?}", mth);
|
|
|
|
// If this is a default method, it's possible that it
|
|
// relies on where clauses that do not hold for this
|
|
// particular set of type parameters. Note that this
|
|
// method could then never be called, so we do not want to
|
|
// try and trans it, in that case. Issue #23435.
|
|
if mth.is_provided {
|
|
let predicates = mth.method.predicates.predicates.subst(tcx, &mth.substs);
|
|
if !normalize_and_test_predicates(ccx, predicates.into_vec()) {
|
|
debug!("get_vtable_methods: predicates do not hold");
|
|
return None;
|
|
}
|
|
}
|
|
|
|
Some(mth)
|
|
})
|
|
.collect()
|
|
}
|
|
|
|
/// Replace the self type (&Self or Box<Self>) with an opaque pointer.
|
|
fn opaque_method_ty<'tcx>(tcx: &ty::ctxt<'tcx>, method_ty: &ty::BareFnTy<'tcx>)
|
|
-> &'tcx ty::BareFnTy<'tcx> {
|
|
let mut inputs = method_ty.sig.0.inputs.clone();
|
|
inputs[0] = tcx.mk_mut_ptr(tcx.mk_mach_int(ast::TyI8));
|
|
|
|
tcx.mk_bare_fn(ty::BareFnTy {
|
|
unsafety: method_ty.unsafety,
|
|
abi: method_ty.abi,
|
|
sig: ty::Binder(ty::FnSig {
|
|
inputs: inputs,
|
|
output: method_ty.sig.0.output,
|
|
variadic: method_ty.sig.0.variadic,
|
|
}),
|
|
})
|
|
}
|