Skip to content

Commit c1e2948

Browse files
authored
Rollup merge of #93461 - dtolnay:fmtyield, r=davidtwco
Accommodate yield points in the format_args expansion Fixes #93274. For the case `println!("{} {:?}", "", async {}.await)` in the issue, the expansion before: ```rust ::std::io::_print( ::core::fmt::Arguments::new_v1( &["", " ", "\n"], &[ ::core::fmt::ArgumentV1::new(&"", ::core::fmt::Display::fmt), ::core::fmt::ArgumentV1::new(&async {}.await, ::core::fmt::Debug::fmt), ], ), ); ``` After: ```rust ::std::io::_print( ::core::fmt::Arguments::new_v1( &["", " ", "\n"], &match (&"", &async {}.await) { _args => [ ::core::fmt::ArgumentV1::new(_args.0, ::core::fmt::Display::fmt), ::core::fmt::ArgumentV1::new(_args.1, ::core::fmt::Debug::fmt), ], }, ), ); ```
2 parents 8fd2ff5 + 858d6a0 commit c1e2948

File tree

2 files changed

+84
-4
lines changed

2 files changed

+84
-4
lines changed

compiler/rustc_builtin_macros/src/format.rs

+51-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
@@ -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+
}
+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// check-pass
2+
// edition:2021
3+
4+
macro_rules! m {
5+
() => {
6+
async {}.await
7+
};
8+
}
9+
10+
async fn with_await() {
11+
println!("{} {:?}", "", async {}.await);
12+
}
13+
14+
async fn with_macro_call_expr() {
15+
println!("{} {:?}", "", m!());
16+
}
17+
18+
async fn with_macro_call_stmt_semi() {
19+
println!("{} {:?}", "", { m!(); });
20+
}
21+
22+
async fn with_macro_call_stmt_braced() {
23+
println!("{} {:?}", "", { m!{} });
24+
}
25+
26+
fn assert_send(_: impl Send) {}
27+
28+
fn main() {
29+
assert_send(with_await());
30+
assert_send(with_macro_call_expr());
31+
assert_send(with_macro_call_stmt_semi());
32+
assert_send(with_macro_call_stmt_braced());
33+
}

0 commit comments

Comments
 (0)