Skip to content

Commit cc36f88

Browse files
committed
Handle closures correctly in MIR inlining
1 parent b5a3ab2 commit cc36f88

File tree

2 files changed

+109
-10
lines changed

2 files changed

+109
-10
lines changed

src/librustc_mir/transform/inline.rs

Lines changed: 56 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ use rustc::mir::transform::{MirPass, MirSource};
2020
use rustc::mir::visit::*;
2121
use rustc::ty::{self, Instance, Ty, TyCtxt, TypeFoldable};
2222
use rustc::ty::subst::{Subst,Substs};
23+
use rustc::hir::map::definitions::DefPathData;
2324

2425
use std::collections::VecDeque;
2526
use super::simplify::{remove_dead_blocks, CfgSimplifier};
@@ -550,22 +551,31 @@ impl<'a, 'tcx> Inliner<'a, 'tcx> {
550551
Operand::Consume(cast_tmp)
551552
}
552553

553-
fn make_call_args(&self, args: Vec<Operand<'tcx>>,
554-
callsite: &CallSite<'tcx>, caller_mir: &mut Mir<'tcx>) -> Vec<Operand<'tcx>> {
555-
let tcx = self.tcx;
554+
fn make_call_args(
555+
&self,
556+
args: Vec<Operand<'tcx>>,
557+
callsite: &CallSite<'tcx>,
558+
caller_mir: &mut Mir<'tcx>,
559+
) -> Vec<Operand<'tcx>> {
556560
// FIXME: Analysis of the usage of the arguments to avoid
557561
// unnecessary temporaries.
558-
args.into_iter().map(|a| {
559-
if let Operand::Consume(Lvalue::Local(local)) = a {
562+
563+
fn create_temp_if_necessary<'a, 'tcx: 'a>(
564+
arg: Operand<'tcx>,
565+
tcx: TyCtxt<'a, 'tcx, 'tcx>,
566+
callsite: &CallSite<'tcx>,
567+
caller_mir: &mut Mir<'tcx>,
568+
) -> Operand<'tcx> {
569+
if let Operand::Consume(Lvalue::Local(local)) = arg {
560570
if caller_mir.local_kind(local) == LocalKind::Temp {
561571
// Reuse the operand if it's a temporary already
562-
return a;
572+
return arg;
563573
}
564574
}
565575

566-
debug!("Creating temp for argument");
576+
debug!("Creating temp for argument {:?}", arg);
567577
// Otherwise, create a temporary for the arg
568-
let arg = Rvalue::Use(a);
578+
let arg = Rvalue::Use(arg);
569579

570580
let ty = arg.ty(caller_mir, tcx);
571581

@@ -575,11 +585,47 @@ impl<'a, 'tcx> Inliner<'a, 'tcx> {
575585

576586
let stmt = Statement {
577587
source_info: callsite.location,
578-
kind: StatementKind::Assign(arg_tmp.clone(), arg)
588+
kind: StatementKind::Assign(arg_tmp.clone(), arg),
579589
};
580590
caller_mir[callsite.bb].statements.push(stmt);
581591
Operand::Consume(arg_tmp)
582-
}).collect()
592+
}
593+
594+
let tcx = self.tcx;
595+
596+
// A closure is passed its self-type and a tuple like `(arg1, arg2, ...)`,
597+
// hence mappings to tuple fields are needed.
598+
if tcx.def_key(callsite.callee).disambiguated_data.data == DefPathData::ClosureExpr {
599+
let mut args = args.into_iter();
600+
601+
let self_ = create_temp_if_necessary(args.next().unwrap(), tcx, callsite, caller_mir);
602+
603+
let tuple = if let Operand::Consume(lvalue) =
604+
create_temp_if_necessary(args.next().unwrap(), tcx, callsite, caller_mir)
605+
{
606+
lvalue
607+
} else {
608+
unreachable!()
609+
};
610+
assert!(args.next().is_none());
611+
612+
let tuple_tys = if let ty::TyTuple(s, _) = tuple.ty(caller_mir, tcx).to_ty(tcx).sty {
613+
s
614+
} else {
615+
bug!("Closure arguments are not passed as a tuple");
616+
};
617+
618+
let mut res = Vec::with_capacity(1 + tuple_tys.len());
619+
res.push(self_);
620+
res.extend(tuple_tys.iter().enumerate().map(|(i, ty)| {
621+
Operand::Consume(tuple.clone().field(Field::new(i), ty))
622+
}));
623+
res
624+
} else {
625+
args.into_iter()
626+
.map(|a| create_temp_if_necessary(a, tcx, callsite, caller_mir))
627+
.collect()
628+
}
583629
}
584630
}
585631

src/test/mir-opt/inline-closure.rs

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// compile-flags: -Z span_free_formats
12+
13+
// Tests that MIR inliner can handle closure arguments. (#45894)
14+
15+
fn main() {
16+
println!("{}", foo(0, 14));
17+
}
18+
19+
fn foo<T: Copy>(_t: T, q: i32) -> i32 {
20+
let x = |_t, _q| _t;
21+
x(q*2, q*3)
22+
}
23+
24+
// END RUST SOURCE
25+
// START rustc.foo.Inline.after.mir
26+
// ...
27+
// bb0: {
28+
// StorageLive(_3);
29+
// _3 = [closure@NodeId(28)];
30+
// StorageLive(_4);
31+
// _4 = &_3;
32+
// StorageLive(_5);
33+
// StorageLive(_6);
34+
// StorageLive(_7);
35+
// _7 = _2;
36+
// _6 = Mul(_7, const 2i32);
37+
// StorageDead(_7);
38+
// StorageLive(_8);
39+
// StorageLive(_9);
40+
// _9 = _2;
41+
// _8 = Mul(_9, const 3i32);
42+
// StorageDead(_9);
43+
// _5 = (_6, _8);
44+
// _0 = (_5.0: i32);
45+
// StorageDead(_5);
46+
// StorageDead(_8);
47+
// StorageDead(_6);
48+
// StorageDead(_4);
49+
// StorageDead(_3);
50+
// return;
51+
// }
52+
// ...
53+
// END rustc.foo.Inline.after.mir

0 commit comments

Comments
 (0)