Auto merge of #106824 - m-ou-se:format-args-flatten, r=oli-obk
Flatten/inline format_args!() and (string and int) literal arguments into format_args!() Implements https://github.com/rust-lang/rust/issues/78356 Gated behind `-Zflatten-format-args=yes`. Part of #99012 This change inlines string literals, integer literals and nested format_args!() into format_args!() during ast lowering, making all of the following pairs result in equivalent hir: ```rust println!("Hello, {}!", "World"); println!("Hello, World!"); ``` ```rust println!("[info] {}", format_args!("error")); println!("[info] error"); ``` ```rust println!("[{}] {}", status, format_args!("error: {}", msg)); println!("[{}] error: {}", status, msg); ``` ```rust println!("{} + {} = {}", 1, 2, 1 + 2); println!("1 + 2 = {}", 1 + 2); ``` And so on. This is useful for macros. E.g. a `log::info!()` macro could just pass the tokens from the user directly into a `format_args!()` that gets efficiently flattened/inlined into a `format_args!("info: {}")`. It also means that `dbg!(x)` will have its file, line, and expression name inlined: ```rust eprintln!("[{}:{}] {} = {:#?}", file!(), line!(), stringify!(x), x); // before eprintln!("[example.rs:1] x = {:#?}", x); // after ``` Which can be nice in some cases, but also means a lot more unique static strings than before if dbg!() is used a lot.
This commit is contained in:
commit
1203e0866e
@ -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<P<Ty>> {
|
||||
let kind = match &self.kind {
|
||||
|
@ -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<FormatArgument> {
|
||||
&mut self.arguments
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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<usize> = 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:
|
||||
// <core::fmt::Arguments>::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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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));
|
||||
|
@ -1422,6 +1422,9 @@ options! {
|
||||
fewer_names: Option<bool> = (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],
|
||||
|
@ -984,6 +984,7 @@ symbols! {
|
||||
never_type_fallback,
|
||||
new,
|
||||
new_binary,
|
||||
new_const,
|
||||
new_debug,
|
||||
new_display,
|
||||
new_lower_exp,
|
||||
|
@ -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],
|
||||
|
@ -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]
|
||||
|
@ -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]
|
||||
|
@ -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)| {
|
||||
|
@ -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 ())
|
||||
|
@ -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
|
||||
|
@ -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!("");
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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`.
|
||||
|
8
tests/ui/unpretty/flattened-format-args.rs
Normal file
8
tests/ui/unpretty/flattened-format-args.rs
Normal file
@ -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");
|
||||
}
|
16
tests/ui/unpretty/flattened-format-args.stdout
Normal file
16
tests/ui/unpretty/flattened-format-args.stdout
Normal file
@ -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)]));
|
||||
};
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user