Skip to content

Commit 8a98531

Browse files
committed
Auto merge of #45913 - sinkuu:mir-inlining-closure, r=arielb1
Handle closures correctly in MIR inlining Fixes #45894.
2 parents ff0f5de + a1f7bad commit 8a98531

File tree

4 files changed

+113
-29
lines changed

4 files changed

+113
-29
lines changed

src/librustc/ty/util.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -621,9 +621,13 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
621621
result
622622
}
623623

624+
pub fn is_closure(self, def_id: DefId) -> bool {
625+
self.def_key(def_id).disambiguated_data.data == DefPathData::ClosureExpr
626+
}
627+
624628
pub fn closure_base_def_id(self, def_id: DefId) -> DefId {
625629
let mut def_id = def_id;
626-
while self.def_key(def_id).disambiguated_data.data == DefPathData::ClosureExpr {
630+
while self.is_closure(def_id) {
627631
def_id = self.parent_def_id(def_id).unwrap_or_else(|| {
628632
bug!("closure {:?} has no parent", def_id);
629633
});

src/librustc_mir/transform/check_unsafety.rs

+5-6
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ use rustc::ty::maps::Providers;
1515
use rustc::ty::{self, TyCtxt};
1616
use rustc::hir;
1717
use rustc::hir::def_id::DefId;
18-
use rustc::hir::map::DefPathData;
1918
use rustc::lint::builtin::{SAFE_EXTERN_STATICS, UNUSED_UNSAFE};
2019
use rustc::mir::*;
2120
use rustc::mir::visit::{LvalueContext, Visitor};
@@ -362,11 +361,11 @@ fn report_unused_unsafe(tcx: TyCtxt, used_unsafe: &FxHashSet<ast::NodeId>, id: a
362361

363362
pub fn check_unsafety<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) {
364363
debug!("check_unsafety({:?})", def_id);
365-
match tcx.def_key(def_id).disambiguated_data.data {
366-
// closures are handled by their parent fn.
367-
DefPathData::ClosureExpr => return,
368-
_ => {}
369-
};
364+
365+
// closures are handled by their parent fn.
366+
if tcx.is_closure(def_id) {
367+
return;
368+
}
370369

371370
let UnsafetyCheckResult {
372371
violations,

src/librustc_mir/transform/inline.rs

+61-22
Original file line numberDiff line numberDiff line change
@@ -550,36 +550,75 @@ impl<'a, 'tcx> Inliner<'a, 'tcx> {
550550
Operand::Consume(cast_tmp)
551551
}
552552

553-
fn make_call_args(&self, args: Vec<Operand<'tcx>>,
554-
callsite: &CallSite<'tcx>, caller_mir: &mut Mir<'tcx>) -> Vec<Operand<'tcx>> {
553+
fn make_call_args(
554+
&self,
555+
args: Vec<Operand<'tcx>>,
556+
callsite: &CallSite<'tcx>,
557+
caller_mir: &mut Mir<'tcx>,
558+
) -> Vec<Operand<'tcx>> {
555559
let tcx = self.tcx;
560+
561+
// A closure is passed its self-type and a tuple like `(arg1, arg2, ...)`,
562+
// hence mappings to tuple fields are needed.
563+
if tcx.is_closure(callsite.callee) {
564+
let mut args = args.into_iter();
565+
let self_ = self.create_temp_if_necessary(args.next().unwrap(), callsite, caller_mir);
566+
let tuple = self.create_temp_if_necessary(args.next().unwrap(), callsite, caller_mir);
567+
assert!(args.next().is_none());
568+
569+
let tuple_tys = if let ty::TyTuple(s, _) = tuple.ty(caller_mir, tcx).to_ty(tcx).sty {
570+
s
571+
} else {
572+
bug!("Closure arguments are not passed as a tuple");
573+
};
574+
575+
let mut res = Vec::with_capacity(1 + tuple_tys.len());
576+
res.push(Operand::Consume(self_));
577+
res.extend(tuple_tys.iter().enumerate().map(|(i, ty)| {
578+
Operand::Consume(tuple.clone().field(Field::new(i), ty))
579+
}));
580+
res
581+
} else {
582+
args.into_iter()
583+
.map(|a| Operand::Consume(self.create_temp_if_necessary(a, callsite, caller_mir)))
584+
.collect()
585+
}
586+
}
587+
588+
/// If `arg` is already a temporary, returns it. Otherwise, introduces a fresh
589+
/// temporary `T` and an instruction `T = arg`, and returns `T`.
590+
fn create_temp_if_necessary(
591+
&self,
592+
arg: Operand<'tcx>,
593+
callsite: &CallSite<'tcx>,
594+
caller_mir: &mut Mir<'tcx>,
595+
) -> Lvalue<'tcx> {
556596
// FIXME: Analysis of the usage of the arguments to avoid
557597
// unnecessary temporaries.
558-
args.into_iter().map(|a| {
559-
if let Operand::Consume(Lvalue::Local(local)) = a {
560-
if caller_mir.local_kind(local) == LocalKind::Temp {
561-
// Reuse the operand if it's a temporary already
562-
return a;
563-
}
598+
599+
if let Operand::Consume(Lvalue::Local(local)) = arg {
600+
if caller_mir.local_kind(local) == LocalKind::Temp {
601+
// Reuse the operand if it's a temporary already
602+
return Lvalue::Local(local);
564603
}
604+
}
565605

566-
debug!("Creating temp for argument");
567-
// Otherwise, create a temporary for the arg
568-
let arg = Rvalue::Use(a);
606+
debug!("Creating temp for argument {:?}", arg);
607+
// Otherwise, create a temporary for the arg
608+
let arg = Rvalue::Use(arg);
569609

570-
let ty = arg.ty(caller_mir, tcx);
610+
let ty = arg.ty(caller_mir, self.tcx);
571611

572-
let arg_tmp = LocalDecl::new_temp(ty, callsite.location.span);
573-
let arg_tmp = caller_mir.local_decls.push(arg_tmp);
574-
let arg_tmp = Lvalue::Local(arg_tmp);
612+
let arg_tmp = LocalDecl::new_temp(ty, callsite.location.span);
613+
let arg_tmp = caller_mir.local_decls.push(arg_tmp);
614+
let arg_tmp = Lvalue::Local(arg_tmp);
575615

576-
let stmt = Statement {
577-
source_info: callsite.location,
578-
kind: StatementKind::Assign(arg_tmp.clone(), arg)
579-
};
580-
caller_mir[callsite.bb].statements.push(stmt);
581-
Operand::Consume(arg_tmp)
582-
}).collect()
616+
let stmt = Statement {
617+
source_info: callsite.location,
618+
kind: StatementKind::Assign(arg_tmp.clone(), arg),
619+
};
620+
caller_mir[callsite.bb].statements.push(stmt);
621+
arg_tmp
583622
}
584623
}
585624

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

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
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, q)
22+
}
23+
24+
// END RUST SOURCE
25+
// START rustc.foo.Inline.after.mir
26+
// ...
27+
// bb0: {
28+
// ...
29+
// _3 = [closure@NodeId(28)];
30+
// ...
31+
// _4 = &_3;
32+
// ...
33+
// _6 = _2;
34+
// ...
35+
// _7 = _2;
36+
// _5 = (_6, _7);
37+
// _0 = (_5.0: i32);
38+
// ...
39+
// return;
40+
// }
41+
// ...
42+
// END rustc.foo.Inline.after.mir

0 commit comments

Comments
 (0)