1
Fork 0

Emit simpler code from format_args

This commit is contained in:
David Tolnay 2021-11-28 10:46:06 -08:00
parent ee5d8d37ba
commit abf1d94b1a
No known key found for this signature in database
GPG key ID: F9BA143B95FF6D82
9 changed files with 112 additions and 92 deletions

View file

@ -39,6 +39,7 @@ use rustc_span::{Span, DUMMY_SP};
use std::cmp::Ordering; use std::cmp::Ordering;
use std::convert::TryFrom; use std::convert::TryFrom;
use std::fmt; use std::fmt;
use std::mem;
#[cfg(test)] #[cfg(test)]
mod tests; mod tests;
@ -1276,6 +1277,19 @@ impl Expr {
ExprKind::Err => ExprPrecedence::Err, ExprKind::Err => ExprPrecedence::Err,
} }
} }
pub fn take(&mut self) -> Self {
mem::replace(
self,
Expr {
id: DUMMY_NODE_ID,
kind: ExprKind::Err,
span: DUMMY_SP,
attrs: ThinVec::new(),
tokens: None,
},
)
}
} }
/// Limit types of a range (inclusive or exclusive) /// Limit types of a range (inclusive or exclusive)

View file

@ -13,6 +13,7 @@ use rustc_span::symbol::{sym, Ident, Symbol};
use rustc_span::{MultiSpan, Span}; use rustc_span::{MultiSpan, Span};
use std::borrow::Cow; use std::borrow::Cow;
use std::cmp::Ordering;
use std::collections::hash_map::Entry; use std::collections::hash_map::Entry;
#[derive(PartialEq)] #[derive(PartialEq)]
@ -744,78 +745,93 @@ impl<'a, 'b> Context<'a, 'b> {
/// Actually builds the expression which the format_args! block will be /// Actually builds the expression which the format_args! block will be
/// expanded to. /// expanded to.
fn into_expr(self) -> P<ast::Expr> { fn into_expr(self) -> P<ast::Expr> {
let mut args = Vec::with_capacity( let mut original_args = self.args;
let mut fmt_args = Vec::with_capacity(
self.arg_unique_types.iter().map(|v| v.len()).sum::<usize>() + self.count_args.len(), self.arg_unique_types.iter().map(|v| v.len()).sum::<usize>() + self.count_args.len(),
); );
let mut heads = Vec::with_capacity(self.args.len());
// First, build up the static array which will become our precompiled // First, build up the static array which will become our precompiled
// format "string" // format "string"
let pieces = self.ecx.expr_vec_slice(self.fmtsp, self.str_pieces); let pieces = self.ecx.expr_vec_slice(self.fmtsp, self.str_pieces);
// Before consuming the expressions, we have to remember spans for // We need to construct a &[ArgumentV1] to pass into the fmt::Arguments
// count arguments as they are now generated separate from other // constructor. In general the expressions in this slice might be
// arguments, hence have no access to the `P<ast::Expr>`'s. // permuted from their order in original_args (such as in the case of
let spans_pos: Vec<_> = self.args.iter().map(|e| e.span).collect(); // "{1} {0}"), or may have multiple entries referring to the same
// element of original_args ("{0} {0}").
//
// The following Iterator<Item = (usize, &ArgumentType)> has one item
// per element of our output slice, identifying the index of which
// element of original_args it's passing, and that argument's type.
let fmt_arg_index_and_ty = self
.arg_unique_types
.iter()
.enumerate()
.flat_map(|(i, unique_types)| unique_types.iter().map(move |ty| (i, ty)))
.chain(self.count_args.iter().map(|i| (*i, &Count)));
// Right now there is a bug such that for the expression: // Figure out whether there are permuted or repeated elements. If not,
// foo(bar(&1)) // we can generate simpler code.
// the lifetime of `1` doesn't outlast the call to `bar`, so it's not let nicely_ordered = fmt_arg_index_and_ty
// valid for the call to `foo`. To work around this all arguments to the .clone()
// format! string are shoved into locals. Furthermore, we shove the address .is_sorted_by(|(i, _), (j, _)| (i < j).then_some(Ordering::Less));
// of each variable because we don't want to move out of the arguments
// passed to this function. // We want to emit:
for (i, e) in self.args.into_iter().enumerate() { //
for arg_ty in self.arg_unique_types[i].iter() { // [ArgumentV1::new(&$arg0, …), ArgumentV1::new(&$arg1, …), …]
args.push(Context::format_arg(self.ecx, self.macsp, e.span, arg_ty, i)); //
} // However, it's only legal to do so if $arg0, $arg1, … were written in
// use the arg span for `&arg` so that borrowck errors // exactly that order by the programmer. When arguments are permuted, we
// point to the specific expression passed to the macro // want them evaluated in the order written by the programmer, not in
// (the span is otherwise unavailable in MIR) // the order provided to fmt::Arguments. When arguments are repeated, we
heads.push(self.ecx.expr_addr_of(e.span.with_ctxt(self.macsp.ctxt()), e)); // want the expression evaluated only once.
} //
for index in self.count_args { // Thus in the not nicely ordered case we emit the following instead:
let span = spans_pos[index]; //
args.push(Context::format_arg(self.ecx, self.macsp, span, &Count, index)); // 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.
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 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()))
} else {
let def_site = self.ecx.with_def_site_ctxt(span);
let args_tuple = self.ecx.expr_ident(def_site, Ident::new(sym::_args, def_site));
let member = Ident::new(sym::integer(arg_index), def_site);
self.ecx.expr(def_site, ast::ExprKind::Field(args_tuple, member))
};
fmt_args.push(Context::format_arg(self.ecx, self.macsp, span, arg_ty, arg));
} }
let args_array = self.ecx.expr_vec(self.macsp, args); let args_array = self.ecx.expr_vec(self.macsp, fmt_args);
let args_slice = self.ecx.expr_addr_of(
self.macsp,
if nicely_ordered {
args_array
} else {
// In the !nicely_ordered case, none of the exprs were moved
// away in the previous loop.
//
// This uses the arg span for `&arg` so that borrowck errors
// point to the specific expression passed to the macro (the
// span is otherwise unavailable in MIR).
let heads = original_args
.into_iter()
.map(|e| self.ecx.expr_addr_of(e.span.with_ctxt(self.macsp.ctxt()), e))
.collect();
// Constructs an AST equivalent to: let pat = self.ecx.pat_ident(self.macsp, Ident::new(sym::_args, self.macsp));
// let arm = self.ecx.arm(self.macsp, pat, args_array);
// match (&arg0, &arg1) { let head = self.ecx.expr(self.macsp, ast::ExprKind::Tup(heads));
// (tmp0, tmp1) => args_array self.ecx.expr_match(self.macsp, head, vec![arm])
// } },
// );
// It was:
//
// let tmp0 = &arg0;
// let tmp1 = &arg1;
// args_array
//
// Because of #11585 the new temporary lifetime rule, the enclosing
// statements for these temporaries become the let's themselves.
// If one or more of them are RefCell's, RefCell borrow() will also
// end there; they don't last long enough for args_array to use them.
// The match expression solves the scope problem.
//
// Note, it may also very well be transformed to:
//
// match arg0 {
// ref tmp0 => {
// match arg1 => {
// ref tmp1 => args_array } } }
//
// But the nested match expression is proved to perform not as well
// as series of let's; the first approach does.
let args_match = {
let pat = self.ecx.pat_ident(self.macsp, Ident::new(sym::_args, self.macsp));
let arm = self.ecx.arm(self.macsp, pat, args_array);
let head = self.ecx.expr(self.macsp, ast::ExprKind::Tup(heads));
self.ecx.expr_match(self.macsp, head, vec![arm])
};
let args_slice = self.ecx.expr_addr_of(self.macsp, args_match);
// Now create the fmt::Arguments struct with all our locals we created. // Now create the fmt::Arguments struct with all our locals we created.
let (fn_name, fn_args) = if self.all_pieces_simple { let (fn_name, fn_args) = if self.all_pieces_simple {
@ -848,11 +864,9 @@ impl<'a, 'b> Context<'a, 'b> {
macsp: Span, macsp: Span,
mut sp: Span, mut sp: Span,
ty: &ArgumentType, ty: &ArgumentType,
arg_index: usize, arg: P<ast::Expr>,
) -> P<ast::Expr> { ) -> P<ast::Expr> {
sp = ecx.with_def_site_ctxt(sp); sp = ecx.with_def_site_ctxt(sp);
let arg = ecx.expr_ident(sp, Ident::new(sym::_args, sp));
let arg = ecx.expr(sp, ast::ExprKind::Field(arg, Ident::new(sym::integer(arg_index), sp)));
let trait_ = match *ty { let trait_ = match *ty {
Placeholder(trait_) if trait_ == "<invalid>" => return DummyResult::raw_expr(sp, true), Placeholder(trait_) if trait_ == "<invalid>" => return DummyResult::raw_expr(sp, true),
Placeholder(trait_) => trait_, Placeholder(trait_) => trait_,

View file

@ -6,6 +6,7 @@
#![feature(bool_to_option)] #![feature(bool_to_option)]
#![feature(crate_visibility_modifier)] #![feature(crate_visibility_modifier)]
#![feature(decl_macro)] #![feature(decl_macro)]
#![feature(is_sorted)]
#![feature(nll)] #![feature(nll)]
#![feature(proc_macro_internals)] #![feature(proc_macro_internals)]
#![feature(proc_macro_quote)] #![feature(proc_macro_quote)]

View file

@ -9,10 +9,5 @@ extern crate std;
// pp-exact:dollar-crate.pp // pp-exact:dollar-crate.pp
fn main() { fn main() {
{ { ::std::io::_print(::core::fmt::Arguments::new_v1(&["rust\n"], &[])); };
::std::io::_print(::core::fmt::Arguments::new_v1(&["rust\n"],
&match () {
_args => [],
}));
};
} }

View file

@ -41,16 +41,7 @@ pub fn bar() ({
[&str; 1]) [&str; 1])
as as
&[&str; 1]), &[&str; 1]),
(&(match (() (&([]
as
())
{
_args
=>
([]
as
[ArgumentV1; 0]),
}
as as
[ArgumentV1; 0]) [ArgumentV1; 0])
as as

View file

@ -18,11 +18,8 @@ LL | bug!();
error: unexpected token: `{ error: unexpected token: `{
let res = let res =
::alloc::fmt::format(::core::fmt::Arguments::new_v1(&[""], ::alloc::fmt::format(::core::fmt::Arguments::new_v1(&[""],
&match (&"u8",) { &[::core::fmt::ArgumentV1::new(&"u8",
_args => ::core::fmt::Display::fmt)]));
[::core::fmt::ArgumentV1::new(_args.0,
::core::fmt::Display::fmt)],
}));
res res
}.as_str()` }.as_str()`
--> $DIR/key-value-expansion.rs:48:23 --> $DIR/key-value-expansion.rs:48:23

View file

@ -9,7 +9,7 @@ LL | let c1 : () = c;
| expected due to this | expected due to this
| |
= note: expected unit type `()` = note: expected unit type `()`
found closure `[mod1::f<T>::{closure#0} closure_substs=(unavailable) substs=[T, _#22t, extern "rust-call" fn(()), _#23t]]` found closure `[mod1::f<T>::{closure#0} closure_substs=(unavailable) substs=[T, _#19t, extern "rust-call" fn(()), _#20t]]`
help: use parentheses to call this closure help: use parentheses to call this closure
| |
LL | let c1 : () = c(); LL | let c1 : () = c();

View file

@ -9,7 +9,7 @@ LL | let c1 : () = c;
| expected due to this | expected due to this
| |
= note: expected unit type `()` = note: expected unit type `()`
found closure `[f<T>::{closure#0} closure_substs=(unavailable) substs=[T, _#22t, extern "rust-call" fn(()), _#23t]]` found closure `[f<T>::{closure#0} closure_substs=(unavailable) substs=[T, _#19t, extern "rust-call" fn(()), _#20t]]`
help: use parentheses to call this closure help: use parentheses to call this closure
| |
LL | let c1 : () = c(); LL | let c1 : () = c();

View file

@ -1,8 +1,16 @@
error[E0284]: type annotations needed: cannot satisfy `<u64 as Test<_>>::Output == _` error[E0282]: type annotations needed
--> $DIR/issue-69455.rs:29:26 --> $DIR/issue-69455.rs:29:5
| |
LL | type Output;
| ------------ `<Self as Test<Rhs>>::Output` defined here
...
LL | println!("{}", 23u64.test(xs.iter().sum())); LL | println!("{}", 23u64.test(xs.iter().sum()));
| ^^^^ cannot satisfy `<u64 as Test<_>>::Output == _` | ^^^^^^^^^^^^^^^---------------------------^
| | |
| | this method call resolves to `<Self as Test<Rhs>>::Output`
| cannot infer type for type parameter `T` declared on the associated function `new`
|
= note: this error originates in the macro `$crate::format_args_nl` (in Nightly builds, run with -Z macro-backtrace for more info)
error[E0283]: type annotations needed error[E0283]: type annotations needed
--> $DIR/issue-69455.rs:29:26 --> $DIR/issue-69455.rs:29:26
@ -25,5 +33,5 @@ LL | println!("{}", 23u64.test(xs.iter().sum::<S>()));
error: aborting due to 2 previous errors error: aborting due to 2 previous errors
Some errors have detailed explanations: E0283, E0284. Some errors have detailed explanations: E0282, E0283.
For more information about an error, try `rustc --explain E0283`. For more information about an error, try `rustc --explain E0282`.