]> git.lizzy.rs Git - rust.git/blobdiff - compiler/rustc_span/src/span_encoding.rs
Rollup merge of #106886 - dtolnay:fastinstall, r=Mark-Simulacrum
[rust.git] / compiler / rustc_span / src / span_encoding.rs
index f0e91e5a6a917c3373fa399a2e496fe243ab805d..d48c4f7e5a8119f1c84f2a9b32d043dcab05e898 100644 (file)
@@ -4,7 +4,7 @@
 // The encoding format for inline spans were obtained by optimizing over crates in rustc/libstd.
 // See https://internals.rust-lang.org/t/rfc-compiler-refactoring-spans/1357/28
 
-use crate::def_id::LocalDefId;
+use crate::def_id::{DefIndex, LocalDefId};
 use crate::hygiene::SyntaxContext;
 use crate::SPAN_TRACK;
 use crate::{BytePos, SpanData};
@@ -13,8 +13,8 @@
 
 /// A compressed span.
 ///
-/// Whereas [`SpanData`] is 12 bytes, which is a bit too big to stick everywhere, `Span`
-/// is a form that only takes up 8 bytes, with less space for the length and
+/// Whereas [`SpanData`] is 16 bytes, which is a bit too big to stick everywhere, `Span`
+/// is a form that only takes up 8 bytes, with less space for the length, parent and
 /// context. The vast majority (99.9%+) of `SpanData` instances will fit within
 /// those 8 bytes; any `SpanData` whose fields don't fit into a `Span` are
 /// stored in a separate interner table, and the `Span` will index into that
@@ -25,7 +25,7 @@
 /// slower because only 80--90% of spans could be stored inline (even less in
 /// very large crates) and so the interner was used a lot more.
 ///
-/// Inline (compressed) format:
+/// Inline (compressed) format with no parent:
 /// - `span.base_or_index == span_data.lo`
 /// - `span.len_or_tag == len == span_data.hi - span_data.lo` (must be `<= MAX_LEN`)
 /// - `span.ctxt_or_tag == span_data.ctxt` (must be `<= MAX_CTXT`)
 /// - `span.len_or_tag == LEN_TAG` (high bit set, all other bits are zero)
 /// - `span.ctxt_or_tag == span_data.ctxt` (must be `<= MAX_CTXT`)
 ///
+/// Inline (compressed) format with root context:
+/// - `span.base_or_index == span_data.lo`
+/// - `span.len_or_tag == len == span_data.hi - span_data.lo` (must be `<= MAX_LEN`)
+/// - `span.len_or_tag` has top bit (`PARENT_MASK`) set
+/// - `span.ctxt == span_data.parent` (must be `<= MAX_CTXT`)
+///
 /// Interned format:
 /// - `span.base_or_index == index` (indexes into the interner table)
 /// - `span.len_or_tag == LEN_TAG` (high bit set, all other bits are zero)
@@ -73,7 +79,8 @@ pub struct Span {
     ctxt_or_tag: u16,
 }
 
-const LEN_TAG: u16 = 0b1000_0000_0000_0000;
+const LEN_TAG: u16 = 0b1111_1111_1111_1111;
+const PARENT_MASK: u16 = 0b1000_0000_0000_0000;
 const MAX_LEN: u32 = 0b0111_1111_1111_1111;
 const CTXT_TAG: u32 = 0b1111_1111_1111_1111;
 const MAX_CTXT: u32 = CTXT_TAG - 1;
@@ -95,16 +102,32 @@ pub fn new(
 
         let (base, len, ctxt2) = (lo.0, hi.0 - lo.0, ctxt.as_u32());
 
-        if len <= MAX_LEN && ctxt2 <= MAX_CTXT && parent.is_none() {
-            // Inline format.
-            Span { base_or_index: base, len_or_tag: len as u16, ctxt_or_tag: ctxt2 as u16 }
-        } else {
-            // Interned format.
-            let index =
-                with_span_interner(|interner| interner.intern(&SpanData { lo, hi, ctxt, parent }));
-            let ctxt_or_tag = if ctxt2 <= MAX_CTXT { ctxt2 } else { CTXT_TAG } as u16;
-            Span { base_or_index: index, len_or_tag: LEN_TAG, ctxt_or_tag }
+        if len <= MAX_LEN && ctxt2 <= MAX_CTXT {
+            let len_or_tag = len as u16;
+            debug_assert_eq!(len_or_tag & PARENT_MASK, 0);
+
+            if let Some(parent) = parent {
+                // Inline format with parent.
+                let len_or_tag = len_or_tag | PARENT_MASK;
+                let parent2 = parent.local_def_index.as_u32();
+                if ctxt2 == SyntaxContext::root().as_u32() && parent2 <= MAX_CTXT {
+                    return Span { base_or_index: base, len_or_tag, ctxt_or_tag: parent2 as u16 };
+                }
+            } else {
+                // Inline format with ctxt.
+                return Span {
+                    base_or_index: base,
+                    len_or_tag: len as u16,
+                    ctxt_or_tag: ctxt2 as u16,
+                };
+            }
         }
+
+        // Interned format.
+        let index =
+            with_span_interner(|interner| interner.intern(&SpanData { lo, hi, ctxt, parent }));
+        let ctxt_or_tag = if ctxt2 <= MAX_CTXT { ctxt2 } else { CTXT_TAG } as u16;
+        Span { base_or_index: index, len_or_tag: LEN_TAG, ctxt_or_tag }
     }
 
     #[inline]
@@ -122,12 +145,25 @@ pub fn data(self) -> SpanData {
     pub fn data_untracked(self) -> SpanData {
         if self.len_or_tag != LEN_TAG {
             // Inline format.
-            debug_assert!(self.len_or_tag as u32 <= MAX_LEN);
-            SpanData {
-                lo: BytePos(self.base_or_index),
-                hi: BytePos(self.base_or_index + self.len_or_tag as u32),
-                ctxt: SyntaxContext::from_u32(self.ctxt_or_tag as u32),
-                parent: None,
+            if self.len_or_tag & PARENT_MASK == 0 {
+                debug_assert!(self.len_or_tag as u32 <= MAX_LEN);
+                SpanData {
+                    lo: BytePos(self.base_or_index),
+                    hi: BytePos(self.base_or_index + self.len_or_tag as u32),
+                    ctxt: SyntaxContext::from_u32(self.ctxt_or_tag as u32),
+                    parent: None,
+                }
+            } else {
+                let len = self.len_or_tag & !PARENT_MASK;
+                debug_assert!(len as u32 <= MAX_LEN);
+                let parent =
+                    LocalDefId { local_def_index: DefIndex::from_u32(self.ctxt_or_tag as u32) };
+                SpanData {
+                    lo: BytePos(self.base_or_index),
+                    hi: BytePos(self.base_or_index + len as u32),
+                    ctxt: SyntaxContext::root(),
+                    parent: Some(parent),
+                }
             }
         } else {
             // Interned format.
@@ -141,8 +177,14 @@ pub fn data_untracked(self) -> SpanData {
     pub fn ctxt(self) -> SyntaxContext {
         let ctxt_or_tag = self.ctxt_or_tag as u32;
         if ctxt_or_tag <= MAX_CTXT {
-            // Inline format or interned format with inline ctxt.
-            SyntaxContext::from_u32(ctxt_or_tag)
+            if self.len_or_tag == LEN_TAG || self.len_or_tag & PARENT_MASK == 0 {
+                // Inline format or interned format with inline ctxt.
+                SyntaxContext::from_u32(ctxt_or_tag)
+            } else {
+                // Inline format or interned format with inline parent.
+                // We know that the SyntaxContext is root.
+                SyntaxContext::root()
+            }
         } else {
             // Interned format.
             let index = self.base_or_index;