mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-24 07:44:10 +00:00
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:
commit
7312e0a163
@ -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);
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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(
|
||||
|
@ -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);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user