diff options
| author | Alex Crichton <alex@alexcrichton.com> | 2019-05-21 15:37:07 -0700 |
|---|---|---|
| committer | Alex Crichton <alex@alexcrichton.com> | 2019-05-21 15:37:07 -0700 |
| commit | fe3dd0b50fef21d14591c960a9610bafb224cdbf (patch) | |
| tree | ee0b62d8e500d4ce4b6f50b4fe5d9056b9826072 /src/libsyntax_pos | |
| parent | e764f475ca7fffd6167ea991afc7d1b2b3f642dc (diff) | |
| parent | 50a0defd5a93523067ef239936cc2e0755220904 (diff) | |
| download | rust-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.rs | 7 | ||||
| -rw-r--r-- | src/libsyntax_pos/hygiene.rs | 5 | ||||
| -rw-r--r-- | src/libsyntax_pos/lib.rs | 6 | ||||
| -rw-r--r-- | src/libsyntax_pos/symbol.rs | 596 |
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] |
