diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 079b9e43373..4692420206c 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -39,6 +39,7 @@ use rustc_span::{Span, DUMMY_SP}; use std::cmp::Ordering; use std::convert::TryFrom; use std::fmt; +use std::mem; #[cfg(test)] mod tests; @@ -1276,6 +1277,19 @@ impl Expr { 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) diff --git a/compiler/rustc_builtin_macros/src/format.rs b/compiler/rustc_builtin_macros/src/format.rs index 407aaacb889..50ef350feb6 100644 --- a/compiler/rustc_builtin_macros/src/format.rs +++ b/compiler/rustc_builtin_macros/src/format.rs @@ -13,6 +13,7 @@ use rustc_span::symbol::{sym, Ident, Symbol}; use rustc_span::{MultiSpan, Span}; use std::borrow::Cow; +use std::cmp::Ordering; use std::collections::hash_map::Entry; #[derive(PartialEq)] @@ -744,78 +745,93 @@ impl<'a, 'b> Context<'a, 'b> { /// Actually builds the expression which the format_args! block will be /// expanded to. fn into_expr(self) -> P { - 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::() + self.count_args.len(), ); - let mut heads = Vec::with_capacity(self.args.len()); // First, build up the static array which will become our precompiled // format "string" let pieces = self.ecx.expr_vec_slice(self.fmtsp, self.str_pieces); - // Before consuming the expressions, we have to remember spans for - // count arguments as they are now generated separate from other - // arguments, hence have no access to the `P`'s. - let spans_pos: Vec<_> = self.args.iter().map(|e| e.span).collect(); + // We need to construct a &[ArgumentV1] to pass into the fmt::Arguments + // constructor. In general the expressions in this slice might be + // permuted from their order in original_args (such as in the case of + // "{1} {0}"), or may have multiple entries referring to the same + // element of original_args ("{0} {0}"). + // + // The following Iterator 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: - // foo(bar(&1)) - // the lifetime of `1` doesn't outlast the call to `bar`, so it's not - // valid for the call to `foo`. To work around this all arguments to the - // format! string are shoved into locals. Furthermore, we shove the address - // of each variable because we don't want to move out of the arguments - // passed to this function. - for (i, e) in self.args.into_iter().enumerate() { - for arg_ty in self.arg_unique_types[i].iter() { - args.push(Context::format_arg(self.ecx, self.macsp, e.span, arg_ty, i)); - } - // use 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) - heads.push(self.ecx.expr_addr_of(e.span.with_ctxt(self.macsp.ctxt()), e)); - } - for index in self.count_args { - let span = spans_pos[index]; - args.push(Context::format_arg(self.ecx, self.macsp, span, &Count, index)); + // Figure out whether there are permuted or repeated elements. If not, + // we can generate simpler code. + let nicely_ordered = fmt_arg_index_and_ty + .clone() + .is_sorted_by(|(i, _), (j, _)| (i < j).then_some(Ordering::Less)); + + // We want to emit: + // + // [ArgumentV1::new(&$arg0, …), ArgumentV1::new(&$arg1, …), …] + // + // However, it's only legal to do so if $arg0, $arg1, … were written in + // exactly that order by the programmer. When arguments are permuted, we + // want them evaluated in the order written by the programmer, not in + // 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: + // + // 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: - // - // match (&arg0, &arg1) { - // (tmp0, tmp1) => args_array - // } - // - // 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); + 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]) + }, + ); // Now create the fmt::Arguments struct with all our locals we created. let (fn_name, fn_args) = if self.all_pieces_simple { @@ -848,11 +864,9 @@ impl<'a, 'b> Context<'a, 'b> { macsp: Span, mut sp: Span, ty: &ArgumentType, - arg_index: usize, + arg: P, ) -> P { 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 { Placeholder(trait_) if trait_ == "" => return DummyResult::raw_expr(sp, true), Placeholder(trait_) => trait_, diff --git a/compiler/rustc_builtin_macros/src/lib.rs b/compiler/rustc_builtin_macros/src/lib.rs index 65f785b9097..68edcb1b92c 100644 --- a/compiler/rustc_builtin_macros/src/lib.rs +++ b/compiler/rustc_builtin_macros/src/lib.rs @@ -6,6 +6,7 @@ #![feature(bool_to_option)] #![feature(crate_visibility_modifier)] #![feature(decl_macro)] +#![feature(is_sorted)] #![feature(nll)] #![feature(proc_macro_internals)] #![feature(proc_macro_quote)] diff --git a/src/test/pretty/dollar-crate.pp b/src/test/pretty/dollar-crate.pp index 84eda08d203..3af37955f23 100644 --- a/src/test/pretty/dollar-crate.pp +++ b/src/test/pretty/dollar-crate.pp @@ -9,10 +9,5 @@ extern crate std; // pp-exact:dollar-crate.pp fn main() { - { - ::std::io::_print(::core::fmt::Arguments::new_v1(&["rust\n"], - &match () { - _args => [], - })); - }; + { ::std::io::_print(::core::fmt::Arguments::new_v1(&["rust\n"], &[])); }; } diff --git a/src/test/pretty/issue-4264.pp b/src/test/pretty/issue-4264.pp index 529daab9038..93967e720c1 100644 --- a/src/test/pretty/issue-4264.pp +++ b/src/test/pretty/issue-4264.pp @@ -41,16 +41,7 @@ pub fn bar() ({ [&str; 1]) as &[&str; 1]), - (&(match (() - as - ()) - { - _args - => - ([] - as - [ArgumentV1; 0]), - } + (&([] as [ArgumentV1; 0]) as diff --git a/src/test/ui/attributes/key-value-expansion.stderr b/src/test/ui/attributes/key-value-expansion.stderr index 878afb39214..268e382327e 100644 --- a/src/test/ui/attributes/key-value-expansion.stderr +++ b/src/test/ui/attributes/key-value-expansion.stderr @@ -18,11 +18,8 @@ LL | bug!(); error: unexpected token: `{ let res = ::alloc::fmt::format(::core::fmt::Arguments::new_v1(&[""], - &match (&"u8",) { - _args => - [::core::fmt::ArgumentV1::new(_args.0, - ::core::fmt::Display::fmt)], - })); + &[::core::fmt::ArgumentV1::new(&"u8", + ::core::fmt::Display::fmt)])); res }.as_str()` --> $DIR/key-value-expansion.rs:48:23 diff --git a/src/test/ui/closures/print/closure-print-generic-trim-off-verbose-2.stderr b/src/test/ui/closures/print/closure-print-generic-trim-off-verbose-2.stderr index ee394d64a1d..bdc71b8dcaa 100644 --- a/src/test/ui/closures/print/closure-print-generic-trim-off-verbose-2.stderr +++ b/src/test/ui/closures/print/closure-print-generic-trim-off-verbose-2.stderr @@ -9,7 +9,7 @@ LL | let c1 : () = c; | expected due to this | = note: expected unit type `()` - found closure `[mod1::f::{closure#0} closure_substs=(unavailable) substs=[T, _#22t, extern "rust-call" fn(()), _#23t]]` + found closure `[mod1::f::{closure#0} closure_substs=(unavailable) substs=[T, _#19t, extern "rust-call" fn(()), _#20t]]` help: use parentheses to call this closure | LL | let c1 : () = c(); diff --git a/src/test/ui/closures/print/closure-print-generic-verbose-2.stderr b/src/test/ui/closures/print/closure-print-generic-verbose-2.stderr index 11b9fa7e40c..453f18891d3 100644 --- a/src/test/ui/closures/print/closure-print-generic-verbose-2.stderr +++ b/src/test/ui/closures/print/closure-print-generic-verbose-2.stderr @@ -9,7 +9,7 @@ LL | let c1 : () = c; | expected due to this | = note: expected unit type `()` - found closure `[f::{closure#0} closure_substs=(unavailable) substs=[T, _#22t, extern "rust-call" fn(()), _#23t]]` + found closure `[f::{closure#0} closure_substs=(unavailable) substs=[T, _#19t, extern "rust-call" fn(()), _#20t]]` help: use parentheses to call this closure | LL | let c1 : () = c(); diff --git a/src/test/ui/issues/issue-69455.stderr b/src/test/ui/issues/issue-69455.stderr index da84a6b52da..95617e4ecc8 100644 --- a/src/test/ui/issues/issue-69455.stderr +++ b/src/test/ui/issues/issue-69455.stderr @@ -1,8 +1,16 @@ -error[E0284]: type annotations needed: cannot satisfy `>::Output == _` - --> $DIR/issue-69455.rs:29:26 +error[E0282]: type annotations needed + --> $DIR/issue-69455.rs:29:5 | +LL | type Output; + | ------------ `>::Output` defined here +... LL | println!("{}", 23u64.test(xs.iter().sum())); - | ^^^^ cannot satisfy `>::Output == _` + | ^^^^^^^^^^^^^^^---------------------------^ + | | | + | | this method call resolves to `>::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 --> $DIR/issue-69455.rs:29:26 @@ -25,5 +33,5 @@ LL | println!("{}", 23u64.test(xs.iter().sum::())); error: aborting due to 2 previous errors -Some errors have detailed explanations: E0283, E0284. -For more information about an error, try `rustc --explain E0283`. +Some errors have detailed explanations: E0282, E0283. +For more information about an error, try `rustc --explain E0282`.