@@ -87,6 +87,45 @@ pub struct Span {
87
87
ctxt_or_parent_or_marker : u16 ,
88
88
}
89
89
90
+ impl Span {
91
+ #[ inline]
92
+ fn data_inline_ctxt ( self ) -> SpanData {
93
+ let len = self . len_with_tag_or_marker as u32 ;
94
+ debug_assert ! ( len <= MAX_LEN ) ;
95
+ SpanData {
96
+ lo : BytePos ( self . lo_or_index ) ,
97
+ hi : BytePos ( self . lo_or_index . debug_strict_add ( len) ) ,
98
+ ctxt : SyntaxContext :: from_u32 ( self . ctxt_or_parent_or_marker as u32 ) ,
99
+ parent : None ,
100
+ }
101
+ }
102
+ #[ inline]
103
+ fn data_inline_parent ( self ) -> SpanData {
104
+ let len = ( self . len_with_tag_or_marker & !PARENT_TAG ) as u32 ;
105
+ debug_assert ! ( len <= MAX_LEN ) ;
106
+ let parent = LocalDefId {
107
+ local_def_index : DefIndex :: from_u32 ( self . ctxt_or_parent_or_marker as u32 ) ,
108
+ } ;
109
+ SpanData {
110
+ lo : BytePos ( self . lo_or_index ) ,
111
+ hi : BytePos ( self . lo_or_index . debug_strict_add ( len) ) ,
112
+ ctxt : SyntaxContext :: root ( ) ,
113
+ parent : Some ( parent) ,
114
+ }
115
+ }
116
+ #[ inline]
117
+ fn data_partially_interned ( self ) -> SpanData {
118
+ SpanData {
119
+ ctxt : SyntaxContext :: from_u32 ( self . ctxt_or_parent_or_marker as u32 ) ,
120
+ ..with_span_interner ( |interner| interner. spans [ self . lo_or_index as usize ] )
121
+ }
122
+ }
123
+ #[ inline]
124
+ fn data_interned ( self ) -> SpanData {
125
+ with_span_interner ( |interner| interner. spans [ self . lo_or_index as usize ] )
126
+ }
127
+ }
128
+
90
129
// `MAX_LEN` is chosen so that `PARENT_TAG | MAX_LEN` is distinct from
91
130
// `BASE_LEN_INTERNED_MARKER`. (If `MAX_LEN` was 1 higher, this wouldn't be true.)
92
131
const MAX_LEN : u32 = 0b0111_1111_1111_1110 ;
@@ -111,42 +150,49 @@ impl Span {
111
150
std:: mem:: swap ( & mut lo, & mut hi) ;
112
151
}
113
152
114
- let ( lo2 , len, ctxt2 ) = ( lo . 0 , hi . 0 - lo . 0 , ctxt . as_u32 ( ) ) ;
115
-
153
+ // Small len may enable one of fully inline formats (or may not).
154
+ let ( len , ctxt32 ) = ( hi . 0 - lo . 0 , ctxt . as_u32 ( ) ) ;
116
155
if len <= MAX_LEN {
117
- if ctxt2 <= MAX_CTXT && parent. is_none ( ) {
156
+ if ctxt32 <= MAX_CTXT && parent. is_none ( ) {
118
157
// Inline-context format.
119
158
return Span {
120
- lo_or_index : lo2 ,
159
+ lo_or_index : lo . 0 ,
121
160
len_with_tag_or_marker : len as u16 ,
122
- ctxt_or_parent_or_marker : ctxt2 as u16 ,
161
+ ctxt_or_parent_or_marker : ctxt32 as u16 ,
123
162
} ;
124
- } else if ctxt2 == SyntaxContext :: root ( ) . as_u32 ( )
163
+ } else if ctxt32 == 0
125
164
&& let Some ( parent) = parent
126
- && let parent2 = parent. local_def_index . as_u32 ( )
127
- && parent2 <= MAX_CTXT
165
+ && let parent32 = parent. local_def_index . as_u32 ( )
166
+ && parent32 <= MAX_CTXT
128
167
{
129
168
// Inline-parent format.
130
169
return Span {
131
- lo_or_index : lo2 ,
170
+ lo_or_index : lo . 0 ,
132
171
len_with_tag_or_marker : PARENT_TAG | len as u16 ,
133
- ctxt_or_parent_or_marker : parent2 as u16 ,
172
+ ctxt_or_parent_or_marker : parent32 as u16 ,
134
173
} ;
135
174
}
136
175
}
137
176
138
- // Partially-interned or fully-interned format.
139
- let index =
140
- with_span_interner ( |interner| interner. intern ( & SpanData { lo, hi, ctxt, parent } ) ) ;
141
- let ctxt_or_parent_or_marker = if ctxt2 <= MAX_CTXT {
142
- ctxt2 as u16 // partially-interned
143
- } else {
144
- CTXT_INTERNED_MARKER // fully-interned
177
+ // Otherwise small ctxt may enable the partially inline format.
178
+ let index = |ctxt| {
179
+ with_span_interner ( |interner| interner. intern ( & SpanData { lo, hi, ctxt, parent } ) )
145
180
} ;
146
- Span {
147
- lo_or_index : index,
148
- len_with_tag_or_marker : BASE_LEN_INTERNED_MARKER ,
149
- ctxt_or_parent_or_marker,
181
+ if ctxt32 <= MAX_CTXT {
182
+ // Partially-interned format.
183
+ Span {
184
+ // Interned ctxt should never be read, so it can use any value.
185
+ lo_or_index : index ( SyntaxContext :: from_u32 ( u32:: MAX ) ) ,
186
+ len_with_tag_or_marker : BASE_LEN_INTERNED_MARKER ,
187
+ ctxt_or_parent_or_marker : ctxt32 as u16 ,
188
+ }
189
+ } else {
190
+ // Interned format.
191
+ Span {
192
+ lo_or_index : index ( ctxt) ,
193
+ len_with_tag_or_marker : BASE_LEN_INTERNED_MARKER ,
194
+ ctxt_or_parent_or_marker : CTXT_INTERNED_MARKER ,
195
+ }
150
196
}
151
197
}
152
198
@@ -166,34 +212,17 @@ impl Span {
166
212
if self . len_with_tag_or_marker != BASE_LEN_INTERNED_MARKER {
167
213
if self . len_with_tag_or_marker & PARENT_TAG == 0 {
168
214
// Inline-context format.
169
- let len = self . len_with_tag_or_marker as u32 ;
170
- debug_assert ! ( len <= MAX_LEN ) ;
171
- SpanData {
172
- lo : BytePos ( self . lo_or_index ) ,
173
- hi : BytePos ( self . lo_or_index . debug_strict_add ( len) ) ,
174
- ctxt : SyntaxContext :: from_u32 ( self . ctxt_or_parent_or_marker as u32 ) ,
175
- parent : None ,
176
- }
215
+ self . data_inline_ctxt ( )
177
216
} else {
178
217
// Inline-parent format.
179
- let len = ( self . len_with_tag_or_marker & !PARENT_TAG ) as u32 ;
180
- debug_assert ! ( len <= MAX_LEN ) ;
181
- let parent = LocalDefId {
182
- local_def_index : DefIndex :: from_u32 ( self . ctxt_or_parent_or_marker as u32 ) ,
183
- } ;
184
- SpanData {
185
- lo : BytePos ( self . lo_or_index ) ,
186
- hi : BytePos ( self . lo_or_index . debug_strict_add ( len) ) ,
187
- ctxt : SyntaxContext :: root ( ) ,
188
- parent : Some ( parent) ,
189
- }
218
+ self . data_inline_parent ( )
190
219
}
220
+ } else if self . ctxt_or_parent_or_marker != CTXT_INTERNED_MARKER {
221
+ // Partially-interned format.
222
+ self . data_partially_interned ( )
191
223
} else {
192
- // Fully-interned or partially-interned format. In either case,
193
- // the interned value contains all the data, so we don't need to
194
- // distinguish them.
195
- let index = self . lo_or_index ;
196
- with_span_interner ( |interner| interner. spans [ index as usize ] )
224
+ // Interned format.
225
+ self . data_interned ( )
197
226
}
198
227
}
199
228
@@ -214,27 +243,73 @@ impl Span {
214
243
}
215
244
}
216
245
246
+ // For optimization we are interested in cases in which the context is inline and the context
247
+ // update doesn't change format. All non-inline or format changing scenarios require accessing
248
+ // interner and can fall back to `Span::new`.
249
+ #[ inline]
250
+ pub fn update_ctxt ( & mut self , update : impl FnOnce ( SyntaxContext ) -> SyntaxContext ) {
251
+ let ( updated_ctxt32, data) ;
252
+ if self . len_with_tag_or_marker != BASE_LEN_INTERNED_MARKER {
253
+ if self . len_with_tag_or_marker & PARENT_TAG == 0 {
254
+ // Inline-context format.
255
+ updated_ctxt32 =
256
+ update ( SyntaxContext :: from_u32 ( self . ctxt_or_parent_or_marker as u32 ) ) . as_u32 ( ) ;
257
+ // Any small new context including zero will preserve the format.
258
+ if updated_ctxt32 <= MAX_CTXT {
259
+ self . ctxt_or_parent_or_marker = updated_ctxt32 as u16 ;
260
+ return ;
261
+ }
262
+ data = self . data_inline_ctxt ( ) ;
263
+ } else {
264
+ // Inline-parent format.
265
+ updated_ctxt32 = update ( SyntaxContext :: root ( ) ) . as_u32 ( ) ;
266
+ // Only if the new context is zero the format will be preserved.
267
+ if updated_ctxt32 == 0 {
268
+ // Do nothing.
269
+ return ;
270
+ }
271
+ data = self . data_inline_parent ( ) ;
272
+ }
273
+ } else if self . ctxt_or_parent_or_marker != CTXT_INTERNED_MARKER {
274
+ // Partially-interned format.
275
+ updated_ctxt32 =
276
+ update ( SyntaxContext :: from_u32 ( self . ctxt_or_parent_or_marker as u32 ) ) . as_u32 ( ) ;
277
+ // Any small new context excluding zero will preserve the format.
278
+ // Zero may change the format to `InlineParent` if parent and len are small enough.
279
+ if updated_ctxt32 <= MAX_CTXT && updated_ctxt32 != 0 {
280
+ self . ctxt_or_parent_or_marker = updated_ctxt32 as u16 ;
281
+ return ;
282
+ }
283
+ data = self . data_partially_interned ( ) ;
284
+ } else {
285
+ // Interned format.
286
+ data = self . data_interned ( ) ;
287
+ updated_ctxt32 = update ( data. ctxt ) . as_u32 ( ) ;
288
+ }
289
+
290
+ // We could not keep the span in the same inline format, fall back to the complete logic.
291
+ * self = data. with_ctxt ( SyntaxContext :: from_u32 ( updated_ctxt32) ) ;
292
+ }
293
+
217
294
// Returns either syntactic context, if it can be retrieved without taking the interner lock,
218
295
// or an index into the interner if it cannot.
219
296
#[ inline]
220
297
fn inline_ctxt ( self ) -> Result < SyntaxContext , usize > {
221
- Ok ( if self . len_with_tag_or_marker != BASE_LEN_INTERNED_MARKER {
298
+ if self . len_with_tag_or_marker != BASE_LEN_INTERNED_MARKER {
222
299
if self . len_with_tag_or_marker & PARENT_TAG == 0 {
223
300
// Inline-context format.
224
- SyntaxContext :: from_u32 ( self . ctxt_or_parent_or_marker as u32 )
301
+ Ok ( SyntaxContext :: from_u32 ( self . ctxt_or_parent_or_marker as u32 ) )
225
302
} else {
226
- // Inline-parent format. We know that the SyntaxContext is root.
227
- SyntaxContext :: root ( )
303
+ // Inline-parent format.
304
+ Ok ( SyntaxContext :: root ( ) )
228
305
}
229
306
} else if self . ctxt_or_parent_or_marker != CTXT_INTERNED_MARKER {
230
- // Partially-interned format. This path avoids looking up the
231
- // interned value, and is the whole point of the
232
- // partially-interned format.
233
- SyntaxContext :: from_u32 ( self . ctxt_or_parent_or_marker as u32 )
307
+ // Partially-interned format.
308
+ Ok ( SyntaxContext :: from_u32 ( self . ctxt_or_parent_or_marker as u32 ) )
234
309
} else {
235
- // Fully-interned format.
236
- return Err ( self . lo_or_index as usize ) ;
237
- } )
310
+ // Interned format.
311
+ Err ( self . lo_or_index as usize )
312
+ }
238
313
}
239
314
240
315
/// This function is used as a fast path when decoding the full `SpanData` is not necessary.
0 commit comments