未验证 提交 c1e2948c 编写于 作者: M Matthias Krüger 提交者: GitHub

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),
            ],
        },
    ),
);
```
......@@ -4,6 +4,7 @@
use rustc_ast as ast;
use rustc_ast::ptr::P;
use rustc_ast::tokenstream::TokenStream;
use rustc_ast::visit::{self, Visitor};
use rustc_ast::{token, BlockCheckMode, UnsafeSource};
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_errors::{pluralize, Applicability, DiagnosticBuilder};
......@@ -788,17 +789,31 @@ fn into_expr(self) -> P<ast::Expr> {
// the order provided to fmt::Arguments. When arguments are repeated, we
// want the expression evaluated only once.
//
// Thus in the not nicely ordered case we emit the following instead:
// Further, if any arg _after the first one_ contains a yield point such
// as `await` or `yield`, the above short form is inconvenient for the
// caller because it would keep a temporary of type ArgumentV1 alive
// across the yield point. ArgumentV1 can't implement Send since it
// holds a type-erased arbitrary type.
//
// Thus in the not nicely ordered case, and in the yielding case, we
// emit the following instead:
//
// match (&$arg0, &$arg1, …) {
// args => [ArgumentV1::new(args.$i, …), ArgumentV1::new(args.$j, …), …]
// }
//
// for the sequence of indices $i, $j, … governed by fmt_arg_index_and_ty.
// This more verbose representation ensures that all arguments are
// evaluated a single time each, in the order written by the programmer,
// and that the surrounding future/generator (if any) is Send whenever
// possible.
let no_need_for_match =
nicely_ordered && !original_args.iter().skip(1).any(|e| may_contain_yield_point(e));
for (arg_index, arg_ty) in fmt_arg_index_and_ty {
let e = &mut original_args[arg_index];
let span = e.span;
let arg = if nicely_ordered {
let arg = if no_need_for_match {
let expansion_span = e.span.with_ctxt(self.macsp.ctxt());
// The indices are strictly ordered so e has not been taken yet.
self.ecx.expr_addr_of(expansion_span, P(e.take()))
......@@ -814,10 +829,10 @@ fn into_expr(self) -> P<ast::Expr> {
let args_array = self.ecx.expr_vec(self.macsp, fmt_args);
let args_slice = self.ecx.expr_addr_of(
self.macsp,
if nicely_ordered {
if no_need_for_match {
args_array
} else {
// In the !nicely_ordered case, none of the exprs were moved
// In the !no_need_for_match case, none of the exprs were moved
// away in the previous loop.
//
// This uses the arg span for `&arg` so that borrowck errors
......@@ -1226,3 +1241,35 @@ pub fn expand_preparsed_format_args(
cx.into_expr()
}
fn may_contain_yield_point(e: &ast::Expr) -> bool {
struct MayContainYieldPoint(bool);
impl Visitor<'_> for MayContainYieldPoint {
fn visit_expr(&mut self, e: &ast::Expr) {
if let ast::ExprKind::Await(_) | ast::ExprKind::Yield(_) = e.kind {
self.0 = true;
} else {
visit::walk_expr(self, e);
}
}
fn visit_mac_call(&mut self, _: &ast::MacCall) {
self.0 = true;
}
fn visit_attribute(&mut self, _: &ast::Attribute) {
// Conservatively assume this may be a proc macro attribute in
// expression position.
self.0 = true;
}
fn visit_item(&mut self, _: &ast::Item) {
// Do not recurse into nested items.
}
}
let mut visitor = MayContainYieldPoint(false);
visitor.visit_expr(e);
visitor.0
}
// check-pass
// edition:2021
macro_rules! m {
() => {
async {}.await
};
}
async fn with_await() {
println!("{} {:?}", "", async {}.await);
}
async fn with_macro_call_expr() {
println!("{} {:?}", "", m!());
}
async fn with_macro_call_stmt_semi() {
println!("{} {:?}", "", { m!(); });
}
async fn with_macro_call_stmt_braced() {
println!("{} {:?}", "", { m!{} });
}
fn assert_send(_: impl Send) {}
fn main() {
assert_send(with_await());
assert_send(with_macro_call_expr());
assert_send(with_macro_call_stmt_semi());
assert_send(with_macro_call_stmt_braced());
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册