@@ -7,13 +7,172 @@ use rustc_hir as hir;
7
7
use rustc_span:: {
8
8
sym,
9
9
symbol:: { kw, Ident } ,
10
- Span ,
10
+ Span , Symbol ,
11
11
} ;
12
+ use std:: borrow:: Cow ;
12
13
13
14
impl < ' hir > LoweringContext < ' _ , ' hir > {
14
15
pub ( crate ) fn lower_format_args ( & mut self , sp : Span , fmt : & FormatArgs ) -> hir:: ExprKind < ' hir > {
15
- expand_format_args ( self , sp, fmt)
16
+ // Never call the const constructor of `fmt::Arguments` if the
17
+ // format_args!() had any arguments _before_ flattening/inlining.
18
+ let allow_const = fmt. arguments . all_args ( ) . is_empty ( ) ;
19
+ let mut fmt = Cow :: Borrowed ( fmt) ;
20
+ if self . tcx . sess . opts . unstable_opts . flatten_format_args {
21
+ fmt = flatten_format_args ( fmt) ;
22
+ fmt = inline_literals ( fmt) ;
23
+ }
24
+ expand_format_args ( self , sp, & fmt, allow_const)
25
+ }
26
+ }
27
+
28
+ /// Flattens nested `format_args!()` into one.
29
+ ///
30
+ /// Turns
31
+ ///
32
+ /// `format_args!("a {} {} {}.", 1, format_args!("b{}!", 2), 3)`
33
+ ///
34
+ /// into
35
+ ///
36
+ /// `format_args!("a {} b{}! {}.", 1, 2, 3)`.
37
+ fn flatten_format_args ( mut fmt : Cow < ' _ , FormatArgs > ) -> Cow < ' _ , FormatArgs > {
38
+ let mut i = 0 ;
39
+ while i < fmt. template . len ( ) {
40
+ if let FormatArgsPiece :: Placeholder ( placeholder) = & fmt. template [ i]
41
+ && let FormatTrait :: Display | FormatTrait :: Debug = & placeholder. format_trait
42
+ && let Ok ( arg_index) = placeholder. argument . index
43
+ && let arg = fmt. arguments . all_args ( ) [ arg_index] . expr . peel_parens_and_refs ( )
44
+ && let ExprKind :: FormatArgs ( _) = & arg. kind
45
+ // Check that this argument is not used by any other placeholders.
46
+ && fmt. template . iter ( ) . enumerate ( ) . all ( |( j, p) |
47
+ i == j ||
48
+ !matches ! ( p, FormatArgsPiece :: Placeholder ( placeholder)
49
+ if placeholder. argument. index == Ok ( arg_index) )
50
+ )
51
+ {
52
+ // Now we need to mutate the outer FormatArgs.
53
+ // If this is the first time, this clones the outer FormatArgs.
54
+ let fmt = fmt. to_mut ( ) ;
55
+
56
+ // Take the inner FormatArgs out of the outer arguments, and
57
+ // replace it by the inner arguments. (We can't just put those at
58
+ // the end, because we need to preserve the order of evaluation.)
59
+
60
+ let args = fmt. arguments . all_args_mut ( ) ;
61
+ let remaining_args = args. split_off ( arg_index + 1 ) ;
62
+ let old_arg_offset = args. len ( ) ;
63
+ let mut fmt2 = & mut args. pop ( ) . unwrap ( ) . expr ; // The inner FormatArgs.
64
+ let fmt2 = loop { // Unwrap the Expr to get to the FormatArgs.
65
+ match & mut fmt2. kind {
66
+ ExprKind :: Paren ( inner) | ExprKind :: AddrOf ( BorrowKind :: Ref , _, inner) => fmt2 = inner,
67
+ ExprKind :: FormatArgs ( fmt2) => break fmt2,
68
+ _ => unreachable ! ( ) ,
69
+ }
70
+ } ;
71
+
72
+ args. append ( fmt2. arguments . all_args_mut ( ) ) ;
73
+ let new_arg_offset = args. len ( ) ;
74
+ args. extend ( remaining_args) ;
75
+
76
+ // Correct the indexes that refer to the arguments after the newly inserted arguments.
77
+ for_all_argument_indexes ( & mut fmt. template , |index| {
78
+ if * index >= old_arg_offset {
79
+ * index -= old_arg_offset;
80
+ * index += new_arg_offset;
81
+ }
82
+ } ) ;
83
+
84
+ // Now merge the placeholders:
85
+
86
+ let rest = fmt. template . split_off ( i + 1 ) ;
87
+ fmt. template . pop ( ) ; // remove the placeholder for the nested fmt args.
88
+ // Insert the pieces from the nested format args, but correct any
89
+ // placeholders to point to the correct argument index.
90
+ for_all_argument_indexes ( & mut fmt2. template , |index| * index += arg_index) ;
91
+ fmt. template . append ( & mut fmt2. template ) ;
92
+ fmt. template . extend ( rest) ;
93
+
94
+ // Don't increment `i` here, so we recurse into the newly added pieces.
95
+ } else {
96
+ i += 1 ;
97
+ }
16
98
}
99
+ fmt
100
+ }
101
+
102
+ /// Inline literals into the format string.
103
+ ///
104
+ /// Turns
105
+ ///
106
+ /// `format_args!("Hello, {}! {} {}", "World", 123, x)`
107
+ ///
108
+ /// into
109
+ ///
110
+ /// `format_args!("Hello, World! 123 {}", x)`.
111
+ fn inline_literals ( mut fmt : Cow < ' _ , FormatArgs > ) -> Cow < ' _ , FormatArgs > {
112
+ let mut was_inlined = vec ! [ false ; fmt. arguments. all_args( ) . len( ) ] ;
113
+ let mut inlined_anything = false ;
114
+
115
+ for i in 0 ..fmt. template . len ( ) {
116
+ let FormatArgsPiece :: Placeholder ( placeholder) = & fmt. template [ i] else { continue } ;
117
+ let Ok ( arg_index) = placeholder. argument . index else { continue } ;
118
+
119
+ let mut literal = None ;
120
+
121
+ if let FormatTrait :: Display = placeholder. format_trait
122
+ && placeholder. format_options == Default :: default ( )
123
+ && let arg = fmt. arguments . all_args ( ) [ arg_index] . expr . peel_parens_and_refs ( )
124
+ && let ExprKind :: Lit ( lit) = arg. kind
125
+ {
126
+ if let token:: LitKind :: Str | token:: LitKind :: StrRaw ( _) = lit. kind
127
+ && let Ok ( LitKind :: Str ( s, _) ) = LitKind :: from_token_lit ( lit)
128
+ {
129
+ literal = Some ( s) ;
130
+ } else if let token:: LitKind :: Integer = lit. kind
131
+ && let Ok ( LitKind :: Int ( n, _) ) = LitKind :: from_token_lit ( lit)
132
+ {
133
+ literal = Some ( Symbol :: intern ( & n. to_string ( ) ) ) ;
134
+ }
135
+ }
136
+
137
+ if let Some ( literal) = literal {
138
+ // Now we need to mutate the outer FormatArgs.
139
+ // If this is the first time, this clones the outer FormatArgs.
140
+ let fmt = fmt. to_mut ( ) ;
141
+ // Replace the placeholder with the literal.
142
+ fmt. template [ i] = FormatArgsPiece :: Literal ( literal) ;
143
+ was_inlined[ arg_index] = true ;
144
+ inlined_anything = true ;
145
+ }
146
+ }
147
+
148
+ // Remove the arguments that were inlined.
149
+ if inlined_anything {
150
+ let fmt = fmt. to_mut ( ) ;
151
+
152
+ let mut remove = was_inlined;
153
+
154
+ // Don't remove anything that's still used.
155
+ for_all_argument_indexes ( & mut fmt. template , |index| remove[ * index] = false ) ;
156
+
157
+ // Drop all the arguments that are marked for removal.
158
+ let mut remove_it = remove. iter ( ) ;
159
+ fmt. arguments . all_args_mut ( ) . retain ( |_| remove_it. next ( ) != Some ( & true ) ) ;
160
+
161
+ // Calculate the mapping of old to new indexes for the remaining arguments.
162
+ let index_map: Vec < usize > = remove
163
+ . into_iter ( )
164
+ . scan ( 0 , |i, remove| {
165
+ let mapped = * i;
166
+ * i += !remove as usize ;
167
+ Some ( mapped)
168
+ } )
169
+ . collect ( ) ;
170
+
171
+ // Correct the indexes that refer to arguments that have shifted position.
172
+ for_all_argument_indexes ( & mut fmt. template , |index| * index = index_map[ * index] ) ;
173
+ }
174
+
175
+ fmt
17
176
}
18
177
19
178
#[ derive( Copy , Clone , Debug , Hash , PartialEq , Eq ) ]
@@ -189,11 +348,26 @@ fn expand_format_args<'hir>(
189
348
ctx : & mut LoweringContext < ' _ , ' hir > ,
190
349
macsp : Span ,
191
350
fmt : & FormatArgs ,
351
+ allow_const : bool ,
192
352
) -> hir:: ExprKind < ' hir > {
353
+ let mut incomplete_lit = String :: new ( ) ;
193
354
let lit_pieces =
194
355
ctx. arena . alloc_from_iter ( fmt. template . iter ( ) . enumerate ( ) . filter_map ( |( i, piece) | {
195
356
match piece {
196
- & FormatArgsPiece :: Literal ( s) => Some ( ctx. expr_str ( fmt. span , s) ) ,
357
+ & FormatArgsPiece :: Literal ( s) => {
358
+ // Coalesce adjacent literal pieces.
359
+ if let Some ( FormatArgsPiece :: Literal ( _) ) = fmt. template . get ( i + 1 ) {
360
+ incomplete_lit. push_str ( s. as_str ( ) ) ;
361
+ None
362
+ } else if !incomplete_lit. is_empty ( ) {
363
+ incomplete_lit. push_str ( s. as_str ( ) ) ;
364
+ let s = Symbol :: intern ( & incomplete_lit) ;
365
+ incomplete_lit. clear ( ) ;
366
+ Some ( ctx. expr_str ( fmt. span , s) )
367
+ } else {
368
+ Some ( ctx. expr_str ( fmt. span , s) )
369
+ }
370
+ }
197
371
& FormatArgsPiece :: Placeholder ( _) => {
198
372
// Inject empty string before placeholders when not already preceded by a literal piece.
199
373
if i == 0 || matches ! ( fmt. template[ i - 1 ] , FormatArgsPiece :: Placeholder ( _) ) {
@@ -244,6 +418,18 @@ fn expand_format_args<'hir>(
244
418
245
419
let arguments = fmt. arguments . all_args ( ) ;
246
420
421
+ if allow_const && arguments. is_empty ( ) && argmap. is_empty ( ) {
422
+ // Generate:
423
+ // <core::fmt::Arguments>::new_const(lit_pieces)
424
+ let new = ctx. arena . alloc ( ctx. expr_lang_item_type_relative (
425
+ macsp,
426
+ hir:: LangItem :: FormatArguments ,
427
+ sym:: new_const,
428
+ ) ) ;
429
+ let new_args = ctx. arena . alloc_from_iter ( [ lit_pieces] ) ;
430
+ return hir:: ExprKind :: Call ( new, new_args) ;
431
+ }
432
+
247
433
// If the args array contains exactly all the original arguments once,
248
434
// in order, we can use a simple array instead of a `match` construction.
249
435
// However, if there's a yield point in any argument except the first one,
@@ -290,25 +476,14 @@ fn expand_format_args<'hir>(
290
476
let args_ident = Ident :: new ( sym:: args, macsp) ;
291
477
let ( args_pat, args_hir_id) = ctx. pat_ident ( macsp, args_ident) ;
292
478
let args = ctx. arena . alloc_from_iter ( argmap. iter ( ) . map ( |& ( arg_index, ty) | {
293
- if let Some ( arg) = arguments. get ( arg_index) {
294
- let sp = arg. expr . span . with_ctxt ( macsp. ctxt ( ) ) ;
295
- let args_ident_expr = ctx. expr_ident ( macsp, args_ident, args_hir_id) ;
296
- let arg = ctx. arena . alloc ( ctx. expr (
297
- sp,
298
- hir:: ExprKind :: Field (
299
- args_ident_expr,
300
- Ident :: new ( sym:: integer ( arg_index) , macsp) ,
301
- ) ,
302
- ) ) ;
303
- make_argument ( ctx, sp, arg, ty)
304
- } else {
305
- ctx. expr (
306
- macsp,
307
- hir:: ExprKind :: Err (
308
- ctx. tcx . sess . delay_span_bug ( macsp, format ! ( "no arg at {arg_index}" ) ) ,
309
- ) ,
310
- )
311
- }
479
+ let arg = & arguments[ arg_index] ;
480
+ let sp = arg. expr . span . with_ctxt ( macsp. ctxt ( ) ) ;
481
+ let args_ident_expr = ctx. expr_ident ( macsp, args_ident, args_hir_id) ;
482
+ let arg = ctx. arena . alloc ( ctx. expr (
483
+ sp,
484
+ hir:: ExprKind :: Field ( args_ident_expr, Ident :: new ( sym:: integer ( arg_index) , macsp) ) ,
485
+ ) ) ;
486
+ make_argument ( ctx, sp, arg, ty)
312
487
} ) ) ;
313
488
let elements: Vec < _ > = arguments
314
489
. iter ( )
@@ -409,3 +584,22 @@ fn may_contain_yield_point(e: &ast::Expr) -> bool {
409
584
visitor. visit_expr ( e) ;
410
585
visitor. 0
411
586
}
587
+
588
+ fn for_all_argument_indexes ( template : & mut [ FormatArgsPiece ] , mut f : impl FnMut ( & mut usize ) ) {
589
+ for piece in template {
590
+ let FormatArgsPiece :: Placeholder ( placeholder) = piece else { continue } ;
591
+ if let Ok ( index) = & mut placeholder. argument . index {
592
+ f ( index) ;
593
+ }
594
+ if let Some ( FormatCount :: Argument ( FormatArgPosition { index : Ok ( index) , .. } ) ) =
595
+ & mut placeholder. format_options . width
596
+ {
597
+ f ( index) ;
598
+ }
599
+ if let Some ( FormatCount :: Argument ( FormatArgPosition { index : Ok ( index) , .. } ) ) =
600
+ & mut placeholder. format_options . precision
601
+ {
602
+ f ( index) ;
603
+ }
604
+ }
605
+ }
0 commit comments