about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2016-01-06 09:00:57 +0000
committerbors <bors@rust-lang.org>2016-01-06 09:00:57 +0000
commit7312e0a16328e6227d7fc593176a1fd12fea4342 (patch)
treeee92eacaf624f1e6bf5b777f61ea97f96ae77325
parentdc1f442634c2e37a8b80d59e27edcc24b5614d4e (diff)
parente281509dcee2ef1ca06b333b6a40e8520c642d15 (diff)
downloadrust-7312e0a16328e6227d7fc593176a1fd12fea4342.tar.gz
rust-7312e0a16328e6227d7fc593176a1fd12fea4342.zip
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.
-rw-r--r--src/librustc_mir/hair/cx/expr.rs22
-rw-r--r--src/librustc_trans/trans/mir/constant.rs6
-rw-r--r--src/librustc_trans/trans/mir/did.rs28
-rw-r--r--src/test/run-pass/mir_trans_calls.rs26
4 files changed, 77 insertions, 5 deletions
diff --git a/src/librustc_mir/hair/cx/expr.rs b/src/librustc_mir/hair/cx/expr.rs
index ce78a39d599..aa6257345fe 100644
--- a/src/librustc_mir/hair/cx/expr.rs
+++ b/src/librustc_mir/hair/cx/expr.rs
@@ -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);
diff --git a/src/librustc_trans/trans/mir/constant.rs b/src/librustc_trans/trans/mir/constant.rs
index 12839df87b1..0f4a0407298 100644
--- a/src/librustc_trans/trans/mir/constant.rs
+++ b/src/librustc_trans/trans/mir/constant.rs
@@ -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)
             }
diff --git a/src/librustc_trans/trans/mir/did.rs b/src/librustc_trans/trans/mir/did.rs
index d4a7a7c8b48..737c3dace2e 100644
--- a/src/librustc_trans/trans/mir/did.rs
+++ b/src/librustc_trans/trans/mir/did.rs
@@ -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(
diff --git a/src/test/run-pass/mir_trans_calls.rs b/src/test/run-pass/mir_trans_calls.rs
index cf3d3d0720b..bd236e95d1c 100644
--- a/src/test/run-pass/mir_trans_calls.rs
+++ b/src/test/run-pass/mir_trans_calls.rs
@@ -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);
 }