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,7 +25,7 @@ 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_or_tag == span_data.ctxt` (must be `<= MAX_CTXT`)
@@ -35,6 +35,12 @@ use rustc_data_structures::fx::FxIndexSet;
3535/// - `span.len_or_tag == LEN_TAG` (high bit set, all other bits are zero)
3636/// - `span.ctxt_or_tag == span_data.ctxt` (must be `<= MAX_CTXT`)
3737///
38+ /// Inline (compressed) format with root context:
39+ /// - `span.base_or_index == span_data.lo`
40+ /// - `span.len_or_tag == len == span_data.hi - span_data.lo` (must be `<= MAX_LEN`)
41+ /// - `span.len_or_tag` has top bit (`PARENT_MASK`) set
42+ /// - `span.ctxt == span_data.parent` (must be `<= MAX_CTXT`)
43+ ///
3844/// Interned format:
3945/// - `span.base_or_index == index` (indexes into the interner table)
4046/// - `span.len_or_tag == LEN_TAG` (high bit set, all other bits are zero)
@@ -73,7 +79,8 @@ pub struct Span {
7379 ctxt_or_tag : u16 ,
7480}
7581
76- const LEN_TAG : u16 = 0b1000_0000_0000_0000 ;
82+ const LEN_TAG : u16 = 0b1111_1111_1111_1111 ;
83+ const PARENT_MASK : u16 = 0b1000_0000_0000_0000 ;
7784const MAX_LEN : u32 = 0b0111_1111_1111_1111 ;
7885const CTXT_TAG : u32 = 0b1111_1111_1111_1111 ;
7986const MAX_CTXT : u32 = CTXT_TAG - 1 ;
@@ -95,16 +102,32 @@ impl Span {
95102
96103 let ( base, len, ctxt2) = ( lo. 0 , hi. 0 - lo. 0 , ctxt. as_u32 ( ) ) ;
97104
98- if len <= MAX_LEN && ctxt2 <= MAX_CTXT && parent. is_none ( ) {
99- // Inline format.
100- Span { base_or_index : base, len_or_tag : len as u16 , ctxt_or_tag : ctxt2 as u16 }
101- } else {
102- // Interned format.
103- let index =
104- with_span_interner ( |interner| interner. intern ( & SpanData { lo, hi, ctxt, parent } ) ) ;
105- let ctxt_or_tag = if ctxt2 <= MAX_CTXT { ctxt2 } else { CTXT_TAG } as u16 ;
106- Span { base_or_index : index, len_or_tag : LEN_TAG , ctxt_or_tag }
105+ if len <= MAX_LEN && ctxt2 <= MAX_CTXT {
106+ let len_or_tag = len as u16 ;
107+ debug_assert_eq ! ( len_or_tag & PARENT_MASK , 0 ) ;
108+
109+ if let Some ( parent) = parent {
110+ // Inline format with parent.
111+ let len_or_tag = len_or_tag | PARENT_MASK ;
112+ let parent2 = parent. local_def_index . as_u32 ( ) ;
113+ if ctxt2 == SyntaxContext :: root ( ) . as_u32 ( ) && parent2 <= MAX_CTXT {
114+ return Span { base_or_index : base, len_or_tag, ctxt_or_tag : parent2 as u16 } ;
115+ }
116+ } else {
117+ // Inline format with ctxt.
118+ return Span {
119+ base_or_index : base,
120+ len_or_tag : len as u16 ,
121+ ctxt_or_tag : ctxt2 as u16 ,
122+ } ;
123+ }
107124 }
125+
126+ // Interned format.
127+ let index =
128+ with_span_interner ( |interner| interner. intern ( & SpanData { lo, hi, ctxt, parent } ) ) ;
129+ let ctxt_or_tag = if ctxt2 <= MAX_CTXT { ctxt2 } else { CTXT_TAG } as u16 ;
130+ Span { base_or_index : index, len_or_tag : LEN_TAG , ctxt_or_tag }
108131 }
109132
110133 #[ inline]
@@ -122,12 +145,25 @@ impl Span {
122145 pub fn data_untracked ( self ) -> SpanData {
123146 if self . len_or_tag != LEN_TAG {
124147 // Inline format.
125- debug_assert ! ( self . len_or_tag as u32 <= MAX_LEN ) ;
126- SpanData {
127- lo : BytePos ( self . base_or_index ) ,
128- hi : BytePos ( self . base_or_index + self . len_or_tag as u32 ) ,
129- ctxt : SyntaxContext :: from_u32 ( self . ctxt_or_tag as u32 ) ,
130- parent : None ,
148+ if self . len_or_tag & PARENT_MASK == 0 {
149+ debug_assert ! ( self . len_or_tag as u32 <= MAX_LEN ) ;
150+ SpanData {
151+ lo : BytePos ( self . base_or_index ) ,
152+ hi : BytePos ( self . base_or_index + self . len_or_tag as u32 ) ,
153+ ctxt : SyntaxContext :: from_u32 ( self . ctxt_or_tag as u32 ) ,
154+ parent : None ,
155+ }
156+ } else {
157+ let len = self . len_or_tag & !PARENT_MASK ;
158+ debug_assert ! ( len as u32 <= MAX_LEN ) ;
159+ let parent =
160+ LocalDefId { local_def_index : DefIndex :: from_u32 ( self . ctxt_or_tag as u32 ) } ;
161+ SpanData {
162+ lo : BytePos ( self . base_or_index ) ,
163+ hi : BytePos ( self . base_or_index + len as u32 ) ,
164+ ctxt : SyntaxContext :: root ( ) ,
165+ parent : Some ( parent) ,
166+ }
131167 }
132168 } else {
133169 // Interned format.
@@ -141,8 +177,14 @@ impl Span {
141177 pub fn ctxt ( self ) -> SyntaxContext {
142178 let ctxt_or_tag = self . ctxt_or_tag as u32 ;
143179 if ctxt_or_tag <= MAX_CTXT {
144- // Inline format or interned format with inline ctxt.
145- SyntaxContext :: from_u32 ( ctxt_or_tag)
180+ if self . len_or_tag == LEN_TAG || self . len_or_tag & PARENT_MASK == 0 {
181+ // Inline format or interned format with inline ctxt.
182+ SyntaxContext :: from_u32 ( ctxt_or_tag)
183+ } else {
184+ // Inline format or interned format with inline parent.
185+ // We know that the SyntaxContext is root.
186+ SyntaxContext :: root ( )
187+ }
146188 } else {
147189 // Interned format.
148190 let index = self . base_or_index ;
0 commit comments