about summary refs log tree commit diff
path: root/src/libsyntax_pos
diff options
context:
space:
mode:
authorAlex Crichton <alex@alexcrichton.com>2019-05-21 15:37:07 -0700
committerAlex Crichton <alex@alexcrichton.com>2019-05-21 15:37:07 -0700
commitfe3dd0b50fef21d14591c960a9610bafb224cdbf (patch)
treeee0b62d8e500d4ce4b6f50b4fe5d9056b9826072 /src/libsyntax_pos
parente764f475ca7fffd6167ea991afc7d1b2b3f642dc (diff)
parent50a0defd5a93523067ef239936cc2e0755220904 (diff)
downloadrust-fe3dd0b50fef21d14591c960a9610bafb224cdbf.tar.gz
rust-fe3dd0b50fef21d14591c960a9610bafb224cdbf.zip
Merge remote-tracking branch 'origin/master' into azure-pipelines
Diffstat (limited to 'src/libsyntax_pos')
-rw-r--r--src/libsyntax_pos/edition.rs7
-rw-r--r--src/libsyntax_pos/hygiene.rs5
-rw-r--r--src/libsyntax_pos/lib.rs6
-rw-r--r--src/libsyntax_pos/symbol.rs596
4 files changed, 535 insertions, 79 deletions
diff --git a/src/libsyntax_pos/edition.rs b/src/libsyntax_pos/edition.rs
index a0b0052f26d..00cd00f2837 100644
--- a/src/libsyntax_pos/edition.rs
+++ b/src/libsyntax_pos/edition.rs
@@ -1,3 +1,4 @@
+use crate::symbol::{Symbol, sym};
 use std::fmt;
 use std::str::FromStr;
 
@@ -44,10 +45,10 @@ impl Edition {
         }
     }
 
-    pub fn feature_name(&self) -> &'static str {
+    pub fn feature_name(&self) -> Symbol {
         match *self {
-            Edition::Edition2015 => "rust_2015_preview",
-            Edition::Edition2018 => "rust_2018_preview",
+            Edition::Edition2015 => sym::rust_2015_preview,
+            Edition::Edition2018 => sym::rust_2018_preview,
         }
     }
 
diff --git a/src/libsyntax_pos/hygiene.rs b/src/libsyntax_pos/hygiene.rs
index a901afdff43..1d9dc26bf60 100644
--- a/src/libsyntax_pos/hygiene.rs
+++ b/src/libsyntax_pos/hygiene.rs
@@ -591,6 +591,10 @@ impl ExpnFormat {
 /// The kind of compiler desugaring.
 #[derive(Clone, Copy, Hash, Debug, PartialEq, Eq, RustcEncodable, RustcDecodable)]
 pub enum CompilerDesugaringKind {
+    /// We desugar `if c { i } else { e }` to `match $ExprKind::Use(c) { true => i, _ => e }`.
+    /// However, we do not want to blame `c` for unreachability but rather say that `i`
+    /// is unreachable. This desugaring kind allows us to avoid blaming `c`.
+    IfTemporary,
     QuestionMark,
     TryBlock,
     /// Desugaring of an `impl Trait` in return type position
@@ -605,6 +609,7 @@ pub enum CompilerDesugaringKind {
 impl CompilerDesugaringKind {
     pub fn name(self) -> Symbol {
         Symbol::intern(match self {
+            CompilerDesugaringKind::IfTemporary => "if",
             CompilerDesugaringKind::Async => "async",
             CompilerDesugaringKind::Await => "await",
             CompilerDesugaringKind::QuestionMark => "?",
diff --git a/src/libsyntax_pos/lib.rs b/src/libsyntax_pos/lib.rs
index 3f09405a5c8..39859f25f97 100644
--- a/src/libsyntax_pos/lib.rs
+++ b/src/libsyntax_pos/lib.rs
@@ -33,7 +33,7 @@ mod span_encoding;
 pub use span_encoding::{Span, DUMMY_SP};
 
 pub mod symbol;
-pub use symbol::symbols;
+pub use symbol::{Symbol, sym};
 
 mod analyze_source_file;
 
@@ -388,12 +388,12 @@ impl Span {
     /// Checks if a span is "internal" to a macro in which `#[unstable]`
     /// items can be used (that is, a macro marked with
     /// `#[allow_internal_unstable]`).
-    pub fn allows_unstable(&self, feature: &str) -> bool {
+    pub fn allows_unstable(&self, feature: Symbol) -> bool {
         match self.ctxt().outer().expn_info() {
             Some(info) => info
                 .allow_internal_unstable
                 .map_or(false, |features| features.iter().any(|&f|
-                    f == feature || f == "allow_internal_unstable_backcompat_hack"
+                    f == feature || f == sym::allow_internal_unstable_backcompat_hack
                 )),
             None => false,
         }
diff --git a/src/libsyntax_pos/symbol.rs b/src/libsyntax_pos/symbol.rs
index 20759217b54..a07c7eb897e 100644
--- a/src/libsyntax_pos/symbol.rs
+++ b/src/libsyntax_pos/symbol.rs
@@ -30,7 +30,6 @@ symbols! {
 
         // Keywords that are used in stable Rust.
         As:                 "as",
-        Box:                "box",
         Break:              "break",
         Const:              "const",
         Continue:           "continue",
@@ -69,6 +68,7 @@ symbols! {
         // Keywords that are used in unstable Rust or reserved for future use.
         Abstract:           "abstract",
         Become:             "become",
+        Box:                "box",
         Do:                 "do",
         Final:              "final",
         Macro:              "macro",
@@ -99,88 +99,388 @@ symbols! {
         Union:              "union",
     }
 
-    // Other symbols that can be referred to with syntax_pos::symbols::*
-    Other {
+    // Symbols that can be referred to with syntax_pos::sym::*. The symbol is
+    // the stringified identifier unless otherwise specified (e.g.
+    // `proc_dash_macro` represents "proc-macro").
+    Symbols {
+        aarch64_target_feature,
+        abi,
+        abi_amdgpu_kernel,
+        abi_msp430_interrupt,
+        abi_ptx,
+        abi_sysv64,
+        abi_thiscall,
+        abi_unadjusted,
+        abi_vectorcall,
+        abi_x86_interrupt,
+        aborts,
+        advanced_slice_patterns,
+        adx_target_feature,
         alias,
         align,
+        alignstack,
+        all,
+        allocator,
+        allocator_internals,
         alloc_error_handler,
         allow,
+        allowed,
         allow_fail,
         allow_internal_unsafe,
         allow_internal_unstable,
+        allow_internal_unstable_backcompat_hack,
+        always,
+        any,
+        arbitrary_self_types,
+        arm_target_feature,
+        asm,
+        associated_consts,
+        associated_type_defaults,
+        associated_types,
+        async_await,
+        attr,
+        attributes,
+        attr_literals,
+        augmented_assignments,
         automatically_derived,
+        avx512_target_feature,
+        await_macro,
+        bin,
+        bind_by_move_pattern_guards,
+        block,
+        borrowck_graphviz_postflow,
+        borrowck_graphviz_preflow,
+        box_patterns,
+        box_syntax,
+        braced_empty_structs,
+        C,
+        cdylib,
         cfg,
         cfg_attr,
+        cfg_attr_multi,
+        cfg_target_feature,
+        cfg_target_has_atomic,
+        cfg_target_thread_local,
+        cfg_target_vendor,
+        clone,
+        clone_closures,
+        clone_from,
+        closure_to_fn_coercion,
+        cmpxchg16b_target_feature,
         cold,
+        compile_error,
         compiler_builtins,
+        concat_idents,
+        conservative_impl_trait,
+        console,
+        const_compare_raw_pointers,
+        const_fn,
+        const_fn_union,
+        const_generics,
+        const_indexing,
+        const_let,
+        const_panic,
+        const_raw_ptr_deref,
+        const_raw_ptr_to_usize_cast,
+        const_transmute,
+        contents,
+        convert,
+        copy_closures,
+        core,
+        core_intrinsics,
         crate_id,
+        crate_in_paths,
         crate_name,
         crate_type,
+        crate_visibility_modifier,
+        custom_attribute,
+        custom_derive,
+        custom_inner_attributes,
+        custom_test_frameworks,
+        c_variadic,
+        decl_macro,
         default_lib_allocator,
+        default_type_parameter_fallback,
+        default_type_params,
         deny,
         deprecated,
         derive,
         doc,
+        doc_alias,
+        doc_cfg,
+        doc_keyword,
+        doc_masked,
+        doc_spotlight,
+        document_private_items,
+        dotdoteq_in_patterns,
+        dotdot_in_tuple_patterns,
+        double_braced_crate: "{{crate}}",
+        double_braced_impl: "{{impl}}",
+        double_braced_misc: "{{misc}}",
+        double_braced_closure: "{{closure}}",
+        double_braced_constructor: "{{constructor}}",
+        double_braced_constant: "{{constant}}",
+        double_braced_opaque: "{{opaque}}",
+        dropck_eyepatch,
+        dropck_parametricity,
+        drop_types_in_const,
+        dylib,
+        dyn_trait,
+        eh_personality,
+        eh_unwind_resume,
+        enable,
+        err,
+        Err,
+        except,
+        exclusive_range_pattern,
+        exhaustive_integer_patterns,
+        exhaustive_patterns,
+        existential_type,
+        expected,
         export_name,
+        extern_absolute_paths,
+        external_doc,
+        extern_crate_item_prelude,
+        extern_crate_self,
+        extern_in_paths,
+        extern_prelude,
+        extern_types,
+        f16c_target_feature,
         feature,
         ffi_returns_twice,
+        field_init_shorthand,
+        file,
+        fn_must_use,
         forbid,
+        format_args_nl,
+        from,
+        From,
+        from_error,
+        from_generator,
+        from_ok,
         fundamental,
+        future,
+        Future,
+        gen_future,
+        generators,
+        generic_associated_types,
+        generic_param_attrs,
         global_allocator,
+        global_asm,
+        globs,
+        hexagon_target_feature,
+        hidden,
+        homogeneous_aggregate,
+        html_favicon_url,
+        html_logo_url,
+        html_no_source,
+        html_playground_url,
+        html_root_url,
+        i128,
+        i128_type,
+        i16,
+        i32,
+        i64,
+        i8,
+        ident,
+        if_let,
+        if_while_or_patterns,
         ignore,
+        impl_header_lifetime_elision,
+        impl_trait_in_bindings,
+        import_shadowing,
+        in_band_lifetimes,
         include,
+        inclusive_range_syntax,
+        infer_outlives_requirements,
+        infer_static_outlives_requirements,
         inline,
+        intel,
+        into_iter,
+        IntoIterator,
+        into_result,
+        intrinsics,
+        irrefutable_let_patterns,
+        isize,
+        issue,
+        issue_5723_bootstrap,
+        issue_tracker_base_url,
+        item_like_imports,
+        iter,
+        Iterator,
         keyword,
+        kind,
+        label,
+        label_break_value,
         lang,
+        lang_items,
+        lib,
         link,
+        linkage,
         link_args,
+        link_cfg,
+        link_llvm_intrinsics,
         link_name,
         link_section,
-        linkage,
+        lint_reasons,
+        local_inner_macros,
+        log_syntax,
+        loop_break_value,
+        macro_at_most_once_rep,
         macro_escape,
         macro_export,
+        macro_lifetime_matcher,
+        macro_literal_matcher,
+        macro_reexport,
+        macro_rules,
+        macros_in_extern,
         macro_use,
+        macro_vis_matcher,
         main,
+        managed_boxes,
         marker,
+        marker_trait_attr,
         masked,
+        match_beginning_vert,
+        match_default_bindings,
         may_dangle,
+        message,
+        meta,
+        min_const_fn,
+        min_const_unsafe_fn,
+        mips_target_feature,
+        mmx_target_feature,
+        module,
+        more_struct_aliases,
+        movbe_target_feature,
         must_use,
         naked,
+        naked_functions,
+        name,
         needs_allocator,
         needs_panic_runtime,
+        negate_unsigned,
+        never,
+        never_type,
+        next,
+        __next,
+        nll,
         no_builtins,
         no_core,
+        no_crate_inject,
         no_debug,
+        no_default_passes,
         no_implicit_prelude,
+        no_inline,
         no_link,
         no_main,
         no_mangle,
+        non_ascii_idents,
+        None,
+        non_exhaustive,
+        non_modrs_mods,
+        no_stack_check,
         no_start,
         no_std,
-        non_exhaustive,
+        not,
+        note,
+        Ok,
         omit_gdb_pretty_printer_section,
+        on,
+        on_unimplemented,
+        oom,
+        ops,
         optimize,
+        optimize_attribute,
+        optin_builtin_traits,
+        option,
+        Option,
+        opt_out_copy,
+        Output,
+        overlapping_marker_traits,
+        packed,
         panic_handler,
+        panic_impl,
+        panic_implementation,
         panic_runtime,
+        passes,
         path,
+        pattern_parentheses,
+        Pending,
+        pin,
+        Pin,
+        pinned,
+        platform_intrinsics,
         plugin,
         plugin_registrar,
+        plugins,
+        Poll,
+        poll_with_tls_context,
+        powerpc_target_feature,
+        precise_pointer_size_matching,
+        prelude,
         prelude_import,
+        primitive,
+        proc_dash_macro: "proc-macro",
         proc_macro,
         proc_macro_attribute,
         proc_macro_derive,
+        proc_macro_expr,
+        proc_macro_gen,
+        proc_macro_hygiene,
+        proc_macro_mod,
+        proc_macro_non_items,
+        proc_macro_path_invoc,
         profiler_runtime,
+        pub_restricted,
+        pushpop_unsafe,
+        quad_precision_float,
+        question_mark,
+        quote,
+        Range,
+        RangeFrom,
+        RangeFull,
+        RangeInclusive,
+        RangeTo,
+        RangeToInclusive,
+        raw_identifiers,
+        Ready,
+        reason,
         recursion_limit,
         reexport_test_harness_main,
+        reflect,
+        relaxed_adts,
         repr,
+        repr128,
+        repr_align,
+        repr_align_enum,
+        repr_packed,
+        repr_simd,
+        repr_transparent,
+        re_rebalance_coherence,
+        result,
+        Result,
+        Return,
+        rlib,
+        rtm_target_feature,
+        rust,
+        rust_2015_preview,
+        rust_2018_preview,
+        rust_begin_unwind,
+        rustc_allocator_nounwind,
+        rustc_allow_const_fn_ptr,
         rustc_args_required_const,
+        rustc_attrs,
         rustc_clean,
         rustc_const_unstable,
         rustc_conversion_suggestion,
         rustc_copy_clone_marker,
         rustc_def_path,
         rustc_deprecated,
+        rustc_diagnostic_macros,
         rustc_dirty,
+        rustc_doc_only_macro,
+        rustc_dump_env_program_clauses,
         rustc_dump_program_clauses,
         rustc_dump_user_substs,
         rustc_error,
@@ -191,13 +491,21 @@ symbols! {
         rustc_layout_scalar_valid_range_end,
         rustc_layout_scalar_valid_range_start,
         rustc_mir,
+        rustc_object_lifetime_default,
         rustc_on_unimplemented,
         rustc_outlives,
         rustc_paren_sugar,
         rustc_partition_codegened,
         rustc_partition_reused,
+        rustc_peek,
+        rustc_peek_definite_init,
+        rustc_peek_maybe_init,
+        rustc_peek_maybe_uninit,
+        rustc_private,
         rustc_proc_macro_decls,
+        rustc_promotable,
         rustc_regions,
+        rustc_stable,
         rustc_std_internal_symbol,
         rustc_symbol_name,
         rustc_synthetic,
@@ -205,23 +513,112 @@ symbols! {
         rustc_then_this_would_need,
         rustc_transparent_macro,
         rustc_variance,
+        rustdoc,
+        rust_eh_personality,
+        rust_eh_unwind_resume,
+        rust_oom,
+        __rust_unstable_column,
+        rvalue_static_promotion,
         sanitizer_runtime,
+        self_in_typedefs,
+        self_struct_ctor,
+        Send,
         should_panic,
         simd,
+        simd_ffi,
+        since,
+        size,
+        slice_patterns,
+        slicing_syntax,
+        Some,
+        specialization,
+        speed,
         spotlight,
+        sse4a_target_feature,
         stable,
+        staged_api,
         start,
+        static_in_const,
+        staticlib,
+        static_nobundle,
+        static_recursion,
+        std,
+        str,
+        stmt_expr_attributes,
+        stop_after_dataflow,
+        struct_field_attributes,
+        struct_inherit,
         structural_match,
+        struct_variant,
+        suggestion,
         target_feature,
+        target_has_atomic,
+        target_thread_local,
+        task,
+        tbm_target_feature,
+        termination_trait,
+        termination_trait_test,
+        test,
+        test_2018_feature,
+        test_accepted_feature,
+        test_removed_feature,
         test_runner,
         thread_local,
+        tool_attributes,
+        tool_lints,
+        trace_macros,
+        trait_alias,
+        transmute,
+        transparent,
+        trivial_bounds,
+        Try,
+        try_blocks,
+        try_trait,
+        tuple_indexing,
+        ty,
+        type_alias_enum_variants,
+        type_ascription,
         type_length_limit,
+        type_macros,
+        u128,
+        u16,
+        u32,
+        u64,
+        u8,
+        unboxed_closures,
+        underscore_const_names,
+        underscore_imports,
+        underscore_lifetimes,
+        uniform_paths,
+        universal_impl_trait,
+        unmarked_api,
+        unreachable_code,
+        unrestricted_attribute_tokens,
         unsafe_destructor_blind_to_params,
+        unsafe_no_drop_flag,
+        unsized_locals,
+        unsized_tuple_coercion,
         unstable,
+        untagged_unions,
         unwind,
+        unwind_attributes,
         used,
+        use_extern_macros,
+        use_nested_groups,
+        usize,
+        v1,
+        val,
+        vis,
+        visible_private_types,
+        volatile,
         warn,
+        warn_directory_ownership,
+        wasm_import_module,
+        wasm_target_feature,
+        while_let,
+        windows,
         windows_subsystem,
+        Yield,
     }
 }
 
@@ -233,10 +630,12 @@ pub struct Ident {
 
 impl Ident {
     #[inline]
+    /// Constructs a new identifier from a symbol and a span.
     pub const fn new(name: Symbol, span: Span) -> Ident {
         Ident { name, span }
     }
 
+    /// Constructs a new identifier with an empty syntax context.
     #[inline]
     pub const fn with_empty_ctxt(name: Symbol) -> Ident {
         Ident::new(name, DUMMY_SP)
@@ -247,11 +646,16 @@ impl Ident {
         Ident::with_empty_ctxt(string.as_symbol())
     }
 
-    /// Maps a string to an identifier with an empty syntax context.
+    /// Maps a string to an identifier with an empty span.
     pub fn from_str(string: &str) -> Ident {
         Ident::with_empty_ctxt(Symbol::intern(string))
     }
 
+    /// Maps a string and a span to an identifier.
+    pub fn from_str_and_span(string: &str, span: Span) -> Ident {
+        Ident::new(Symbol::intern(string), span)
+    }
+
     /// Replaces `lo` and `hi` with those from `span`, but keep hygiene context.
     pub fn with_span_pos(self, span: Span) -> Ident {
         Ident::new(self.name, span.with_ctxt(self.span.ctxt()))
@@ -279,14 +683,23 @@ impl Ident {
         Ident::new(self.name, self.span.modern_and_legacy())
     }
 
+    /// Transforms an identifier into one with the same name, but gensymed.
     pub fn gensym(self) -> Ident {
-        Ident::new(self.name.gensymed(), self.span)
+        let name = with_interner(|interner| interner.gensymed(self.name));
+        Ident::new(name, self.span)
     }
 
+    /// Transforms an underscore identifier into one with the same name, but
+    /// gensymed. Leaves non-underscore identifiers unchanged.
     pub fn gensym_if_underscore(self) -> Ident {
         if self.name == keywords::Underscore.name() { self.gensym() } else { self }
     }
 
+    // WARNING: this function is deprecated and will be removed in the future.
+    pub fn is_gensymed(self) -> bool {
+        with_interner(|interner| interner.is_gensymed(self.name))
+    }
+
     pub fn as_str(self) -> LocalInternedString {
         self.name.as_str()
     }
@@ -339,17 +752,34 @@ impl Decodable for Ident {
         Ok(if !string.starts_with('#') {
             Ident::from_str(&string)
         } else { // FIXME(jseyfried): intercrate hygiene
-            Ident::with_empty_ctxt(Symbol::gensym(&string[1..]))
+            Ident::from_str(&string[1..]).gensym()
         })
     }
 }
 
-/// A symbol is an interned or gensymed string. The use of `newtype_index!` means
-/// that `Option<Symbol>` only takes up 4 bytes, because `newtype_index!` reserves
-/// the last 256 values for tagging purposes.
+/// A symbol is an interned or gensymed string. A gensym is a symbol that is
+/// never equal to any other symbol.
 ///
-/// Note that `Symbol` cannot directly be a `newtype_index!` because it implements
-/// `fmt::Debug`, `Encodable`, and `Decodable` in special ways.
+/// Conceptually, a gensym can be thought of as a normal symbol with an
+/// invisible unique suffix. Gensyms are useful when creating new identifiers
+/// that must not match any existing identifiers, e.g. during macro expansion
+/// and syntax desugaring. Because gensyms should always be identifiers, all
+/// gensym operations are on `Ident` rather than `Symbol`. (Indeed, in the
+/// future the gensym-ness may be moved from `Symbol` to hygiene data.)
+///
+/// Examples:
+/// ```
+/// assert_eq!(Ident::from_str("x"), Ident::from_str("x"))
+/// assert_ne!(Ident::from_str("x").gensym(), Ident::from_str("x"))
+/// assert_ne!(Ident::from_str("x").gensym(), Ident::from_str("x").gensym())
+/// ```
+/// Internally, a symbol is implemented as an index, and all operations
+/// (including hashing, equality, and ordering) operate on that index. The use
+/// of `newtype_index!` means that `Option<Symbol>` only takes up 4 bytes,
+/// because `newtype_index!` reserves the last 256 values for tagging purposes.
+///
+/// Note that `Symbol` cannot directly be a `newtype_index!` because it
+/// implements `fmt::Debug`, `Encodable`, and `Decodable` in special ways.
 #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
 pub struct Symbol(SymbolIndex);
 
@@ -367,23 +797,6 @@ impl Symbol {
         with_interner(|interner| interner.intern(string))
     }
 
-    pub fn interned(self) -> Self {
-        with_interner(|interner| interner.interned(self))
-    }
-
-    /// Gensyms a new `usize`, using the current interner.
-    pub fn gensym(string: &str) -> Self {
-        with_interner(|interner| interner.gensym(string))
-    }
-
-    pub fn gensymed(self) -> Self {
-        with_interner(|interner| interner.gensymed(self))
-    }
-
-    pub fn is_gensymed(self) -> bool {
-        with_interner(|interner| interner.is_gensymed(self))
-    }
-
     pub fn as_str(self) -> LocalInternedString {
         with_interner(|interner| unsafe {
             LocalInternedString {
@@ -432,12 +845,6 @@ impl Decodable for Symbol {
     }
 }
 
-impl<T: std::ops::Deref<Target=str>> PartialEq<T> for Symbol {
-    fn eq(&self, other: &T) -> bool {
-        self.as_str() == other.deref()
-    }
-}
-
 // The `&'static str`s in this type actually point into the arena.
 //
 // Note that normal symbols are indexed upward from 0, and gensyms are indexed
@@ -453,15 +860,16 @@ pub struct Interner {
 impl Interner {
     fn prefill(init: &[&str]) -> Self {
         let mut this = Interner::default();
-        for &string in init {
-            if string == "" {
-                // We can't allocate empty strings in the arena, so handle this here.
-                let name = Symbol::new(this.strings.len() as u32);
-                this.names.insert("", name);
-                this.strings.push("");
-            } else {
-                this.intern(string);
-            }
+        this.names.reserve(init.len());
+        this.strings.reserve(init.len());
+
+        // We can't allocate empty strings in the arena, so handle this here.
+        assert!(keywords::Invalid.name().as_u32() == 0 && init[0].is_empty());
+        this.names.insert("", keywords::Invalid.name());
+        this.strings.push("");
+
+        for string in &init[1..] {
+            this.intern(string);
         }
         this
     }
@@ -488,19 +896,14 @@ impl Interner {
         name
     }
 
-    pub fn interned(&self, symbol: Symbol) -> Symbol {
+    fn interned(&self, symbol: Symbol) -> Symbol {
         if (symbol.0.as_usize()) < self.strings.len() {
             symbol
         } else {
-            self.interned(self.gensyms[(SymbolIndex::MAX_AS_U32 - symbol.0.as_u32()) as usize])
+            self.gensyms[(SymbolIndex::MAX_AS_U32 - symbol.0.as_u32()) as usize]
         }
     }
 
-    fn gensym(&mut self, string: &str) -> Symbol {
-        let symbol = self.intern(string);
-        self.gensymed(symbol)
-    }
-
     fn gensymed(&mut self, symbol: Symbol) -> Symbol {
         self.gensyms.push(symbol);
         Symbol::new(SymbolIndex::MAX_AS_U32 - self.gensyms.len() as u32 + 1)
@@ -510,10 +913,15 @@ impl Interner {
         symbol.0.as_usize() >= self.strings.len()
     }
 
+    // Get the symbol as a string. `Symbol::as_str()` should be used in
+    // preference to this function.
     pub fn get(&self, symbol: Symbol) -> &str {
         match self.strings.get(symbol.0.as_usize()) {
             Some(string) => string,
-            None => self.get(self.gensyms[(SymbolIndex::MAX_AS_U32 - symbol.0.as_u32()) as usize]),
+            None => {
+                let symbol = self.gensyms[(SymbolIndex::MAX_AS_U32 - symbol.0.as_u32()) as usize];
+                self.strings[symbol.0.as_usize()]
+            }
         }
     }
 }
@@ -541,7 +949,8 @@ pub mod keywords {
     keywords!();
 }
 
-pub mod symbols {
+// This module has a very short name because it's used a lot.
+pub mod sym {
     use super::Symbol;
     symbols!();
 }
@@ -611,17 +1020,34 @@ fn with_interner<T, F: FnOnce(&mut Interner) -> T>(f: F) -> T {
     GLOBALS.with(|globals| f(&mut *globals.symbol_interner.lock()))
 }
 
-/// 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.
+/// An alternative to `Symbol` and `InternedString`, useful when the chars
+/// within the symbol need to be accessed. It is best used for temporary
+/// values.
+///
+/// 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.
 #[derive(Clone, Copy, Hash, PartialOrd, Eq, Ord)]
 pub struct LocalInternedString {
     string: &'static str,
 }
 
 impl LocalInternedString {
+    /// Maps a string to its interned representation.
+    pub fn intern(string: &str) -> Self {
+        let string = with_interner(|interner| {
+            let symbol = interner.intern(string);
+            interner.strings[symbol.0.as_usize()]
+        });
+        LocalInternedString {
+            string: unsafe { std::mem::transmute::<&str, &str>(string) }
+        }
+    }
+
     pub fn as_interned_str(self) -> InternedString {
         InternedString {
             symbol: Symbol::intern(self.string)
@@ -698,7 +1124,7 @@ impl fmt::Display for LocalInternedString {
 
 impl Decodable for LocalInternedString {
     fn decode<D: Decoder>(d: &mut D) -> Result<LocalInternedString, D::Error> {
-        Ok(Symbol::intern(&d.read_str()?).as_str())
+        Ok(LocalInternedString::intern(&d.read_str()?))
     }
 }
 
@@ -708,13 +1134,32 @@ impl Encodable for LocalInternedString {
     }
 }
 
-/// Represents a string stored in the string interner.
+/// An alternative to `Symbol` that is focused on string contents. It has two
+/// main differences to `Symbol`.
+///
+/// First, its implementations of `Hash`, `PartialOrd` and `Ord` work with the
+/// string chars rather than the symbol integer. This is useful when hash
+/// stability is required across compile sessions, or a guaranteed sort
+/// ordering is required.
+///
+/// Second, gensym-ness is irrelevant. E.g.:
+/// ```
+/// assert_ne!(Symbol::gensym("x"), Symbol::gensym("x"))
+/// assert_eq!(Symbol::gensym("x").as_interned_str(), Symbol::gensym("x").as_interned_str())
+/// ```
 #[derive(Clone, Copy, Eq)]
 pub struct InternedString {
     symbol: Symbol,
 }
 
 impl InternedString {
+    /// Maps a string to its interned representation.
+    pub fn intern(string: &str) -> Self {
+        InternedString {
+            symbol: Symbol::intern(string)
+        }
+    }
+
     pub fn with<F: FnOnce(&str) -> R, R>(self, f: F) -> R {
         let str = with_interner(|interner| {
             interner.get(self.symbol) as *const str
@@ -725,6 +1170,15 @@ impl InternedString {
         unsafe { f(&*str) }
     }
 
+    fn with2<F: FnOnce(&str, &str) -> R, R>(self, other: &InternedString, f: F) -> R {
+        let (self_str, other_str) = with_interner(|interner| {
+            (interner.get(self.symbol) as *const str,
+             interner.get(other.symbol) as *const str)
+        });
+        // This is safe for the same reason that `with` is safe.
+        unsafe { f(&*self_str, &*other_str) }
+    }
+
     pub fn as_symbol(self) -> Symbol {
         self.symbol
     }
@@ -745,7 +1199,7 @@ impl PartialOrd<InternedString> for InternedString {
         if self.symbol == other.symbol {
             return Some(Ordering::Equal);
         }
-        self.with(|self_str| other.with(|other_str| self_str.partial_cmp(other_str)))
+        self.with2(other, |self_str, other_str| self_str.partial_cmp(other_str))
     }
 }
 
@@ -754,7 +1208,7 @@ impl Ord for InternedString {
         if self.symbol == other.symbol {
             return Ordering::Equal;
         }
-        self.with(|self_str| other.with(|other_str| self_str.cmp(&other_str)))
+        self.with2(other, |self_str, other_str| self_str.cmp(other_str))
     }
 }
 
@@ -794,12 +1248,6 @@ impl<'a> PartialEq<InternedString> for &'a String {
     }
 }
 
-impl std::convert::From<InternedString> for String {
-    fn from(val: InternedString) -> String {
-        val.as_symbol().to_string()
-    }
-}
-
 impl fmt::Debug for InternedString {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         self.with(|str| fmt::Debug::fmt(&str, f))
@@ -814,7 +1262,7 @@ impl fmt::Display for InternedString {
 
 impl Decodable for InternedString {
     fn decode<D: Decoder>(d: &mut D) -> Result<InternedString, D::Error> {
-        Ok(Symbol::intern(&d.read_str()?).as_interned_str())
+        Ok(InternedString::intern(&d.read_str()?))
     }
 }
 
@@ -841,11 +1289,13 @@ mod tests {
         assert_eq!(i.intern("cat"), Symbol::new(1));
         // dog is still at zero
         assert_eq!(i.intern("dog"), Symbol::new(0));
-        assert_eq!(i.gensym("zebra"), Symbol::new(SymbolIndex::MAX_AS_U32));
+        let z = i.intern("zebra");
+        assert_eq!(i.gensymed(z), Symbol::new(SymbolIndex::MAX_AS_U32));
         // gensym of same string gets new number:
-        assert_eq!(i.gensym("zebra"), Symbol::new(SymbolIndex::MAX_AS_U32 - 1));
+        assert_eq!(i.gensymed(z), Symbol::new(SymbolIndex::MAX_AS_U32 - 1));
         // gensym of *existing* string gets new number:
-        assert_eq!(i.gensym("dog"), Symbol::new(SymbolIndex::MAX_AS_U32 - 2));
+        let d = i.intern("dog");
+        assert_eq!(i.gensymed(d), Symbol::new(SymbolIndex::MAX_AS_U32 - 2));
     }
 
     #[test]