Auto merge of #30692 - michaelwoerister:mir-overloaded-fn-calls, r=nikomatsakis

So far, calls going through `Fn::call`, `FnMut::call_mut`, or `FnOnce::call_once` have not been translated properly into MIR:
The call `f(a, b, c)` where `f: Fn(T1, T2, T3)` would end up in MIR as:
```
call `f` with arguments  `a`, `b`, `c`
```
What we really want is:
```
call `Fn::call` with arguments  `f`, `a`, `b`, `c`
```
This PR transforms these kinds of overloaded calls during `HIR -> HAIR` translation.

What's still a bit funky is that the `Fn` traits expect arguments to be tupled but due to special handling type-checking and trans, we do not actually tuple arguments and everything still checks out fine. So, after this PR we end up with MIR containing calls where function signature and arguments seemingly don't match:
```
call Fn::call(&self, args: (T1, T2, T3)) with arguments `f`, `a`, `b`, `c`
```
instead of
```
call Fn::call(&self, args: (T1, T2, T3)) with arguments `f`, (`a`, `b`, `c`)  //  <- args tupled!
```
It would be nice if the call traits could go without special handling in MIR and later on.
This commit is contained in:
bors 2016-01-06 09:00:57 +00:00
commit 7312e0a163
4 changed files with 77 additions and 5 deletions

View File

@ -46,6 +46,26 @@ impl<'tcx> Mirror<'tcx> for &'tcx hir::Expr {
}
}
hir::ExprCall(ref fun, ref args) => {
if cx.tcx.is_method_call(self.id) {
// The callee is something implementing Fn, FnMut, or FnOnce.
// Find the actual method implementation being called and
// build the appropriate UFCS call expression with the
// callee-object as self parameter.
let method = method_callee(cx, self, ty::MethodCall::expr(self.id));
let mut argrefs = vec![fun.to_ref()];
argrefs.extend(args.iter().map(|a| a.to_ref()));
ExprKind::Call {
fun: method.to_ref(),
args: argrefs,
}
} else {
ExprKind::Call { fun: fun.to_ref(), args: args.to_ref() }
}
}
hir::ExprAddrOf(mutbl, ref expr) => {
let region = match expr_ty.sty {
ty::TyRef(r, _) => r,
@ -328,8 +348,6 @@ impl<'tcx> Mirror<'tcx> for &'tcx hir::Expr {
ExprKind::Vec { fields: fields.to_ref() },
hir::ExprTup(ref fields) =>
ExprKind::Tuple { fields: fields.to_ref() },
hir::ExprCall(ref fun, ref args) =>
ExprKind::Call { fun: fun.to_ref(), args: args.to_ref() },
};
let temp_lifetime = cx.tcx.region_maps.temporary_scope(self.id);

View File

@ -89,8 +89,10 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
{
let ty = bcx.monomorphize(&constant.ty);
match constant.literal {
mir::Literal::Item { def_id, kind, substs } =>
self.trans_item_ref(bcx, ty, kind, substs, def_id),
mir::Literal::Item { def_id, kind, substs } => {
let substs = bcx.tcx().mk_substs(bcx.monomorphize(&substs));
self.trans_item_ref(bcx, ty, kind, substs, def_id)
}
mir::Literal::Value { ref value } => {
self.trans_constval(bcx, value, ty)
}

View File

@ -21,6 +21,7 @@ use rustc::middle::traits;
use rustc::mir::repr::ItemKind;
use trans::common::{Block, fulfill_obligation};
use trans::base;
use trans::closure;
use trans::expr;
use trans::monomorphize;
use trans::meth;
@ -38,6 +39,9 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
substs: &'tcx Substs<'tcx>,
did: DefId)
-> OperandRef<'tcx> {
debug!("trans_item_ref(ty={:?}, kind={:?}, substs={:?}, did={})",
ty, kind, substs, bcx.tcx().item_path_str(did));
match kind {
ItemKind::Function => self.trans_fn_ref(bcx, ty, substs, did),
ItemKind::Method => match bcx.tcx().impl_or_trait_item(did).container() {
@ -68,6 +72,9 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
substs: &'tcx Substs<'tcx>,
did: DefId)
-> OperandRef<'tcx> {
debug!("trans_fn_ref(ty={:?}, substs={:?}, did={})",
ty, substs, bcx.tcx().item_path_str(did));
let did = inline::maybe_instantiate_inline(bcx.ccx(), did);
if !substs.types.is_empty() || is_named_tuple_constructor(bcx.tcx(), did) {
@ -101,9 +108,14 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
trait_id: DefId,
substs: &'tcx Substs<'tcx>)
-> OperandRef<'tcx> {
debug!("trans_static_method(ty={:?}, method={}, trait={}, substs={:?})",
ty,
bcx.tcx().item_path_str(method_id),
bcx.tcx().item_path_str(trait_id),
substs);
let ccx = bcx.ccx();
let tcx = bcx.tcx();
let mname = tcx.item_name(method_id);
let subst::SeparateVecsPerParamSpace {
types: rcvr_type,
selfs: rcvr_self,
@ -118,6 +130,9 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
match vtbl {
traits::VtableImpl(traits::VtableImplData { impl_def_id, substs: imp_substs, .. }) => {
assert!(!imp_substs.types.needs_infer());
let mname = tcx.item_name(method_id);
let subst::SeparateVecsPerParamSpace {
types: impl_type,
selfs: impl_self,
@ -130,6 +145,17 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
let mthsubsts = tcx.mk_substs(mth.substs);
self.trans_fn_ref(bcx, ty, mthsubsts, mth.method.def_id)
},
traits::VtableClosure(data) => {
let trait_closure_kind = bcx.tcx().lang_items.fn_trait_kind(trait_id).unwrap();
let llfn = closure::trans_closure_method(bcx.ccx(),
data.closure_def_id,
data.substs,
trait_closure_kind);
OperandRef {
ty: ty,
val: OperandValue::Immediate(llfn)
}
},
traits::VtableObject(ref data) => {
let idx = traits::get_vtable_index_of_object_method(tcx, data, method_id);
OperandRef::from_rvalue_datum(

View File

@ -93,6 +93,26 @@ fn test8() -> isize {
Two::two()
}
#[rustc_mir]
fn test_closure<F>(f: &F, x: i32, y: i32) -> i32
where F: Fn(i32, i32) -> i32
{
f(x, y)
}
#[rustc_mir]
fn test_fn_object(f: &Fn(i32, i32) -> i32, x: i32, y: i32) -> i32 {
f(x, y)
}
#[rustc_mir]
fn test_fn_impl(f: &&Fn(i32, i32) -> i32, x: i32, y: i32) -> i32 {
// This call goes through the Fn implementation for &Fn provided in
// core::ops::impls. It expands to a static Fn::call() that calls the
// Fn::call() implemenation of the object shim underneath.
f(x, y)
}
fn main() {
assert_eq!(test1(1, (2, 3), &[4, 5, 6]), (1, (2, 3), &[4, 5, 6][..]));
assert_eq!(test2(98), 98);
@ -103,4 +123,10 @@ fn main() {
// assert_eq!(test6(&Foo, 12367), 12367);
assert_eq!(test7(), 1);
assert_eq!(test8(), 2);
let closure = |x: i32, y: i32| { x + y };
assert_eq!(test_closure(&closure, 100, 1), 101);
let function_object = &closure as &Fn(i32, i32) -> i32;
assert_eq!(test_fn_object(function_object, 100, 2), 102);
assert_eq!(test_fn_impl(&function_object, 100, 3), 103);
}