// except according to those terms.
//! An "interner" is a data structure that associates values with usize tags and
-//! allows bidirectional lookup; i.e. given a value, one can easily find the
+//! allows bidirectional lookup; i.e., given a value, one can easily find the
//! type, and vice versa.
-use hygiene::SyntaxContext;
-use {Span, DUMMY_SP, GLOBALS};
-
-use rustc_data_structures::fx::FxHashMap;
use arena::DroplessArena;
+use rustc_data_structures::fx::FxHashMap;
use serialize::{Decodable, Decoder, Encodable, Encoder};
+
use std::fmt;
use std::str;
use std::cmp::{PartialEq, Ordering, PartialOrd, Ord};
use std::hash::{Hash, Hasher};
+use hygiene::SyntaxContext;
+use {Span, DUMMY_SP, GLOBALS};
+
#[derive(Copy, Clone, Eq)]
pub struct Ident {
pub name: Symbol,
pub const fn new(name: Symbol, span: Span) -> Ident {
Ident { name, span }
}
+
#[inline]
pub const fn with_empty_ctxt(name: Symbol) -> Ident {
Ident::new(name, DUMMY_SP)
/// "Normalize" ident for use in comparisons using "item hygiene".
/// Identifiers with same string value become same if they came from the same "modern" macro
- /// (e.g. `macro` item, but not `macro_rules` item) and stay different if they came from
+ /// (e.g., `macro` item, but not `macro_rules` item) and stay different if they came from
/// different "modern" macros.
/// Technically, this operation strips all non-opaque marks from ident's syntactic context.
pub fn modern(self) -> Ident {
/// "Normalize" ident for use in comparisons using "local variable hygiene".
/// Identifiers with same string value become same if they came from the same non-transparent
- /// macro (e.g. `macro` or `macro_rules!` items) and stay different if they came from different
+ /// macro (e.g., `macro` or `macro_rules!` items) and stay different if they came from different
/// non-transparent macros.
/// Technically, this operation strips all transparent marks from ident's syntactic context.
pub fn modern_and_legacy(self) -> Ident {
Ident::new(self.name.gensymed(), self.span)
}
+ pub fn gensym_if_underscore(self) -> Ident {
+ if self.name == keywords::Underscore.name() { self.gensym() } else { self }
+ }
+
pub fn as_str(self) -> LocalInternedString {
self.name.as_str()
}
fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
if self.span.ctxt().modern() == SyntaxContext::empty() {
s.emit_str(&self.as_str())
- } else { // FIXME(jseyfried) intercrate hygiene
+ } else { // FIXME(jseyfried): intercrate hygiene
let mut string = "#".to_owned();
string.push_str(&self.as_str());
s.emit_str(&string)
let string = d.read_str()?;
Ok(if !string.starts_with('#') {
Ident::from_str(&string)
- } else { // FIXME(jseyfried) intercrate hygiene
+ } else { // FIXME(jseyfried): intercrate hygiene
Ident::with_empty_ctxt(Symbol::gensym(&string[1..]))
})
}
pub struct Symbol(u32);
// The interner is pointed to by a thread local value which is only set on the main thread
-// with parallelization is disabled. So we don't allow Symbol to transfer between threads
+// with parallelization is disabled. So we don't allow `Symbol` to transfer between threads
// to avoid panics and other errors, even though it would be memory safe to do so.
#[cfg(not(parallel_queries))]
impl !Send for Symbol { }
with_interner(|interner| interner.interned(self))
}
- /// gensym's a new usize, using the current interner.
+ /// Gensyms a new usize, using the current interner.
pub fn gensym(string: &str) -> Self {
with_interner(|interner| interner.gensym(string))
}
}
}
-// The &'static strs in this type actually point into the arena
+// The `&'static str`s in this type actually point into the arena.
#[derive(Default)]
pub struct Interner {
arena: DroplessArena,
let mut this = Interner::default();
for &string in init {
if string == "" {
- // We can't allocate empty strings in the arena, so handle this here
+ // We can't allocate empty strings in the arena, so handle this here.
let name = Symbol(this.strings.len() as u32);
this.names.insert("", name);
this.strings.push("");
let name = Symbol(self.strings.len() as u32);
- // from_utf8_unchecked is safe since we just allocated a &str which is known to be utf8
+ // `from_utf8_unchecked` is safe since we just allocated a `&str` which is known to be
+ // UTF-8.
let string: &str = unsafe {
str::from_utf8_unchecked(self.arena.alloc_slice(string.as_bytes()))
};
- // It is safe to extend the arena allocation to 'static because we only access
- // these while the arena is still alive
+ // It is safe to extend the arena allocation to `'static` because we only access
+ // these while the arena is still alive.
let string: &'static str = unsafe {
&*(string as *const str)
};
}
}}
-// NB: leaving holes in the ident table is bad! a different ident will get
+// N.B., leaving holes in the ident table is bad! a different ident will get
// interned with the id from the hole, but it will be between the min and max
// of the reserved words, and thus tagged as "reserved".
// After modifying this list adjust `is_special`, `is_used_keyword`/`is_unused_keyword`,
// Special reserved identifiers used internally for elided lifetimes,
// unnamed method parameters, crate root module, error recovery etc.
(0, Invalid, "")
- (1, CrateRoot, "{{root}}")
+ (1, PathRoot, "{{root}}")
(2, DollarCrate, "$crate")
(3, Underscore, "_")
- // Keywords used in the language.
+ // Keywords that are used in stable Rust.
(4, As, "as")
(5, Box, "box")
(6, Break, "break")
(25, Pub, "pub")
(26, Ref, "ref")
(27, Return, "return")
- (28, SelfValue, "self")
- (29, SelfType, "Self")
+ (28, SelfLower, "self")
+ (29, SelfUpper, "Self")
(30, Static, "static")
(31, Struct, "struct")
(32, Super, "super")
(38, Where, "where")
(39, While, "while")
- // Keywords reserved for future use.
+ // Keywords that are used in unstable Rust or reserved for future use.
(40, Abstract, "abstract")
(41, Become, "become")
(42, Do, "do")
(49, Virtual, "virtual")
(50, Yield, "yield")
- // Edition-specific keywords reserved for future use.
- (51, Async, "async") // >= 2018 Edition only
- (52, Dyn, "dyn") // >= 2018 Edition only
+ // Edition-specific keywords that are used in stable Rust.
+ (51, Dyn, "dyn") // >= 2018 Edition only
+
+ // Edition-specific keywords that are used in unstable Rust or reserved for future use.
+ (52, Async, "async") // >= 2018 Edition only
(53, Try, "try") // >= 2018 Edition only
// Special lifetime names
(56, Auto, "auto")
(57, Catch, "catch")
(58, Default, "default")
- (59, Union, "union")
- (60, Existential, "existential")
+ (59, Existential, "existential")
+ (60, Union, "union")
}
impl Symbol {
+ fn is_used_keyword_2018(self) -> bool {
+ self == keywords::Dyn.name()
+ }
+
fn is_unused_keyword_2018(self) -> bool {
self >= keywords::Async.name() && self <= keywords::Try.name()
}
}
impl Ident {
- // Returns true for reserved identifiers used internally for elided lifetimes,
+ // Returns `true` for reserved identifiers used internally for elided lifetimes,
// unnamed method parameters, crate root module, error recovery etc.
pub fn is_special(self) -> bool {
self.name <= keywords::Underscore.name()
/// Returns `true` if the token is a keyword used in the language.
pub fn is_used_keyword(self) -> bool {
- self.name >= keywords::As.name() && self.name <= keywords::While.name()
+ // Note: `span.edition()` is relatively expensive, don't call it unless necessary.
+ self.name >= keywords::As.name() && self.name <= keywords::While.name() ||
+ self.name.is_used_keyword_2018() && self.span.rust_2018()
}
/// Returns `true` if the token is a keyword reserved for possible future use.
/// A keyword or reserved identifier that can be used as a path segment.
pub fn is_path_segment_keyword(self) -> bool {
self.name == keywords::Super.name() ||
- self.name == keywords::SelfValue.name() ||
- self.name == keywords::SelfType.name() ||
+ self.name == keywords::SelfLower.name() ||
+ self.name == keywords::SelfUpper.name() ||
self.name == keywords::Extern.name() ||
self.name == keywords::Crate.name() ||
- self.name == keywords::CrateRoot.name() ||
+ self.name == keywords::PathRoot.name() ||
self.name == keywords::DollarCrate.name()
}
// We see this identifier in a normal identifier position, like variable name or a type.
// How was it written originally? Did it use the raw form? Let's try to guess.
pub fn is_raw_guess(self) -> bool {
- self.name != keywords::Invalid.name() &&
+ self.name != keywords::Invalid.name() && self.name != keywords::Underscore.name() &&
self.is_reserved() && !self.is_path_segment_keyword()
}
}
/// Represents a string stored in the interner. Because the interner outlives any thread
/// which uses this type, we can safely treat `string` which points to interner data,
/// as an immortal string, as long as this type never crosses between threads.
-// FIXME: Ensure that the interner outlives any thread which uses LocalInternedString,
-// by creating a new thread right after constructing the interner
+// FIXME: ensure that the interner outlives any thread which uses `LocalInternedString`,
+// by creating a new thread right after constructing the interner.
#[derive(Clone, Copy, Hash, PartialOrd, Eq, Ord)]
pub struct LocalInternedString {
string: &'static str,
}
}
-/// Represents a string stored in the string interner
+/// Represents a string stored in the string interner.
#[derive(Clone, Copy, Eq)]
pub struct InternedString {
symbol: Symbol,