Accommodate yield points in the format_args expansion
This commit is contained in:
parent
1dd02e32a3
commit
47f92a58a4
1 changed files with 49 additions and 4 deletions
|
@ -4,6 +4,7 @@ use Position::*;
|
||||||
use rustc_ast as ast;
|
use rustc_ast as ast;
|
||||||
use rustc_ast::ptr::P;
|
use rustc_ast::ptr::P;
|
||||||
use rustc_ast::tokenstream::TokenStream;
|
use rustc_ast::tokenstream::TokenStream;
|
||||||
|
use rustc_ast::visit::{self, Visitor};
|
||||||
use rustc_ast::{token, BlockCheckMode, UnsafeSource};
|
use rustc_ast::{token, BlockCheckMode, UnsafeSource};
|
||||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
||||||
use rustc_errors::{pluralize, Applicability, DiagnosticBuilder};
|
use rustc_errors::{pluralize, Applicability, DiagnosticBuilder};
|
||||||
|
@ -788,17 +789,31 @@ impl<'a, 'b> Context<'a, 'b> {
|
||||||
// the order provided to fmt::Arguments. When arguments are repeated, we
|
// the order provided to fmt::Arguments. When arguments are repeated, we
|
||||||
// want the expression evaluated only once.
|
// 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, …) {
|
// match (&$arg0, &$arg1, …) {
|
||||||
// args => [ArgumentV1::new(args.$i, …), ArgumentV1::new(args.$j, …), …]
|
// args => [ArgumentV1::new(args.$i, …), ArgumentV1::new(args.$j, …), …]
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
// for the sequence of indices $i, $j, … governed by fmt_arg_index_and_ty.
|
// 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 {
|
for (arg_index, arg_ty) in fmt_arg_index_and_ty {
|
||||||
let e = &mut original_args[arg_index];
|
let e = &mut original_args[arg_index];
|
||||||
let span = e.span;
|
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());
|
let expansion_span = e.span.with_ctxt(self.macsp.ctxt());
|
||||||
// The indices are strictly ordered so e has not been taken yet.
|
// The indices are strictly ordered so e has not been taken yet.
|
||||||
self.ecx.expr_addr_of(expansion_span, P(e.take()))
|
self.ecx.expr_addr_of(expansion_span, P(e.take()))
|
||||||
|
@ -814,10 +829,10 @@ impl<'a, 'b> Context<'a, 'b> {
|
||||||
let args_array = self.ecx.expr_vec(self.macsp, fmt_args);
|
let args_array = self.ecx.expr_vec(self.macsp, fmt_args);
|
||||||
let args_slice = self.ecx.expr_addr_of(
|
let args_slice = self.ecx.expr_addr_of(
|
||||||
self.macsp,
|
self.macsp,
|
||||||
if nicely_ordered {
|
if no_need_for_match {
|
||||||
args_array
|
args_array
|
||||||
} else {
|
} 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.
|
// away in the previous loop.
|
||||||
//
|
//
|
||||||
// This uses the arg span for `&arg` so that borrowck errors
|
// This uses the arg span for `&arg` so that borrowck errors
|
||||||
|
@ -1216,3 +1231,33 @@ pub fn expand_preparsed_format_args(
|
||||||
|
|
||||||
cx.into_expr()
|
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(_) | ast::ExprKind::MacCall(_) =
|
||||||
|
e.kind
|
||||||
|
{
|
||||||
|
self.0 = true;
|
||||||
|
} else {
|
||||||
|
visit::walk_expr(self, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue