@@ -11,6 +11,7 @@ use rustc_expand::base::{self, *};
11
11
use rustc_parse_format as parse;
12
12
use rustc_span:: symbol:: { sym, Ident , Symbol } ;
13
13
use rustc_span:: { MultiSpan , Span } ;
14
+ use smallvec:: SmallVec ;
14
15
15
16
use std:: borrow:: Cow ;
16
17
use std:: collections:: hash_map:: Entry ;
@@ -744,78 +745,95 @@ impl<'a, 'b> Context<'a, 'b> {
744
745
/// Actually builds the expression which the format_args! block will be
745
746
/// expanded to.
746
747
fn into_expr ( self ) -> P < ast:: Expr > {
747
- let mut args = Vec :: with_capacity (
748
+ let mut original_args = self . args ;
749
+ let mut fmt_args = Vec :: with_capacity (
748
750
self . arg_unique_types . iter ( ) . map ( |v| v. len ( ) ) . sum :: < usize > ( ) + self . count_args . len ( ) ,
749
751
) ;
750
- let mut heads = Vec :: with_capacity ( self . args . len ( ) ) ;
751
752
752
753
// First, build up the static array which will become our precompiled
753
754
// format "string"
754
755
let pieces = self . ecx . expr_vec_slice ( self . fmtsp , self . str_pieces ) ;
755
756
756
- // Before consuming the expressions, we have to remember spans for
757
- // count arguments as they are now generated separate from other
758
- // arguments, hence have no access to the `P<ast::Expr>`'s.
759
- let spans_pos: Vec < _ > = self . args . iter ( ) . map ( |e| e. span ) . collect ( ) ;
760
-
761
- // Right now there is a bug such that for the expression:
762
- // foo(bar(&1))
763
- // the lifetime of `1` doesn't outlast the call to `bar`, so it's not
764
- // valid for the call to `foo`. To work around this all arguments to the
765
- // format! string are shoved into locals. Furthermore, we shove the address
766
- // of each variable because we don't want to move out of the arguments
767
- // passed to this function.
768
- for ( i, e) in self . args . into_iter ( ) . enumerate ( ) {
769
- for arg_ty in self . arg_unique_types [ i] . iter ( ) {
770
- args. push ( Context :: format_arg ( self . ecx , self . macsp , e. span , arg_ty, i) ) ;
771
- }
772
- // use the arg span for `&arg` so that borrowck errors
773
- // point to the specific expression passed to the macro
774
- // (the span is otherwise unavailable in MIR)
775
- heads. push ( self . ecx . expr_addr_of ( e. span . with_ctxt ( self . macsp . ctxt ( ) ) , e) ) ;
776
- }
777
- for index in self . count_args {
778
- let span = spans_pos[ index] ;
779
- args. push ( Context :: format_arg ( self . ecx , self . macsp , span, & Count , index) ) ;
757
+ // We need to construct a &[ArgumentV1] to pass into the fmt::Arguments
758
+ // constructor. In general the expressions in this slice might be
759
+ // permuted from their order in original_args (such as in the case of
760
+ // "{1} {0}"), or may have multiple entries referring to the same
761
+ // element of original_args ("{0} {0}").
762
+ //
763
+ // The following vector has one item per element of our output slice,
764
+ // identifying the index of which element of original_args it's passing,
765
+ // and that argument's type.
766
+ let mut fmt_arg_index_and_ty = SmallVec :: < [ ( usize , & ArgumentType ) ; 8 ] > :: new ( ) ;
767
+ for ( i, unique_types) in self . arg_unique_types . iter ( ) . enumerate ( ) {
768
+ fmt_arg_index_and_ty. extend ( unique_types. iter ( ) . map ( |ty| ( i, ty) ) ) ;
780
769
}
770
+ fmt_arg_index_and_ty. extend ( self . count_args . iter ( ) . map ( |& i| ( i, & Count ) ) ) ;
781
771
782
- let args_array = self . ecx . expr_vec ( self . macsp , args) ;
783
-
784
- // Constructs an AST equivalent to:
785
- //
786
- // match (&arg0, &arg1) {
787
- // (tmp0, tmp1) => args_array
788
- // }
772
+ // Figure out whether there are permuted or repeated elements. If not,
773
+ // we can generate simpler code.
789
774
//
790
- // It was:
775
+ // The sequence has no indices out of order or repeated if: for every
776
+ // adjacent pair of elements, the first one's index is less than the
777
+ // second one's index.
778
+ let nicely_ordered =
779
+ fmt_arg_index_and_ty. array_windows ( ) . all ( |[ ( i, _i_ty) , ( j, _j_ty) ] | i < j) ;
780
+
781
+ // We want to emit:
791
782
//
792
- // let tmp0 = &arg0;
793
- // let tmp1 = &arg1;
794
- // args_array
783
+ // [ArgumentV1::new(&$arg0, …), ArgumentV1::new(&$arg1, …), …]
795
784
//
796
- // Because of #11585 the new temporary lifetime rule, the enclosing
797
- // statements for these temporaries become the let's themselves.
798
- // If one or more of them are RefCell's, RefCell borrow() will also
799
- // end there; they don't last long enough for args_array to use them.
800
- // The match expression solves the scope problem .
785
+ // However, it's only legal to do so if $arg0, $arg1, … were written in
786
+ // exactly that order by the programmer. When arguments are permuted, we
787
+ // want them evaluated in the order written by the programmer, not in
788
+ // the order provided to fmt::Arguments. When arguments are repeated, we
789
+ // want the expression evaluated only once .
801
790
//
802
- // Note, it may also very well be transformed to :
791
+ // Thus in the not nicely ordered case we emit the following instead :
803
792
//
804
- // match arg0 {
805
- // ref tmp0 => {
806
- // match arg1 => {
807
- // ref tmp1 => args_array } } }
793
+ // match (&$arg0, &$arg1, …) {
794
+ // _args => [ArgumentV1::new(_args.$i, …), ArgumentV1::new(_args.$j, …), …]
795
+ // }
808
796
//
809
- // But the nested match expression is proved to perform not as well
810
- // as series of let's; the first approach does.
811
- let args_match = {
812
- let pat = self . ecx . pat_ident ( self . macsp , Ident :: new ( sym:: _args, self . macsp ) ) ;
813
- let arm = self . ecx . arm ( self . macsp , pat, args_array) ;
814
- let head = self . ecx . expr ( self . macsp , ast:: ExprKind :: Tup ( heads) ) ;
815
- self . ecx . expr_match ( self . macsp , head, vec ! [ arm] )
816
- } ;
797
+ // for the sequence of indices $i, $j, … governed by fmt_arg_index_and_ty.
798
+ for ( arg_index, arg_ty) in fmt_arg_index_and_ty {
799
+ let e = & mut original_args[ arg_index] ;
800
+ let span = e. span ;
801
+ let arg = if nicely_ordered {
802
+ let expansion_span = e. span . with_ctxt ( self . macsp . ctxt ( ) ) ;
803
+ // The indices are strictly ordered so e has not been taken yet.
804
+ self . ecx . expr_addr_of ( expansion_span, P ( e. take ( ) ) )
805
+ } else {
806
+ let def_site = self . ecx . with_def_site_ctxt ( span) ;
807
+ let args_tuple = self . ecx . expr_ident ( def_site, Ident :: new ( sym:: _args, def_site) ) ;
808
+ let member = Ident :: new ( sym:: integer ( arg_index) , def_site) ;
809
+ self . ecx . expr ( def_site, ast:: ExprKind :: Field ( args_tuple, member) )
810
+ } ;
811
+ fmt_args. push ( Context :: format_arg ( self . ecx , self . macsp , span, arg_ty, arg) ) ;
812
+ }
817
813
818
- let args_slice = self . ecx . expr_addr_of ( self . macsp , args_match) ;
814
+ let args_array = self . ecx . expr_vec ( self . macsp , fmt_args) ;
815
+ let args_slice = self . ecx . expr_addr_of (
816
+ self . macsp ,
817
+ if nicely_ordered {
818
+ args_array
819
+ } else {
820
+ // In the !nicely_ordered case, none of the exprs were moved
821
+ // away in the previous loop.
822
+ //
823
+ // This uses the arg span for `&arg` so that borrowck errors
824
+ // point to the specific expression passed to the macro (the
825
+ // span is otherwise unavailable in the MIR used by borrowck).
826
+ let heads = original_args
827
+ . into_iter ( )
828
+ . map ( |e| self . ecx . expr_addr_of ( e. span . with_ctxt ( self . macsp . ctxt ( ) ) , e) )
829
+ . collect ( ) ;
830
+
831
+ let pat = self . ecx . pat_ident ( self . macsp , Ident :: new ( sym:: _args, self . macsp ) ) ;
832
+ let arm = self . ecx . arm ( self . macsp , pat, args_array) ;
833
+ let head = self . ecx . expr ( self . macsp , ast:: ExprKind :: Tup ( heads) ) ;
834
+ self . ecx . expr_match ( self . macsp , head, vec ! [ arm] )
835
+ } ,
836
+ ) ;
819
837
820
838
// Now create the fmt::Arguments struct with all our locals we created.
821
839
let ( fn_name, fn_args) = if self . all_pieces_simple {
@@ -848,11 +866,9 @@ impl<'a, 'b> Context<'a, 'b> {
848
866
macsp : Span ,
849
867
mut sp : Span ,
850
868
ty : & ArgumentType ,
851
- arg_index : usize ,
869
+ arg : P < ast :: Expr > ,
852
870
) -> P < ast:: Expr > {
853
871
sp = ecx. with_def_site_ctxt ( sp) ;
854
- let arg = ecx. expr_ident ( sp, Ident :: new ( sym:: _args, sp) ) ;
855
- let arg = ecx. expr ( sp, ast:: ExprKind :: Field ( arg, Ident :: new ( sym:: integer ( arg_index) , sp) ) ) ;
856
872
let trait_ = match * ty {
857
873
Placeholder ( trait_) if trait_ == "<invalid>" => return DummyResult :: raw_expr ( sp, true ) ,
858
874
Placeholder ( trait_) => trait_,
0 commit comments