@@ -4,6 +4,7 @@ use Position::*;
4
4
use rustc_ast as ast;
5
5
use rustc_ast:: ptr:: P ;
6
6
use rustc_ast:: tokenstream:: TokenStream ;
7
+ use rustc_ast:: visit:: { self , Visitor } ;
7
8
use rustc_ast:: { token, BlockCheckMode , UnsafeSource } ;
8
9
use rustc_data_structures:: fx:: { FxHashMap , FxHashSet } ;
9
10
use rustc_errors:: { pluralize, Applicability , DiagnosticBuilder } ;
@@ -788,17 +789,31 @@ impl<'a, 'b> Context<'a, 'b> {
788
789
// the order provided to fmt::Arguments. When arguments are repeated, we
789
790
// want the expression evaluated only once.
790
791
//
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:
792
800
//
793
801
// match (&$arg0, &$arg1, …) {
794
802
// args => [ArgumentV1::new(args.$i, …), ArgumentV1::new(args.$j, …), …]
795
803
// }
796
804
//
797
805
// 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
+
798
813
for ( arg_index, arg_ty) in fmt_arg_index_and_ty {
799
814
let e = & mut original_args[ arg_index] ;
800
815
let span = e. span ;
801
- let arg = if nicely_ordered {
816
+ let arg = if no_need_for_match {
802
817
let expansion_span = e. span . with_ctxt ( self . macsp . ctxt ( ) ) ;
803
818
// The indices are strictly ordered so e has not been taken yet.
804
819
self . ecx . expr_addr_of ( expansion_span, P ( e. take ( ) ) )
@@ -814,10 +829,10 @@ impl<'a, 'b> Context<'a, 'b> {
814
829
let args_array = self . ecx . expr_vec ( self . macsp , fmt_args) ;
815
830
let args_slice = self . ecx . expr_addr_of (
816
831
self . macsp ,
817
- if nicely_ordered {
832
+ if no_need_for_match {
818
833
args_array
819
834
} 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
821
836
// away in the previous loop.
822
837
//
823
838
// This uses the arg span for `&arg` so that borrowck errors
@@ -1226,3 +1241,35 @@ pub fn expand_preparsed_format_args(
1226
1241
1227
1242
cx. into_expr ( )
1228
1243
}
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