@@ -4,6 +4,7 @@ use Position::*;
44use rustc_ast as ast;
55use rustc_ast:: ptr:: P ;
66use rustc_ast:: tokenstream:: TokenStream ;
7+ use rustc_ast:: visit:: { self , Visitor } ;
78use rustc_ast:: { token, BlockCheckMode , UnsafeSource } ;
89use rustc_data_structures:: fx:: { FxHashMap , FxHashSet } ;
910use rustc_errors:: { pluralize, Applicability , DiagnosticBuilder } ;
@@ -788,17 +789,31 @@ impl<'a, 'b> Context<'a, 'b> {
788789 // the order provided to fmt::Arguments. When arguments are repeated, we
789790 // want the expression evaluated only once.
790791 //
791- // Thus in the not nicely ordered case we emit the following instead:
792+ // Further, if any arg _after the first one_ contains a yield point such
793+ // as `await` or `yield`, the above short form is inconvenient for the
794+ // caller because it would keep a temporary of type ArgumentV1 alive
795+ // across the yield point. ArgumentV1 can't implement Send since it
796+ // holds a type-erased arbitrary type.
797+ //
798+ // Thus in the not nicely ordered case, and in the yielding case, we
799+ // emit the following instead:
792800 //
793801 // match (&$arg0, &$arg1, …) {
794802 // args => [ArgumentV1::new(args.$i, …), ArgumentV1::new(args.$j, …), …]
795803 // }
796804 //
797805 // for the sequence of indices $i, $j, … governed by fmt_arg_index_and_ty.
806+ // This more verbose representation ensures that all arguments are
807+ // evaluated a single time each, in the order written by the programmer,
808+ // and that the surrounding future/generator (if any) is Send whenever
809+ // possible.
810+ let no_need_for_match =
811+ nicely_ordered && !original_args. iter ( ) . skip ( 1 ) . any ( |e| may_contain_yield_point ( e) ) ;
812+
798813 for ( arg_index, arg_ty) in fmt_arg_index_and_ty {
799814 let e = & mut original_args[ arg_index] ;
800815 let span = e. span ;
801- let arg = if nicely_ordered {
816+ let arg = if no_need_for_match {
802817 let expansion_span = e. span . with_ctxt ( self . macsp . ctxt ( ) ) ;
803818 // The indices are strictly ordered so e has not been taken yet.
804819 self . ecx . expr_addr_of ( expansion_span, P ( e. take ( ) ) )
@@ -814,10 +829,10 @@ impl<'a, 'b> Context<'a, 'b> {
814829 let args_array = self . ecx . expr_vec ( self . macsp , fmt_args) ;
815830 let args_slice = self . ecx . expr_addr_of (
816831 self . macsp ,
817- if nicely_ordered {
832+ if no_need_for_match {
818833 args_array
819834 } else {
820- // In the !nicely_ordered case, none of the exprs were moved
835+ // In the !no_need_for_match case, none of the exprs were moved
821836 // away in the previous loop.
822837 //
823838 // This uses the arg span for `&arg` so that borrowck errors
@@ -1216,3 +1231,33 @@ pub fn expand_preparsed_format_args(
12161231
12171232 cx. into_expr ( )
12181233}
1234+
1235+ fn may_contain_yield_point ( e : & ast:: Expr ) -> bool {
1236+ struct MayContainYieldPoint ( bool ) ;
1237+
1238+ impl Visitor < ' _ > for MayContainYieldPoint {
1239+ fn visit_expr ( & mut self , e : & ast:: Expr ) {
1240+ if let ast:: ExprKind :: Await ( _) | ast:: ExprKind :: Yield ( _) | ast:: ExprKind :: MacCall ( _) =
1241+ e. kind
1242+ {
1243+ self . 0 = true ;
1244+ } else {
1245+ visit:: walk_expr ( self , e) ;
1246+ }
1247+ }
1248+
1249+ fn visit_attribute ( & mut self , _: & ast:: Attribute ) {
1250+ // Conservatively assume this may be a proc macro attribute in
1251+ // expression position.
1252+ self . 0 = true ;
1253+ }
1254+
1255+ fn visit_item ( & mut self , _: & ast:: Item ) {
1256+ // Do not recurse into nested items.
1257+ }
1258+ }
1259+
1260+ let mut visitor = MayContainYieldPoint ( false ) ;
1261+ visitor. visit_expr ( e) ;
1262+ visitor. 0
1263+ }
0 commit comments