// 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 or the MIT license // , 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 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) 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: &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` from a value of type `Foo`, 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::>() .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>> { 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) 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, }), }) }