diff options
Diffstat (limited to 'src/librustc_trans/trans/callee.rs')
-rw-r--r-- | src/librustc_trans/trans/callee.rs | 852 |
1 files changed, 448 insertions, 404 deletions
diff --git a/src/librustc_trans/trans/callee.rs b/src/librustc_trans/trans/callee.rs index 05e5ac808d0..17d08cd6c2f 100644 --- a/src/librustc_trans/trans/callee.rs +++ b/src/librustc_trans/trans/callee.rs @@ -25,15 +25,19 @@ use middle::def_id::DefId; use middle::infer; use middle::subst; use middle::subst::{Substs}; +use middle::traits; use rustc::front::map as hir_map; +use trans::abi::{Abi, FnType}; use trans::adt; +use trans::attributes; use trans::base; use trans::base::*; use trans::build::*; use trans::cleanup; use trans::cleanup::CleanupMethods; -use trans::common::{self, Block, Result, NodeIdAndSpan, ExprId, CrateContext, - ExprOrMethodCall, FunctionContext, MethodCallKey}; +use trans::closure; +use trans::common::{self, Block, Result, CrateContext, FunctionContext}; +use trans::common::{C_uint, C_undef}; use trans::consts; use trans::datum::*; use trans::debuginfo::DebugLoc; @@ -41,37 +45,40 @@ use trans::declare; use trans::expr; use trans::glue; use trans::inline; -use trans::foreign; use trans::intrinsic; +use trans::machine::{llalign_of_min, llsize_of_store}; use trans::meth; -use trans::monomorphize; +use trans::monomorphize::{self, Instance}; use trans::type_::Type; use trans::type_of; +use trans::value::Value; use trans::Disr; use middle::ty::{self, Ty, TyCtxt, TypeFoldable}; use rustc_front::hir; -use syntax::abi::Abi; -use syntax::ast; use syntax::codemap::DUMMY_SP; use syntax::errors; use syntax::ptr::P; -pub enum CalleeData<'tcx> { +use std::cmp; + +#[derive(Debug)] +pub enum CalleeData { /// Constructor for enum variant/tuple-like-struct. NamedTupleConstructor(Disr), /// Function pointer. Fn(ValueRef), - Intrinsic(ast::NodeId, &'tcx subst::Substs<'tcx>), + Intrinsic, /// Trait object found in the vtable at that index. Virtual(usize) } +#[derive(Debug)] pub struct Callee<'tcx> { - pub data: CalleeData<'tcx>, + pub data: CalleeData, pub ty: Ty<'tcx> } @@ -96,26 +103,19 @@ impl<'tcx> Callee<'tcx> { pub fn method<'blk>(bcx: Block<'blk, 'tcx>, method: ty::MethodCallee<'tcx>) -> Callee<'tcx> { let substs = bcx.tcx().mk_substs(bcx.fcx.monomorphize(&method.substs)); - let ty = bcx.fcx.monomorphize(&method.ty); - Callee::def(bcx.ccx(), method.def_id, substs, ty) + Callee::def(bcx.ccx(), method.def_id, substs) } /// Function or method definition. pub fn def<'a>(ccx: &CrateContext<'a, 'tcx>, def_id: DefId, - substs: &'tcx subst::Substs<'tcx>, - ty: Ty<'tcx>) + substs: &'tcx subst::Substs<'tcx>) -> Callee<'tcx> { let tcx = ccx.tcx(); if substs.self_ty().is_some() { // Only trait methods can have a Self parameter. - let method_item = tcx.impl_or_trait_item(def_id); - let trait_id = method_item.container().id(); - let trait_ref = ty::Binder(substs.to_trait_ref(tcx, trait_id)); - let vtbl = common::fulfill_obligation(ccx, DUMMY_SP, trait_ref); - return meth::callee_for_trait_impl(ccx, def_id, substs, - trait_id, ty, vtbl); + return Callee::trait_method(ccx, def_id, substs); } let maybe_node_id = inline::get_local_instance(ccx, def_id) @@ -123,34 +123,112 @@ impl<'tcx> Callee<'tcx> { let maybe_ast_node = maybe_node_id.and_then(|node_id| { tcx.map.find(node_id) }); - match maybe_ast_node { + + let data = match maybe_ast_node { Some(hir_map::NodeStructCtor(_)) => { - return Callee { - data: NamedTupleConstructor(Disr(0)), - ty: ty - }; + NamedTupleConstructor(Disr(0)) } Some(hir_map::NodeVariant(_)) => { let vinfo = common::inlined_variant_def(ccx, maybe_node_id.unwrap()); - assert_eq!(vinfo.kind(), ty::VariantKind::Tuple); + NamedTupleConstructor(Disr::from(vinfo.disr_val)) + } + Some(hir_map::NodeForeignItem(fi)) if { + let abi = tcx.map.get_foreign_abi(fi.id); + abi == Abi::RustIntrinsic || abi == Abi::PlatformIntrinsic + } => Intrinsic, - return Callee { - data: NamedTupleConstructor(Disr::from(vinfo.disr_val)), - ty: ty + _ => return Callee::ptr(get_fn(ccx, def_id, substs)) + }; + + Callee { + data: data, + ty: def_ty(tcx, def_id, substs) + } + } + + /// Trait method, which has to be resolved to an impl method. + pub fn trait_method<'a>(ccx: &CrateContext<'a, 'tcx>, + def_id: DefId, + substs: &'tcx subst::Substs<'tcx>) + -> Callee<'tcx> { + let tcx = ccx.tcx(); + + let method_item = tcx.impl_or_trait_item(def_id); + let trait_id = method_item.container().id(); + let trait_ref = ty::Binder(substs.to_trait_ref(tcx, trait_id)); + match common::fulfill_obligation(ccx, DUMMY_SP, trait_ref) { + traits::VtableImpl(vtable_impl) => { + let impl_did = vtable_impl.impl_def_id; + let mname = tcx.item_name(def_id); + // create a concatenated set of substitutions which includes + // those from the impl and those from the method: + let impl_substs = vtable_impl.substs.with_method_from(&substs); + let substs = tcx.mk_substs(impl_substs); + let mth = meth::get_impl_method(tcx, impl_did, substs, mname); + + // Translate the function, bypassing Callee::def. + // That is because default methods have the same ID as the + // trait method used to look up the impl method that ended + // up here, so calling Callee::def would infinitely recurse. + Callee::ptr(get_fn(ccx, mth.method.def_id, mth.substs)) + } + traits::VtableClosure(vtable_closure) => { + // The substitutions should have no type parameters remaining + // after passing through fulfill_obligation + let trait_closure_kind = tcx.lang_items.fn_trait_kind(trait_id).unwrap(); + let llfn = closure::trans_closure_method(ccx, + vtable_closure.closure_def_id, + vtable_closure.substs, + trait_closure_kind); + + let method_ty = def_ty(tcx, def_id, substs); + let fn_ptr_ty = match method_ty.sty { + ty::TyFnDef(_, _, fty) => tcx.mk_ty(ty::TyFnPtr(fty)), + _ => unreachable!("expected fn item type, found {}", + method_ty) }; + Callee::ptr(immediate_rvalue(llfn, fn_ptr_ty)) } - Some(hir_map::NodeForeignItem(fi)) => { - let abi = tcx.map.get_foreign_abi(fi.id); - if abi == Abi::RustIntrinsic || abi == Abi::PlatformIntrinsic { - return Callee { - data: Intrinsic(fi.id, substs), - ty: ty - }; + traits::VtableFnPointer(fn_ty) => { + let trait_closure_kind = tcx.lang_items.fn_trait_kind(trait_id).unwrap(); + let llfn = trans_fn_pointer_shim(ccx, trait_closure_kind, fn_ty); + + let method_ty = def_ty(tcx, def_id, substs); + let fn_ptr_ty = match method_ty.sty { + ty::TyFnDef(_, _, fty) => tcx.mk_ty(ty::TyFnPtr(fty)), + _ => unreachable!("expected fn item type, found {}", + method_ty) + }; + Callee::ptr(immediate_rvalue(llfn, fn_ptr_ty)) + } + traits::VtableObject(ref data) => { + Callee { + data: Virtual(traits::get_vtable_index_of_object_method( + tcx, data, def_id)), + ty: def_ty(tcx, def_id, substs) } } - _ => {} + vtable => { + unreachable!("resolved vtable bad vtable {:?} in trans", vtable); + } + } + } + + /// Get the abi::FnType for a direct call. Mainly deals with the fact + /// that a Virtual call doesn't take the vtable, like its shim does. + /// The extra argument types are for variadic (extern "C") functions. + pub fn direct_fn_type<'a>(&self, ccx: &CrateContext<'a, 'tcx>, + extra_args: &[Ty<'tcx>]) -> FnType { + let abi = self.ty.fn_abi(); + let sig = ccx.tcx().erase_late_bound_regions(self.ty.fn_sig()); + let sig = infer::normalize_associated_type(ccx.tcx(), &sig); + let mut fn_ty = FnType::unadjusted(ccx, abi, &sig, extra_args); + if let Virtual(_) = self.data { + // Don't pass the vtable, it's not an argument of the virtual fn. + fn_ty.args[1].ignore(); } - Callee::ptr(trans_fn_ref_with_substs(ccx, def_id, Some(ty), substs)) + fn_ty.adjust_for_abi(ccx, abi, &sig); + fn_ty } /// This behemoth of a function translates function calls. Unfortunately, in @@ -175,51 +253,36 @@ impl<'tcx> Callee<'tcx> { /// Turn the callee into a function pointer. pub fn reify<'a>(self, ccx: &CrateContext<'a, 'tcx>) -> Datum<'tcx, Rvalue> { + let fn_ptr_ty = match self.ty.sty { + ty::TyFnDef(_, _, f) => ccx.tcx().mk_ty(ty::TyFnPtr(f)), + _ => self.ty + }; match self.data { Fn(llfn) => { - let fn_ptr_ty = match self.ty.sty { - ty::TyFnDef(_, _, f) => ccx.tcx().mk_ty(ty::TyFnPtr(f)), - _ => self.ty - }; immediate_rvalue(llfn, fn_ptr_ty) } - Virtual(idx) => meth::trans_object_shim(ccx, self.ty, idx), + Virtual(idx) => { + let llfn = meth::trans_object_shim(ccx, self.ty, idx); + immediate_rvalue(llfn, fn_ptr_ty) + } NamedTupleConstructor(_) => match self.ty.sty { ty::TyFnDef(def_id, substs, _) => { - return trans_fn_ref_with_substs(ccx, def_id, Some(self.ty), substs); + return get_fn(ccx, def_id, substs); } _ => unreachable!("expected fn item type, found {}", self.ty) }, - Intrinsic(..) => unreachable!("intrinsic {} getting reified", self.ty) + Intrinsic => unreachable!("intrinsic {} getting reified", self.ty) } } } -/// Translates a reference (with id `ref_id`) to the fn/method with id `def_id` into a function -/// pointer. This may require monomorphization or inlining. -pub fn trans_fn_ref<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, - def_id: DefId, - node: ExprOrMethodCall, - param_substs: &'tcx subst::Substs<'tcx>) - -> Datum<'tcx, Rvalue> { - let _icx = push_ctxt("trans_fn_ref"); - - let substs = common::node_id_substs(ccx, node, param_substs); - debug!("trans_fn_ref(def_id={:?}, node={:?}, substs={:?})", - def_id, - node, - substs); - let ref_ty = match node { - ExprId(0) => return trans_fn_ref_with_substs(ccx, def_id, None, substs), - ExprId(id) => ccx.tcx().node_id_to_type(id), - MethodCallKey(method_call) => { - ccx.tcx().tables.borrow().method_map[&method_call].ty - } - }; - let ref_ty = monomorphize::apply_param_substs(ccx.tcx(), - param_substs, - &ref_ty); - trans_fn_ref_with_substs(ccx, def_id, Some(ref_ty), substs) +/// Given a DefId and some Substs, produces the monomorphic item type. +fn def_ty<'tcx>(tcx: &TyCtxt<'tcx>, + def_id: DefId, + substs: &'tcx subst::Substs<'tcx>) + -> Ty<'tcx> { + let ty = tcx.lookup_item_type(def_id).ty; + monomorphize::apply_param_substs(tcx, substs, &ty) } /// Translates an adapter that implements the `Fn` trait for a fn @@ -251,6 +314,21 @@ pub fn trans_fn_pointer_shim<'a, 'tcx>( ty::ClosureKind::Fn | ty::ClosureKind::FnMut => true, ty::ClosureKind::FnOnce => false, }; + + let llfnpointer = match bare_fn_ty.sty { + ty::TyFnDef(def_id, substs, _) => { + // Function definitions have to be turned into a pointer. + let llfn = Callee::def(ccx, def_id, substs).reify(ccx).val; + if !is_by_ref { + // A by-value fn item is ignored, so the shim has + // the same signature as the original function. + return llfn; + } + Some(llfn) + } + _ => None + }; + let bare_fn_ty_maybe_ref = if is_by_ref { tcx.mk_imm_ref(tcx.mk_region(ty::ReStatic), bare_fn_ty) } else { @@ -285,58 +363,48 @@ pub fn trans_fn_pointer_shim<'a, 'tcx>( let sig = tcx.erase_late_bound_regions(sig); let sig = infer::normalize_associated_type(ccx.tcx(), &sig); let tuple_input_ty = tcx.mk_tup(sig.inputs.to_vec()); + let sig = ty::FnSig { + inputs: vec![bare_fn_ty_maybe_ref, + tuple_input_ty], + output: sig.output, + variadic: false + }; + let fn_ty = FnType::new(ccx, Abi::RustCall, &sig, &[]); let tuple_fn_ty = tcx.mk_fn_ptr(ty::BareFnTy { unsafety: hir::Unsafety::Normal, abi: Abi::RustCall, - sig: ty::Binder(ty::FnSig { - inputs: vec![bare_fn_ty_maybe_ref, - tuple_input_ty], - output: sig.output, - variadic: false - }) + sig: ty::Binder(sig) }); debug!("tuple_fn_ty: {:?}", tuple_fn_ty); // let function_name = link::mangle_internal_name_by_type_and_seq(ccx, bare_fn_ty, "fn_pointer_shim"); - let llfn = declare::declare_internal_rust_fn(ccx, &function_name[..], tuple_fn_ty); + let llfn = declare::define_internal_fn(ccx, &function_name, tuple_fn_ty); // 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); + fcx = FunctionContext::new(ccx, llfn, fn_ty, None, empty_substs, &block_arena); + let mut bcx = fcx.init(false, None); let llargs = get_params(fcx.llfn); - let self_idx = fcx.arg_offset(); - let llfnpointer = match bare_fn_ty.sty { - ty::TyFnDef(def_id, substs, _) => { - // Function definitions have to be turned into a pointer. - Callee::def(ccx, def_id, substs, bare_fn_ty).reify(ccx).val - } - + let self_idx = fcx.fn_ty.ret.is_indirect() as usize; + let llfnpointer = llfnpointer.unwrap_or_else(|| { // the first argument (`self`) will be ptr to the fn pointer - _ => if is_by_ref { + if is_by_ref { Load(bcx, llargs[self_idx]) } else { llargs[self_idx] } - }; + }); assert!(!fcx.needs_ret_allocas); let dest = fcx.llretslotptr.get().map(|_| - expr::SaveIn(fcx.get_ret_slot(bcx, sig.output, "ret_slot")) + expr::SaveIn(fcx.get_ret_slot(bcx, "ret_slot")) ); let callee = Callee { @@ -345,7 +413,7 @@ pub fn trans_fn_pointer_shim<'a, 'tcx>( }; bcx = callee.call(bcx, DebugLoc::None, ArgVals(&llargs[(self_idx + 1)..]), dest).bcx; - finish_fn(&fcx, bcx, sig.output, DebugLoc::None); + fcx.finish(bcx, DebugLoc::None); ccx.fn_pointer_shims().borrow_mut().insert(bare_fn_ty_maybe_ref, llfn); @@ -359,25 +427,14 @@ pub fn trans_fn_pointer_shim<'a, 'tcx>( /// /// - `ccx`: the crate context /// - `def_id`: def id of the fn or method item being referenced -/// - `node`: node id of the reference to the fn/method, if applicable. -/// This parameter may be zero; but, if so, the resulting value may not -/// have the right type, so it must be cast before being used. -/// - `ref_ty`: monotype of the reference to the fn/method, if applicable. -/// This parameter may be None; but, if so, the resulting value may not -/// have the right type, so it must be cast before being used. /// - `substs`: values for each of the fn/method's parameters -pub fn trans_fn_ref_with_substs<'a, 'tcx>( - ccx: &CrateContext<'a, 'tcx>, - def_id: DefId, - ref_ty: Option<Ty<'tcx>>, - substs: &'tcx subst::Substs<'tcx>) - -> Datum<'tcx, Rvalue> -{ - let _icx = push_ctxt("trans_fn_ref_with_substs"); +fn get_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, + def_id: DefId, + substs: &'tcx subst::Substs<'tcx>) + -> Datum<'tcx, Rvalue> { let tcx = ccx.tcx(); - debug!("trans_fn_ref_with_substs(def_id={:?}, ref_ty={:?}, substs={:?})", - def_id, ref_ty, substs); + debug!("get_fn(def_id={:?}, substs={:?})", def_id, substs); assert!(!substs.types.needs_infer()); assert!(!substs.types.has_escaping_regions()); @@ -407,7 +464,7 @@ pub fn trans_fn_ref_with_substs<'a, 'tcx>( let must_monomorphise = !substs.types.is_empty() || is_named_tuple_constructor(tcx, def_id); - debug!("trans_fn_ref_with_substs({:?}) must_monomorphise: {}", + debug!("get_fn({:?}) must_monomorphise: {}", def_id, must_monomorphise); // Create a monomorphic version of generic functions @@ -416,9 +473,7 @@ pub fn trans_fn_ref_with_substs<'a, 'tcx>( assert_eq!(def_id.krate, LOCAL_CRATE); let substs = tcx.mk_substs(substs.clone().erase_regions()); - let (mut val, fn_ty, must_cast) = - monomorphize::monomorphic_fn(ccx, def_id, substs); - let fn_ty = ref_ty.unwrap_or(fn_ty); + let (val, fn_ty) = monomorphize::monomorphic_fn(ccx, def_id, substs); let fn_ptr_ty = match fn_ty.sty { ty::TyFnDef(_, _, fty) => { // Create a fn pointer with the substituted signature. @@ -426,36 +481,72 @@ pub fn trans_fn_ref_with_substs<'a, 'tcx>( } _ => unreachable!("expected fn item type, found {}", fn_ty) }; - if must_cast && ref_ty.is_some() { - let llptrty = type_of::type_of(ccx, fn_ptr_ty); - if llptrty != common::val_ty(val) { - val = consts::ptrcast(val, llptrty); - } - } + assert_eq!(type_of::type_of(ccx, fn_ptr_ty), common::val_ty(val)); return immediate_rvalue(val, fn_ptr_ty); } // Find the actual function pointer. - let local_node = ccx.tcx().map.as_local_node_id(def_id); - let mut datum = if let Some(node_id) = local_node { - // Type scheme of the function item (may have type params) - let fn_type_scheme = tcx.lookup_item_type(def_id); - let fn_type = match fn_type_scheme.ty.sty { - ty::TyFnDef(_, _, fty) => { - // Create a fn pointer with the normalized signature. - tcx.mk_fn_ptr(infer::normalize_associated_type(tcx, fty)) + let ty = ccx.tcx().lookup_item_type(def_id).ty; + let fn_ptr_ty = match ty.sty { + ty::TyFnDef(_, _, fty) => { + // Create a fn pointer with the normalized signature. + tcx.mk_fn_ptr(infer::normalize_associated_type(tcx, fty)) + } + _ => unreachable!("expected fn item type, found {}", ty) + }; + + let instance = Instance::mono(ccx.tcx(), def_id); + if let Some(&llfn) = ccx.instances().borrow().get(&instance) { + return immediate_rvalue(llfn, fn_ptr_ty); + } + + let attrs; + let local_id = ccx.tcx().map.as_local_node_id(def_id); + let maybe_node = local_id.and_then(|id| tcx.map.find(id)); + let (sym, attrs, local_item) = match maybe_node { + Some(hir_map::NodeItem(&hir::Item { + ref attrs, id, span, node: hir::ItemFn(..), .. + })) | + Some(hir_map::NodeTraitItem(&hir::TraitItem { + ref attrs, id, span, node: hir::MethodTraitItem(_, Some(_)), .. + })) | + Some(hir_map::NodeImplItem(&hir::ImplItem { + ref attrs, id, span, node: hir::ImplItemKind::Method(..), .. + })) => { + let sym = exported_name(ccx, id, ty, attrs); + + if declare::get_defined_value(ccx, &sym).is_some() { + ccx.sess().span_fatal(span, + &format!("symbol `{}` is already defined", sym)); } - _ => unreachable!("expected fn item type, found {}", - fn_type_scheme.ty) - }; - // Internal reference. - immediate_rvalue(get_item_val(ccx, node_id), fn_type) - } else { - // External reference. - get_extern_fn(ccx, def_id) + (sym, &attrs[..], Some(id)) + } + + Some(hir_map::NodeForeignItem(&hir::ForeignItem { + ref attrs, name, node: hir::ForeignItemFn(..), .. + })) => { + (imported_name(name, attrs).to_string(), &attrs[..], None) + } + + None => { + attrs = ccx.sess().cstore.item_attrs(def_id); + (ccx.sess().cstore.item_symbol(def_id), &attrs[..], None) + } + + ref variant => { + ccx.sess().bug(&format!("get_fn: unexpected variant: {:?}", variant)) + } }; + let llfn = declare::declare_fn(ccx, &sym, ty); + attributes::from_fn_attrs(ccx, attrs, llfn); + if let Some(id) = local_item { + // FIXME(eddyb) Doubt all extern fn should allow unwinding. + attributes::unwind(llfn, true); + ccx.item_symbols().borrow_mut().insert(id, sym); + } + // This is subtle and surprising, but sometimes we have to bitcast // the resulting fn pointer. The reason has to do with external // functions. If you have two crates that both bind the same C @@ -479,30 +570,23 @@ pub fn trans_fn_ref_with_substs<'a, 'tcx>( // This can occur on either a crate-local or crate-external // reference. It also occurs when testing libcore and in some // other weird situations. Annoying. - let llptrty = type_of::type_of(ccx, datum.ty); - if common::val_ty(datum.val) != llptrty { - debug!("trans_fn_ref_with_substs(): casting pointer!"); - datum.val = consts::ptrcast(datum.val, llptrty); + let llptrty = type_of::type_of(ccx, fn_ptr_ty); + let llfn = if common::val_ty(llfn) != llptrty { + debug!("get_fn: casting {:?} to {:?}", llfn, llptrty); + consts::ptrcast(llfn, llptrty) } else { - debug!("trans_fn_ref_with_substs(): not casting pointer!"); - } + debug!("get_fn: not casting pointer!"); + llfn + }; + + ccx.instances().borrow_mut().insert(instance, llfn); - datum + immediate_rvalue(llfn, fn_ptr_ty) } // ______________________________________________________________________ // Translating calls -pub fn trans_lang_call<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - did: DefId, - args: &[ValueRef], - dest: Option<expr::Dest>, - debug_loc: DebugLoc) - -> Result<'blk, 'tcx> { - let datum = trans_fn_ref(bcx.ccx(), did, ExprId(0), bcx.fcx.param_substs); - Callee::ptr(datum).call(bcx, debug_loc, ArgVals(args), dest) -} - fn trans_call_inner<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>, debug_loc: DebugLoc, callee: Callee<'tcx>, @@ -519,33 +603,29 @@ fn trans_call_inner<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>, let fcx = bcx.fcx; let ccx = fcx.ccx; - let (abi, ret_ty) = match callee.ty.sty { - ty::TyFnDef(_, _, ref f) | ty::TyFnPtr(ref f) => { - let sig = bcx.tcx().erase_late_bound_regions(&f.sig); - let sig = infer::normalize_associated_type(bcx.tcx(), &sig); - (f.abi, sig.output) + let abi = callee.ty.fn_abi(); + let sig = callee.ty.fn_sig(); + let output = bcx.tcx().erase_late_bound_regions(&sig.output()); + let output = infer::normalize_associated_type(bcx.tcx(), &output); + + let extra_args = match args { + ArgExprs(args) if abi != Abi::RustCall => { + args[sig.0.inputs.len()..].iter().map(|expr| { + common::expr_ty_adjusted(bcx, expr) + }).collect() } - _ => panic!("expected fn item or ptr in Callee::call") + _ => vec![] }; + let fn_ty = callee.direct_fn_type(ccx, &extra_args); - match callee.data { - Intrinsic(node, substs) => { + let mut callee = match callee.data { + Intrinsic => { assert!(abi == Abi::RustIntrinsic || abi == Abi::PlatformIntrinsic); assert!(dest.is_some()); - let call_info = match debug_loc { - DebugLoc::At(id, span) => NodeIdAndSpan { id: id, span: span }, - DebugLoc::None => { - bcx.sess().bug("No call info for intrinsic call?") - } - }; - - let arg_cleanup_scope = fcx.push_custom_cleanup_scope(); - return intrinsic::trans_intrinsic_call(bcx, node, callee.ty, - arg_cleanup_scope, args, - dest.unwrap(), - substs, - call_info); + return intrinsic::trans_intrinsic_call(bcx, callee.ty, &fn_ty, + args, dest.unwrap(), + debug_loc); } NamedTupleConstructor(disr) => { assert!(dest.is_some()); @@ -557,34 +637,25 @@ fn trans_call_inner<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>, dest.unwrap(), debug_loc); } - _ => {} - } - - // Intrinsics should not become actual functions. - // We trans them in place in `trans_intrinsic_call` - assert!(abi != Abi::RustIntrinsic && abi != Abi::PlatformIntrinsic); - - let is_rust_fn = abi == Abi::Rust || abi == Abi::RustCall; + f => f + }; // Generate a location to store the result. If the user does // not care about the result, just make a stack slot. let opt_llretslot = dest.and_then(|dest| match dest { expr::SaveIn(dst) => Some(dst), expr::Ignore => { - let ret_ty = match ret_ty { - ty::FnConverging(ret_ty) => ret_ty, - ty::FnDiverging => ccx.tcx().mk_nil() + let needs_drop = || match output { + ty::FnConverging(ret_ty) => bcx.fcx.type_needs_drop(ret_ty), + ty::FnDiverging => false }; - if !is_rust_fn || - type_of::return_uses_outptr(ccx, ret_ty) || - bcx.fcx.type_needs_drop(ret_ty) { + if fn_ty.ret.is_indirect() || fn_ty.ret.cast.is_some() || needs_drop() { // Push the out-pointer if we use an out-pointer for this // return type, otherwise push "undef". - if common::type_is_zero_size(ccx, ret_ty) { - let llty = type_of::type_of(ccx, ret_ty); - Some(common::C_undef(llty.ptr_to())) + if fn_ty.ret.is_ignore() { + Some(C_undef(fn_ty.ret.original_ty.ptr_to())) } else { - let llresult = alloc_ty(bcx, ret_ty, "__llret"); + let llresult = alloca(bcx, fn_ty.ret.original_ty, "__llret"); call_lifetime_start(bcx, llresult); Some(llresult) } @@ -594,128 +665,98 @@ fn trans_call_inner<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>, } }); - let mut llresult = unsafe { - llvm::LLVMGetUndef(Type::nil(ccx).ptr_to().to_ref()) - }; + // If there no destination, return must be direct, with no cast. + if opt_llretslot.is_none() { + assert!(!fn_ty.ret.is_indirect() && fn_ty.ret.cast.is_none()); + } - let arg_cleanup_scope = fcx.push_custom_cleanup_scope(); + let mut llargs = Vec::new(); - // The code below invokes the function, using either the Rust - // conventions (if it is a rust fn) or the native conventions - // (otherwise). The important part is that, when all is said - // and done, either the return value of the function will have been - // written in opt_llretslot (if it is Some) or `llresult` will be - // set appropriately (otherwise). - if is_rust_fn { - let mut llargs = Vec::new(); - - if let (ty::FnConverging(ret_ty), Some(mut llretslot)) = (ret_ty, opt_llretslot) { - if type_of::return_uses_outptr(ccx, ret_ty) { - let llformal_ret_ty = type_of::type_of(ccx, ret_ty).ptr_to(); - let llret_ty = common::val_ty(llretslot); - if llformal_ret_ty != llret_ty { - // this could happen due to e.g. subtyping - debug!("casting actual return type ({}) to match formal ({})", - bcx.llty_str(llret_ty), bcx.llty_str(llformal_ret_ty)); - llretslot = PointerCast(bcx, llretslot, llformal_ret_ty); - } - llargs.push(llretslot); - } + if fn_ty.ret.is_indirect() { + let mut llretslot = opt_llretslot.unwrap(); + if let Some(ty) = fn_ty.ret.cast { + llretslot = PointerCast(bcx, llretslot, ty.ptr_to()); } + llargs.push(llretslot); + } - let arg_start = llargs.len(); - - // Push the arguments. - bcx = trans_args(bcx, - args, - callee.ty, - &mut llargs, - cleanup::CustomScope(arg_cleanup_scope), - abi); + let arg_cleanup_scope = fcx.push_custom_cleanup_scope(); + bcx = trans_args(bcx, abi, &fn_ty, &mut callee, args, &mut llargs, + cleanup::CustomScope(arg_cleanup_scope)); + fcx.scopes.borrow_mut().last_mut().unwrap().drop_non_lifetime_clean(); - fcx.scopes.borrow_mut().last_mut().unwrap().drop_non_lifetime_clean(); + let llfn = match callee { + Fn(f) => f, + _ => unreachable!("expected fn pointer callee, found {:?}", callee) + }; - let datum = match callee.data { - Fn(f) => immediate_rvalue(f, callee.ty), - Virtual(idx) => { - // The data and vtable pointers were split by trans_arg_datum. - let vtable = llargs.remove(arg_start + 1); - meth::get_virtual_method(bcx, vtable, idx, callee.ty) - } - _ => unreachable!() - }; + let (llret, mut bcx) = base::invoke(bcx, llfn, &llargs, debug_loc); + if !bcx.unreachable.get() { + fn_ty.apply_attrs_callsite(llret); + } - // Invoke the actual rust fn and update bcx/llresult. - let (llret, b) = base::invoke(bcx, - datum.val, - &llargs[..], - datum.ty, - debug_loc); - bcx = b; - llresult = llret; - - // If the Rust convention for this type is return via - // the return value, copy it into llretslot. - match (opt_llretslot, ret_ty) { - (Some(llretslot), ty::FnConverging(ret_ty)) => { - if !type_of::return_uses_outptr(bcx.ccx(), ret_ty) && - !common::type_is_zero_size(bcx.ccx(), ret_ty) - { - store_ty(bcx, llret, llretslot, ret_ty) - } + // If the function we just called does not use an outpointer, + // store the result into the rust outpointer. Cast the outpointer + // type to match because some ABIs will use a different type than + // the Rust type. e.g., a {u32,u32} struct could be returned as + // u64. + if !fn_ty.ret.is_ignore() && !fn_ty.ret.is_indirect() { + if let Some(llforeign_ret_ty) = fn_ty.ret.cast { + let llrust_ret_ty = fn_ty.ret.original_ty; + let llretslot = opt_llretslot.unwrap(); + + // The actual return type is a struct, but the ABI + // adaptation code has cast it into some scalar type. The + // code that follows is the only reliable way I have + // found to do a transform like i64 -> {i32,i32}. + // Basically we dump the data onto the stack then memcpy it. + // + // Other approaches I tried: + // - Casting rust ret pointer to the foreign type and using Store + // is (a) unsafe if size of foreign type > size of rust type and + // (b) runs afoul of strict aliasing rules, yielding invalid + // assembly under -O (specifically, the store gets removed). + // - Truncating foreign type to correct integral type and then + // bitcasting to the struct type yields invalid cast errors. + let llscratch = base::alloca(bcx, llforeign_ret_ty, "__cast"); + base::call_lifetime_start(bcx, llscratch); + Store(bcx, llret, llscratch); + let llscratch_i8 = PointerCast(bcx, llscratch, Type::i8(ccx).ptr_to()); + let llretptr_i8 = PointerCast(bcx, llretslot, Type::i8(ccx).ptr_to()); + let llrust_size = llsize_of_store(ccx, llrust_ret_ty); + let llforeign_align = llalign_of_min(ccx, llforeign_ret_ty); + let llrust_align = llalign_of_min(ccx, llrust_ret_ty); + let llalign = cmp::min(llforeign_align, llrust_align); + debug!("llrust_size={}", llrust_size); + + if !bcx.unreachable.get() { + base::call_memcpy(&B(bcx), llretptr_i8, llscratch_i8, + C_uint(ccx, llrust_size), llalign as u32); } - (_, _) => {} + base::call_lifetime_end(bcx, llscratch); + } else if let Some(llretslot) = opt_llretslot { + base::store_ty(bcx, llret, llretslot, output.unwrap()); } - } else { - // Lang items are the only case where dest is None, and - // they are always Rust fns. - assert!(dest.is_some()); - - let mut llargs = Vec::new(); - let (llfn, arg_tys) = match (callee.data, &args) { - (Fn(f), &ArgExprs(a)) => { - (f, a.iter().map(|x| common::expr_ty_adjusted(bcx, &x)).collect()) - } - _ => panic!("expected fn ptr and arg exprs.") - }; - bcx = trans_args(bcx, - args, - callee.ty, - &mut llargs, - cleanup::CustomScope(arg_cleanup_scope), - abi); - fcx.scopes.borrow_mut().last_mut().unwrap().drop_non_lifetime_clean(); - - bcx = foreign::trans_native_call(bcx, - callee.ty, - llfn, - opt_llretslot.unwrap(), - &llargs[..], - arg_tys, - debug_loc); } fcx.pop_and_trans_custom_cleanup_scope(bcx, arg_cleanup_scope); // If the caller doesn't care about the result of this fn call, // drop the temporary slot we made. - match (dest, opt_llretslot, ret_ty) { + match (dest, opt_llretslot, output) { (Some(expr::Ignore), Some(llretslot), ty::FnConverging(ret_ty)) => { // drop the value if it is not being saved. - bcx = glue::drop_ty(bcx, - llretslot, - ret_ty, - debug_loc); + bcx = glue::drop_ty(bcx, llretslot, ret_ty, debug_loc); call_lifetime_end(bcx, llretslot); } _ => {} } - if ret_ty == ty::FnDiverging { + if output == ty::FnDiverging { Unreachable(bcx); } - Result::new(bcx, llresult) + Result::new(bcx, llret) } pub enum CallArgs<'a, 'tcx> { @@ -741,20 +782,19 @@ pub enum CallArgs<'a, 'tcx> { fn trans_args_under_call_abi<'blk, 'tcx>( mut bcx: Block<'blk, 'tcx>, arg_exprs: &[P<hir::Expr>], - fn_ty: Ty<'tcx>, + callee: &mut CalleeData, + fn_ty: &FnType, llargs: &mut Vec<ValueRef>, arg_cleanup_scope: cleanup::ScopeId) -> Block<'blk, 'tcx> { - let sig = bcx.tcx().erase_late_bound_regions(&fn_ty.fn_sig()); - let sig = infer::normalize_associated_type(bcx.tcx(), &sig); - let args = sig.inputs; + let mut arg_idx = 0; // Translate the `self` argument first. let arg_datum = unpack_datum!(bcx, expr::trans(bcx, &arg_exprs[0])); bcx = trans_arg_datum(bcx, - args[0], arg_datum, + callee, fn_ty, &mut arg_idx, arg_cleanup_scope, llargs); @@ -781,8 +821,8 @@ fn trans_args_under_call_abi<'blk, 'tcx>( adt::trans_field_ptr(bcx, repr_ptr, srcval, Disr(0), i) }).to_expr_datum(); bcx = trans_arg_datum(bcx, - field_type, arg_datum, + callee, fn_ty, &mut arg_idx, arg_cleanup_scope, llargs); } @@ -796,64 +836,20 @@ fn trans_args_under_call_abi<'blk, 'tcx>( bcx } -fn trans_overloaded_call_args<'blk, 'tcx>( - mut bcx: Block<'blk, 'tcx>, - arg_exprs: Vec<&hir::Expr>, - fn_ty: Ty<'tcx>, - llargs: &mut Vec<ValueRef>, - arg_cleanup_scope: cleanup::ScopeId) - -> Block<'blk, 'tcx> { - // Translate the `self` argument first. - let sig = bcx.tcx().erase_late_bound_regions(&fn_ty.fn_sig()); - let sig = infer::normalize_associated_type(bcx.tcx(), &sig); - let arg_tys = sig.inputs; - - let arg_datum = unpack_datum!(bcx, expr::trans(bcx, arg_exprs[0])); - bcx = trans_arg_datum(bcx, - arg_tys[0], - arg_datum, - arg_cleanup_scope, - llargs); - - // Now untuple the rest of the arguments. - let tuple_type = arg_tys[1]; - match tuple_type.sty { - ty::TyTuple(ref field_types) => { - for (i, &field_type) in field_types.iter().enumerate() { - let arg_datum = - unpack_datum!(bcx, expr::trans(bcx, arg_exprs[i + 1])); - bcx = trans_arg_datum(bcx, - field_type, - arg_datum, - arg_cleanup_scope, - llargs); - } - } - _ => { - bcx.sess().span_bug(arg_exprs[0].span, - "argument to `.call()` wasn't a tuple?!") - } - }; - - bcx -} - -pub fn trans_args<'a, 'blk, 'tcx>(cx: Block<'blk, 'tcx>, +pub fn trans_args<'a, 'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + abi: Abi, + fn_ty: &FnType, + callee: &mut CalleeData, args: CallArgs<'a, 'tcx>, - fn_ty: Ty<'tcx>, llargs: &mut Vec<ValueRef>, - arg_cleanup_scope: cleanup::ScopeId, - abi: Abi) + arg_cleanup_scope: cleanup::ScopeId) -> Block<'blk, 'tcx> { debug!("trans_args(abi={})", abi); let _icx = push_ctxt("trans_args"); - let sig = cx.tcx().erase_late_bound_regions(&fn_ty.fn_sig()); - let sig = infer::normalize_associated_type(cx.tcx(), &sig); - let arg_tys = sig.inputs; - let variadic = sig.variadic; - let mut bcx = cx; + let mut bcx = bcx; + let mut arg_idx = 0; // First we figure out the caller's view of the types of the arguments. // This will be needed if this is a generic call, because the callee has @@ -863,79 +859,90 @@ pub fn trans_args<'a, 'blk, 'tcx>(cx: Block<'blk, 'tcx>, if abi == Abi::RustCall { // This is only used for direct calls to the `call`, // `call_mut` or `call_once` functions. - return trans_args_under_call_abi(cx, - arg_exprs, - fn_ty, + return trans_args_under_call_abi(bcx, + arg_exprs, callee, fn_ty, llargs, arg_cleanup_scope) } - let num_formal_args = arg_tys.len(); - for (i, arg_expr) in arg_exprs.iter().enumerate() { - let arg_ty = if i >= num_formal_args { - assert!(variadic); - common::expr_ty_adjusted(cx, &arg_expr) - } else { - arg_tys[i] - }; - + for arg_expr in arg_exprs { let arg_datum = unpack_datum!(bcx, expr::trans(bcx, &arg_expr)); - bcx = trans_arg_datum(bcx, arg_ty, arg_datum, + bcx = trans_arg_datum(bcx, + arg_datum, + callee, fn_ty, &mut arg_idx, arg_cleanup_scope, llargs); } } ArgOverloadedCall(arg_exprs) => { - return trans_overloaded_call_args(cx, - arg_exprs, - fn_ty, - llargs, - arg_cleanup_scope) + for expr in arg_exprs { + let arg_datum = + unpack_datum!(bcx, expr::trans(bcx, expr)); + bcx = trans_arg_datum(bcx, + arg_datum, + callee, fn_ty, &mut arg_idx, + arg_cleanup_scope, + llargs); + } } ArgOverloadedOp(lhs, rhs) => { - assert!(!variadic); - - bcx = trans_arg_datum(bcx, arg_tys[0], lhs, + bcx = trans_arg_datum(bcx, lhs, + callee, fn_ty, &mut arg_idx, arg_cleanup_scope, llargs); if let Some(rhs) = rhs { - assert_eq!(arg_tys.len(), 2); - bcx = trans_arg_datum(bcx, arg_tys[1], rhs, + bcx = trans_arg_datum(bcx, rhs, + callee, fn_ty, &mut arg_idx, arg_cleanup_scope, llargs); - } else { - assert_eq!(arg_tys.len(), 1); } } ArgVals(vs) => { - llargs.extend_from_slice(vs); + match *callee { + Virtual(idx) => { + llargs.push(vs[0]); + + let fn_ptr = meth::get_virtual_method(bcx, vs[1], idx); + let llty = fn_ty.llvm_type(bcx.ccx()).ptr_to(); + *callee = Fn(PointerCast(bcx, fn_ptr, llty)); + llargs.extend_from_slice(&vs[2..]); + } + _ => llargs.extend_from_slice(vs) + } } } bcx } -pub fn trans_arg_datum<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - formal_arg_ty: Ty<'tcx>, - arg_datum: Datum<'tcx, Expr>, - arg_cleanup_scope: cleanup::ScopeId, - llargs: &mut Vec<ValueRef>) - -> Block<'blk, 'tcx> { +fn trans_arg_datum<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + arg_datum: Datum<'tcx, Expr>, + callee: &mut CalleeData, + fn_ty: &FnType, + next_idx: &mut usize, + arg_cleanup_scope: cleanup::ScopeId, + llargs: &mut Vec<ValueRef>) + -> Block<'blk, 'tcx> { let _icx = push_ctxt("trans_arg_datum"); let mut bcx = bcx; - let ccx = bcx.ccx(); - debug!("trans_arg_datum({:?})", - formal_arg_ty); + debug!("trans_arg_datum({:?})", arg_datum); - let arg_datum_ty = arg_datum.ty; + let arg = &fn_ty.args[*next_idx]; + *next_idx += 1; + + // Fill padding with undef value, where applicable. + if let Some(ty) = arg.pad { + llargs.push(C_undef(ty)); + } - debug!(" arg datum: {}", arg_datum.to_string(bcx.ccx())); + // Determine whether we want a by-ref datum even if not appropriate. + let want_by_ref = arg.is_indirect() || arg.cast.is_some(); - let mut val = if common::type_is_fat_ptr(bcx.tcx(), arg_datum_ty) && - !bcx.fcx.type_needs_drop(arg_datum_ty) { - arg_datum.val + let fat_ptr = common::type_is_fat_ptr(bcx.tcx(), arg_datum.ty); + let (by_ref, val) = if fat_ptr && !bcx.fcx.type_needs_drop(arg_datum.ty) { + (true, arg_datum.val) } else { // Make this an rvalue, since we are going to be // passing ownership. @@ -944,33 +951,70 @@ pub fn trans_arg_datum<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, // Now that arg_datum is owned, get it into the appropriate // mode (ref vs value). - let arg_datum = unpack_datum!( - bcx, arg_datum.to_appropriate_datum(bcx)); + let arg_datum = unpack_datum!(bcx, if want_by_ref { + arg_datum.to_ref_datum(bcx) + } else { + arg_datum.to_appropriate_datum(bcx) + }); // Technically, ownership of val passes to the callee. // However, we must cleanup should we panic before the // callee is actually invoked. - arg_datum.add_clean(bcx.fcx, arg_cleanup_scope) + (arg_datum.kind.is_by_ref(), + arg_datum.add_clean(bcx.fcx, arg_cleanup_scope)) }; - if type_of::arg_is_indirect(ccx, formal_arg_ty) && formal_arg_ty != arg_datum_ty { - // this could happen due to e.g. subtyping - let llformal_arg_ty = type_of::type_of_explicit_arg(ccx, formal_arg_ty); - debug!("casting actual type ({}) to match formal ({})", - bcx.val_to_string(val), bcx.llty_str(llformal_arg_ty)); - debug!("Rust types: {:?}; {:?}", arg_datum_ty, - formal_arg_ty); - val = PointerCast(bcx, val, llformal_arg_ty); + if arg.is_ignore() { + return bcx; } - debug!("--- trans_arg_datum passing {}", bcx.val_to_string(val)); + debug!("--- trans_arg_datum passing {:?}", Value(val)); - if common::type_is_fat_ptr(bcx.tcx(), formal_arg_ty) { + if fat_ptr { + // Fat pointers should be passed without any transformations. + assert!(!arg.is_indirect() && arg.cast.is_none()); llargs.push(Load(bcx, expr::get_dataptr(bcx, val))); - llargs.push(Load(bcx, expr::get_meta(bcx, val))); - } else { - llargs.push(val); + + let info_arg = &fn_ty.args[*next_idx]; + *next_idx += 1; + assert!(!info_arg.is_indirect() && info_arg.cast.is_none()); + let info = Load(bcx, expr::get_meta(bcx, val)); + + if let Virtual(idx) = *callee { + // We have to grab the fn pointer from the vtable when + // handling the first argument, ensure that here. + assert_eq!(*next_idx, 2); + assert!(info_arg.is_ignore()); + let fn_ptr = meth::get_virtual_method(bcx, info, idx); + let llty = fn_ty.llvm_type(bcx.ccx()).ptr_to(); + *callee = Fn(PointerCast(bcx, fn_ptr, llty)); + } else { + assert!(!info_arg.is_ignore()); + llargs.push(info); + } + return bcx; + } + + let mut val = val; + if by_ref && !arg.is_indirect() { + // Have to load the argument, maybe while casting it. + if arg.original_ty == Type::i1(bcx.ccx()) { + // We store bools as i8 so we need to truncate to i1. + val = LoadRangeAssert(bcx, val, 0, 2, llvm::False); + val = Trunc(bcx, val, arg.original_ty); + } else if let Some(ty) = arg.cast { + val = Load(bcx, PointerCast(bcx, val, ty.ptr_to())); + if !bcx.unreachable.get() { + let llalign = llalign_of_min(bcx.ccx(), arg.ty); + unsafe { + llvm::LLVMSetAlignment(val, llalign); + } + } + } else { + val = Load(bcx, val); + } } + llargs.push(val); bcx } |