#![feature(optin_builtin_traits)]
#![allow(unused_attributes)]
#![feature(specialization)]
-#![feature(staged_api)]
use std::borrow::Cow;
use std::cell::{Cell, RefCell};
-use std::cmp;
+use std::cmp::{self, Ordering};
use std::fmt;
use std::hash::Hasher;
use std::ops::{Add, Sub};
pub mod hygiene;
pub use hygiene::{SyntaxContext, ExpnInfo, ExpnFormat, NameAndSpan, CompilerDesugaringKind};
+mod span_encoding;
+pub use span_encoding::{Span, DUMMY_SP};
+
pub mod symbol;
pub type FileName = String;
/// able to use many of the functions on spans in codemap and you cannot assume
/// that the length of the span = hi - lo; there may be space in the BytePos
/// range between files.
+///
+/// `SpanData` is public because `Span` uses a thread-local interner and can't be
+/// sent to other threads, but some pieces of performance infra run in a separate thread.
+/// Using `Span` is generally preferred.
#[derive(Clone, Copy, Hash, PartialEq, Eq, Ord, PartialOrd)]
-pub struct Span {
- #[unstable(feature = "rustc_private", issue = "27812")]
- #[rustc_deprecated(since = "1.21", reason = "use getters/setters instead")]
+pub struct SpanData {
pub lo: BytePos,
- #[unstable(feature = "rustc_private", issue = "27812")]
- #[rustc_deprecated(since = "1.21", reason = "use getters/setters instead")]
pub hi: BytePos,
/// Information about where the macro came from, if this piece of
/// code was created by a macro expansion.
- #[unstable(feature = "rustc_private", issue = "27812")]
- #[rustc_deprecated(since = "1.21", reason = "use getters/setters instead")]
pub ctxt: SyntaxContext,
}
-#[allow(deprecated)]
-pub const DUMMY_SP: Span = Span { lo: BytePos(0), hi: BytePos(0), ctxt: NO_EXPANSION };
+// The interner in thread-local, so `Span` shouldn't move between threads.
+impl !Send for Span {}
+impl !Sync for Span {}
+
+impl PartialOrd for Span {
+ fn partial_cmp(&self, rhs: &Self) -> Option<Ordering> {
+ PartialOrd::partial_cmp(&self.data(), &rhs.data())
+ }
+}
+impl Ord for Span {
+ fn cmp(&self, rhs: &Self) -> Ordering {
+ Ord::cmp(&self.data(), &rhs.data())
+ }
+}
/// A collection of spans. Spans have two orthogonal attributes:
///
}
impl Span {
- #[allow(deprecated)]
- #[inline]
- pub fn new(lo: BytePos, hi: BytePos, ctxt: SyntaxContext) -> Self {
- if lo <= hi { Span { lo, hi, ctxt } } else { Span { lo: hi, hi: lo, ctxt } }
- }
-
- #[allow(deprecated)]
#[inline]
pub fn lo(self) -> BytePos {
- self.lo
+ self.data().lo
}
#[inline]
pub fn with_lo(self, lo: BytePos) -> Span {
- Span::new(lo, self.hi(), self.ctxt())
+ let base = self.data();
+ Span::new(lo, base.hi, base.ctxt)
}
- #[allow(deprecated)]
#[inline]
pub fn hi(self) -> BytePos {
- self.hi
+ self.data().hi
}
#[inline]
pub fn with_hi(self, hi: BytePos) -> Span {
- Span::new(self.lo(), hi, self.ctxt())
+ let base = self.data();
+ Span::new(base.lo, hi, base.ctxt)
}
- #[allow(deprecated)]
#[inline]
pub fn ctxt(self) -> SyntaxContext {
- self.ctxt
+ self.data().ctxt
}
#[inline]
pub fn with_ctxt(self, ctxt: SyntaxContext) -> Span {
- Span::new(self.lo(), self.hi(), ctxt)
+ let base = self.data();
+ Span::new(base.lo, base.hi, ctxt)
}
/// Returns a new span representing just the end-point of this span
}
}
+impl fmt::Debug for SpanData {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ SPAN_DEBUG.with(|span_debug| span_debug.get()(Span::new(self.lo, self.hi, self.ctxt), f))
+ }
+}
+
impl MultiSpan {
pub fn new() -> MultiSpan {
MultiSpan {
--- /dev/null
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Spans are encoded using 1-bit tag and 2 different encoding formats (one for each tag value).
+// One format is used for keeping span data inline,
+// another contains index into an out-of-line span interner.
+// 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 {BytePos, SpanData};
+use hygiene::SyntaxContext;
+
+use rustc_data_structures::fx::FxHashMap;
+use std::cell::RefCell;
+
+/// A compressed span.
+/// Contains either fields of `SpanData` inline if they are small, or index into span interner.
+/// The primary goal of `Span` is to be as small as possible and fit into other structures
+/// (that's why it uses `packed` as well). Decoding speed is the second priority.
+/// See `SpanData` for the info on span fields in decoded representation.
+#[derive(Clone, Copy, PartialEq, Eq, Hash)]
+#[repr(packed)]
+pub struct Span(u32);
+
+/// Dummy span, both position and length are zero, syntax context is zero as well.
+/// This span is kept inline and encoded with format 0.
+pub const DUMMY_SP: Span = Span(0);
+
+impl Span {
+ #[inline]
+ pub fn new(lo: BytePos, hi: BytePos, ctxt: SyntaxContext) -> Self {
+ encode(&match lo <= hi {
+ true => SpanData { lo, hi, ctxt },
+ false => SpanData { lo: hi, hi: lo, ctxt },
+ })
+ }
+
+ #[inline]
+ pub fn data(self) -> SpanData {
+ decode(self)
+ }
+}
+
+// Tags
+const TAG_INLINE: u32 = 0;
+const TAG_INTERNED: u32 = 1;
+const TAG_MASK: u32 = 1;
+
+// Fields indexes
+const BASE_INDEX: usize = 0;
+const LEN_INDEX: usize = 1;
+const CTXT_INDEX: usize = 2;
+
+// Tag = 0, inline format.
+// -----------------------------------
+// | base 31:8 | len 7:1 | tag 0:0 |
+// -----------------------------------
+const INLINE_SIZES: [u32; 3] = [24, 7, 0];
+const INLINE_OFFSETS: [u32; 3] = [8, 1, 1];
+
+// Tag = 1, interned format.
+// ------------------------
+// | index 31:1 | tag 0:0 |
+// ------------------------
+const INTERNED_INDEX_SIZE: u32 = 31;
+const INTERNED_INDEX_OFFSET: u32 = 1;
+
+#[inline]
+fn encode(sd: &SpanData) -> Span {
+ let (base, len, ctxt) = (sd.lo.0, sd.hi.0 - sd.lo.0, sd.ctxt.0);
+
+ let val = if (base >> INLINE_SIZES[BASE_INDEX]) == 0 &&
+ (len >> INLINE_SIZES[LEN_INDEX]) == 0 &&
+ (ctxt >> INLINE_SIZES[CTXT_INDEX]) == 0 {
+ (base << INLINE_OFFSETS[BASE_INDEX]) | (len << INLINE_OFFSETS[LEN_INDEX]) |
+ (ctxt << INLINE_OFFSETS[CTXT_INDEX]) | TAG_INLINE
+ } else {
+ let index = with_span_interner(|interner| interner.intern(sd));
+ (index << INTERNED_INDEX_OFFSET) | TAG_INTERNED
+ };
+ Span(val)
+}
+
+#[inline]
+fn decode(span: Span) -> SpanData {
+ let val = span.0;
+
+ // Extract a field at position `pos` having size `size`.
+ let extract = |pos: u32, size: u32| {
+ let mask = ((!0u32) as u64 >> (32 - size)) as u32; // Can't shift u32 by 32
+ (val >> pos) & mask
+ };
+
+ let (base, len, ctxt) = if val & TAG_MASK == TAG_INLINE {(
+ extract(INLINE_OFFSETS[BASE_INDEX], INLINE_SIZES[BASE_INDEX]),
+ extract(INLINE_OFFSETS[LEN_INDEX], INLINE_SIZES[LEN_INDEX]),
+ extract(INLINE_OFFSETS[CTXT_INDEX], INLINE_SIZES[CTXT_INDEX]),
+ )} else {
+ let index = extract(INTERNED_INDEX_OFFSET, INTERNED_INDEX_SIZE);
+ return with_span_interner(|interner| *interner.get(index));
+ };
+ SpanData { lo: BytePos(base), hi: BytePos(base + len), ctxt: SyntaxContext(ctxt) }
+}
+
+#[derive(Default)]
+struct SpanInterner {
+ spans: FxHashMap<SpanData, u32>,
+ span_data: Vec<SpanData>,
+}
+
+impl SpanInterner {
+ fn intern(&mut self, span_data: &SpanData) -> u32 {
+ if let Some(index) = self.spans.get(span_data) {
+ return *index;
+ }
+
+ let index = self.spans.len() as u32;
+ self.span_data.push(*span_data);
+ self.spans.insert(*span_data, index);
+ index
+ }
+
+ #[inline]
+ fn get(&self, index: u32) -> &SpanData {
+ &self.span_data[index as usize]
+ }
+}
+
+// If an interner exists in TLS, return it. Otherwise, prepare a fresh one.
+#[inline]
+fn with_span_interner<T, F: FnOnce(&mut SpanInterner) -> T>(f: F) -> T {
+ thread_local!(static INTERNER: RefCell<SpanInterner> = {
+ RefCell::new(SpanInterner::default())
+ });
+ INTERNER.with(|interner| f(&mut *interner.borrow_mut()))
+}