Skip to content

Commit 47f92a5

Browse files
committed
Accommodate yield points in the format_args expansion
1 parent 1dd02e3 commit 47f92a5

File tree

1 file changed

+49
-4
lines changed

1 file changed

+49
-4
lines changed

compiler/rustc_builtin_macros/src/format.rs

+49-4
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use Position::*;
44
use rustc_ast as ast;
55
use rustc_ast::ptr::P;
66
use rustc_ast::tokenstream::TokenStream;
7+
use rustc_ast::visit::{self, Visitor};
78
use rustc_ast::{token, BlockCheckMode, UnsafeSource};
89
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
910
use 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

Comments
 (0)