blob: 983ee564c35b1051fcc447b514d4719d05578707 [file] [log] [blame]
// 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.
//! Handles translation of callees as well as other call-related
//! things. Callees are a superset of normal rust values and sometimes
//! have different representations. In particular, top-level fn items
//! and methods are represented as just a fn ptr and not a full
//! closure.
pub use self::CalleeData::*;
pub use self::CallArgs::*;
use arena::TypedArena;
use back::symbol_names;
use llvm::{self, ValueRef, get_params};
use middle::cstore::LOCAL_CRATE;
use rustc::hir::def_id::DefId;
use rustc::ty::subst;
use rustc::traits;
use rustc::hir::map as hir_map;
use abi::{Abi, FnType};
use adt;
use attributes;
use base;
use base::*;
use build::*;
use cleanup;
use cleanup::CleanupMethods;
use closure;
use common::{self, Block, Result, CrateContext, FunctionContext, C_undef};
use consts;
use datum::*;
use debuginfo::DebugLoc;
use declare;
use expr;
use glue;
use inline;
use intrinsic;
use machine::llalign_of_min;
use meth;
use monomorphize::{self, Instance};
use trans_item::TransItem;
use type_::Type;
use type_of;
use value::Value;
use Disr;
use rustc::ty::{self, Ty, TyCtxt, TypeFoldable};
use rustc::hir;
use syntax_pos::DUMMY_SP;
use errors;
use syntax::ptr::P;
#[derive(Debug)]
pub enum CalleeData {
/// Constructor for enum variant/tuple-like-struct.
NamedTupleConstructor(Disr),
/// Function pointer.
Fn(ValueRef),
Intrinsic,
/// Trait object found in the vtable at that index.
Virtual(usize)
}
#[derive(Debug)]
pub struct Callee<'tcx> {
pub data: CalleeData,
pub ty: Ty<'tcx>
}
impl<'tcx> Callee<'tcx> {
/// Function pointer.
pub fn ptr(datum: Datum<'tcx, Rvalue>) -> Callee<'tcx> {
Callee {
data: Fn(datum.val),
ty: datum.ty
}
}
/// Trait or impl method call.
pub fn method_call<'blk>(bcx: Block<'blk, 'tcx>,
method_call: ty::MethodCall)
-> Callee<'tcx> {
let method = bcx.tcx().tables.borrow().method_map[&method_call];
Callee::method(bcx, method)
}
/// Trait or impl method.
pub fn method<'blk>(bcx: Block<'blk, 'tcx>,
method: ty::MethodCallee<'tcx>) -> Callee<'tcx> {
let substs = bcx.fcx.monomorphize(&method.substs);
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>)
-> Callee<'tcx> {
let tcx = ccx.tcx();
if substs.self_ty().is_some() {
// Only trait methods can have a Self parameter.
return Callee::trait_method(ccx, def_id, substs);
}
let maybe_node_id = inline::get_local_instance(ccx, def_id)
.and_then(|def_id| tcx.map.as_local_node_id(def_id));
let maybe_ast_node = maybe_node_id.and_then(|node_id| {
tcx.map.find(node_id)
});
let data = match maybe_ast_node {
Some(hir_map::NodeStructCtor(_)) => {
NamedTupleConstructor(Disr(0))
}
Some(hir_map::NodeVariant(_)) => {
let vinfo = common::inlined_variant_def(ccx, maybe_node_id.unwrap());
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::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));
let trait_ref = tcx.normalize_associated_type(&trait_ref);
match common::fulfill_obligation(ccx.shared(), 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_fn_ptr(fty),
_ => bug!("expected fn item type, found {}",
method_ty)
};
Callee::ptr(immediate_rvalue(llfn, fn_ptr_ty))
}
traits::VtableFnPointer(vtable_fn_pointer) => {
let trait_closure_kind = tcx.lang_items.fn_trait_kind(trait_id).unwrap();
let llfn = trans_fn_pointer_shim(ccx, trait_closure_kind, vtable_fn_pointer.fn_ty);
let method_ty = def_ty(tcx, def_id, substs);
let fn_ptr_ty = match method_ty.sty {
ty::TyFnDef(_, _, fty) => tcx.mk_fn_ptr(fty),
_ => bug!("expected fn item type, found {}",
method_ty)
};
Callee::ptr(immediate_rvalue(llfn, fn_ptr_ty))
}
traits::VtableObject(ref data) => {
Callee {
data: Virtual(tcx.get_vtable_index_of_object_method(data, def_id)),
ty: def_ty(tcx, def_id, substs)
}
}
vtable => {
bug!("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 = ccx.tcx().normalize_associated_type(&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();
}
fn_ty.adjust_for_abi(ccx, abi, &sig);
fn_ty
}
/// This behemoth of a function translates function calls. Unfortunately, in
/// order to generate more efficient LLVM output at -O0, it has quite a complex
/// signature (refactoring this into two functions seems like a good idea).
///
/// In particular, for lang items, it is invoked with a dest of None, and in
/// that case the return value contains the result of the fn. The lang item must
/// not return a structural type or else all heck breaks loose.
///
/// For non-lang items, `dest` is always Some, and hence the result is written
/// into memory somewhere. Nonetheless we return the actual return value of the
/// function.
pub fn call<'a, 'blk>(self, bcx: Block<'blk, 'tcx>,
debug_loc: DebugLoc,
args: CallArgs<'a, 'tcx>,
dest: Option<expr::Dest>)
-> Result<'blk, 'tcx> {
trans_call_inner(bcx, debug_loc, self, args, dest)
}
/// 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_fn_ptr(f),
_ => self.ty
};
match self.data {
Fn(llfn) => {
immediate_rvalue(llfn, fn_ptr_ty)
}
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 get_fn(ccx, def_id, substs);
}
_ => bug!("expected fn item type, found {}", self.ty)
},
Intrinsic => bug!("intrinsic {} getting reified", self.ty)
}
}
}
/// Given a DefId and some Substs, produces the monomorphic item type.
fn def_ty<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, '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
/// pointer. This is basically the equivalent of something like:
///
/// ```
/// impl<'a> Fn(&'a int) -> &'a int for fn(&int) -> &int {
/// extern "rust-abi" fn call(&self, args: (&'a int,)) -> &'a int {
/// (*self)(args.0)
/// }
/// }
/// ```
///
/// but for the bare function type given.
pub fn trans_fn_pointer_shim<'a, 'tcx>(
ccx: &'a CrateContext<'a, 'tcx>,
closure_kind: ty::ClosureKind,
bare_fn_ty: Ty<'tcx>)
-> ValueRef
{
let _icx = push_ctxt("trans_fn_pointer_shim");
let tcx = ccx.tcx();
// Normalize the type for better caching.
let bare_fn_ty = tcx.normalize_associated_type(&bare_fn_ty);
// If this is an impl of `Fn` or `FnMut` trait, the receiver is `&self`.
let is_by_ref = match closure_kind {
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::ReErased), bare_fn_ty)
} else {
bare_fn_ty
};
// Check if we already trans'd this shim.
match ccx.fn_pointer_shims().borrow().get(&bare_fn_ty_maybe_ref) {
Some(&llval) => { return llval; }
None => { }
}
debug!("trans_fn_pointer_shim(bare_fn_ty={:?})",
bare_fn_ty);
// Construct the "tuply" version of `bare_fn_ty`. It takes two arguments: `self`,
// which is the fn pointer, and `args`, which is the arguments tuple.
let sig = match bare_fn_ty.sty {
ty::TyFnDef(_, _,
&ty::BareFnTy { unsafety: hir::Unsafety::Normal,
abi: Abi::Rust,
ref sig }) |
ty::TyFnPtr(&ty::BareFnTy { unsafety: hir::Unsafety::Normal,
abi: Abi::Rust,
ref sig }) => sig,
_ => {
bug!("trans_fn_pointer_shim invoked on invalid type: {}",
bare_fn_ty);
}
};
let sig = tcx.erase_late_bound_regions(sig);
let sig = ccx.tcx().normalize_associated_type(&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(tcx.mk_bare_fn(ty::BareFnTy {
unsafety: hir::Unsafety::Normal,
abi: Abi::RustCall,
sig: ty::Binder(sig)
}));
debug!("tuple_fn_ty: {:?}", tuple_fn_ty);
//
let function_name =
symbol_names::internal_name_from_type_and_suffix(ccx,
bare_fn_ty,
"fn_pointer_shim");
let llfn = declare::define_internal_fn(ccx, &function_name, tuple_fn_ty);
attributes::set_frame_pointer_elimination(ccx, llfn);
//
let (block_arena, fcx): (TypedArena<_>, FunctionContext);
block_arena = TypedArena::new();
fcx = FunctionContext::new(ccx, llfn, fn_ty, None, &block_arena);
let mut bcx = fcx.init(false, None);
let llargs = get_params(fcx.llfn);
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 {
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, "ret_slot"))
);
let callee = Callee {
data: Fn(llfnpointer),
ty: bare_fn_ty
};
bcx = callee.call(bcx, DebugLoc::None, ArgVals(&llargs[(self_idx + 1)..]), dest).bcx;
fcx.finish(bcx, DebugLoc::None);
ccx.fn_pointer_shims().borrow_mut().insert(bare_fn_ty_maybe_ref, llfn);
llfn
}
/// Translates a reference to a fn/method item, monomorphizing and
/// inlining as it goes.
///
/// # Parameters
///
/// - `ccx`: the crate context
/// - `def_id`: def id of the fn or method item being referenced
/// - `substs`: values for each of the fn/method's parameters
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!("get_fn(def_id={:?}, substs={:?})", def_id, substs);
assert!(!substs.types.needs_infer());
assert!(!substs.types.has_escaping_regions());
// Check whether this fn has an inlined copy and, if so, redirect
// def_id to the local id of the inlined copy.
let def_id = inline::maybe_instantiate_inline(ccx, def_id);
fn is_named_tuple_constructor(tcx: TyCtxt, def_id: DefId) -> bool {
let node_id = match tcx.map.as_local_node_id(def_id) {
Some(n) => n,
None => { return false; }
};
let map_node = errors::expect(
&tcx.sess.diagnostic(),
tcx.map.find(node_id),
|| "local item should be in ast map".to_string());
match map_node {
hir_map::NodeVariant(v) => {
v.node.data.is_tuple()
}
hir_map::NodeStructCtor(_) => true,
_ => false
}
}
let must_monomorphise =
!substs.types.is_empty() || is_named_tuple_constructor(tcx, def_id);
debug!("get_fn({:?}) must_monomorphise: {}",
def_id, must_monomorphise);
// Create a monomorphic version of generic functions
if must_monomorphise {
// Should be either intra-crate or inlined.
assert_eq!(def_id.krate, LOCAL_CRATE);
let substs = tcx.normalize_associated_type(&substs);
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.
tcx.mk_fn_ptr(fty)
}
_ => bug!("expected fn item type, found {}", fn_ty)
};
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 ty = ccx.tcx().lookup_item_type(def_id).ty;
let fn_ptr_ty = match ty.sty {
ty::TyFnDef(_, _, ref fty) => {
// Create a fn pointer with the normalized signature.
tcx.mk_fn_ptr(tcx.normalize_associated_type(fty))
}
_ => bug!("expected fn item type, found {}", ty)
};
let instance = Instance::mono(ccx.shared(), def_id);
if let Some(&llfn) = ccx.instances().borrow().get(&instance) {
return immediate_rvalue(llfn, fn_ptr_ty);
}
let local_id = ccx.tcx().map.as_local_node_id(def_id);
let local_item = match local_id.and_then(|id| tcx.map.find(id)) {
Some(hir_map::NodeItem(&hir::Item {
span, node: hir::ItemFn(..), ..
})) |
Some(hir_map::NodeTraitItem(&hir::TraitItem {
span, node: hir::MethodTraitItem(_, Some(_)), ..
})) |
Some(hir_map::NodeImplItem(&hir::ImplItem {
span, node: hir::ImplItemKind::Method(..), ..
})) => {
Some(span)
}
_ => None
};
// 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
// library, they may not use precisely the same types: for
// example, they will probably each declare their own structs,
// which are distinct types from LLVM's point of view (nominal
// types).
//
// Now, if those two crates are linked into an application, and
// they contain inlined code, you can wind up with a situation
// where both of those functions wind up being loaded into this
// application simultaneously. In that case, the same function
// (from LLVM's point of view) requires two types. But of course
// LLVM won't allow one function to have two types.
//
// What we currently do, therefore, is declare the function with
// one of the two types (whichever happens to come first) and then
// bitcast as needed when the function is referenced to make sure
// it has the type we expect.
//
// 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 sym = ccx.symbol_map().get_or_compute(ccx.shared(),
TransItem::Fn(instance));
let llptrty = type_of::type_of(ccx, fn_ptr_ty);
let llfn = if let Some(llfn) = declare::get_declared_value(ccx, &sym) {
if let Some(span) = local_item {
if declare::get_defined_value(ccx, &sym).is_some() {
ccx.sess().span_fatal(span,
&format!("symbol `{}` is already defined", &sym));
}
}
if common::val_ty(llfn) != llptrty {
if local_item.is_some() {
bug!("symbol `{}` previously declared as {:?}, now wanted as {:?}",
sym, Value(llfn), llptrty);
}
debug!("get_fn: casting {:?} to {:?}", llfn, llptrty);
consts::ptrcast(llfn, llptrty)
} else {
debug!("get_fn: not casting pointer!");
llfn
}
} else {
let llfn = declare::declare_fn(ccx, &sym, ty);
assert_eq!(common::val_ty(llfn), llptrty);
debug!("get_fn: not casting pointer!");
let attrs = ccx.tcx().get_attrs(def_id);
attributes::from_fn_attrs(ccx, &attrs, llfn);
if local_item.is_some() {
// FIXME(eddyb) Doubt all extern fn should allow unwinding.
attributes::unwind(llfn, true);
}
llfn
};
ccx.instances().borrow_mut().insert(instance, llfn);
immediate_rvalue(llfn, fn_ptr_ty)
}
// ______________________________________________________________________
// Translating calls
fn trans_call_inner<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
debug_loc: DebugLoc,
callee: Callee<'tcx>,
args: CallArgs<'a, 'tcx>,
dest: Option<expr::Dest>)
-> Result<'blk, 'tcx> {
// Introduce a temporary cleanup scope that will contain cleanups
// for the arguments while they are being evaluated. The purpose
// this cleanup is to ensure that, should a panic occur while
// evaluating argument N, the values for arguments 0...N-1 are all
// cleaned up. If no panic occurs, the values are handed off to
// the callee, and hence none of the cleanups in this temporary
// scope will ever execute.
let fcx = bcx.fcx;
let ccx = fcx.ccx;
let abi = callee.ty.fn_abi();
let sig = callee.ty.fn_sig();
let output = bcx.tcx().erase_late_bound_regions(&sig.output());
let output = bcx.tcx().normalize_associated_type(&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()
}
_ => vec![]
};
let fn_ty = callee.direct_fn_type(ccx, &extra_args);
let mut callee = match callee.data {
Intrinsic => {
assert!(abi == Abi::RustIntrinsic || abi == Abi::PlatformIntrinsic);
assert!(dest.is_some());
return intrinsic::trans_intrinsic_call(bcx, callee.ty, &fn_ty,
args, dest.unwrap(),
debug_loc);
}
NamedTupleConstructor(disr) => {
assert!(dest.is_some());
return base::trans_named_tuple_constructor(bcx,
callee.ty,
disr,
args,
dest.unwrap(),
debug_loc);
}
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 needs_drop = || match output {
ty::FnConverging(ret_ty) => bcx.fcx.type_needs_drop(ret_ty),
ty::FnDiverging => false
};
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 fn_ty.ret.is_ignore() {
Some(C_undef(fn_ty.ret.original_ty.ptr_to()))
} else {
let llresult = alloca(bcx, fn_ty.ret.original_ty, "__llret");
call_lifetime_start(bcx, llresult);
Some(llresult)
}
} else {
None
}
}
});
// 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 mut llargs = Vec::new();
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_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();
let llfn = match callee {
Fn(f) => f,
_ => bug!("expected fn pointer callee, found {:?}", callee)
};
let (llret, mut bcx) = base::invoke(bcx, llfn, &llargs, debug_loc);
if !bcx.unreachable.get() {
fn_ty.apply_attrs_callsite(llret);
// 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_indirect() {
if let Some(llretslot) = opt_llretslot {
fn_ty.ret.store(&bcx.build(), llret, llretslot);
}
}
}
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, 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);
call_lifetime_end(bcx, llretslot);
}
_ => {}
}
if output == ty::FnDiverging {
Unreachable(bcx);
}
Result::new(bcx, llret)
}
pub enum CallArgs<'a, 'tcx> {
/// Supply value of arguments as a list of expressions that must be
/// translated. This is used in the common case of `foo(bar, qux)`.
ArgExprs(&'a [P<hir::Expr>]),
/// Supply value of arguments as a list of LLVM value refs; frequently
/// used with lang items and so forth, when the argument is an internal
/// value.
ArgVals(&'a [ValueRef]),
/// For overloaded operators: `(lhs, Option(rhs))`.
/// `lhs` is the left-hand-side and `rhs` is the datum
/// of the right-hand-side argument (if any).
ArgOverloadedOp(Datum<'tcx, Expr>, Option<Datum<'tcx, Expr>>),
/// Supply value of arguments as a list of expressions that must be
/// translated, for overloaded call operators.
ArgOverloadedCall(Vec<&'a hir::Expr>),
}
fn trans_args_under_call_abi<'blk, 'tcx>(
mut bcx: Block<'blk, 'tcx>,
arg_exprs: &[P<hir::Expr>],
callee: &mut CalleeData,
fn_ty: &FnType,
llargs: &mut Vec<ValueRef>,
arg_cleanup_scope: cleanup::ScopeId)
-> Block<'blk, 'tcx>
{
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,
arg_datum,
callee, fn_ty, &mut arg_idx,
arg_cleanup_scope,
llargs);
// Now untuple the rest of the arguments.
let tuple_expr = &arg_exprs[1];
let tuple_type = common::node_id_type(bcx, tuple_expr.id);
match tuple_type.sty {
ty::TyTuple(ref field_types) => {
let tuple_datum = unpack_datum!(bcx,
expr::trans(bcx, &tuple_expr));
let tuple_lvalue_datum =
unpack_datum!(bcx,
tuple_datum.to_lvalue_datum(bcx,
"args",
tuple_expr.id));
let repr = adt::represent_type(bcx.ccx(), tuple_type);
let repr_ptr = &repr;
for (i, field_type) in field_types.iter().enumerate() {
let arg_datum = tuple_lvalue_datum.get_element(
bcx,
field_type,
|srcval| {
adt::trans_field_ptr(bcx, repr_ptr, srcval, Disr(0), i)
}).to_expr_datum();
bcx = trans_arg_datum(bcx,
arg_datum,
callee, fn_ty, &mut arg_idx,
arg_cleanup_scope,
llargs);
}
}
_ => {
span_bug!(tuple_expr.span,
"argument to `.call()` wasn't a tuple?!")
}
};
bcx
}
pub fn trans_args<'a, 'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
abi: Abi,
fn_ty: &FnType,
callee: &mut CalleeData,
args: CallArgs<'a, 'tcx>,
llargs: &mut Vec<ValueRef>,
arg_cleanup_scope: cleanup::ScopeId)
-> Block<'blk, 'tcx> {
debug!("trans_args(abi={})", abi);
let _icx = push_ctxt("trans_args");
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
// to cast her view of the arguments to the caller's view.
match args {
ArgExprs(arg_exprs) => {
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(bcx,
arg_exprs, callee, fn_ty,
llargs,
arg_cleanup_scope)
}
for arg_expr in arg_exprs {
let arg_datum = unpack_datum!(bcx, expr::trans(bcx, &arg_expr));
bcx = trans_arg_datum(bcx,
arg_datum,
callee, fn_ty, &mut arg_idx,
arg_cleanup_scope,
llargs);
}
}
ArgOverloadedCall(arg_exprs) => {
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) => {
bcx = trans_arg_datum(bcx, lhs,
callee, fn_ty, &mut arg_idx,
arg_cleanup_scope,
llargs);
if let Some(rhs) = rhs {
bcx = trans_arg_datum(bcx, rhs,
callee, fn_ty, &mut arg_idx,
arg_cleanup_scope,
llargs);
}
}
ArgVals(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
}
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;
debug!("trans_arg_datum({:?})", arg_datum);
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));
}
// Determine whether we want a by-ref datum even if not appropriate.
let want_by_ref = arg.is_indirect() || arg.cast.is_some();
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.
let arg_datum = unpack_datum!(
bcx, arg_datum.to_rvalue_datum(bcx, "arg"));
// Now that arg_datum is owned, get it into the appropriate
// mode (ref vs value).
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.kind.is_by_ref(),
arg_datum.add_clean(bcx.fcx, arg_cleanup_scope))
};
if arg.is_ignore() {
return bcx;
}
debug!("--- trans_arg_datum passing {:?}", Value(val));
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)));
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
}