diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs
index 6503bf2bab7..5d164bc4b3c 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 d021bea5eca..356b9bb6371 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 4095e225a80..72352b138cb 100644
--- a/compiler/rustc_ast_lowering/src/format.rs
+++ b/compiler/rustc_ast_lowering/src/format.rs
@@ -7,15 +7,174 @@ 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)]
enum ArgumentType {
Format(FormatTrait),
@@ -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 18d84a7023a..014810dba9c 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 b466a3fcdee..0548379dc2f 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 0154c719ef6..abe5af8f9e0 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 c9821bf8109..6d764237dc8 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 805a1e51ae9..dd0105c0eb4 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 61807635813..c1c80c46c78 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 e135bd9feee..c0e32068eca 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 e0fa1fe2824..4020a433d62 100644
--- a/tests/pretty/issue-4264.pp
+++ b/tests/pretty/issue-4264.pp
@@ -32,13 +32,11 @@ fn bar() ({
({
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 79e6b94f1ac..5ad38e4fd98 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 245c3a40e05..f032ea779dd 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 0d8b7c12d8a..5bdb2bf1954 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 4bf39db5874..c39920d444d 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 00000000000..705dded169a
--- /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 00000000000..a8fe8da0024
--- /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)]));
+ };
+ }