@@ -22,6 +22,7 @@ use rustc::ty::{self, Instance, Ty, TyCtxt, TypeFoldable};
22
22
use rustc:: ty:: subst:: { Subst , Substs } ;
23
23
24
24
use std:: collections:: VecDeque ;
25
+ use std:: iter;
25
26
use transform:: { MirPass , MirSource } ;
26
27
use super :: simplify:: { remove_dead_blocks, CfgSimplifier } ;
27
28
@@ -558,8 +559,29 @@ impl<'a, 'tcx> Inliner<'a, 'tcx> {
558
559
) -> Vec < Operand < ' tcx > > {
559
560
let tcx = self . tcx ;
560
561
561
- // A closure is passed its self-type and a tuple like `(arg1, arg2, ...)`,
562
- // hence mappings to tuple fields are needed.
562
+ // There is a bit of a mismatch between the *caller* of a closure and the *callee*.
563
+ // The caller provides the arguments wrapped up in a tuple:
564
+ //
565
+ // tuple_tmp = (a, b, c)
566
+ // Fn::call(closure_ref, tuple_tmp)
567
+ //
568
+ // meanwhile the closure body expects the arguments (here, `a`, `b`, and `c`)
569
+ // as distinct arguments. (This is the "rust-call" ABI hack.) Normally, trans has
570
+ // the job of unpacking this tuple. But here, we are trans. =) So we want to create
571
+ // a vector like
572
+ //
573
+ // [closure_ref, tuple_tmp.0, tuple_tmp.1, tuple_tmp.2]
574
+ //
575
+ // Except for one tiny wrinkle: we don't actually want `tuple_tmp.0`. It's more convenient
576
+ // if we "spill" that into *another* temporary, so that we can map the argument
577
+ // variable in the callee MIR directly to an argument variable on our side.
578
+ // So we introduce temporaries like:
579
+ //
580
+ // tmp0 = tuple_tmp.0
581
+ // tmp1 = tuple_tmp.1
582
+ // tmp2 = tuple_tmp.2
583
+ //
584
+ // and the vector is `[closure_ref, tmp0, tmp1, tmp2]`.
563
585
if tcx. is_closure ( callsite. callee ) {
564
586
let mut args = args. into_iter ( ) ;
565
587
let self_ = self . create_temp_if_necessary ( args. next ( ) . unwrap ( ) , callsite, caller_mir) ;
@@ -572,12 +594,21 @@ impl<'a, 'tcx> Inliner<'a, 'tcx> {
572
594
bug ! ( "Closure arguments are not passed as a tuple" ) ;
573
595
} ;
574
596
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
597
+ // The `closure_ref` in our example above.
598
+ let closure_ref_arg = iter:: once ( Operand :: Consume ( self_) ) ;
599
+
600
+ // The `tmp0`, `tmp1`, and `tmp2` in our example abonve.
601
+ let tuple_tmp_args =
602
+ tuple_tys. iter ( ) . enumerate ( ) . map ( |( i, ty) | {
603
+ // This is e.g. `tuple_tmp.0` in our example above.
604
+ let tuple_field = Operand :: Consume ( tuple. clone ( ) . field ( Field :: new ( i) , ty) ) ;
605
+
606
+ // Spill to a local to make e.g. `tmp0`.
607
+ let tmp = self . create_temp_if_necessary ( tuple_field, callsite, caller_mir) ;
608
+ Operand :: Consume ( tmp)
609
+ } ) ;
610
+
611
+ closure_ref_arg. chain ( tuple_tmp_args) . collect ( )
581
612
} else {
582
613
args. into_iter ( )
583
614
. map ( |a| Operand :: Consume ( self . create_temp_if_necessary ( a, callsite, caller_mir) ) )
0 commit comments