@@ -12,6 +12,34 @@ use rustc_span::{
1212} ;
1313use std:: borrow:: Cow ;
1414
15+ fn int_ty_max ( int_ty : IntTy ) -> Option < u128 > {
16+ match int_ty {
17+ // isize is platform-dependent, so we should use
18+ // TyCtxt.data_layout.pointer_size
19+ // This is available, for example, from a LoweringContext
20+ IntTy :: Isize => None ,
21+ IntTy :: I8 => Some ( i8:: MAX as u128 ) ,
22+ IntTy :: I16 => Some ( i16:: MAX as u128 ) ,
23+ IntTy :: I32 => Some ( i32:: MAX as u128 ) ,
24+ IntTy :: I64 => Some ( i64:: MAX as u128 ) ,
25+ IntTy :: I128 => Some ( i128:: MAX as u128 ) ,
26+ }
27+ }
28+
29+ fn uint_ty_max ( uint_ty : UintTy ) -> Option < u128 > {
30+ match uint_ty {
31+ // usize is platform-dependent, so we should use
32+ // TyCtxt.data_layout.pointer_size
33+ // This is available, for example, from a LoweringContext
34+ UintTy :: Usize => None ,
35+ UintTy :: U8 => Some ( u8:: MAX as u128 ) ,
36+ UintTy :: U16 => Some ( u16:: MAX as u128 ) ,
37+ UintTy :: U32 => Some ( u32:: MAX as u128 ) ,
38+ UintTy :: U64 => Some ( u64:: MAX as u128 ) ,
39+ UintTy :: U128 => Some ( u128:: MAX as u128 ) ,
40+ }
41+ }
42+
1543impl < ' hir > LoweringContext < ' _ , ' hir > {
1644 pub ( crate ) fn lower_format_args ( & mut self , sp : Span , fmt : & FormatArgs ) -> hir:: ExprKind < ' hir > {
1745 // Never call the const constructor of `fmt::Arguments` if the
@@ -20,10 +48,104 @@ impl<'hir> LoweringContext<'_, 'hir> {
2048 let mut fmt = Cow :: Borrowed ( fmt) ;
2149 if self . tcx . sess . opts . unstable_opts . flatten_format_args {
2250 fmt = flatten_format_args ( fmt) ;
23- fmt = inline_literals ( fmt) ;
51+ fmt = self . inline_literals ( fmt) ;
2452 }
2553 expand_format_args ( self , sp, & fmt, allow_const)
2654 }
55+
56+ /// Try to convert a literal into an interned string
57+ fn try_inline_lit ( & self , lit : token:: Lit ) -> Option < Symbol > {
58+ match LitKind :: from_token_lit ( lit) {
59+ Ok ( LitKind :: Str ( s, _) ) => Some ( s) ,
60+ Ok ( LitKind :: Int ( n, ty) ) => {
61+ // platform-dependent usize and isize MAX
62+ let usize_bits = self . tcx . data_layout . pointer_size . bits ( ) ;
63+ let usize_max = if usize_bits >= 128 { u128:: MAX } else { 1u128 << usize_bits - 1 } ;
64+ let isize_max = usize_max >> 1 ;
65+ match ty {
66+ // unsuffixed integer literals are assumed to be i32's
67+ LitIntType :: Unsuffixed => ( n <= i32:: MAX as u128 ) . then_some ( Symbol :: intern ( & n. to_string ( ) ) ) ,
68+ LitIntType :: Signed ( int_ty) => {
69+ let max_literal = int_ty_max ( int_ty) . unwrap_or ( isize_max) ;
70+ ( n <= max_literal) . then_some ( Symbol :: intern ( & n. to_string ( ) ) )
71+ }
72+ LitIntType :: Unsigned ( uint_ty) => {
73+ let max_literal = uint_ty_max ( uint_ty) . unwrap_or ( usize_max) ;
74+ ( n <= max_literal) . then_some ( Symbol :: intern ( & n. to_string ( ) ) )
75+ }
76+ }
77+ }
78+ _ => None ,
79+ }
80+ }
81+
82+ /// Inline literals into the format string.
83+ ///
84+ /// Turns
85+ ///
86+ /// `format_args!("Hello, {}! {} {}", "World", 123, x)`
87+ ///
88+ /// into
89+ ///
90+ /// `format_args!("Hello, World! 123 {}", x)`.
91+ fn inline_literals < ' fmt > ( & self , mut fmt : Cow < ' fmt , FormatArgs > ) -> Cow < ' fmt , FormatArgs > {
92+ let mut was_inlined = vec ! [ false ; fmt. arguments. all_args( ) . len( ) ] ;
93+ let mut inlined_anything = false ;
94+
95+ for i in 0 ..fmt. template . len ( ) {
96+ let FormatArgsPiece :: Placeholder ( placeholder) = & fmt. template [ i] else { continue } ;
97+ let Ok ( arg_index) = placeholder. argument . index else { continue } ;
98+
99+ let mut literal = None ;
100+
101+ if let FormatTrait :: Display = placeholder. format_trait
102+ && placeholder. format_options == Default :: default ( )
103+ && let arg = fmt. arguments . all_args ( ) [ arg_index] . expr . peel_parens_and_refs ( )
104+ && let ExprKind :: Lit ( lit) = arg. kind
105+ {
106+ literal = self . try_inline_lit ( lit) ;
107+ }
108+
109+ if let Some ( literal) = literal {
110+ // Now we need to mutate the outer FormatArgs.
111+ // If this is the first time, this clones the outer FormatArgs.
112+ let fmt = fmt. to_mut ( ) ;
113+ // Replace the placeholder with the literal.
114+ fmt. template [ i] = FormatArgsPiece :: Literal ( literal) ;
115+ was_inlined[ arg_index] = true ;
116+ inlined_anything = true ;
117+ }
118+ }
119+
120+ // Remove the arguments that were inlined.
121+ if inlined_anything {
122+ let fmt = fmt. to_mut ( ) ;
123+
124+ let mut remove = was_inlined;
125+
126+ // Don't remove anything that's still used.
127+ for_all_argument_indexes ( & mut fmt. template , |index| remove[ * index] = false ) ;
128+
129+ // Drop all the arguments that are marked for removal.
130+ let mut remove_it = remove. iter ( ) ;
131+ fmt. arguments . all_args_mut ( ) . retain ( |_| remove_it. next ( ) != Some ( & true ) ) ;
132+
133+ // Calculate the mapping of old to new indexes for the remaining arguments.
134+ let index_map: Vec < usize > = remove
135+ . into_iter ( )
136+ . scan ( 0 , |i, remove| {
137+ let mapped = * i;
138+ * i += !remove as usize ;
139+ Some ( mapped)
140+ } )
141+ . collect ( ) ;
142+
143+ // Correct the indexes that refer to arguments that have shifted position.
144+ for_all_argument_indexes ( & mut fmt. template , |index| * index = index_map[ * index] ) ;
145+ }
146+
147+ fmt
148+ }
27149}
28150
29151/// Flattens nested `format_args!()` into one.
@@ -103,82 +225,6 @@ fn flatten_format_args(mut fmt: Cow<'_, FormatArgs>) -> Cow<'_, FormatArgs> {
103225 fmt
104226}
105227
106- /// Inline literals into the format string.
107- ///
108- /// Turns
109- ///
110- /// `format_args!("Hello, {}! {} {}", "World", 123, x)`
111- ///
112- /// into
113- ///
114- /// `format_args!("Hello, World! 123 {}", x)`.
115- fn inline_literals ( mut fmt : Cow < ' _ , FormatArgs > ) -> Cow < ' _ , FormatArgs > {
116- let mut was_inlined = vec ! [ false ; fmt. arguments. all_args( ) . len( ) ] ;
117- let mut inlined_anything = false ;
118-
119- for i in 0 ..fmt. template . len ( ) {
120- let FormatArgsPiece :: Placeholder ( placeholder) = & fmt. template [ i] else { continue } ;
121- let Ok ( arg_index) = placeholder. argument . index else { continue } ;
122-
123- let mut literal = None ;
124-
125- if let FormatTrait :: Display = placeholder. format_trait
126- && placeholder. format_options == Default :: default ( )
127- && let arg = fmt. arguments . all_args ( ) [ arg_index] . expr . peel_parens_and_refs ( )
128- && let ExprKind :: Lit ( lit) = arg. kind
129- {
130- if let token:: LitKind :: Str | token:: LitKind :: StrRaw ( _) = lit. kind
131- && let Ok ( LitKind :: Str ( s, _) ) = LitKind :: from_token_lit ( lit)
132- {
133- literal = Some ( s) ;
134- } else if let token:: LitKind :: Integer = lit. kind
135- && let Ok ( LitKind :: Int ( n, _) ) = LitKind :: from_token_lit ( lit)
136- {
137- literal = Some ( Symbol :: intern ( & n. to_string ( ) ) ) ;
138- }
139- }
140-
141- if let Some ( literal) = literal {
142- // Now we need to mutate the outer FormatArgs.
143- // If this is the first time, this clones the outer FormatArgs.
144- let fmt = fmt. to_mut ( ) ;
145- // Replace the placeholder with the literal.
146- fmt. template [ i] = FormatArgsPiece :: Literal ( literal) ;
147- was_inlined[ arg_index] = true ;
148- inlined_anything = true ;
149- }
150- }
151-
152- // Remove the arguments that were inlined.
153- if inlined_anything {
154- let fmt = fmt. to_mut ( ) ;
155-
156- let mut remove = was_inlined;
157-
158- // Don't remove anything that's still used.
159- for_all_argument_indexes ( & mut fmt. template , |index| remove[ * index] = false ) ;
160-
161- // Drop all the arguments that are marked for removal.
162- let mut remove_it = remove. iter ( ) ;
163- fmt. arguments . all_args_mut ( ) . retain ( |_| remove_it. next ( ) != Some ( & true ) ) ;
164-
165- // Calculate the mapping of old to new indexes for the remaining arguments.
166- let index_map: Vec < usize > = remove
167- . into_iter ( )
168- . scan ( 0 , |i, remove| {
169- let mapped = * i;
170- * i += !remove as usize ;
171- Some ( mapped)
172- } )
173- . collect ( ) ;
174-
175- // Correct the indexes that refer to arguments that have shifted position.
176- for_all_argument_indexes ( & mut fmt. template , |index| * index = index_map[ * index] ) ;
177- }
178-
179- fmt
180- }
181-
182228#[ derive( Copy , Clone , Debug , Hash , PartialEq , Eq ) ]
183229enum ArgumentType {
184230 Format ( FormatTrait ) ,
0 commit comments