diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 6503bf2bab762..5d164bc4b3cf5 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -1184,6 +1184,15 @@ impl Expr { expr } + pub fn peel_parens_and_refs(&self) -> &Expr { + let mut expr = self; + while let ExprKind::Paren(inner) | ExprKind::AddrOf(BorrowKind::Ref, _, inner) = &expr.kind + { + expr = inner; + } + expr + } + /// Attempts to reparse as `Ty` (for diagnostic purposes). pub fn to_ty(&self) -> Option> { let kind = match &self.kind { diff --git a/compiler/rustc_ast/src/format.rs b/compiler/rustc_ast/src/format.rs index d021bea5ecacb..356b9bb6371e2 100644 --- a/compiler/rustc_ast/src/format.rs +++ b/compiler/rustc_ast/src/format.rs @@ -131,8 +131,8 @@ impl FormatArguments { &self.arguments[..] } - pub fn all_args_mut(&mut self) -> &mut [FormatArgument] { - &mut self.arguments[..] + pub fn all_args_mut(&mut self) -> &mut Vec { + &mut self.arguments } } diff --git a/compiler/rustc_ast_lowering/src/format.rs b/compiler/rustc_ast_lowering/src/format.rs index 4095e225a8019..72352b138cbf4 100644 --- a/compiler/rustc_ast_lowering/src/format.rs +++ b/compiler/rustc_ast_lowering/src/format.rs @@ -7,13 +7,172 @@ use rustc_hir as hir; use rustc_span::{ sym, symbol::{kw, Ident}, - Span, + Span, Symbol, }; +use std::borrow::Cow; impl<'hir> LoweringContext<'_, 'hir> { pub(crate) fn lower_format_args(&mut self, sp: Span, fmt: &FormatArgs) -> hir::ExprKind<'hir> { - expand_format_args(self, sp, fmt) + // Never call the const constructor of `fmt::Arguments` if the + // format_args!() had any arguments _before_ flattening/inlining. + let allow_const = fmt.arguments.all_args().is_empty(); + let mut fmt = Cow::Borrowed(fmt); + if self.tcx.sess.opts.unstable_opts.flatten_format_args { + fmt = flatten_format_args(fmt); + fmt = inline_literals(fmt); + } + expand_format_args(self, sp, &fmt, allow_const) + } +} + +/// Flattens nested `format_args!()` into one. +/// +/// Turns +/// +/// `format_args!("a {} {} {}.", 1, format_args!("b{}!", 2), 3)` +/// +/// into +/// +/// `format_args!("a {} b{}! {}.", 1, 2, 3)`. +fn flatten_format_args(mut fmt: Cow<'_, FormatArgs>) -> Cow<'_, FormatArgs> { + let mut i = 0; + while i < fmt.template.len() { + if let FormatArgsPiece::Placeholder(placeholder) = &fmt.template[i] + && let FormatTrait::Display | FormatTrait::Debug = &placeholder.format_trait + && let Ok(arg_index) = placeholder.argument.index + && let arg = fmt.arguments.all_args()[arg_index].expr.peel_parens_and_refs() + && let ExprKind::FormatArgs(_) = &arg.kind + // Check that this argument is not used by any other placeholders. + && fmt.template.iter().enumerate().all(|(j, p)| + i == j || + !matches!(p, FormatArgsPiece::Placeholder(placeholder) + if placeholder.argument.index == Ok(arg_index)) + ) + { + // Now we need to mutate the outer FormatArgs. + // If this is the first time, this clones the outer FormatArgs. + let fmt = fmt.to_mut(); + + // Take the inner FormatArgs out of the outer arguments, and + // replace it by the inner arguments. (We can't just put those at + // the end, because we need to preserve the order of evaluation.) + + let args = fmt.arguments.all_args_mut(); + let remaining_args = args.split_off(arg_index + 1); + let old_arg_offset = args.len(); + let mut fmt2 = &mut args.pop().unwrap().expr; // The inner FormatArgs. + let fmt2 = loop { // Unwrap the Expr to get to the FormatArgs. + match &mut fmt2.kind { + ExprKind::Paren(inner) | ExprKind::AddrOf(BorrowKind::Ref, _, inner) => fmt2 = inner, + ExprKind::FormatArgs(fmt2) => break fmt2, + _ => unreachable!(), + } + }; + + args.append(fmt2.arguments.all_args_mut()); + let new_arg_offset = args.len(); + args.extend(remaining_args); + + // Correct the indexes that refer to the arguments after the newly inserted arguments. + for_all_argument_indexes(&mut fmt.template, |index| { + if *index >= old_arg_offset { + *index -= old_arg_offset; + *index += new_arg_offset; + } + }); + + // Now merge the placeholders: + + let rest = fmt.template.split_off(i + 1); + fmt.template.pop(); // remove the placeholder for the nested fmt args. + // Insert the pieces from the nested format args, but correct any + // placeholders to point to the correct argument index. + for_all_argument_indexes(&mut fmt2.template, |index| *index += arg_index); + fmt.template.append(&mut fmt2.template); + fmt.template.extend(rest); + + // Don't increment `i` here, so we recurse into the newly added pieces. + } else { + i += 1; + } } + fmt +} + +/// Inline literals into the format string. +/// +/// Turns +/// +/// `format_args!("Hello, {}! {} {}", "World", 123, x)` +/// +/// into +/// +/// `format_args!("Hello, World! 123 {}", x)`. +fn inline_literals(mut fmt: Cow<'_, FormatArgs>) -> Cow<'_, FormatArgs> { + let mut was_inlined = vec![false; fmt.arguments.all_args().len()]; + let mut inlined_anything = false; + + for i in 0..fmt.template.len() { + let FormatArgsPiece::Placeholder(placeholder) = &fmt.template[i] else { continue }; + let Ok(arg_index) = placeholder.argument.index else { continue }; + + let mut literal = None; + + if let FormatTrait::Display = placeholder.format_trait + && placeholder.format_options == Default::default() + && let arg = fmt.arguments.all_args()[arg_index].expr.peel_parens_and_refs() + && let ExprKind::Lit(lit) = arg.kind + { + if let token::LitKind::Str | token::LitKind::StrRaw(_) = lit.kind + && let Ok(LitKind::Str(s, _)) = LitKind::from_token_lit(lit) + { + literal = Some(s); + } else if let token::LitKind::Integer = lit.kind + && let Ok(LitKind::Int(n, _)) = LitKind::from_token_lit(lit) + { + literal = Some(Symbol::intern(&n.to_string())); + } + } + + if let Some(literal) = literal { + // Now we need to mutate the outer FormatArgs. + // If this is the first time, this clones the outer FormatArgs. + let fmt = fmt.to_mut(); + // Replace the placeholder with the literal. + fmt.template[i] = FormatArgsPiece::Literal(literal); + was_inlined[arg_index] = true; + inlined_anything = true; + } + } + + // Remove the arguments that were inlined. + if inlined_anything { + let fmt = fmt.to_mut(); + + let mut remove = was_inlined; + + // Don't remove anything that's still used. + for_all_argument_indexes(&mut fmt.template, |index| remove[*index] = false); + + // Drop all the arguments that are marked for removal. + let mut remove_it = remove.iter(); + fmt.arguments.all_args_mut().retain(|_| remove_it.next() != Some(&true)); + + // Calculate the mapping of old to new indexes for the remaining arguments. + let index_map: Vec = remove + .into_iter() + .scan(0, |i, remove| { + let mapped = *i; + *i += !remove as usize; + Some(mapped) + }) + .collect(); + + // Correct the indexes that refer to arguments that have shifted position. + for_all_argument_indexes(&mut fmt.template, |index| *index = index_map[*index]); + } + + fmt } #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] @@ -189,11 +348,26 @@ fn expand_format_args<'hir>( ctx: &mut LoweringContext<'_, 'hir>, macsp: Span, fmt: &FormatArgs, + allow_const: bool, ) -> hir::ExprKind<'hir> { + let mut incomplete_lit = String::new(); let lit_pieces = ctx.arena.alloc_from_iter(fmt.template.iter().enumerate().filter_map(|(i, piece)| { match piece { - &FormatArgsPiece::Literal(s) => Some(ctx.expr_str(fmt.span, s)), + &FormatArgsPiece::Literal(s) => { + // Coalesce adjacent literal pieces. + if let Some(FormatArgsPiece::Literal(_)) = fmt.template.get(i + 1) { + incomplete_lit.push_str(s.as_str()); + None + } else if !incomplete_lit.is_empty() { + incomplete_lit.push_str(s.as_str()); + let s = Symbol::intern(&incomplete_lit); + incomplete_lit.clear(); + Some(ctx.expr_str(fmt.span, s)) + } else { + Some(ctx.expr_str(fmt.span, s)) + } + } &FormatArgsPiece::Placeholder(_) => { // Inject empty string before placeholders when not already preceded by a literal piece. if i == 0 || matches!(fmt.template[i - 1], FormatArgsPiece::Placeholder(_)) { @@ -244,6 +418,18 @@ fn expand_format_args<'hir>( let arguments = fmt.arguments.all_args(); + if allow_const && arguments.is_empty() && argmap.is_empty() { + // Generate: + // ::new_const(lit_pieces) + let new = ctx.arena.alloc(ctx.expr_lang_item_type_relative( + macsp, + hir::LangItem::FormatArguments, + sym::new_const, + )); + let new_args = ctx.arena.alloc_from_iter([lit_pieces]); + return hir::ExprKind::Call(new, new_args); + } + // If the args array contains exactly all the original arguments once, // in order, we can use a simple array instead of a `match` construction. // However, if there's a yield point in any argument except the first one, @@ -290,25 +476,14 @@ fn expand_format_args<'hir>( let args_ident = Ident::new(sym::args, macsp); let (args_pat, args_hir_id) = ctx.pat_ident(macsp, args_ident); let args = ctx.arena.alloc_from_iter(argmap.iter().map(|&(arg_index, ty)| { - if let Some(arg) = arguments.get(arg_index) { - let sp = arg.expr.span.with_ctxt(macsp.ctxt()); - let args_ident_expr = ctx.expr_ident(macsp, args_ident, args_hir_id); - let arg = ctx.arena.alloc(ctx.expr( - sp, - hir::ExprKind::Field( - args_ident_expr, - Ident::new(sym::integer(arg_index), macsp), - ), - )); - make_argument(ctx, sp, arg, ty) - } else { - ctx.expr( - macsp, - hir::ExprKind::Err( - ctx.tcx.sess.delay_span_bug(macsp, format!("no arg at {arg_index}")), - ), - ) - } + let arg = &arguments[arg_index]; + let sp = arg.expr.span.with_ctxt(macsp.ctxt()); + let args_ident_expr = ctx.expr_ident(macsp, args_ident, args_hir_id); + let arg = ctx.arena.alloc(ctx.expr( + sp, + hir::ExprKind::Field(args_ident_expr, Ident::new(sym::integer(arg_index), macsp)), + )); + make_argument(ctx, sp, arg, ty) })); let elements: Vec<_> = arguments .iter() @@ -409,3 +584,22 @@ fn may_contain_yield_point(e: &ast::Expr) -> bool { visitor.visit_expr(e); visitor.0 } + +fn for_all_argument_indexes(template: &mut [FormatArgsPiece], mut f: impl FnMut(&mut usize)) { + for piece in template { + let FormatArgsPiece::Placeholder(placeholder) = piece else { continue }; + if let Ok(index) = &mut placeholder.argument.index { + f(index); + } + if let Some(FormatCount::Argument(FormatArgPosition { index: Ok(index), .. })) = + &mut placeholder.format_options.width + { + f(index); + } + if let Some(FormatCount::Argument(FormatArgPosition { index: Ok(index), .. })) = + &mut placeholder.format_options.precision + { + f(index); + } + } +} diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index 18d84a7023aa1..014810dba9cce 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -744,6 +744,7 @@ fn test_unstable_options_tracking_hash() { tracked!(emit_thin_lto, false); tracked!(export_executable_symbols, true); tracked!(fewer_names, Some(true)); + tracked!(flatten_format_args, true); tracked!(force_unstable_if_unmarked, true); tracked!(fuel, Some(("abc".to_string(), 99))); tracked!(function_sections, Some(false)); diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index b466a3fcdee91..0548379dc2fc1 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -1422,6 +1422,9 @@ options! { fewer_names: Option = (None, parse_opt_bool, [TRACKED], "reduce memory use by retaining fewer names within compilation artifacts (LLVM-IR) \ (default: no)"), + flatten_format_args: bool = (false, parse_bool, [TRACKED], + "flatten nested format_args!() and literals into a simplified format_args!() call \ + (default: no)"), force_unstable_if_unmarked: bool = (false, parse_bool, [TRACKED], "force all crates to be `rustc_private` unstable (default: no)"), fuel: Option<(String, u64)> = (None, parse_optimization_fuel, [TRACKED], diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 0154c719ef608..abe5af8f9e0cf 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -984,6 +984,7 @@ symbols! { never_type_fallback, new, new_binary, + new_const, new_debug, new_display, new_lower_exp, diff --git a/library/core/src/fmt/mod.rs b/library/core/src/fmt/mod.rs index c9821bf8109a7..6d764237dc8ae 100644 --- a/library/core/src/fmt/mod.rs +++ b/library/core/src/fmt/mod.rs @@ -392,8 +392,31 @@ enum FlagV1 { } impl<'a> Arguments<'a> { + #[doc(hidden)] + #[inline] + #[unstable(feature = "fmt_internals", issue = "none")] + #[rustc_const_unstable(feature = "const_fmt_arguments_new", issue = "none")] + pub const fn new_const(pieces: &'a [&'static str]) -> Self { + if pieces.len() > 1 { + panic!("invalid args"); + } + Arguments { pieces, fmt: None, args: &[] } + } + /// When using the format_args!() macro, this function is used to generate the /// Arguments structure. + #[cfg(not(bootstrap))] + #[doc(hidden)] + #[inline] + #[unstable(feature = "fmt_internals", reason = "internal to format_args!", issue = "none")] + pub fn new_v1(pieces: &'a [&'static str], args: &'a [ArgumentV1<'a>]) -> Arguments<'a> { + if pieces.len() < args.len() || pieces.len() > args.len() + 1 { + panic!("invalid args"); + } + Arguments { pieces, fmt: None, args } + } + + #[cfg(bootstrap)] #[doc(hidden)] #[inline] #[unstable(feature = "fmt_internals", reason = "internal to format_args!", issue = "none")] @@ -417,8 +440,7 @@ impl<'a> Arguments<'a> { #[doc(hidden)] #[inline] #[unstable(feature = "fmt_internals", reason = "internal to format_args!", issue = "none")] - #[rustc_const_unstable(feature = "const_fmt_arguments_new", issue = "none")] - pub const fn new_v1_formatted( + pub fn new_v1_formatted( pieces: &'a [&'static str], args: &'a [ArgumentV1<'a>], fmt: &'a [rt::v1::Argument], diff --git a/library/core/src/panicking.rs b/library/core/src/panicking.rs index 805a1e51ae9c0..dd0105c0eb4fa 100644 --- a/library/core/src/panicking.rs +++ b/library/core/src/panicking.rs @@ -111,7 +111,7 @@ pub const fn panic(expr: &'static str) -> ! { // truncation and padding (even though none is used here). Using // Arguments::new_v1 may allow the compiler to omit Formatter::pad from the // output binary, saving up to a few kilobytes. - panic_fmt(fmt::Arguments::new_v1(&[expr], &[])); + panic_fmt(fmt::Arguments::new_const(&[expr])); } /// Like `panic`, but without unwinding and track_caller to reduce the impact on codesize. @@ -120,7 +120,7 @@ pub const fn panic(expr: &'static str) -> ! { #[lang = "panic_nounwind"] // needed by codegen for non-unwinding panics #[rustc_nounwind] pub fn panic_nounwind(expr: &'static str) -> ! { - panic_nounwind_fmt(fmt::Arguments::new_v1(&[expr], &[])); + panic_nounwind_fmt(fmt::Arguments::new_const(&[expr])); } #[inline] diff --git a/library/core/tests/fmt/mod.rs b/library/core/tests/fmt/mod.rs index 61807635813c4..c1c80c46c78b7 100644 --- a/library/core/tests/fmt/mod.rs +++ b/library/core/tests/fmt/mod.rs @@ -22,11 +22,11 @@ fn test_pointer_formats_data_pointer() { #[test] fn test_estimated_capacity() { assert_eq!(format_args!("").estimated_capacity(), 0); - assert_eq!(format_args!("{}", "").estimated_capacity(), 0); + assert_eq!(format_args!("{}", {""}).estimated_capacity(), 0); assert_eq!(format_args!("Hello").estimated_capacity(), 5); - assert_eq!(format_args!("Hello, {}!", "").estimated_capacity(), 16); - assert_eq!(format_args!("{}, hello!", "World").estimated_capacity(), 0); - assert_eq!(format_args!("{}. 16-bytes piece", "World").estimated_capacity(), 32); + assert_eq!(format_args!("Hello, {}!", {""}).estimated_capacity(), 16); + assert_eq!(format_args!("{}, hello!", {"World"}).estimated_capacity(), 0); + assert_eq!(format_args!("{}. 16-bytes piece", {"World"}).estimated_capacity(), 32); } #[test] diff --git a/src/tools/clippy/clippy_utils/src/macros.rs b/src/tools/clippy/clippy_utils/src/macros.rs index e135bd9feee54..c0e32068ecacc 100644 --- a/src/tools/clippy/clippy_utils/src/macros.rs +++ b/src/tools/clippy/clippy_utils/src/macros.rs @@ -533,6 +533,14 @@ struct FormatArgsValues<'tcx> { } impl<'tcx> FormatArgsValues<'tcx> { + fn new_empty(format_string_span: SpanData) -> Self { + Self { + value_args: Vec::new(), + pos_to_value_index: Vec::new(), + format_string_span, + } + } + fn new(args: &'tcx Expr<'tcx>, format_string_span: SpanData) -> Self { let mut pos_to_value_index = Vec::new(); let mut value_args = Vec::new(); @@ -997,12 +1005,13 @@ impl<'tcx> FormatArgsExpn<'tcx> { .find(|&name| matches!(name, sym::const_format_args | sym::format_args | sym::format_args_nl))?; let newline = macro_name == sym::format_args_nl; + // ::core::fmt::Arguments::new_const(pieces) // ::core::fmt::Arguments::new_v1(pieces, args) // ::core::fmt::Arguments::new_v1_formatted(pieces, args, fmt, _unsafe_arg) - if let ExprKind::Call(callee, [pieces, args, rest @ ..]) = expr.kind + if let ExprKind::Call(callee, [pieces, rest @ ..]) = expr.kind && let ExprKind::Path(QPath::TypeRelative(ty, seg)) = callee.kind && let TyKind::Path(QPath::LangItem(LangItem::FormatArguments, _, _)) = ty.kind - && matches!(seg.ident.as_str(), "new_v1" | "new_v1_formatted") + && matches!(seg.ident.as_str(), "new_const" | "new_v1" | "new_v1_formatted") { let format_string = FormatString::new(cx, pieces)?; @@ -1026,7 +1035,7 @@ impl<'tcx> FormatArgsExpn<'tcx> { return None; } - let positions = if let Some(fmt_arg) = rest.first() { + let positions = if let Some(fmt_arg) = rest.get(1) { // If the argument contains format specs, `new_v1_formatted(_, _, fmt, _)`, parse // them. @@ -1042,7 +1051,11 @@ impl<'tcx> FormatArgsExpn<'tcx> { })) }; - let values = FormatArgsValues::new(args, format_string.span.data()); + let values = if let Some(args) = rest.first() { + FormatArgsValues::new(args, format_string.span.data()) + } else { + FormatArgsValues::new_empty(format_string.span.data()) + }; let args = izip!(positions, parsed_args, parser.arg_places) .map(|(position, parsed_arg, arg_span)| { diff --git a/tests/pretty/issue-4264.pp b/tests/pretty/issue-4264.pp index e0fa1fe28246e..4020a433d6254 100644 --- a/tests/pretty/issue-4264.pp +++ b/tests/pretty/issue-4264.pp @@ -32,13 +32,11 @@ ({ let res = ((::alloc::fmt::format as - for<'a> fn(Arguments<'a>) -> String {format})(((<#[lang = "format_arguments"]>::new_v1 + for<'a> fn(Arguments<'a>) -> String {format})(((<#[lang = "format_arguments"]>::new_const as - fn(&[&'static str], &[core::fmt::ArgumentV1<'_>]) -> Arguments<'_> {Arguments::<'_>::new_v1})((&([("test" - as &str)] as [&str; 1]) as &[&str; 1]), - (&([] as [core::fmt::ArgumentV1<'_>; 0]) as - &[core::fmt::ArgumentV1<'_>; 0])) as Arguments<'_>)) as - String); + fn(&[&'static str]) -> Arguments<'_> {Arguments::<'_>::new_const})((&([("test" + as &str)] as [&str; 1]) as &[&str; 1])) as Arguments<'_>)) + as String); (res as String) } as String); } as ()) diff --git a/tests/rustdoc-ui/z-help.stdout b/tests/rustdoc-ui/z-help.stdout index 79e6b94f1aca5..5ad38e4fd9821 100644 --- a/tests/rustdoc-ui/z-help.stdout +++ b/tests/rustdoc-ui/z-help.stdout @@ -44,6 +44,7 @@ -Z export-executable-symbols=val -- export symbols from executables, as if they were dynamic libraries -Z extra-const-ub-checks=val -- turns on more checks to detect const UB, which can be slow (default: no) -Z fewer-names=val -- reduce memory use by retaining fewer names within compilation artifacts (LLVM-IR) (default: no) + -Z flatten-format-args=val -- flatten nested format_args!() and literals into a simplified format_args!() call (default: no) -Z force-unstable-if-unmarked=val -- force all crates to be `rustc_private` unstable (default: no) -Z fuel=val -- set the optimization fuel quota for a crate -Z function-sections=val -- whether each function should go in its own section diff --git a/tests/ui/borrowck/issue-64453.stderr b/tests/ui/borrowck/issue-64453.stderr index 245c3a40e0506..f032ea779dd48 100644 --- a/tests/ui/borrowck/issue-64453.stderr +++ b/tests/ui/borrowck/issue-64453.stderr @@ -1,4 +1,4 @@ -error: `Arguments::<'a>::new_v1` is not yet stable as a const fn +error: `Arguments::<'a>::new_const` is not yet stable as a const fn --> $DIR/issue-64453.rs:4:31 | LL | static settings_dir: String = format!(""); diff --git a/tests/ui/consts/const-eval/format.rs b/tests/ui/consts/const-eval/format.rs index 0d8b7c12d8abb..5bdb2bf195439 100644 --- a/tests/ui/consts/const-eval/format.rs +++ b/tests/ui/consts/const-eval/format.rs @@ -1,12 +1,13 @@ const fn failure() { panic!("{:?}", 0); //~^ ERROR cannot call non-const formatting macro in constant functions + //~| ERROR cannot call non-const fn `Arguments::<'_>::new_v1` in constant functions } const fn print() { println!("{:?}", 0); //~^ ERROR cannot call non-const formatting macro in constant functions - //~| ERROR `Arguments::<'a>::new_v1` is not yet stable as a const fn + //~| ERROR cannot call non-const fn `Arguments::<'_>::new_v1` in constant functions //~| ERROR cannot call non-const fn `_print` in constant functions } diff --git a/tests/ui/consts/const-eval/format.stderr b/tests/ui/consts/const-eval/format.stderr index 4bf39db58746c..c39920d444def 100644 --- a/tests/ui/consts/const-eval/format.stderr +++ b/tests/ui/consts/const-eval/format.stderr @@ -7,8 +7,17 @@ LL | panic!("{:?}", 0); = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants = note: this error originates in the macro `$crate::const_format_args` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) +error[E0015]: cannot call non-const fn `Arguments::<'_>::new_v1` in constant functions + --> $DIR/format.rs:2:5 + | +LL | panic!("{:?}", 0); + | ^^^^^^^^^^^^^^^^^ + | + = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants + = note: this error originates in the macro `$crate::const_format_args` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) + error[E0015]: cannot call non-const formatting macro in constant functions - --> $DIR/format.rs:7:22 + --> $DIR/format.rs:8:22 | LL | println!("{:?}", 0); | ^ @@ -16,17 +25,17 @@ LL | println!("{:?}", 0); = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) -error: `Arguments::<'a>::new_v1` is not yet stable as a const fn - --> $DIR/format.rs:7:5 +error[E0015]: cannot call non-const fn `Arguments::<'_>::new_v1` in constant functions + --> $DIR/format.rs:8:5 | LL | println!("{:?}", 0); | ^^^^^^^^^^^^^^^^^^^ | - = help: add `#![feature(const_fmt_arguments_new)]` to the crate attributes to enable + = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0015]: cannot call non-const fn `_print` in constant functions - --> $DIR/format.rs:7:5 + --> $DIR/format.rs:8:5 | LL | println!("{:?}", 0); | ^^^^^^^^^^^^^^^^^^^ @@ -63,19 +72,19 @@ LL | panic!("{:?}", 0); = note: this note originates in the macro `$crate::const_format_args` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) note: erroneous constant used - --> $DIR/format.rs:7:14 + --> $DIR/format.rs:8:14 | LL | println!("{:?}", 0); | ^^^^^^ note: erroneous constant used - --> $DIR/format.rs:7:14 + --> $DIR/format.rs:8:14 | LL | println!("{:?}", 0); | ^^^^^^ note: erroneous constant used - --> $DIR/format.rs:7:22 + --> $DIR/format.rs:8:22 | LL | println!("{:?}", 0); | ^ @@ -83,13 +92,13 @@ LL | println!("{:?}", 0); = note: this note originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) note: erroneous constant used - --> $DIR/format.rs:7:22 + --> $DIR/format.rs:8:22 | LL | println!("{:?}", 0); | ^ | = note: this note originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to 4 previous errors +error: aborting due to 5 previous errors For more information about this error, try `rustc --explain E0015`. diff --git a/tests/ui/unpretty/flattened-format-args.rs b/tests/ui/unpretty/flattened-format-args.rs new file mode 100644 index 0000000000000..705dded169a19 --- /dev/null +++ b/tests/ui/unpretty/flattened-format-args.rs @@ -0,0 +1,8 @@ +// compile-flags: -Zunpretty=hir -Zflatten-format-args=yes +// check-pass + +fn main() { + let x = 1; + // Should flatten to println!("a 123 b {x} xyz\n"): + println!("a {} {}", format_args!("{} b {x}", 123), "xyz"); +} diff --git a/tests/ui/unpretty/flattened-format-args.stdout b/tests/ui/unpretty/flattened-format-args.stdout new file mode 100644 index 0000000000000..a8fe8da002472 --- /dev/null +++ b/tests/ui/unpretty/flattened-format-args.stdout @@ -0,0 +1,16 @@ +#[prelude_import] +use ::std::prelude::rust_2015::*; +#[macro_use] +extern crate std; +// compile-flags: -Zunpretty=hir -Zflatten-format-args=yes +// check-pass + +fn main() { + let x = 1; + // Should flatten to println!("a 123 b {x} xyz\n"): + { + ::std::io::_print(<#[lang = "format_arguments"]>::new_v1(&["a 123 b ", + " xyz\n"], + &[<#[lang = "format_argument"]>::new_display(&x)])); + }; + }