44// The encoding format for inline spans were obtained by optimizing over crates in rustc/libstd.
55// See https://internals.rust-lang.org/t/rfc-compiler-refactoring-spans/1357/28
66
7- use crate :: def_id:: LocalDefId ;
7+ use crate :: def_id:: { DefIndex , LocalDefId } ;
88use crate :: hygiene:: SyntaxContext ;
99use crate :: SPAN_TRACK ;
1010use crate :: { BytePos , SpanData } ;
@@ -13,8 +13,8 @@ use rustc_data_structures::fx::FxIndexSet;
1313
1414/// A compressed span.
1515///
16- /// Whereas [`SpanData`] is 12 bytes, which is a bit too big to stick everywhere, `Span`
17- /// is a form that only takes up 8 bytes, with less space for the length and
16+ /// Whereas [`SpanData`] is 16 bytes, which is a bit too big to stick everywhere, `Span`
17+ /// is a form that only takes up 8 bytes, with less space for the length, parent and
1818/// context. The vast majority (99.9%+) of `SpanData` instances will fit within
1919/// those 8 bytes; any `SpanData` whose fields don't fit into a `Span` are
2020/// stored in a separate interner table, and the `Span` will index into that
@@ -25,11 +25,17 @@ use rustc_data_structures::fx::FxIndexSet;
2525/// slower because only 80--90% of spans could be stored inline (even less in
2626/// very large crates) and so the interner was used a lot more.
2727///
28- /// Inline (compressed) format:
28+ /// Inline (compressed) format with no parent :
2929/// - `span.base_or_index == span_data.lo`
3030/// - `span.len_or_tag == len == span_data.hi - span_data.lo` (must be `<= MAX_LEN`)
3131/// - `span.ctxt == span_data.ctxt` (must be `<= MAX_CTXT`)
3232///
33+ /// Inline (compressed) format with root context:
34+ /// - `span.base_or_index == span_data.lo`
35+ /// - `span.len_or_tag == len == span_data.hi - span_data.lo` (must be `<= MAX_LEN`)
36+ /// - `span.len_or_tag` has top bit (`PARENT_MASK`) set
37+ /// - `span.ctxt == span_data.parent` (must be `<= MAX_CTXT`)
38+ ///
3339/// Interned format:
3440/// - `span.base_or_index == index` (indexes into the interner table)
3541/// - `span.len_or_tag == LEN_TAG` (high bit set, all other bits are zero)
@@ -76,7 +82,8 @@ pub struct Span {
7682 ctxt_or_zero : u16 ,
7783}
7884
79- const LEN_TAG : u16 = 0b1000_0000_0000_0000 ;
85+ const LEN_TAG : u16 = 0b1111_1111_1111_1111 ;
86+ const PARENT_MASK : u16 = 0b1000_0000_0000_0000 ;
8087const MAX_LEN : u32 = 0b0111_1111_1111_1111 ;
8188const MAX_CTXT : u32 = 0b1111_1111_1111_1111 ;
8289
@@ -97,15 +104,27 @@ impl Span {
97104
98105 let ( base, len, ctxt2) = ( lo. 0 , hi. 0 - lo. 0 , ctxt. as_u32 ( ) ) ;
99106
100- if len <= MAX_LEN && ctxt2 <= MAX_CTXT && parent. is_none ( ) {
101- // Inline format.
102- Span { base_or_index : base, len_or_tag : len as u16 , ctxt_or_zero : ctxt2 as u16 }
103- } else {
104- // Interned format.
105- let index =
106- with_span_interner ( |interner| interner. intern ( & SpanData { lo, hi, ctxt, parent } ) ) ;
107- Span { base_or_index : index, len_or_tag : LEN_TAG , ctxt_or_zero : 0 }
107+ if len <= MAX_LEN && ctxt2 <= MAX_CTXT {
108+ let len_or_tag = len as u16 ;
109+ debug_assert_eq ! ( len_or_tag & PARENT_MASK , 0 ) ;
110+ if let Some ( parent) = parent {
111+ // Inline format with parent.
112+ let len_or_tag = len_or_tag | PARENT_MASK ;
113+ let parent = parent. local_def_index . as_u32 ( ) ;
114+ if ctxt2 == SyntaxContext :: root ( ) . as_u32 ( ) && parent <= MAX_CTXT {
115+ return Span { base_or_index : base, len_or_tag, ctxt_or_zero : parent as u16 } ;
116+ }
117+ } else if ctxt2 <= MAX_CTXT {
118+ // Inline format.
119+ let ctxt_or_zero = ctxt2 as u16 ;
120+ return Span { base_or_index : base, len_or_tag, ctxt_or_zero } ;
121+ }
108122 }
123+
124+ // Interned format.
125+ let index =
126+ with_span_interner ( |interner| interner. intern ( & SpanData { lo, hi, ctxt, parent } ) ) ;
127+ Span { base_or_index : index, len_or_tag : LEN_TAG , ctxt_or_zero : 0 }
109128 }
110129
111130 #[ inline]
@@ -123,12 +142,23 @@ impl Span {
123142 pub fn data_untracked ( self ) -> SpanData {
124143 if self . len_or_tag != LEN_TAG {
125144 // Inline format.
126- debug_assert ! ( self . len_or_tag as u32 <= MAX_LEN ) ;
127- SpanData {
128- lo : BytePos ( self . base_or_index ) ,
129- hi : BytePos ( self . base_or_index + self . len_or_tag as u32 ) ,
130- ctxt : SyntaxContext :: from_u32 ( self . ctxt_or_zero as u32 ) ,
131- parent : None ,
145+ if self . len_or_tag & PARENT_MASK == 0 {
146+ SpanData {
147+ lo : BytePos ( self . base_or_index ) ,
148+ hi : BytePos ( self . base_or_index + self . len_or_tag as u32 ) ,
149+ ctxt : SyntaxContext :: from_u32 ( self . ctxt_or_zero as u32 ) ,
150+ parent : None ,
151+ }
152+ } else {
153+ let len = self . len_or_tag & !PARENT_MASK ;
154+ let parent =
155+ LocalDefId { local_def_index : DefIndex :: from_u32 ( self . ctxt_or_zero as u32 ) } ;
156+ SpanData {
157+ lo : BytePos ( self . base_or_index ) ,
158+ hi : BytePos ( self . base_or_index + len as u32 ) ,
159+ ctxt : SyntaxContext :: root ( ) ,
160+ parent : Some ( parent) ,
161+ }
132162 }
133163 } else {
134164 // Interned format.
0 commit comments