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