]> git.lizzy.rs Git - rust.git/blob - src/libsyntax_pos/symbol.rs
Bump to 1.33.0
[rust.git] / src / libsyntax_pos / symbol.rs
1 // Copyright 2016 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 //! An "interner" is a data structure that associates values with usize tags and
12 //! allows bidirectional lookup; i.e., given a value, one can easily find the
13 //! type, and vice versa.
14
15 use arena::DroplessArena;
16 use rustc_data_structures::fx::FxHashMap;
17 use serialize::{Decodable, Decoder, Encodable, Encoder};
18
19 use std::fmt;
20 use std::str;
21 use std::cmp::{PartialEq, Ordering, PartialOrd, Ord};
22 use std::hash::{Hash, Hasher};
23
24 use hygiene::SyntaxContext;
25 use {Span, DUMMY_SP, GLOBALS};
26
27 #[derive(Copy, Clone, Eq)]
28 pub struct Ident {
29     pub name: Symbol,
30     pub span: Span,
31 }
32
33 impl Ident {
34     #[inline]
35     pub const fn new(name: Symbol, span: Span) -> Ident {
36         Ident { name, span }
37     }
38
39     #[inline]
40     pub const fn with_empty_ctxt(name: Symbol) -> Ident {
41         Ident::new(name, DUMMY_SP)
42     }
43
44     /// Maps an interned string to an identifier with an empty syntax context.
45     pub fn from_interned_str(string: InternedString) -> Ident {
46         Ident::with_empty_ctxt(string.as_symbol())
47     }
48
49     /// Maps a string to an identifier with an empty syntax context.
50     pub fn from_str(string: &str) -> Ident {
51         Ident::with_empty_ctxt(Symbol::intern(string))
52     }
53
54     /// Replace `lo` and `hi` with those from `span`, but keep hygiene context.
55     pub fn with_span_pos(self, span: Span) -> Ident {
56         Ident::new(self.name, span.with_ctxt(self.span.ctxt()))
57     }
58
59     pub fn without_first_quote(self) -> Ident {
60         Ident::new(Symbol::intern(self.as_str().trim_start_matches('\'')), self.span)
61     }
62
63     /// "Normalize" ident for use in comparisons using "item hygiene".
64     /// Identifiers with same string value become same if they came from the same "modern" macro
65     /// (e.g., `macro` item, but not `macro_rules` item) and stay different if they came from
66     /// different "modern" macros.
67     /// Technically, this operation strips all non-opaque marks from ident's syntactic context.
68     pub fn modern(self) -> Ident {
69         Ident::new(self.name, self.span.modern())
70     }
71
72     /// "Normalize" ident for use in comparisons using "local variable hygiene".
73     /// Identifiers with same string value become same if they came from the same non-transparent
74     /// macro (e.g., `macro` or `macro_rules!` items) and stay different if they came from different
75     /// non-transparent macros.
76     /// Technically, this operation strips all transparent marks from ident's syntactic context.
77     pub fn modern_and_legacy(self) -> Ident {
78         Ident::new(self.name, self.span.modern_and_legacy())
79     }
80
81     pub fn gensym(self) -> Ident {
82         Ident::new(self.name.gensymed(), self.span)
83     }
84
85     pub fn gensym_if_underscore(self) -> Ident {
86         if self.name == keywords::Underscore.name() { self.gensym() } else { self }
87     }
88
89     pub fn as_str(self) -> LocalInternedString {
90         self.name.as_str()
91     }
92
93     pub fn as_interned_str(self) -> InternedString {
94         self.name.as_interned_str()
95     }
96 }
97
98 impl PartialEq for Ident {
99     fn eq(&self, rhs: &Self) -> bool {
100         self.name == rhs.name && self.span.ctxt() == rhs.span.ctxt()
101     }
102 }
103
104 impl Hash for Ident {
105     fn hash<H: Hasher>(&self, state: &mut H) {
106         self.name.hash(state);
107         self.span.ctxt().hash(state);
108     }
109 }
110
111 impl fmt::Debug for Ident {
112     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
113         write!(f, "{}{:?}", self.name, self.span.ctxt())
114     }
115 }
116
117 impl fmt::Display for Ident {
118     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
119         fmt::Display::fmt(&self.name, f)
120     }
121 }
122
123 impl Encodable for Ident {
124     fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
125         if self.span.ctxt().modern() == SyntaxContext::empty() {
126             s.emit_str(&self.as_str())
127         } else { // FIXME(jseyfried): intercrate hygiene
128             let mut string = "#".to_owned();
129             string.push_str(&self.as_str());
130             s.emit_str(&string)
131         }
132     }
133 }
134
135 impl Decodable for Ident {
136     fn decode<D: Decoder>(d: &mut D) -> Result<Ident, D::Error> {
137         let string = d.read_str()?;
138         Ok(if !string.starts_with('#') {
139             Ident::from_str(&string)
140         } else { // FIXME(jseyfried): intercrate hygiene
141             Ident::with_empty_ctxt(Symbol::gensym(&string[1..]))
142         })
143     }
144 }
145
146 /// A symbol is an interned or gensymed string.
147 #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
148 pub struct Symbol(u32);
149
150 // The interner is pointed to by a thread local value which is only set on the main thread
151 // with parallelization is disabled. So we don't allow `Symbol` to transfer between threads
152 // to avoid panics and other errors, even though it would be memory safe to do so.
153 #[cfg(not(parallel_queries))]
154 impl !Send for Symbol { }
155 #[cfg(not(parallel_queries))]
156 impl !Sync for Symbol { }
157
158 impl Symbol {
159     /// Maps a string to its interned representation.
160     pub fn intern(string: &str) -> Self {
161         with_interner(|interner| interner.intern(string))
162     }
163
164     pub fn interned(self) -> Self {
165         with_interner(|interner| interner.interned(self))
166     }
167
168     /// Gensyms a new usize, using the current interner.
169     pub fn gensym(string: &str) -> Self {
170         with_interner(|interner| interner.gensym(string))
171     }
172
173     pub fn gensymed(self) -> Self {
174         with_interner(|interner| interner.gensymed(self))
175     }
176
177     pub fn as_str(self) -> LocalInternedString {
178         with_interner(|interner| unsafe {
179             LocalInternedString {
180                 string: ::std::mem::transmute::<&str, &str>(interner.get(self))
181             }
182         })
183     }
184
185     pub fn as_interned_str(self) -> InternedString {
186         with_interner(|interner| InternedString {
187             symbol: interner.interned(self)
188         })
189     }
190
191     pub fn as_u32(self) -> u32 {
192         self.0
193     }
194 }
195
196 impl fmt::Debug for Symbol {
197     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
198         let is_gensymed = with_interner(|interner| interner.is_gensymed(*self));
199         if is_gensymed {
200             write!(f, "{}({})", self, self.0)
201         } else {
202             write!(f, "{}", self)
203         }
204     }
205 }
206
207 impl fmt::Display for Symbol {
208     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
209         fmt::Display::fmt(&self.as_str(), f)
210     }
211 }
212
213 impl Encodable for Symbol {
214     fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
215         s.emit_str(&self.as_str())
216     }
217 }
218
219 impl Decodable for Symbol {
220     fn decode<D: Decoder>(d: &mut D) -> Result<Symbol, D::Error> {
221         Ok(Symbol::intern(&d.read_str()?))
222     }
223 }
224
225 impl<T: ::std::ops::Deref<Target=str>> PartialEq<T> for Symbol {
226     fn eq(&self, other: &T) -> bool {
227         self.as_str() == other.deref()
228     }
229 }
230
231 // The `&'static str`s in this type actually point into the arena.
232 #[derive(Default)]
233 pub struct Interner {
234     arena: DroplessArena,
235     names: FxHashMap<&'static str, Symbol>,
236     strings: Vec<&'static str>,
237     gensyms: Vec<Symbol>,
238 }
239
240 impl Interner {
241     fn prefill(init: &[&str]) -> Self {
242         let mut this = Interner::default();
243         for &string in init {
244             if string == "" {
245                 // We can't allocate empty strings in the arena, so handle this here.
246                 let name = Symbol(this.strings.len() as u32);
247                 this.names.insert("", name);
248                 this.strings.push("");
249             } else {
250                 this.intern(string);
251             }
252         }
253         this
254     }
255
256     pub fn intern(&mut self, string: &str) -> Symbol {
257         if let Some(&name) = self.names.get(string) {
258             return name;
259         }
260
261         let name = Symbol(self.strings.len() as u32);
262
263         // `from_utf8_unchecked` is safe since we just allocated a `&str` which is known to be
264         // UTF-8.
265         let string: &str = unsafe {
266             str::from_utf8_unchecked(self.arena.alloc_slice(string.as_bytes()))
267         };
268         // It is safe to extend the arena allocation to `'static` because we only access
269         // these while the arena is still alive.
270         let string: &'static str =  unsafe {
271             &*(string as *const str)
272         };
273         self.strings.push(string);
274         self.names.insert(string, name);
275         name
276     }
277
278     pub fn interned(&self, symbol: Symbol) -> Symbol {
279         if (symbol.0 as usize) < self.strings.len() {
280             symbol
281         } else {
282             self.interned(self.gensyms[(!0 - symbol.0) as usize])
283         }
284     }
285
286     fn gensym(&mut self, string: &str) -> Symbol {
287         let symbol = self.intern(string);
288         self.gensymed(symbol)
289     }
290
291     fn gensymed(&mut self, symbol: Symbol) -> Symbol {
292         self.gensyms.push(symbol);
293         Symbol(!0 - self.gensyms.len() as u32 + 1)
294     }
295
296     fn is_gensymed(&mut self, symbol: Symbol) -> bool {
297         symbol.0 as usize >= self.strings.len()
298     }
299
300     pub fn get(&self, symbol: Symbol) -> &str {
301         match self.strings.get(symbol.0 as usize) {
302             Some(string) => string,
303             None => self.get(self.gensyms[(!0 - symbol.0) as usize]),
304         }
305     }
306 }
307
308 // In this macro, there is the requirement that the name (the number) must be monotonically
309 // increasing by one in the special identifiers, starting at 0; the same holds for the keywords,
310 // except starting from the next number instead of zero.
311 macro_rules! declare_keywords {(
312     $( ($index: expr, $konst: ident, $string: expr) )*
313 ) => {
314     pub mod keywords {
315         use super::{Symbol, Ident};
316         #[derive(Clone, Copy, PartialEq, Eq)]
317         pub struct Keyword {
318             ident: Ident,
319         }
320         impl Keyword {
321             #[inline] pub fn ident(self) -> Ident { self.ident }
322             #[inline] pub fn name(self) -> Symbol { self.ident.name }
323         }
324         $(
325             #[allow(non_upper_case_globals)]
326             pub const $konst: Keyword = Keyword {
327                 ident: Ident::with_empty_ctxt(super::Symbol($index))
328             };
329         )*
330
331         impl ::std::str::FromStr for Keyword {
332             type Err = ();
333
334             fn from_str(s: &str) -> Result<Self, ()> {
335                 match s {
336                     $($string => Ok($konst),)*
337                     _ => Err(()),
338                 }
339             }
340         }
341     }
342
343     impl Interner {
344         pub fn fresh() -> Self {
345             Interner::prefill(&[$($string,)*])
346         }
347     }
348 }}
349
350 // N.B., leaving holes in the ident table is bad! a different ident will get
351 // interned with the id from the hole, but it will be between the min and max
352 // of the reserved words, and thus tagged as "reserved".
353 // After modifying this list adjust `is_special`, `is_used_keyword`/`is_unused_keyword`,
354 // this should be rarely necessary though if the keywords are kept in alphabetic order.
355 declare_keywords! {
356     // Special reserved identifiers used internally for elided lifetimes,
357     // unnamed method parameters, crate root module, error recovery etc.
358     (0,  Invalid,            "")
359     (1,  PathRoot,           "{{root}}")
360     (2,  DollarCrate,        "$crate")
361     (3,  Underscore,         "_")
362
363     // Keywords that are used in stable Rust.
364     (4,  As,                 "as")
365     (5,  Box,                "box")
366     (6,  Break,              "break")
367     (7,  Const,              "const")
368     (8,  Continue,           "continue")
369     (9,  Crate,              "crate")
370     (10, Else,               "else")
371     (11, Enum,               "enum")
372     (12, Extern,             "extern")
373     (13, False,              "false")
374     (14, Fn,                 "fn")
375     (15, For,                "for")
376     (16, If,                 "if")
377     (17, Impl,               "impl")
378     (18, In,                 "in")
379     (19, Let,                "let")
380     (20, Loop,               "loop")
381     (21, Match,              "match")
382     (22, Mod,                "mod")
383     (23, Move,               "move")
384     (24, Mut,                "mut")
385     (25, Pub,                "pub")
386     (26, Ref,                "ref")
387     (27, Return,             "return")
388     (28, SelfLower,          "self")
389     (29, SelfUpper,          "Self")
390     (30, Static,             "static")
391     (31, Struct,             "struct")
392     (32, Super,              "super")
393     (33, Trait,              "trait")
394     (34, True,               "true")
395     (35, Type,               "type")
396     (36, Unsafe,             "unsafe")
397     (37, Use,                "use")
398     (38, Where,              "where")
399     (39, While,              "while")
400
401     // Keywords that are used in unstable Rust or reserved for future use.
402     (40, Abstract,           "abstract")
403     (41, Become,             "become")
404     (42, Do,                 "do")
405     (43, Final,              "final")
406     (44, Macro,              "macro")
407     (45, Override,           "override")
408     (46, Priv,               "priv")
409     (47, Typeof,             "typeof")
410     (48, Unsized,            "unsized")
411     (49, Virtual,            "virtual")
412     (50, Yield,              "yield")
413
414     // Edition-specific keywords that are used in stable Rust.
415     (51, Dyn,                "dyn") // >= 2018 Edition only
416
417     // Edition-specific keywords that are used in unstable Rust or reserved for future use.
418     (52, Async,              "async") // >= 2018 Edition only
419     (53, Try,                "try") // >= 2018 Edition only
420
421     // Special lifetime names
422     (54, UnderscoreLifetime, "'_")
423     (55, StaticLifetime,     "'static")
424
425     // Weak keywords, have special meaning only in specific contexts.
426     (56, Auto,               "auto")
427     (57, Catch,              "catch")
428     (58, Default,            "default")
429     (59, Existential,        "existential")
430     (60, Union,              "union")
431 }
432
433 impl Symbol {
434     fn is_used_keyword_2018(self) -> bool {
435         self == keywords::Dyn.name()
436     }
437
438     fn is_unused_keyword_2018(self) -> bool {
439         self >= keywords::Async.name() && self <= keywords::Try.name()
440     }
441 }
442
443 impl Ident {
444     // Returns `true` for reserved identifiers used internally for elided lifetimes,
445     // unnamed method parameters, crate root module, error recovery etc.
446     pub fn is_special(self) -> bool {
447         self.name <= keywords::Underscore.name()
448     }
449
450     /// Returns `true` if the token is a keyword used in the language.
451     pub fn is_used_keyword(self) -> bool {
452         // Note: `span.edition()` is relatively expensive, don't call it unless necessary.
453         self.name >= keywords::As.name() && self.name <= keywords::While.name() ||
454         self.name.is_used_keyword_2018() && self.span.rust_2018()
455     }
456
457     /// Returns `true` if the token is a keyword reserved for possible future use.
458     pub fn is_unused_keyword(self) -> bool {
459         // Note: `span.edition()` is relatively expensive, don't call it unless necessary.
460         self.name >= keywords::Abstract.name() && self.name <= keywords::Yield.name() ||
461         self.name.is_unused_keyword_2018() && self.span.rust_2018()
462     }
463
464     /// Returns `true` if the token is either a special identifier or a keyword.
465     pub fn is_reserved(self) -> bool {
466         self.is_special() || self.is_used_keyword() || self.is_unused_keyword()
467     }
468
469     /// A keyword or reserved identifier that can be used as a path segment.
470     pub fn is_path_segment_keyword(self) -> bool {
471         self.name == keywords::Super.name() ||
472         self.name == keywords::SelfLower.name() ||
473         self.name == keywords::SelfUpper.name() ||
474         self.name == keywords::Extern.name() ||
475         self.name == keywords::Crate.name() ||
476         self.name == keywords::PathRoot.name() ||
477         self.name == keywords::DollarCrate.name()
478     }
479
480     // We see this identifier in a normal identifier position, like variable name or a type.
481     // How was it written originally? Did it use the raw form? Let's try to guess.
482     pub fn is_raw_guess(self) -> bool {
483         self.name != keywords::Invalid.name() && self.name != keywords::Underscore.name() &&
484         self.is_reserved() && !self.is_path_segment_keyword()
485     }
486 }
487
488 // If an interner exists, return it. Otherwise, prepare a fresh one.
489 #[inline]
490 fn with_interner<T, F: FnOnce(&mut Interner) -> T>(f: F) -> T {
491     GLOBALS.with(|globals| f(&mut *globals.symbol_interner.lock()))
492 }
493
494 /// Represents a string stored in the interner. Because the interner outlives any thread
495 /// which uses this type, we can safely treat `string` which points to interner data,
496 /// as an immortal string, as long as this type never crosses between threads.
497 // FIXME: ensure that the interner outlives any thread which uses `LocalInternedString`,
498 // by creating a new thread right after constructing the interner.
499 #[derive(Clone, Copy, Hash, PartialOrd, Eq, Ord)]
500 pub struct LocalInternedString {
501     string: &'static str,
502 }
503
504 impl LocalInternedString {
505     pub fn as_interned_str(self) -> InternedString {
506         InternedString {
507             symbol: Symbol::intern(self.string)
508         }
509     }
510
511     pub fn get(&self) -> &'static str {
512         self.string
513     }
514 }
515
516 impl<U: ?Sized> ::std::convert::AsRef<U> for LocalInternedString
517 where
518     str: ::std::convert::AsRef<U>
519 {
520     fn as_ref(&self) -> &U {
521         self.string.as_ref()
522     }
523 }
524
525 impl<T: ::std::ops::Deref<Target = str>> ::std::cmp::PartialEq<T> for LocalInternedString {
526     fn eq(&self, other: &T) -> bool {
527         self.string == other.deref()
528     }
529 }
530
531 impl ::std::cmp::PartialEq<LocalInternedString> for str {
532     fn eq(&self, other: &LocalInternedString) -> bool {
533         self == other.string
534     }
535 }
536
537 impl<'a> ::std::cmp::PartialEq<LocalInternedString> for &'a str {
538     fn eq(&self, other: &LocalInternedString) -> bool {
539         *self == other.string
540     }
541 }
542
543 impl ::std::cmp::PartialEq<LocalInternedString> for String {
544     fn eq(&self, other: &LocalInternedString) -> bool {
545         self == other.string
546     }
547 }
548
549 impl<'a> ::std::cmp::PartialEq<LocalInternedString> for &'a String {
550     fn eq(&self, other: &LocalInternedString) -> bool {
551         *self == other.string
552     }
553 }
554
555 impl !Send for LocalInternedString {}
556 impl !Sync for LocalInternedString {}
557
558 impl ::std::ops::Deref for LocalInternedString {
559     type Target = str;
560     fn deref(&self) -> &str { self.string }
561 }
562
563 impl fmt::Debug for LocalInternedString {
564     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
565         fmt::Debug::fmt(self.string, f)
566     }
567 }
568
569 impl fmt::Display for LocalInternedString {
570     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
571         fmt::Display::fmt(self.string, f)
572     }
573 }
574
575 impl Decodable for LocalInternedString {
576     fn decode<D: Decoder>(d: &mut D) -> Result<LocalInternedString, D::Error> {
577         Ok(Symbol::intern(&d.read_str()?).as_str())
578     }
579 }
580
581 impl Encodable for LocalInternedString {
582     fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
583         s.emit_str(self.string)
584     }
585 }
586
587 /// Represents a string stored in the string interner.
588 #[derive(Clone, Copy, Eq)]
589 pub struct InternedString {
590     symbol: Symbol,
591 }
592
593 impl InternedString {
594     pub fn with<F: FnOnce(&str) -> R, R>(self, f: F) -> R {
595         let str = with_interner(|interner| {
596             interner.get(self.symbol) as *const str
597         });
598         // This is safe because the interner keeps string alive until it is dropped.
599         // We can access it because we know the interner is still alive since we use a
600         // scoped thread local to access it, and it was alive at the beginning of this scope
601         unsafe { f(&*str) }
602     }
603
604     pub fn as_symbol(self) -> Symbol {
605         self.symbol
606     }
607
608     pub fn as_str(self) -> LocalInternedString {
609         self.symbol.as_str()
610     }
611 }
612
613 impl Hash for InternedString {
614     fn hash<H: Hasher>(&self, state: &mut H) {
615         self.with(|str| str.hash(state))
616     }
617 }
618
619 impl PartialOrd<InternedString> for InternedString {
620     fn partial_cmp(&self, other: &InternedString) -> Option<Ordering> {
621         if self.symbol == other.symbol {
622             return Some(Ordering::Equal);
623         }
624         self.with(|self_str| other.with(|other_str| self_str.partial_cmp(other_str)))
625     }
626 }
627
628 impl Ord for InternedString {
629     fn cmp(&self, other: &InternedString) -> Ordering {
630         if self.symbol == other.symbol {
631             return Ordering::Equal;
632         }
633         self.with(|self_str| other.with(|other_str| self_str.cmp(&other_str)))
634     }
635 }
636
637 impl<T: ::std::ops::Deref<Target = str>> PartialEq<T> for InternedString {
638     fn eq(&self, other: &T) -> bool {
639         self.with(|string| string == other.deref())
640     }
641 }
642
643 impl PartialEq<InternedString> for InternedString {
644     fn eq(&self, other: &InternedString) -> bool {
645         self.symbol == other.symbol
646     }
647 }
648
649 impl PartialEq<InternedString> for str {
650     fn eq(&self, other: &InternedString) -> bool {
651         other.with(|string| self == string)
652     }
653 }
654
655 impl<'a> PartialEq<InternedString> for &'a str {
656     fn eq(&self, other: &InternedString) -> bool {
657         other.with(|string| *self == string)
658     }
659 }
660
661 impl PartialEq<InternedString> for String {
662     fn eq(&self, other: &InternedString) -> bool {
663         other.with(|string| self == string)
664     }
665 }
666
667 impl<'a> PartialEq<InternedString> for &'a String {
668     fn eq(&self, other: &InternedString) -> bool {
669         other.with(|string| *self == string)
670     }
671 }
672
673 impl ::std::convert::From<InternedString> for String {
674     fn from(val: InternedString) -> String {
675         val.as_symbol().to_string()
676     }
677 }
678
679 impl fmt::Debug for InternedString {
680     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
681         self.with(|str| fmt::Debug::fmt(&str, f))
682     }
683 }
684
685 impl fmt::Display for InternedString {
686     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
687         self.with(|str| fmt::Display::fmt(&str, f))
688     }
689 }
690
691 impl Decodable for InternedString {
692     fn decode<D: Decoder>(d: &mut D) -> Result<InternedString, D::Error> {
693         Ok(Symbol::intern(&d.read_str()?).as_interned_str())
694     }
695 }
696
697 impl Encodable for InternedString {
698     fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
699         self.with(|string| s.emit_str(string))
700     }
701 }
702
703 #[cfg(test)]
704 mod tests {
705     use super::*;
706     use Globals;
707
708     #[test]
709     fn interner_tests() {
710         let mut i: Interner = Interner::default();
711         // first one is zero:
712         assert_eq!(i.intern("dog"), Symbol(0));
713         // re-use gets the same entry:
714         assert_eq!(i.intern("dog"), Symbol(0));
715         // different string gets a different #:
716         assert_eq!(i.intern("cat"), Symbol(1));
717         assert_eq!(i.intern("cat"), Symbol(1));
718         // dog is still at zero
719         assert_eq!(i.intern("dog"), Symbol(0));
720         assert_eq!(i.gensym("zebra"), Symbol(4294967295));
721         // gensym of same string gets new number :
722         assert_eq!(i.gensym("zebra"), Symbol(4294967294));
723         // gensym of *existing* string gets new number:
724         assert_eq!(i.gensym("dog"), Symbol(4294967293));
725     }
726
727     #[test]
728     fn without_first_quote_test() {
729         GLOBALS.set(&Globals::new(), || {
730             let i = Ident::from_str("'break");
731             assert_eq!(i.without_first_quote().name, keywords::Break.name());
732         });
733     }
734 }