@@ -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
@@ -1226,3 +1241,35 @@ pub fn expand_preparsed_format_args(
12261241
12271242 cx. into_expr ( )
12281243}
1244+
1245+ fn may_contain_yield_point ( e : & ast:: Expr ) -> bool {
1246+ struct MayContainYieldPoint ( bool ) ;
1247+
1248+ impl Visitor < ' _ > for MayContainYieldPoint {
1249+ fn visit_expr ( & mut self , e : & ast:: Expr ) {
1250+ if let ast:: ExprKind :: Await ( _) | ast:: ExprKind :: Yield ( _) = e. kind {
1251+ self . 0 = true ;
1252+ } else {
1253+ visit:: walk_expr ( self , e) ;
1254+ }
1255+ }
1256+
1257+ fn visit_mac_call ( & mut self , _: & ast:: MacCall ) {
1258+ self . 0 = true ;
1259+ }
1260+
1261+ fn visit_attribute ( & mut self , _: & ast:: Attribute ) {
1262+ // Conservatively assume this may be a proc macro attribute in
1263+ // expression position.
1264+ self . 0 = true ;
1265+ }
1266+
1267+ fn visit_item ( & mut self , _: & ast:: Item ) {
1268+ // Do not recurse into nested items.
1269+ }
1270+ }
1271+
1272+ let mut visitor = MayContainYieldPoint ( false ) ;
1273+ visitor. visit_expr ( e) ;
1274+ visitor. 0
1275+ }
0 commit comments