@@ -7,13 +7,172 @@ use rustc_hir as hir;
77use rustc_span:: {
88 sym,
99 symbol:: { kw, Ident } ,
10- Span ,
10+ Span , Symbol ,
1111} ;
12+ use std:: borrow:: Cow ;
1213
1314impl < ' hir > LoweringContext < ' _ , ' hir > {
1415 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+ }
1698 }
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
17176}
18177
19178#[ derive( Copy , Clone , Debug , Hash , PartialEq , Eq ) ]
@@ -189,11 +348,26 @@ fn expand_format_args<'hir>(
189348 ctx : & mut LoweringContext < ' _ , ' hir > ,
190349 macsp : Span ,
191350 fmt : & FormatArgs ,
351+ allow_const : bool ,
192352) -> hir:: ExprKind < ' hir > {
353+ let mut incomplete_lit = String :: new ( ) ;
193354 let lit_pieces =
194355 ctx. arena . alloc_from_iter ( fmt. template . iter ( ) . enumerate ( ) . filter_map ( |( i, piece) | {
195356 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+ }
197371 & FormatArgsPiece :: Placeholder ( _) => {
198372 // Inject empty string before placeholders when not already preceded by a literal piece.
199373 if i == 0 || matches ! ( fmt. template[ i - 1 ] , FormatArgsPiece :: Placeholder ( _) ) {
@@ -244,6 +418,18 @@ fn expand_format_args<'hir>(
244418
245419 let arguments = fmt. arguments . all_args ( ) ;
246420
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+
247433 // If the args array contains exactly all the original arguments once,
248434 // in order, we can use a simple array instead of a `match` construction.
249435 // However, if there's a yield point in any argument except the first one,
@@ -290,25 +476,14 @@ fn expand_format_args<'hir>(
290476 let args_ident = Ident :: new ( sym:: args, macsp) ;
291477 let ( args_pat, args_hir_id) = ctx. pat_ident ( macsp, args_ident) ;
292478 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)
312487 } ) ) ;
313488 let elements: Vec < _ > = arguments
314489 . iter ( )
@@ -409,3 +584,22 @@ fn may_contain_yield_point(e: &ast::Expr) -> bool {
409584 visitor. visit_expr ( e) ;
410585 visitor. 0
411586}
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