@@ -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