@@ -12,6 +12,34 @@ use rustc_span::{
12
12
} ;
13
13
use std:: borrow:: Cow ;
14
14
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
+
15
43
impl < ' hir > LoweringContext < ' _ , ' hir > {
16
44
pub ( crate ) fn lower_format_args ( & mut self , sp : Span , fmt : & FormatArgs ) -> hir:: ExprKind < ' hir > {
17
45
// Never call the const constructor of `fmt::Arguments` if the
@@ -20,10 +48,104 @@ impl<'hir> LoweringContext<'_, 'hir> {
20
48
let mut fmt = Cow :: Borrowed ( fmt) ;
21
49
if self . tcx . sess . opts . unstable_opts . flatten_format_args {
22
50
fmt = flatten_format_args ( fmt) ;
23
- fmt = inline_literals ( fmt) ;
51
+ fmt = self . inline_literals ( fmt) ;
24
52
}
25
53
expand_format_args ( self , sp, & fmt, allow_const)
26
54
}
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
+ }
27
149
}
28
150
29
151
/// Flattens nested `format_args!()` into one.
@@ -103,82 +225,6 @@ fn flatten_format_args(mut fmt: Cow<'_, FormatArgs>) -> Cow<'_, FormatArgs> {
103
225
fmt
104
226
}
105
227
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
-
182
228
#[ derive( Copy , Clone , Debug , Hash , PartialEq , Eq ) ]
183
229
enum ArgumentType {
184
230
Format ( FormatTrait ) ,
0 commit comments