Skip to content

Commit 6024960

Browse files
committed
Use super let in format_args!().
This makes it posisble to do: let f = format_args!("Hello, {name}!");
1 parent 8bf5a8d commit 6024960

22 files changed

+299
-337
lines changed

compiler/rustc_ast_lowering/src/format.rs

Lines changed: 46 additions & 115 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
1-
use core::ops::ControlFlow;
21
use std::borrow::Cow;
32

4-
use rustc_ast::visit::Visitor;
53
use rustc_ast::*;
64
use rustc_data_structures::fx::FxIndexMap;
75
use rustc_hir as hir;
@@ -476,77 +474,32 @@ fn expand_format_args<'hir>(
476474
return hir::ExprKind::Call(new, new_args);
477475
}
478476

479-
// If the args array contains exactly all the original arguments once,
480-
// in order, we can use a simple array instead of a `match` construction.
481-
// However, if there's a yield point in any argument except the first one,
482-
// we don't do this, because an Argument cannot be kept across yield points.
483-
//
484-
// This is an optimization, speeding up compilation about 1-2% in some cases.
485-
// See https://github.com/rust-lang/rust/pull/106770#issuecomment-1380790609
486-
let use_simple_array = argmap.len() == arguments.len()
487-
&& argmap.iter().enumerate().all(|(i, (&(j, _), _))| i == j)
488-
&& arguments.iter().skip(1).all(|arg| !may_contain_yield_point(&arg.expr));
489-
490-
let args = if arguments.is_empty() {
477+
let (let_statements, args) = if arguments.is_empty() {
491478
// Generate:
492-
// &<core::fmt::Argument>::none()
493-
//
494-
// Note:
495-
// `none()` just returns `[]`. We use `none()` rather than `[]` to limit the lifetime.
496-
//
497-
// This makes sure that this still fails to compile, even when the argument is inlined:
498-
//
499-
// ```
500-
// let f = format_args!("{}", "a");
501-
// println!("{f}"); // error E0716
502-
// ```
503-
//
504-
// Cases where keeping the object around is allowed, such as `format_args!("a")`,
505-
// are handled above by the `allow_const` case.
506-
let none_fn = ctx.arena.alloc(ctx.expr_lang_item_type_relative(
507-
macsp,
508-
hir::LangItem::FormatArgument,
509-
sym::none,
510-
));
511-
let none = ctx.expr_call(macsp, none_fn, &[]);
512-
ctx.expr(macsp, hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Not, none))
513-
} else if use_simple_array {
514-
// Generate:
515-
// &[
516-
// <core::fmt::Argument>::new_display(&arg0),
517-
// <core::fmt::Argument>::new_lower_hex(&arg1),
518-
// <core::fmt::Argument>::new_debug(&arg2),
519-
// …
520-
// ]
521-
let elements = ctx.arena.alloc_from_iter(arguments.iter().zip(argmap).map(
522-
|(arg, ((_, ty), placeholder_span))| {
523-
let placeholder_span =
524-
placeholder_span.unwrap_or(arg.expr.span).with_ctxt(macsp.ctxt());
525-
let arg_span = match arg.kind {
526-
FormatArgumentKind::Captured(_) => placeholder_span,
527-
_ => arg.expr.span.with_ctxt(macsp.ctxt()),
528-
};
529-
let arg = ctx.lower_expr(&arg.expr);
530-
let ref_arg = ctx.arena.alloc(ctx.expr(
531-
arg_span,
532-
hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Not, arg),
533-
));
534-
make_argument(ctx, placeholder_span, ref_arg, ty)
535-
},
536-
));
537-
ctx.expr_array_ref(macsp, elements)
479+
// []
480+
(None, ctx.arena.alloc(ctx.expr(macsp, hir::ExprKind::Array(&[]))))
538481
} else {
539482
// Generate:
540-
// &match (&arg0, &arg1, &…) {
541-
// args => [
542-
// <core::fmt::Argument>::new_display(args.0),
543-
// <core::fmt::Argument>::new_lower_hex(args.1),
544-
// <core::fmt::Argument>::new_debug(args.0),
545-
// …
546-
// ]
547-
// }
483+
// super let args = (&arg0, &arg1, &…);
548484
let args_ident = Ident::new(sym::args, macsp);
549485
let (args_pat, args_hir_id) = ctx.pat_ident(macsp, args_ident);
486+
let elements = ctx.arena.alloc_from_iter(arguments.iter().map(|arg| {
487+
let arg_expr = ctx.lower_expr(&arg.expr);
488+
ctx.expr(
489+
arg.expr.span.with_ctxt(macsp.ctxt()),
490+
hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Not, arg_expr),
491+
)
492+
}));
493+
let args_tuple = ctx.arena.alloc(ctx.expr(macsp, hir::ExprKind::Tup(elements)));
494+
let let_statement_1 = ctx.stmt_super_let_pat(macsp, args_pat, Some(args_tuple));
495+
496+
// Generate:
497+
// super let args = [
498+
// <core::fmt::Argument>::new_display(args.0),
499+
// <core::fmt::Argument>::new_lower_hex(args.1),
500+
// <core::fmt::Argument>::new_debug(args.0),
501+
// …
502+
// ];
550503
let args = ctx.arena.alloc_from_iter(argmap.iter().map(
551504
|(&(arg_index, ty), &placeholder_span)| {
552505
let arg = &arguments[arg_index];
@@ -567,29 +520,21 @@ fn expand_format_args<'hir>(
567520
make_argument(ctx, placeholder_span, arg, ty)
568521
},
569522
));
570-
let elements = ctx.arena.alloc_from_iter(arguments.iter().map(|arg| {
571-
let arg_expr = ctx.lower_expr(&arg.expr);
572-
ctx.expr(
573-
arg.expr.span.with_ctxt(macsp.ctxt()),
574-
hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Not, arg_expr),
575-
)
576-
}));
577-
let args_tuple = ctx.arena.alloc(ctx.expr(macsp, hir::ExprKind::Tup(elements)));
578-
let array = ctx.arena.alloc(ctx.expr(macsp, hir::ExprKind::Array(args)));
579-
let match_arms = ctx.arena.alloc_from_iter([ctx.arm(args_pat, array)]);
580-
let match_expr = ctx.arena.alloc(ctx.expr_match(
581-
macsp,
582-
args_tuple,
583-
match_arms,
584-
hir::MatchSource::FormatArgs,
585-
));
586-
ctx.expr(
587-
macsp,
588-
hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Not, match_expr),
523+
let args = ctx.arena.alloc(ctx.expr(macsp, hir::ExprKind::Array(args)));
524+
let (args_pat, args_hir_id) = ctx.pat_ident(macsp, args_ident);
525+
let let_statement_2 = ctx.stmt_super_let_pat(macsp, args_pat, Some(args));
526+
(
527+
Some([let_statement_1, let_statement_2]),
528+
ctx.arena.alloc(ctx.expr_ident_mut(macsp, args_ident, args_hir_id)),
589529
)
590530
};
591531

592-
if let Some(format_options) = format_options {
532+
// Generate:
533+
// &args
534+
let args =
535+
ctx.expr(macsp, hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Not, args));
536+
537+
let call = if let Some(format_options) = format_options {
593538
// Generate:
594539
// <core::fmt::Arguments>::new_v1_formatted(
595540
// lit_pieces,
@@ -632,35 +577,21 @@ fn expand_format_args<'hir>(
632577
));
633578
let new_args = ctx.arena.alloc_from_iter([lit_pieces, args]);
634579
hir::ExprKind::Call(new_v1, new_args)
635-
}
636-
}
637-
638-
fn may_contain_yield_point(e: &ast::Expr) -> bool {
639-
struct MayContainYieldPoint;
640-
641-
impl Visitor<'_> for MayContainYieldPoint {
642-
type Result = ControlFlow<()>;
643-
644-
fn visit_expr(&mut self, e: &ast::Expr) -> ControlFlow<()> {
645-
if let ast::ExprKind::Await(_, _) | ast::ExprKind::Yield(_) = e.kind {
646-
ControlFlow::Break(())
647-
} else {
648-
visit::walk_expr(self, e)
649-
}
650-
}
651-
652-
fn visit_mac_call(&mut self, _: &ast::MacCall) -> ControlFlow<()> {
653-
// Macros should be expanded at this point.
654-
unreachable!("unexpanded macro in ast lowering");
655-
}
580+
};
656581

657-
fn visit_item(&mut self, _: &ast::Item) -> ControlFlow<()> {
658-
// Do not recurse into nested items.
659-
ControlFlow::Continue(())
660-
}
582+
if let Some(let_statements) = let_statements {
583+
// Generate:
584+
// {
585+
// super let …
586+
// super let …
587+
// <core::fmt::Arguments>::new_…(…)
588+
// }
589+
let call = ctx.arena.alloc(ctx.expr(macsp, call));
590+
let block = ctx.block_all(macsp, ctx.arena.alloc_from_iter(let_statements), Some(call));
591+
hir::ExprKind::Block(block, None)
592+
} else {
593+
call
661594
}
662-
663-
MayContainYieldPoint.visit_expr(e).is_break()
664595
}
665596

666597
fn for_all_argument_indexes(template: &mut [FormatArgsPiece], mut f: impl FnMut(&mut usize)) {

compiler/rustc_ast_lowering/src/lib.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2230,6 +2230,26 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
22302230
self.stmt(span, hir::StmtKind::Let(self.arena.alloc(local)))
22312231
}
22322232

2233+
fn stmt_super_let_pat(
2234+
&mut self,
2235+
span: Span,
2236+
pat: &'hir hir::Pat<'hir>,
2237+
init: Option<&'hir hir::Expr<'hir>>,
2238+
) -> hir::Stmt<'hir> {
2239+
let hir_id = self.next_id();
2240+
let local = hir::LetStmt {
2241+
super_: Some(span),
2242+
hir_id,
2243+
init,
2244+
pat,
2245+
els: None,
2246+
source: hir::LocalSource::Normal,
2247+
span: self.lower_span(span),
2248+
ty: None,
2249+
};
2250+
self.stmt(span, hir::StmtKind::Let(self.arena.alloc(local)))
2251+
}
2252+
22332253
fn block_expr(&mut self, expr: &'hir hir::Expr<'hir>) -> &'hir hir::Block<'hir> {
22342254
self.block_all(expr.span, &[], Some(expr))
22352255
}

library/core/src/fmt/mod.rs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -635,7 +635,7 @@ impl<'a> Arguments<'a> {
635635
/// When using the format_args!() macro, this function is used to generate the
636636
/// Arguments structure.
637637
#[inline]
638-
pub const fn new_v1<const P: usize, const A: usize>(
638+
pub fn new_v1<const P: usize, const A: usize>(
639639
pieces: &'a [&'static str; P],
640640
args: &'a [rt::Argument<'a>; A],
641641
) -> Arguments<'a> {
@@ -651,6 +651,18 @@ impl<'a> Arguments<'a> {
651651
/// 2. Every `rt::Placeholder::position` value within `fmt` must be a valid index of `args`.
652652
/// 3. Every `rt::Count::Param` within `fmt` must contain a valid index of `args`.
653653
#[inline]
654+
#[cfg(not(bootstrap))]
655+
pub unsafe fn new_v1_formatted(
656+
pieces: &'a [&'static str],
657+
args: &'a [rt::Argument<'a>],
658+
fmt: &'a [rt::Placeholder],
659+
) -> Arguments<'a> {
660+
Arguments { pieces, fmt: Some(fmt), args }
661+
}
662+
663+
/// Bootstrap only.
664+
#[cfg(bootstrap)]
665+
#[inline]
654666
pub const fn new_v1_formatted(
655667
pieces: &'a [&'static str],
656668
args: &'a [rt::Argument<'a>],

library/core/src/fmt/rt.rs

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -190,16 +190,8 @@ impl Argument<'_> {
190190
}
191191
}
192192

193-
/// Used by `format_args` when all arguments are gone after inlining,
194-
/// when using `&[]` would incorrectly allow for a bigger lifetime.
195-
///
196-
/// This fails without format argument inlining, and that shouldn't be different
197-
/// when the argument is inlined:
198-
///
199-
/// ```compile_fail,E0716
200-
/// let f = format_args!("{}", "a");
201-
/// println!("{f}");
202-
/// ```
193+
/// Bootstrap only.
194+
#[cfg(bootstrap)]
203195
#[inline]
204196
pub const fn none() -> [Self; 0] {
205197
[]

library/coretests/tests/fmt/mod.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,22 @@ mod builders;
22
mod float;
33
mod num;
44

5+
#[test]
6+
#[cfg(not(bootstrap))]
7+
fn test_lifetime() {
8+
// Trigger all different forms of expansion,
9+
// and check that each of them can be stored as a variable.
10+
let a = format_args!("hello");
11+
let a = format_args!("hello {a}");
12+
let a = format_args!("hello {a:1}");
13+
let a = format_args!("hello {a} {a:?}");
14+
assert_eq!(a.to_string(), "hello hello hello hello hello hello hello");
15+
16+
// Without arguments, it should also work in consts.
17+
const A: std::fmt::Arguments<'static> = format_args!("hello");
18+
assert_eq!(A.to_string(), "hello");
19+
}
20+
521
#[test]
622
fn test_format_flags() {
723
// No residual flags left by pointer formatting

src/tools/tidy/src/issues.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -311,7 +311,6 @@ ui/borrowck/issue-104639-lifetime-order.rs
311311
ui/borrowck/issue-10876.rs
312312
ui/borrowck/issue-109271-pass-self-into-closure.rs
313313
ui/borrowck/issue-111554.rs
314-
ui/borrowck/issue-114374-invalid-help-fmt-args.rs
315314
ui/borrowck/issue-11493.rs
316315
ui/borrowck/issue-115259-suggest-iter-mut.rs
317316
ui/borrowck/issue-119915-bad-clone-suggestion.rs

tests/coverage/closure.cov-map

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -83,58 +83,60 @@ Number of file 0 mappings: 1
8383
Highest counter ID seen: (none)
8484

8585
Function name: closure::main::{closure#14}
86-
Raw bytes (22): 0x[01, 01, 01, 01, 05, 03, 01, b3, 01, 0d, 02, 1b, 05, 02, 1e, 00, 25, 02, 00, 2f, 00, 33]
86+
Raw bytes (27): 0x[01, 01, 01, 01, 05, 04, 01, b4, 01, 11, 00, 21, 01, 01, 14, 00, 1b, 05, 00, 1e, 00, 25, 02, 00, 2f, 00, 33]
8787
Number of files: 1
8888
- file 0 => global file 1
8989
Number of expressions: 1
9090
- expression 0 operands: lhs = Counter(0), rhs = Counter(1)
91-
Number of file 0 mappings: 3
92-
- Code(Counter(0)) at (prev + 179, 13) to (start + 2, 27)
93-
- Code(Counter(1)) at (prev + 2, 30) to (start + 0, 37)
91+
Number of file 0 mappings: 4
92+
- Code(Counter(0)) at (prev + 180, 17) to (start + 0, 33)
93+
- Code(Counter(0)) at (prev + 1, 20) to (start + 0, 27)
94+
- Code(Counter(1)) at (prev + 0, 30) to (start + 0, 37)
9495
- Code(Expression(0, Sub)) at (prev + 0, 47) to (start + 0, 51)
9596
= (c0 - c1)
9697
Highest counter ID seen: c1
9798

9899
Function name: closure::main::{closure#15}
99-
Raw bytes (37): 0x[01, 01, 01, 01, 05, 06, 01, bb, 01, 09, 00, 0a, 01, 01, 0d, 00, 15, 01, 01, 11, 01, 1b, 05, 01, 1e, 00, 25, 02, 00, 2f, 00, 33, 01, 02, 09, 00, 0a]
100+
Raw bytes (37): 0x[01, 01, 01, 01, 05, 06, 01, bb, 01, 09, 00, 0a, 01, 01, 0d, 01, 21, 01, 02, 14, 00, 1b, 05, 00, 1e, 00, 25, 02, 00, 2f, 00, 33, 01, 02, 09, 00, 0a]
100101
Number of files: 1
101102
- file 0 => global file 1
102103
Number of expressions: 1
103104
- expression 0 operands: lhs = Counter(0), rhs = Counter(1)
104105
Number of file 0 mappings: 6
105106
- Code(Counter(0)) at (prev + 187, 9) to (start + 0, 10)
106-
- Code(Counter(0)) at (prev + 1, 13) to (start + 0, 21)
107-
- Code(Counter(0)) at (prev + 1, 17) to (start + 1, 27)
108-
- Code(Counter(1)) at (prev + 1, 30) to (start + 0, 37)
107+
- Code(Counter(0)) at (prev + 1, 13) to (start + 1, 33)
108+
- Code(Counter(0)) at (prev + 2, 20) to (start + 0, 27)
109+
- Code(Counter(1)) at (prev + 0, 30) to (start + 0, 37)
109110
- Code(Expression(0, Sub)) at (prev + 0, 47) to (start + 0, 51)
110111
= (c0 - c1)
111112
- Code(Counter(0)) at (prev + 2, 9) to (start + 0, 10)
112113
Highest counter ID seen: c1
113114

114115
Function name: closure::main::{closure#16}
115-
Raw bytes (22): 0x[01, 01, 01, 01, 05, 03, 01, c5, 01, 0d, 02, 1b, 05, 02, 1e, 00, 25, 02, 00, 2f, 00, 33]
116+
Raw bytes (27): 0x[01, 01, 01, 01, 05, 04, 01, c6, 01, 11, 00, 21, 01, 01, 14, 00, 1b, 05, 00, 1e, 00, 25, 02, 00, 2f, 00, 33]
116117
Number of files: 1
117118
- file 0 => global file 1
118119
Number of expressions: 1
119120
- expression 0 operands: lhs = Counter(0), rhs = Counter(1)
120-
Number of file 0 mappings: 3
121-
- Code(Counter(0)) at (prev + 197, 13) to (start + 2, 27)
122-
- Code(Counter(1)) at (prev + 2, 30) to (start + 0, 37)
121+
Number of file 0 mappings: 4
122+
- Code(Counter(0)) at (prev + 198, 17) to (start + 0, 33)
123+
- Code(Counter(0)) at (prev + 1, 20) to (start + 0, 27)
124+
- Code(Counter(1)) at (prev + 0, 30) to (start + 0, 37)
123125
- Code(Expression(0, Sub)) at (prev + 0, 47) to (start + 0, 51)
124126
= (c0 - c1)
125127
Highest counter ID seen: c1
126128

127129
Function name: closure::main::{closure#17}
128-
Raw bytes (37): 0x[01, 01, 01, 01, 05, 06, 01, cd, 01, 09, 00, 0a, 01, 01, 0d, 00, 15, 01, 01, 11, 01, 1b, 05, 01, 1e, 00, 25, 02, 00, 2f, 00, 33, 01, 02, 09, 00, 0a]
130+
Raw bytes (37): 0x[01, 01, 01, 01, 05, 06, 01, cd, 01, 09, 00, 0a, 01, 01, 0d, 01, 21, 01, 02, 14, 00, 1b, 05, 00, 1e, 00, 25, 02, 00, 2f, 00, 33, 01, 02, 09, 00, 0a]
129131
Number of files: 1
130132
- file 0 => global file 1
131133
Number of expressions: 1
132134
- expression 0 operands: lhs = Counter(0), rhs = Counter(1)
133135
Number of file 0 mappings: 6
134136
- Code(Counter(0)) at (prev + 205, 9) to (start + 0, 10)
135-
- Code(Counter(0)) at (prev + 1, 13) to (start + 0, 21)
136-
- Code(Counter(0)) at (prev + 1, 17) to (start + 1, 27)
137-
- Code(Counter(1)) at (prev + 1, 30) to (start + 0, 37)
137+
- Code(Counter(0)) at (prev + 1, 13) to (start + 1, 33)
138+
- Code(Counter(0)) at (prev + 2, 20) to (start + 0, 27)
139+
- Code(Counter(1)) at (prev + 0, 30) to (start + 0, 37)
138140
- Code(Expression(0, Sub)) at (prev + 0, 47) to (start + 0, 51)
139141
= (c0 - c1)
140142
- Code(Counter(0)) at (prev + 2, 9) to (start + 0, 10)

0 commit comments

Comments
 (0)