Skip to main content

rustc_span/
span_encoding.rs

1use std::cmp::Ordering;
2use std::hash::Hash;
3
4use rustc_data_structures::fx::FxIndexSet;
5use rustc_data_structures::stable_hash::RawSpan;
6// This code is very hot and uses lots of arithmetic, avoid overflow checks for performance.
7// See https://github.com/rust-lang/rust/pull/119440#issuecomment-1874255727
8use rustc_serialize::int_overflow::DebugStrictAdd;
9
10use crate::def_id::{DefIndex, LocalDefId};
11use crate::hygiene::SyntaxContext;
12use crate::{BytePos, SPAN_TRACK, SpanData};
13
14/// A compressed span.
15///
16/// [`SpanData`] is 16 bytes, which is too big to stick everywhere. `Span` is
17/// only 8 bytes. A large majority of `SpanData` instances can be made to fit
18/// within those 8 bytes. Any `SpanData` whose fields don't fit into a `Span`
19/// are stored in a separate interner table, and the `Span` will index into
20/// that table.
21///
22/// An earlier version of this code used only 4 bytes for `Span`, but that was
23/// slower because many fewer spans could be stored inline and the interner was
24/// used a lot more. That version of the code also predated the storage of
25/// parents.
26///
27/// Experiments with uncompressed spans yielded worse performance in most cases
28/// because memory usage and cache miss rates are significantly higher without
29/// compression.
30///
31/// There are four different span formats and each packs data into the 8 bytes
32/// in a different way. A variable-length prefix in the high bits identifies
33/// which format is being used. It is an invariant that we always use the first
34/// listed format whose requirements are satisfied. `len` is `hi - lo`.
35///
36/// - Inline-context format (requires 15-bit length, 16-bit context, and no parent):
37///
38///     `[ prefix=0 | len:15 | ctxt:16 | lo:32 ]`
39///
40/// - Inline-parent format (requires 14-bit length, root context, and 15-bit parent):
41///
42///     `[ prefix=110 | len:14 | parent:15 | lo:32 ]`
43///
44/// - Inline-pair format (requires 24-bit lo, 7-bit length, 15-bit context, 16-bit parent):
45///
46///     `[ prefix=10 | len:7 | ctxt:15 | parent:16 | lo:24 ]`
47///
48/// - Interned format (all cases not covered above):
49///
50///     `[ prefix=111 | ctxt:29 | index:32 ]`
51///
52/// Notes about this design.
53///
54/// - This configuration was the best one found after many measurements of
55///   real-world crates, and replaced an earlier 8-byte design.
56///
57/// - The size of `lo` depends on the size of the crate and its dependencies.
58///   Crates whose `lo` size exceeds 24-bits are extremely rare.
59///
60/// - The most common numbers of bits needed for `len` are from 0 to 7,
61///   with a peak usually at 3 or 4, and then it drops off quickly from 8
62///   onwards. Lengths larger than 14 bits are rare, but many crates will have
63///   a small number of such lengths (sometimes 20+ bits).
64///
65/// - The number of bits needed for `ctxt` and `parent` values depend partly on
66///   the crate size and partly on the form of the code. For both fields,
67///   values needing more than 15 bits are very rare, and most crates won't hit
68///   these limits.
69///
70/// - The ctxt is stored inline in every format (Every ctxt must fit in
71///   29 bits; if we ever hit that we'd have many gigabytes of contexts, and
72///   certainly would have OOM'd anyway.) This means reading the ctxt never
73///   requires checking the interner. This is good because the ctxt is often
74///   consulted by itself. It also means we don't need to store the ctxt in the
75///   interner table, which we achieve by using `SpanDataNoCtxt`, which
76///   minimizes the number of unique values that need to be interned.
77///
78/// - The interned format is typically used for < 1% of all spans in
79///   incremental builds and < 0.1% of all spans in non-incremental builds.
80///   The difference is because only incremental builds use the parent field.
81///
82/// - The use of `repr(packed(4))` means `Span` has an alignment of 4 bytes,
83///   which keeps many types that contain spans (e.g. AST nodes) smaller.
84///   `repr(packed(1))` isn't worth it; it only shrinks a few types and
85///   performance is a tiny bit worse.
86///
87/// In order to reliably use parented spans in incremental compilation,
88/// accesses to `lo` and `hi` must introduce a dependency to the parent definition's span.
89/// This is performed using the callback `SPAN_TRACK` to access the query engine.
90#[derive(#[automatically_derived]#[doc(hidden)]unsafe impl ::core::clone::TrivialClone
    for Span {
}
#[automatically_derived]impl ::core::clone::Clone for Span {
    #[inline]
    fn clone(&self) -> Self {
        let _: ::core::clone::AssertParamIsClone<u64>;
        *self
    }
}Clone, #[automatically_derived]impl ::core::marker::Copy for Span { }Copy, #[automatically_derived]impl ::core::cmp::Eq for Span {
    #[inline]
    #[doc(hidden)]
    #[coverage(off)]
    fn assert_fields_are_eq(&self) {
        let _: ::core::cmp::AssertParamIsEq<u64>;
    }
}Eq, #[automatically_derived]impl ::core::marker::StructuralPartialEq for Span { }
#[automatically_derived]impl ::core::cmp::PartialEq for Span {
    #[inline]
    fn eq(&self, other: &Self) -> bool { ({ self.0 }) == ({ other.0 }) }
}PartialEq, #[automatically_derived]impl ::core::hash::Hash for Span {
    #[inline]
    fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) {
        ::core::hash::Hash::hash(&{ self.0 }, state)
    }
}Hash)]
91#[rustc_pass_by_value]
92#[repr(packed(4))]
93pub struct Span(u64);
94
95// `SyntaxContext` (`SpanData::ctxt`) and `LocalDefId` (within `SpanData::parent`) aren't orderable.
96// If you want to order spans just on `lo`/`hi`, use explicit comparisons involving `Span::lo_hi`.
97// Or you can wrap your span within `OrdSpan` which impls `PartialOrd`/`Ord` via `Span::lo_hi`.
98impl !PartialOrd for Span {}
99impl !Ord for Span {}
100
101/// Specifies a field within one of the span formats. Provides const operations that (a) let a span
102/// format be fully specified, and (b) check/read/write the field for/from/to a span.
103#[derive(#[automatically_derived]impl ::core::marker::Copy for SpanField { }Copy, #[automatically_derived]#[doc(hidden)]unsafe impl ::core::clone::TrivialClone
    for SpanField {
}
#[automatically_derived]impl ::core::clone::Clone for SpanField {
    #[inline]
    fn clone(&self) -> Self {
        let _: ::core::clone::AssertParamIsClone<u32>;
        *self
    }
}Clone)]
104struct SpanField {
105    /// The field's bit position, where zero means it's in the lowest bits.
106    shift: u32,
107    /// The field's width, in bits.
108    width: u32,
109}
110
111impl SpanField {
112    /// Specifies the top field, i.e. the highest bits. Chained with `below`.
113    #[inline]
114    const fn top(width: u32) -> Self {
115        SpanField { shift: 64 - width, width }
116    }
117
118    /// Specifies a field that comes below the field in `self`. Chainable.
119    #[inline]
120    const fn below(self, width: u32) -> Self {
121        SpanField { shift: self.shift - width, width }
122    }
123
124    /// The mask for the (downshifted to 0..n) field.
125    #[inline]
126    const fn mask(self) -> u64 {
127        (1 << self.width) - 1
128    }
129
130    /// Gets the field from a span-as-`u64` as a `u32`.
131    #[inline]
132    const fn get(self, u: u64) -> u32 {
133        ((u >> self.shift) & self.mask()) as u32
134    }
135
136    /// Sets the field to `v` in an otherwise-zero span-as-`u64`.
137    #[inline]
138    const fn set(self, v: u32) -> u64 {
139        if true {
    if !(v <= self.mask() as u32) {
        ::core::panicking::panic("assertion failed: v <= self.mask() as u32")
    };
};debug_assert!(v <= self.mask() as u32);
140        (v as u64) << self.shift
141    }
142
143    /// Returns non-zero if `v` is too big to fit in the field.
144    #[inline]
145    const fn too_big(self, v: u32) -> u32 {
146        v & !(self.mask() as u32)
147    }
148
149    /// Asserts that a field uses the bottom-most bits.
150    #[inline]
151    const fn assert_is_bottom(self) {
152        if !(self.shift == 0) {
    ::core::panicking::panic("assertion failed: self.shift == 0")
};assert!(self.shift == 0);
153    }
154}
155
156// Convenience structures for all span formats.
157// InlineCtxt is public to allow inline-only fast paths.
158#[derive(#[automatically_derived]#[doc(hidden)]unsafe impl ::core::clone::TrivialClone
    for InlineCtxt {
}
#[automatically_derived]impl ::core::clone::Clone for InlineCtxt {
    #[inline]
    fn clone(&self) -> Self {
        let _: ::core::clone::AssertParamIsClone<u32>;
        *self
    }
}Clone, #[automatically_derived]impl ::core::marker::Copy for InlineCtxt { }Copy)]
159pub(crate) struct InlineCtxt {
160    pub(crate) lo: u32,
161    pub(crate) len: u32,
162    pub(crate) ctxt: u32,
163}
164
165#[derive(#[automatically_derived]#[doc(hidden)]unsafe impl ::core::clone::TrivialClone
    for InlineParent {
}
#[automatically_derived]impl ::core::clone::Clone for InlineParent {
    #[inline]
    fn clone(&self) -> Self {
        let _: ::core::clone::AssertParamIsClone<u32>;
        *self
    }
}Clone, #[automatically_derived]impl ::core::marker::Copy for InlineParent { }Copy)]
166struct InlineParent {
167    lo: u32,
168    len: u32,
169    parent: u32,
170}
171
172#[derive(#[automatically_derived]#[doc(hidden)]unsafe impl ::core::clone::TrivialClone
    for InlinePair {
}
#[automatically_derived]impl ::core::clone::Clone for InlinePair {
    #[inline]
    fn clone(&self) -> Self {
        let _: ::core::clone::AssertParamIsClone<u32>;
        *self
    }
}Clone, #[automatically_derived]impl ::core::marker::Copy for InlinePair { }Copy)]
173struct InlinePair {
174    lo: u32,
175    len: u32,
176    ctxt: u32,
177    parent: u32,
178}
179
180#[derive(#[automatically_derived]#[doc(hidden)]unsafe impl ::core::clone::TrivialClone
    for Interned {
}
#[automatically_derived]impl ::core::clone::Clone for Interned {
    #[inline]
    fn clone(&self) -> Self {
        let _: ::core::clone::AssertParamIsClone<u32>;
        *self
    }
}Clone, #[automatically_derived]impl ::core::marker::Copy for Interned { }Copy)]
181struct Interned {
182    ctxt: u32,
183    index: u32,
184}
185
186impl InlineCtxt {
187    const PREFIX_VALUE: u32 = 0b0;
188    const PREFIX: SpanField = SpanField::top(1);
189    const LEN: SpanField = Self::PREFIX.below(15);
190    const CTXT: SpanField = Self::LEN.below(16);
191    const LO: SpanField = Self::CTXT.below(32);
192
193    #[inline]
194    pub(crate) const fn try_new_span(lo: u32, len: u32, ctxt: u32) -> Option<Span> {
195        if Self::LEN.too_big(len) | Self::CTXT.too_big(ctxt) == 0 {
196            Some(Span(
197                Self::PREFIX.set(Self::PREFIX_VALUE)
198                    | Self::LEN.set(len)
199                    | Self::CTXT.set(ctxt)
200                    | Self::LO.set(lo),
201            ))
202        } else {
203            None
204        }
205    }
206
207    #[inline]
208    fn data(self) -> SpanData {
209        SpanData {
210            lo: BytePos(self.lo),
211            hi: BytePos(self.lo.debug_strict_add(self.len)),
212            ctxt: SyntaxContext::from_u32(self.ctxt),
213            parent: None,
214        }
215    }
216
217    #[inline]
218    fn from_span(span: Span) -> InlineCtxt {
219        let u = span.0;
220        Self { lo: Self::LO.get(u), len: Self::LEN.get(u), ctxt: Self::CTXT.get(u) }
221    }
222
223    #[inline]
224    pub(crate) fn try_from_span(span: Span) -> Option<InlineCtxt> {
225        if Self::PREFIX.get(span.0) == Self::PREFIX_VALUE {
226            Some(Self::from_span(span))
227        } else {
228            None
229        }
230    }
231}
232const _: () = InlineCtxt::LO.assert_is_bottom();
233
234impl InlineParent {
235    const PREFIX_VALUE: u32 = 0b110;
236    const PREFIX: SpanField = SpanField::top(3);
237    const LEN: SpanField = Self::PREFIX.below(14);
238    const PARENT: SpanField = Self::LEN.below(15);
239    const LO: SpanField = Self::PARENT.below(32);
240
241    #[inline]
242    fn try_new_span(lo: u32, len: u32, ctxt: u32, parent: u32) -> Option<Span> {
243        if Self::LEN.too_big(len) | ctxt | Self::PARENT.too_big(parent) == 0 {
244            Some(Span(
245                Self::PREFIX.set(Self::PREFIX_VALUE)
246                    | Self::LEN.set(len)
247                    | Self::PARENT.set(parent)
248                    | Self::LO.set(lo),
249            ))
250        } else {
251            None
252        }
253    }
254
255    #[inline]
256    fn data(self) -> SpanData {
257        SpanData {
258            lo: BytePos(self.lo),
259            hi: BytePos(self.lo.debug_strict_add(self.len)),
260            ctxt: SyntaxContext::root(),
261            parent: Some(LocalDefId { local_def_index: DefIndex::from_u32(self.parent) }),
262        }
263    }
264
265    #[inline]
266    fn from_span(span: Span) -> InlineParent {
267        let u = span.0;
268        Self { lo: Self::LO.get(u), len: Self::LEN.get(u), parent: Self::PARENT.get(u) }
269    }
270}
271const _: () = InlineParent::LO.assert_is_bottom();
272
273impl InlinePair {
274    const PREFIX_VALUE: u32 = 0b10;
275    const PREFIX: SpanField = SpanField::top(2);
276    const LEN: SpanField = Self::PREFIX.below(7);
277    const CTXT: SpanField = Self::LEN.below(15);
278    const PARENT: SpanField = Self::CTXT.below(16);
279    const LO: SpanField = Self::PARENT.below(24);
280
281    #[inline]
282    fn try_new_span(lo: u32, len: u32, ctxt: u32, parent: u32) -> Option<Span> {
283        if Self::LO.too_big(lo)
284            | Self::LEN.too_big(len)
285            | Self::CTXT.too_big(ctxt)
286            | Self::PARENT.too_big(parent)
287            == 0
288        {
289            Some(Span(
290                Self::PREFIX.set(Self::PREFIX_VALUE)
291                    | Self::LEN.set(len)
292                    | Self::CTXT.set(ctxt)
293                    | Self::PARENT.set(parent)
294                    | Self::LO.set(lo),
295            ))
296        } else {
297            None
298        }
299    }
300
301    #[inline]
302    fn data(self) -> SpanData {
303        SpanData {
304            lo: BytePos(self.lo),
305            hi: BytePos(self.lo.debug_strict_add(self.len)),
306            ctxt: SyntaxContext::from_u32(self.ctxt),
307            parent: Some(LocalDefId { local_def_index: DefIndex::from_u32(self.parent) }),
308        }
309    }
310
311    #[inline]
312    fn from_span(span: Span) -> InlinePair {
313        let u = span.0;
314        Self {
315            lo: Self::LO.get(u),
316            len: Self::LEN.get(u),
317            ctxt: Self::CTXT.get(u),
318            parent: Self::PARENT.get(u),
319        }
320    }
321}
322const _: () = InlinePair::LO.assert_is_bottom();
323
324impl Interned {
325    const PREFIX_VALUE: u32 = 0b111;
326    const PREFIX: SpanField = SpanField::top(3);
327    const CTXT: SpanField = Self::PREFIX.below(29);
328    const INDEX: SpanField = Self::CTXT.below(32);
329
330    #[inline]
331    fn new_span(ctxt: u32, index: u32) -> Span {
332        Span(Self::PREFIX.set(Self::PREFIX_VALUE) | Self::CTXT.set(ctxt) | Self::INDEX.set(index))
333    }
334
335    #[inline]
336    fn data(self) -> SpanData {
337        #[cold]
338        #[inline(never)]
339        fn interned(index: u32) -> SpanDataNoCtxt {
340            with_span_interner(|interner| interner.spans[index as usize])
341        }
342
343        let SpanDataNoCtxt { lo, hi, parent } = interned(self.index);
344        SpanData { lo, hi, ctxt: SyntaxContext::from_u32(self.ctxt), parent }
345    }
346
347    #[inline]
348    fn from_span(span: Span) -> Interned {
349        let u = span.0;
350        Self { ctxt: Self::CTXT.get(u), index: Self::INDEX.get(u) }
351    }
352}
353const _: () = Interned::INDEX.assert_is_bottom();
354
355// This code is very hot, and converting span to an enum and matching on it doesn't optimize away
356// properly. So we are using a macro emulating such a match, but expand it directly to an if-else
357// chain.
358macro_rules! match_span_kind {
359    (
360        $span:expr,
361        InlineCtxt($span1:ident) => $arm1:expr,
362        InlineParent($span2:ident) => $arm2:expr,
363        InlinePair($span3:ident) => $arm3:expr,
364        Interned($span4:ident) => $arm4:expr,
365    ) => {{
366        let span64 = $span.0;
367        if InlineCtxt::PREFIX.get(span64) == InlineCtxt::PREFIX_VALUE {
368            let $span1 = InlineCtxt::from_span($span);
369            $arm1
370        } else if InlineParent::PREFIX.get(span64) == InlineParent::PREFIX_VALUE {
371            let $span2 = InlineParent::from_span($span);
372            $arm2
373        } else if InlinePair::PREFIX.get(span64) == InlinePair::PREFIX_VALUE {
374            let $span3 = InlinePair::from_span($span);
375            $arm3
376        } else {
377            let $span4 = Interned::from_span($span);
378            $arm4
379        }
380    }};
381}
382
383/// The dummy span has zero position, length, and context, and no parent.
384pub const DUMMY_SP: Span = InlineCtxt::try_new_span(0, 0, 0).unwrap();
385
386impl Span {
387    /// Create a new `Span`, handling the invalid case where `lo > hi` by swapping.
388    ///
389    /// If possible, ensure that `lo <= hi`, and call `Span::new_ordered` instead.
390    #[inline]
391    pub fn new(
392        mut lo: BytePos,
393        mut hi: BytePos,
394        ctxt: SyntaxContext,
395        parent: Option<LocalDefId>,
396    ) -> Self {
397        if lo > hi {
398            std::mem::swap(&mut lo, &mut hi);
399        }
400        Span::new_ordered(lo, hi, ctxt, parent)
401    }
402
403    /// Create a new `Span`.
404    ///
405    /// This requires that `lo <= hi`.
406    #[inline]
407    pub fn new_ordered(
408        lo: BytePos,
409        hi: BytePos,
410        ctxt: SyntaxContext,
411        parent: Option<LocalDefId>,
412    ) -> Self {
413        #[cold]
414        #[inline(never)]
415        fn interned(lo: BytePos, hi: BytePos, ctxt: u32, parent: Option<LocalDefId>) -> Span {
416            // Interned.
417            {
    match (&Interned::CTXT.too_big(ctxt), &0) {
        (left_val, right_val) => {
            if !(*left_val == *right_val) {
                let kind = ::core::panicking::AssertKind::Eq;
                ::core::panicking::assert_failed(kind, &*left_val,
                    &*right_val, ::core::option::Option::None);
            }
        }
    }
};assert_eq!(Interned::CTXT.too_big(ctxt), 0); // this is a hard limit, with no fallback
418            let index =
419                with_span_interner(|interner| interner.intern(&SpanDataNoCtxt { lo, hi, parent }));
420            Interned::new_span(ctxt, index)
421        }
422
423        if true {
    if !(lo <= hi) { ::core::panicking::panic("assertion failed: lo <= hi") };
};debug_assert!(lo <= hi);
424
425        let lo32 = lo.0;
426        let (len, ctxt32) = (hi.0 - lo32, ctxt.as_u32());
427        if let Some(parent) = parent {
428            if let Some(span) = Span::try_new_span_with_parent(lo32, len, ctxt32, parent) {
429                // InlineParent or InlinePair.
430                return span;
431            }
432        } else if let Some(span) = InlineCtxt::try_new_span(lo32, len, ctxt32) {
433            // InlineCtxt.
434            return span;
435        }
436
437        interned(lo, hi, ctxt32, parent)
438    }
439
440    /// Tries to create an `InlineParent` or `InlinePair` format span. Returns `None` if the fields
441    /// won't fit.
442    #[inline]
443    fn try_new_span_with_parent(lo: u32, len: u32, ctxt: u32, parent: LocalDefId) -> Option<Self> {
444        let parent = parent.local_def_index.as_u32();
445        InlineParent::try_new_span(lo, len, ctxt, parent)
446            .or_else(|| InlinePair::try_new_span(lo, len, ctxt, parent))
447    }
448
449    #[inline]
450    pub fn data(self) -> SpanData {
451        let data = self.data_untracked();
452        if let Some(parent) = data.parent {
453            (*SPAN_TRACK)(parent);
454        }
455        data
456    }
457
458    /// Internal function to translate between an encoded span and the expanded representation.
459    /// This function must not be used outside the incremental engine.
460    #[inline]
461    pub fn data_untracked(self) -> SpanData {
462        {
    let span64 = self.0;
    if InlineCtxt::PREFIX.get(span64) == InlineCtxt::PREFIX_VALUE {
        let span = InlineCtxt::from_span(self);
        span.data()
    } else if InlineParent::PREFIX.get(span64) == InlineParent::PREFIX_VALUE {
        let span = InlineParent::from_span(self);
        span.data()
    } else if InlinePair::PREFIX.get(span64) == InlinePair::PREFIX_VALUE {
        let span = InlinePair::from_span(self);
        span.data()
    } else { let span = Interned::from_span(self); span.data() }
}match_span_kind! {
463            self,
464            InlineCtxt(span) => span.data(),
465            InlineParent(span) => span.data(),
466            InlinePair(span) => span.data(),
467            Interned(span) => span.data(),
468        }
469    }
470
471    /// Returns `true` if this span comes from any kind of macro, desugaring or inlining.
472    #[inline]
473    pub fn from_expansion(self) -> bool {
474        !self.ctxt().is_root()
475    }
476
477    /// Returns `true` if this is a dummy span with lo=0, hi=0. Any context value is allowed, which
478    /// means it's not the same as an equality comparison with `DUMMY_SP`.
479    #[inline]
480    pub fn is_dummy(self) -> bool {
481        {
    let span64 = self.0;
    if InlineCtxt::PREFIX.get(span64) == InlineCtxt::PREFIX_VALUE {
        let span = InlineCtxt::from_span(self);
        span.lo == 0 && span.len == 0
    } else if InlineParent::PREFIX.get(span64) == InlineParent::PREFIX_VALUE {
        let span = InlineParent::from_span(self);
        span.lo == 0 && span.len == 0
    } else if InlinePair::PREFIX.get(span64) == InlinePair::PREFIX_VALUE {
        let span = InlinePair::from_span(self);
        span.lo == 0 && span.len == 0
    } else { let span = Interned::from_span(self); span.data().is_dummy() }
}match_span_kind! {
482            self,
483            InlineCtxt(span) => span.lo == 0 && span.len == 0,
484            InlineParent(span) => span.lo == 0 && span.len == 0,
485            InlinePair(span) => span.lo == 0 && span.len == 0,
486            Interned(span) => span.data().is_dummy(),
487        }
488    }
489
490    #[inline]
491    pub fn map_ctxt(self, map: impl FnOnce(SyntaxContext) -> SyntaxContext) -> Span {
492        let data = {
    let span64 = self.0;
    if InlineCtxt::PREFIX.get(span64) == InlineCtxt::PREFIX_VALUE {
        let span = InlineCtxt::from_span(self);
        {
            let new_ctxt = map(SyntaxContext::from_u32(span.ctxt));
            let new_ctxt32 = new_ctxt.as_u32();
            return if let Some(span) =
                        InlineCtxt::try_new_span(span.lo, span.len, new_ctxt32) {
                    span
                } else { span.data().with_ctxt(new_ctxt) };
        }
    } else if InlineParent::PREFIX.get(span64) == InlineParent::PREFIX_VALUE {
        let span = InlineParent::from_span(self);
        span.data()
    } else if InlinePair::PREFIX.get(span64) == InlinePair::PREFIX_VALUE {
        let span = InlinePair::from_span(self);
        span.data()
    } else { let span = Interned::from_span(self); span.data() }
}match_span_kind! {
493            self,
494            InlineCtxt(span) => {
495                // This format occurs 1-2 orders of magnitude more often than others (#125017),
496                // so it makes sense to micro-optimize it to avoid `span.data()` and `Span::new()`.
497                let new_ctxt = map(SyntaxContext::from_u32(span.ctxt));
498                let new_ctxt32 = new_ctxt.as_u32();
499                return if let Some(span) = InlineCtxt::try_new_span(span.lo, span.len, new_ctxt32) {
500                    span
501                } else {
502                    span.data().with_ctxt(new_ctxt)
503                };
504            },
505            InlineParent(span) => span.data(),
506            InlinePair(span) => span.data(),
507            Interned(span) => span.data(),
508        };
509
510        data.with_ctxt(map(data.ctxt))
511    }
512
513    /// This function is used as a fast path when decoding the full `SpanData` is not necessary.
514    /// It's a cut-down version of `data_untracked` and doesn't require taking the interner lock.
515    #[inline]
516    pub fn ctxt(self) -> SyntaxContext {
517        let ctxt32 = {
    let span64 = self.0;
    if InlineCtxt::PREFIX.get(span64) == InlineCtxt::PREFIX_VALUE {
        let span = InlineCtxt::from_span(self);
        span.ctxt
    } else if InlineParent::PREFIX.get(span64) == InlineParent::PREFIX_VALUE {
        let _span = InlineParent::from_span(self);
        return SyntaxContext::root()
    } else if InlinePair::PREFIX.get(span64) == InlinePair::PREFIX_VALUE {
        let span = InlinePair::from_span(self);
        span.ctxt
    } else { let span = Interned::from_span(self); span.ctxt }
}match_span_kind! {
518            self,
519            InlineCtxt(span) => span.ctxt,
520            InlineParent(_span) => return SyntaxContext::root(),
521            InlinePair(span) => span.ctxt,
522            Interned(span) => span.ctxt,
523        };
524        SyntaxContext::from_u32(ctxt32)
525    }
526
527    #[inline]
528    pub fn with_parent(self, parent: Option<LocalDefId>) -> Span {
529        let data = {
    let span64 = self.0;
    if InlineCtxt::PREFIX.get(span64) == InlineCtxt::PREFIX_VALUE {
        let span = InlineCtxt::from_span(self);
        {
            match parent {
                None => return self,
                Some(parent) if
                    let Some(span) =
                        Span::try_new_span_with_parent(span.lo, span.len, span.ctxt,
                            parent) => {
                    return span;
                }
                _ => span.data(),
            }
        }
    } else if InlineParent::PREFIX.get(span64) == InlineParent::PREFIX_VALUE {
        let span = InlineParent::from_span(self);
        span.data()
    } else if InlinePair::PREFIX.get(span64) == InlinePair::PREFIX_VALUE {
        let span = InlinePair::from_span(self);
        span.data()
    } else { let span = Interned::from_span(self); span.data() }
}match_span_kind! {
530            self,
531            InlineCtxt(span) => {
532                // This format occurs 1-2 orders of magnitude more often than others (#126544),
533                // so it makes sense to micro-optimize it to avoid `span.data()`.
534                match parent {
535                    None => return self,
536                    Some(parent)
537                        if let Some(span) =
538                            Span::try_new_span_with_parent(span.lo, span.len, span.ctxt, parent) =>
539                    {
540                        return span;
541                    }
542                    _ => span.data(),
543                }
544            },
545            InlineParent(span) => span.data(),
546            InlinePair(span) => span.data(),
547            Interned(span) => span.data(),
548        };
549
550        if let Some(old_parent) = data.parent {
551            (*SPAN_TRACK)(old_parent);
552        }
553        data.with_parent(parent)
554    }
555
556    #[inline]
557    pub fn parent(self) -> Option<LocalDefId> {
558        let to_parent = |parent| Some(LocalDefId { local_def_index: DefIndex::from_u32(parent) });
559        {
    let span64 = self.0;
    if InlineCtxt::PREFIX.get(span64) == InlineCtxt::PREFIX_VALUE {
        let _span = InlineCtxt::from_span(self);
        None
    } else if InlineParent::PREFIX.get(span64) == InlineParent::PREFIX_VALUE {
        let span = InlineParent::from_span(self);
        to_parent(span.parent)
    } else if InlinePair::PREFIX.get(span64) == InlinePair::PREFIX_VALUE {
        let span = InlinePair::from_span(self);
        to_parent(span.parent)
    } else { let span = Interned::from_span(self); span.data().parent }
}match_span_kind! {
560            self,
561            InlineCtxt(_span) => None,
562            InlineParent(span) => to_parent(span.parent),
563            InlinePair(span) => to_parent(span.parent),
564            Interned(span) => span.data().parent,
565        }
566    }
567
568    #[inline]
569    pub(crate) fn to_raw_span(self) -> RawSpan {
570        RawSpan(self.0)
571    }
572
573    #[inline]
574    pub fn from_raw_span(RawSpan(a): RawSpan) -> Span {
575        Span(a)
576    }
577}
578
579/// Span wrapper that provides equality and ordering based only on the `lo`/`hi` fields. Exists
580/// because `Span` doesn't impl `PartialOrd`/`Ord` due to the `ctxt` and `parent` fields being
581/// unorderable. Useful when storing things in source code order, e.g. in a `BTreeMap<OrdSpan, T>`.
582#[derive(#[automatically_derived]#[doc(hidden)]unsafe impl ::core::clone::TrivialClone
    for OrdSpan {
}
#[automatically_derived]impl ::core::clone::Clone for OrdSpan {
    #[inline]
    fn clone(&self) -> Self {
        let _: ::core::clone::AssertParamIsClone<Span>;
        *self
    }
}Clone, #[automatically_derived]impl ::core::marker::Copy for OrdSpan { }Copy, #[automatically_derived]impl ::core::fmt::Debug for OrdSpan {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_struct_field1_finish(f, "OrdSpan",
            &&self.0)
    }
}Debug)]
583pub struct OrdSpan(pub Span);
584
585impl PartialEq for OrdSpan {
586    fn eq(&self, rhs: &Self) -> bool {
587        // Ignores `ctxt` and `parent` because they are unorderable.
588        self.0.lo_hi() == rhs.0.lo_hi()
589    }
590}
591
592impl Eq for OrdSpan {}
593
594impl PartialOrd for OrdSpan {
595    fn partial_cmp(&self, rhs: &Self) -> Option<Ordering> {
596        Some(self.cmp(rhs))
597    }
598}
599
600impl Ord for OrdSpan {
601    fn cmp(&self, rhs: &Self) -> Ordering {
602        // Ignores `ctxt` and `parent` because they are unorderable.
603        self.0.lo_hi().cmp(&rhs.0.lo_hi())
604    }
605}
606
607// `OrdSpan` is typically stored in types that rely on ordering, such as `BTreeMap` or `SortedMap`.
608// Hashing shouldn't be necessary.
609impl !Hash for OrdSpan {}
610
611/// `SpanData` minus the `ctxt` field. This is what we actually intern, because we can always store
612/// ctxt inline in `Span`.
613#[derive(#[automatically_derived]#[doc(hidden)]unsafe impl ::core::clone::TrivialClone
    for SpanDataNoCtxt {
}
#[automatically_derived]impl ::core::clone::Clone for SpanDataNoCtxt {
    #[inline]
    fn clone(&self) -> Self {
        let _: ::core::clone::AssertParamIsClone<BytePos>;
        let _: ::core::clone::AssertParamIsClone<Option<LocalDefId>>;
        *self
    }
}Clone, #[automatically_derived]impl ::core::marker::Copy for SpanDataNoCtxt { }Copy, #[automatically_derived]impl ::core::hash::Hash for SpanDataNoCtxt {
    #[inline]
    fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) {
        ::core::hash::Hash::hash(&self.lo, state);
        ::core::hash::Hash::hash(&self.hi, state);
        ::core::hash::Hash::hash(&self.parent, state)
    }
}Hash, #[automatically_derived]impl ::core::marker::StructuralPartialEq for
    SpanDataNoCtxt {
}
#[automatically_derived]impl ::core::cmp::PartialEq for SpanDataNoCtxt {
    #[inline]
    fn eq(&self, other: &Self) -> bool {
        self.lo == other.lo && self.hi == other.hi &&
            self.parent == other.parent
    }
}PartialEq, #[automatically_derived]impl ::core::cmp::Eq for SpanDataNoCtxt {
    #[inline]
    #[doc(hidden)]
    #[coverage(off)]
    fn assert_fields_are_eq(&self) {
        let _: ::core::cmp::AssertParamIsEq<BytePos>;
        let _: ::core::cmp::AssertParamIsEq<Option<LocalDefId>>;
    }
}Eq)]
614struct SpanDataNoCtxt {
615    lo: BytePos,
616    hi: BytePos,
617    parent: Option<LocalDefId>,
618}
619
620#[derive(#[automatically_derived]impl ::core::default::Default for SpanInterner {
    #[inline]
    fn default() -> Self {
        SpanInterner { spans: ::core::default::Default::default() }
    }
}Default)]
621pub(crate) struct SpanInterner {
622    spans: FxIndexSet<SpanDataNoCtxt>,
623}
624
625impl SpanInterner {
626    fn intern(&mut self, span_data_no_ctxt: &SpanDataNoCtxt) -> u32 {
627        let (index, _) = self.spans.insert_full(*span_data_no_ctxt);
628        index as u32
629    }
630}
631
632// If an interner exists, return it. Otherwise, prepare a fresh one.
633#[inline]
634fn with_span_interner<T, F: FnOnce(&mut SpanInterner) -> T>(f: F) -> T {
635    crate::with_session_globals(|session_globals| f(&mut session_globals.span_interner.lock()))
636}