Skip to main content

leo_span/
symbol.rs

1// Copyright (C) 2019-2026 Provable Inc.
2// This file is part of the Leo library.
3
4// The Leo library is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8
9// The Leo library is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU General Public License for more details.
13
14// You should have received a copy of the GNU General Public License
15// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
16
17use crate::source_map::SourceMap;
18
19use core::{
20    borrow::Borrow,
21    cmp::PartialEq,
22    fmt,
23    hash::{Hash, Hasher},
24    num::NonZeroU32,
25    ops::Deref,
26    str,
27};
28use fxhash::FxBuildHasher;
29use indexmap::IndexSet;
30use serde::{Deserialize, Deserializer, Serialize, Serializer};
31use std::cell::RefCell;
32
33// Predefined symbols generated at build time.
34include!(concat!(env!("OUT_DIR"), "/symbols_generated.rs"));
35
36/// An interned string.
37///
38/// Represented as an index internally, with all operations based on that.
39/// A `Symbol` reserves the value `0`, so that `Option<Symbol>` only takes up 4 bytes.
40#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
41pub struct Symbol(
42    #[serde(deserialize_with = "Symbol::serde_to_symbol")]
43    #[serde(serialize_with = "Symbol::serde_from_symbol")]
44    NonZeroU32,
45);
46
47impl Default for Symbol {
48    fn default() -> Self {
49        Symbol(NonZeroU32::MIN)
50    }
51}
52
53impl Symbol {
54    /// Returns the corresponding `Symbol` for the given `index`.
55    pub const fn new(index: u32) -> Self {
56        let index = index.saturating_add(1);
57        Self(match NonZeroU32::new(index) {
58            None => unreachable!(),
59            Some(x) => x,
60        })
61    }
62
63    /// Maps a string to its interned representation.
64    pub fn intern(string: &str) -> Self {
65        with_session_globals(|session_globals| session_globals.symbol_interner.intern(string))
66    }
67
68    /// Convert to effectively a `&'static str` given the `SessionGlobals`.
69    pub fn as_str<R>(self, s: &SessionGlobals, with: impl FnOnce(&str) -> R) -> R {
70        s.symbol_interner.get(self, with)
71    }
72
73    /// Converts this symbol to the raw index.
74    pub const fn as_u32(self) -> u32 {
75        self.0.get() - 1
76    }
77
78    fn serde_to_symbol<'de, D: Deserializer<'de>>(de: D) -> Result<NonZeroU32, D::Error> {
79        Ok(Symbol::intern(<&str>::deserialize(de)?).0)
80    }
81
82    fn serde_from_symbol<S: Serializer>(index: &NonZeroU32, ser: S) -> Result<S::Ok, S::Error> {
83        with_session_globals(|sg| Self(*index).as_str(sg, |s| ser.serialize_str(s)))
84    }
85}
86
87impl fmt::Debug for Symbol {
88    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
89        with_session_globals(|s| self.as_str(s, |s| fmt::Debug::fmt(s, f)))
90    }
91}
92
93impl fmt::Display for Symbol {
94    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
95        with_session_globals(|s| self.as_str(s, |s| fmt::Display::fmt(s, f)))
96    }
97}
98
99/// All the globals for a compiler sessions.
100pub struct SessionGlobals {
101    /// The interner for `Symbol`s used in the compiler.
102    symbol_interner: Interner,
103    /// The source map used in the compiler.
104    pub source_map: SourceMap,
105}
106
107impl Default for SessionGlobals {
108    fn default() -> Self {
109        Self { symbol_interner: Interner::prefilled(), source_map: SourceMap::default() }
110    }
111}
112
113scoped_tls::scoped_thread_local!(pub static SESSION_GLOBALS: SessionGlobals);
114
115/// Creates the session globals and then runs the closure `f`.
116#[inline]
117pub fn create_session_if_not_set_then<R>(f: impl FnOnce(&SessionGlobals) -> R) -> R {
118    if !SESSION_GLOBALS.is_set() {
119        let sg = SessionGlobals::default();
120        SESSION_GLOBALS.set(&sg, || SESSION_GLOBALS.with(f))
121    } else {
122        SESSION_GLOBALS.with(f)
123    }
124}
125
126/// Gives access to read or modify the session globals in `f`.
127#[inline]
128pub fn with_session_globals<R>(f: impl FnOnce(&SessionGlobals) -> R) -> R {
129    SESSION_GLOBALS.with(f)
130}
131
132/// An interned string,
133/// either prefilled "at compile time" (`Static`),
134/// or created at runtime (`Owned`).
135#[derive(Eq)]
136enum InternedStr {
137    /// String is stored "at compile time", i.e. prefilled.
138    Static(&'static str),
139    /// String is constructed and stored during runtime.
140    Owned(Box<str>),
141}
142
143impl Borrow<str> for InternedStr {
144    fn borrow(&self) -> &str {
145        self.deref()
146    }
147}
148
149impl Deref for InternedStr {
150    type Target = str;
151
152    fn deref(&self) -> &Self::Target {
153        match self {
154            Self::Static(s) => s,
155            Self::Owned(s) => s,
156        }
157    }
158}
159
160impl PartialEq for InternedStr {
161    fn eq(&self, other: &InternedStr) -> bool {
162        self.deref() == other.deref()
163    }
164}
165
166impl Hash for InternedStr {
167    fn hash<H: Hasher>(&self, state: &mut H) {
168        self.deref().hash(state);
169    }
170}
171
172/// The inner interner.
173/// This construction is used to get interior mutability in `Interner`.
174struct InnerInterner {
175    // /// Arena used to allocate the strings, giving us `&'static str`s from it.
176    // arena: DroplessArena,
177    /// Registration of strings and symbol index allocation is done in this set.
178    set: IndexSet<InternedStr, FxBuildHasher>,
179}
180
181/// A symbol-to-string interner.
182struct Interner {
183    inner: RefCell<InnerInterner>,
184}
185
186impl Interner {
187    /// Returns an interner prefilled with commonly used strings in Leo.
188    fn prefilled() -> Self {
189        Self::prefill(PRE_DEFINED)
190    }
191
192    /// Returns an interner prefilled with `init`.
193    fn prefill(init: &[&'static str]) -> Self {
194        let inner = InnerInterner {
195            // arena: <_>::default(),
196            set: init.iter().copied().map(InternedStr::Static).collect(),
197        };
198        Self { inner: RefCell::new(inner) }
199    }
200
201    /// Interns `string`, returning a `Symbol` corresponding to it.
202    fn intern(&self, string: &str) -> Symbol {
203        let InnerInterner { set } = &mut *self.inner.borrow_mut();
204
205        if let Some(sym) = set.get_index_of(string) {
206            // Already interned, return that symbol.
207            return Symbol::new(sym as u32);
208        }
209
210        Symbol::new(set.insert_full(InternedStr::Owned(string.into())).0 as u32)
211    }
212
213    /// Returns the corresponding string for the given symbol.
214    fn get<R>(&self, symbol: Symbol, with: impl FnOnce(&str) -> R) -> R {
215        let set = &self.inner.borrow().set;
216        with(set.get_index(symbol.as_u32() as usize).unwrap())
217    }
218}