@@ -13,6 +13,7 @@ use rustc_span::symbol::{sym, Ident, Symbol};
13
13
use rustc_span:: { MultiSpan , Span } ;
14
14
15
15
use std:: borrow:: Cow ;
16
+ use std:: cmp:: Ordering ;
16
17
use std:: collections:: hash_map:: Entry ;
17
18
18
19
#[ derive( PartialEq ) ]
@@ -744,78 +745,93 @@ 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) ) ;
780
- }
781
-
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
- // }
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}").
789
762
//
790
- // It was:
763
+ // The following Iterator<Item = (usize, &ArgumentType)> has one item
764
+ // per element of our output slice, identifying the index of which
765
+ // element of original_args it's passing, and that argument's type.
766
+ let fmt_arg_index_and_ty = self
767
+ . arg_unique_types
768
+ . iter ( )
769
+ . enumerate ( )
770
+ . flat_map ( |( i, unique_types) | unique_types. iter ( ) . map ( move |ty| ( i, ty) ) )
771
+ . chain ( self . count_args . iter ( ) . map ( |i| ( * i, & Count ) ) ) ;
772
+
773
+ // Figure out whether there are permuted or repeated elements. If not,
774
+ // we can generate simpler code.
775
+ let nicely_ordered = fmt_arg_index_and_ty
776
+ . clone ( )
777
+ . is_sorted_by ( |( i, _) , ( j, _) | ( i < j) . then_some ( Ordering :: Less ) ) ;
778
+
779
+ // We want to emit:
791
780
//
792
- // let tmp0 = &arg0;
793
- // let tmp1 = &arg1;
794
- // args_array
781
+ // [ArgumentV1::new(&$arg0, …), ArgumentV1::new(&$arg1, …), …]
795
782
//
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 .
783
+ // However, it's only legal to do so if $arg0, $arg1, … were written in
784
+ // exactly that order by the programmer. When arguments are permuted, we
785
+ // want them evaluated in the order written by the programmer, not in
786
+ // the order provided to fmt::Arguments. When arguments are repeated, we
787
+ // want the expression evaluated only once .
801
788
//
802
- // Note, it may also very well be transformed to :
789
+ // Thus in the not nicely ordered case we emit the following instead :
803
790
//
804
- // match arg0 {
805
- // ref tmp0 => {
806
- // match arg1 => {
807
- // ref tmp1 => args_array } } }
791
+ // match (&$arg0, &$arg1, …) {
792
+ // _args => [ArgumentV1::new(_args.$i, …), ArgumentV1::new(_args.$j, …), …]
793
+ // }
808
794
//
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
- } ;
795
+ // for the sequence of indices $i, $j, … governed by fmt_arg_index_and_ty.
796
+ for ( arg_index, arg_ty) in fmt_arg_index_and_ty {
797
+ let e = & mut original_args[ arg_index] ;
798
+ let span = e. span ;
799
+ let arg = if nicely_ordered {
800
+ let expansion_span = e. span . with_ctxt ( self . macsp . ctxt ( ) ) ;
801
+ // The indices are strictly ordered so e has not been taken yet.
802
+ self . ecx . expr_addr_of ( expansion_span, P ( e. take ( ) ) )
803
+ } else {
804
+ let def_site = self . ecx . with_def_site_ctxt ( span) ;
805
+ let args_tuple = self . ecx . expr_ident ( def_site, Ident :: new ( sym:: _args, def_site) ) ;
806
+ let member = Ident :: new ( sym:: integer ( arg_index) , def_site) ;
807
+ self . ecx . expr ( def_site, ast:: ExprKind :: Field ( args_tuple, member) )
808
+ } ;
809
+ fmt_args. push ( Context :: format_arg ( self . ecx , self . macsp , span, arg_ty, arg) ) ;
810
+ }
817
811
818
- let args_slice = self . ecx . expr_addr_of ( self . macsp , args_match) ;
812
+ let args_array = self . ecx . expr_vec ( self . macsp , fmt_args) ;
813
+ let args_slice = self . ecx . expr_addr_of (
814
+ self . macsp ,
815
+ if nicely_ordered {
816
+ args_array
817
+ } else {
818
+ // In the !nicely_ordered case, none of the exprs were moved
819
+ // away in the previous loop.
820
+ //
821
+ // This uses the arg span for `&arg` so that borrowck errors
822
+ // point to the specific expression passed to the macro (the
823
+ // span is otherwise unavailable in MIR).
824
+ let heads = original_args
825
+ . into_iter ( )
826
+ . map ( |e| self . ecx . expr_addr_of ( e. span . with_ctxt ( self . macsp . ctxt ( ) ) , e) )
827
+ . collect ( ) ;
828
+
829
+ let pat = self . ecx . pat_ident ( self . macsp , Ident :: new ( sym:: _args, self . macsp ) ) ;
830
+ let arm = self . ecx . arm ( self . macsp , pat, args_array) ;
831
+ let head = self . ecx . expr ( self . macsp , ast:: ExprKind :: Tup ( heads) ) ;
832
+ self . ecx . expr_match ( self . macsp , head, vec ! [ arm] )
833
+ } ,
834
+ ) ;
819
835
820
836
// Now create the fmt::Arguments struct with all our locals we created.
821
837
let ( fn_name, fn_args) = if self . all_pieces_simple {
@@ -848,11 +864,9 @@ impl<'a, 'b> Context<'a, 'b> {
848
864
macsp : Span ,
849
865
mut sp : Span ,
850
866
ty : & ArgumentType ,
851
- arg_index : usize ,
867
+ arg : P < ast :: Expr > ,
852
868
) -> P < ast:: Expr > {
853
869
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
870
let trait_ = match * ty {
857
871
Placeholder ( trait_) if trait_ == "<invalid>" => return DummyResult :: raw_expr ( sp, true ) ,
858
872
Placeholder ( trait_) => trait_,
0 commit comments