@@ -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