leo_span/
symbol.rs

1// Copyright (C) 2019-2025 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/// A helper for `symbols` defined below.
34/// The macro's job is to bind conveniently usable `const` items to the symbol names provided.
35/// For example, with `symbol { a, b }` you'd have `sym::a` and `sym::b`.
36macro_rules! consts {
37    ($val: expr, $sym:ident $(,)?) => {
38        #[allow(non_upper_case_globals)]
39        pub const $sym: $crate::symbol::Symbol = $crate::symbol::Symbol::new($val);
40    };
41    ($val: expr, $sym:ident: $_s:literal $(,)?) => {
42        consts!($val, $sym);
43    };
44    ($val: expr, $sym:ident: $_s:literal, $($rest:tt)*) => {
45        consts!($val, $sym);
46        consts!($val + 1, $($rest)*);
47    };
48    ($val: expr, $sym:ident, $($rest:tt)*) => {
49        consts!($val, $sym);
50        consts!($val + 1, $($rest)*);
51    };
52}
53
54/// A helper for `symbols` defined below.
55/// The macro's job is to merge all the hard-coded strings into a single array of strings.
56/// The strategy applied is [push-down accumulation](https://danielkeep.github.io/tlborm/book/pat-push-down-accumulation.html).
57macro_rules! strings {
58    // Final step 0) in the push-down accumulation.
59    // Here, the actual strings gathered in `$acc` are made into an array.
60    // We have to use this approach because e.g., `$e1 , $e2` is not recognized by any syntactic
61    // category in Rust, so a macro cannot produce it.
62    ([$($acc:expr),*] []) => {
63        [$($acc),*]
64    };
65    // Recursive case 1): Handles e.g., `x: "frodo"` and `y: "sam"`
66    // in `symbols! { x: "frodo", y: "sam", z }`.
67    ([$($acc:expr),*] [$_sym:ident: $string:literal, $($rest:tt)*]) => {
68        strings!([$($acc,)* $string] [$($rest)*])
69    };
70    // Recursive case 2): Handles e.g., `x` and `y` in `symbols! { x, y, z }`.
71    ([$($acc:expr),*] [$sym:ident, $($rest:tt)*]) => {
72        strings!([$($acc,)* stringify!($sym)] [$($rest)*])
73    };
74    // Base case 3): As below, but with a specified string, e.g., `symbols! { x, y: "gandalf" }`.
75    ([$($acc:expr),*] [$_sym:ident: $string:literal $(,)?]) => {
76        strings!([$($acc,)* $string] [])
77    };
78    // Base case 4): A single identifier left.
79    // So in e.g., `symbols! { x, y }`, this handles `y` with `x` already in `$acc`.
80    ([$($acc:expr),*] [$sym:ident $(,)?]) => {
81        strings!([$($acc,)* stringify!($sym)] [])
82    };
83}
84
85/// Creates predefined symbols used throughout the Leo compiler and language.
86/// Broadly speaking, any hard-coded string in the compiler should be defined here.
87///
88/// The macro accepts symbols separated by commas,
89/// and a symbol is either specified as a Rust identifier, in which case it is `stringify!`ed,
90/// or as `ident: "string"` where `"string"` is the actual hard-coded string.
91/// The latter case can be used when the hard-coded string is not a valid identifier.
92/// In either case, a `const $ident: Symbol` will be created that you can access as `sym::$ident`.
93macro_rules! symbols {
94    ($($symbols:tt)*) => {
95        const PRE_DEFINED: &[&str] = &strings!([] [$($symbols)*]);
96
97        pub mod sym {
98            consts!(0, $($symbols)*);
99        }
100    };
101}
102
103symbols! {
104    // unary operators
105    abs,
106    abs_wrapped,
107    double,
108    inv,
109    neg,
110    not,
111    square,
112    square_root,
113
114    // binary operators
115    add,
116    add_wrapped,
117    and,
118    div,
119    div_wrapped,
120    eq,
121    gte,
122    gt,
123    lte,
124    lt,
125    Mod: "mod",
126    mul,
127    mul_wrapped,
128    nand,
129    neq,
130    nor,
131    or,
132    pow,
133    pow_wrapped,
134    rem,
135    rem_wrapped,
136    shl,
137    shl_wrapped,
138    shr,
139    shr_wrapped,
140    sub,
141    sub_wrapped,
142    xor,
143
144    // core constants
145    GEN,
146
147    // core functions
148    BHP256,
149    BHP512,
150    BHP768,
151    BHP1024,
152    ChaCha,
153    commit_to_address,
154    commit_to_field,
155    commit_to_group,
156    contains,
157    ECDSA,
158    get,
159    get_or_use,
160    hash_to_address,
161    hash_to_field,
162    hash_to_group,
163    hash_to_i8,
164    hash_to_i16,
165    hash_to_i32,
166    hash_to_i64,
167    hash_to_i128,
168    hash_to_u8,
169    hash_to_u16,
170    hash_to_u32,
171    hash_to_u64,
172    hash_to_u128,
173    hash_to_scalar,
174    hash_to_address_raw,
175    hash_to_field_raw,
176    hash_to_group_raw,
177    hash_to_i8_raw,
178    hash_to_i16_raw,
179    hash_to_i32_raw,
180    hash_to_i64_raw,
181    hash_to_i128_raw,
182    hash_to_u8_raw,
183    hash_to_u16_raw,
184    hash_to_u32_raw,
185    hash_to_u64_raw,
186    hash_to_u128_raw,
187    hash_to_scalar_raw,
188    hash_native,
189    hash_native_raw,
190    Keccak256,
191    Keccak384,
192    Keccak512,
193    Mapping,
194    Storage,
195    Optional,
196    Vector,
197    Pedersen64,
198    Pedersen128,
199    Poseidon2,
200    Poseidon4,
201    Poseidon8,
202    rand_address,
203    rand_bool,
204    rand_field,
205    rand_group,
206    rand_i8,
207    rand_i16,
208    rand_i32,
209    rand_i64,
210    rand_i128,
211    rand_scalar,
212    rand_u8,
213    rand_u16,
214    rand_u32,
215    rand_u64,
216    rand_u128,
217    remove,
218    set,
219    SHA3_256,
220    SHA3_384,
221    SHA3_512,
222    to_x_coordinate,
223    to_y_coordinate,
224    verify,
225    verify_digest,
226    verify_digest_eth,
227    verify_keccak256,
228    verify_keccak256_raw,
229    verify_keccak256_eth,
230    verify_keccak256_eth_raw,
231    verify_keccak384,
232    verify_keccak384_raw,
233    verify_keccak384_eth,
234    verify_keccak384_eth_raw,
235    verify_keccak512,
236    verify_keccak512_raw,
237    verify_keccak512_eth,
238    verify_keccak512_eth_raw,
239    verify_sha3_256,
240    verify_sha3_256_raw,
241    verify_sha3_256_eth,
242    verify_sha3_256_eth_raw,
243    verify_sha3_384,
244    verify_sha3_384_raw,
245    verify_sha3_384_eth,
246    verify_sha3_384_eth_raw,
247    verify_sha3_512,
248    verify_sha3_512_raw,
249    verify_sha3_512_eth,
250    verify_sha3_512_eth_raw,
251    Serialize,
252    Deserialize,
253    to_bits,
254    to_bits_raw,
255    from_bits,
256    from_bits_raw,
257
258    Await: "await",
259    unwrap,
260    unwrap_or,
261    push,
262    len,
263    clear,
264    pop,
265    swap_remove,
266
267    // CheatCodes
268    CheatCode,
269    print_mapping,
270    set_block_height,
271    set_signer,
272
273    // types
274    address,
275    bool,
276    field,
277    group,
278    i8,
279    i16,
280    i32,
281    i64,
282    i128,
283    Future,
284    Fn,
285    record,
286    scalar,
287    signature,
288    string,
289    Struct: "struct",
290    u8,
291    u16,
292    u32,
293    u64,
294    u128,
295
296    // values
297    False: "false",
298    True: "true",
299    None: "none",
300
301    // annotations
302    should_fail,
303    test,
304    noupgrade,
305    custom,
306    admin,
307    key,
308
309    // annotation keys
310    private_key,
311
312    // general keywords
313    As: "as",
314    assert,
315    assert_eq,
316    assert_neq,
317    Async: "async",
318    caller,
319    Const: "const",
320    constant,
321    constructor,
322    decrement,
323    Else: "else",
324    For: "for",
325    function,
326    If: "if",
327    In: "in",
328    import,
329    increment,
330    inline,
331    input,
332    Let: "let",
333    leo,
334    main,
335    mapping,
336    storage,
337    Mut: "mut",
338    Return: "return",
339    script,
340    SelfLower: "self",
341    SelfUpper: "Self",
342    signer,
343    Star: "*",
344    transition,
345    Type: "type",
346
347    aleo,
348    public,
349    private,
350    owner,
351    _nonce,
352    program,
353    ProgramCore: "Program",
354    stub,
355    block,
356    height,
357    network,
358    id,
359    checksum,
360    edition,
361    program_owner,
362}
363
364/// An interned string.
365///
366/// Represented as an index internally, with all operations based on that.
367/// A `Symbol` reserves the value `0`, so that `Option<Symbol>` only takes up 4 bytes.
368#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
369pub struct Symbol(
370    #[serde(deserialize_with = "Symbol::serde_to_symbol")]
371    #[serde(serialize_with = "Symbol::serde_from_symbol")]
372    NonZeroU32,
373);
374
375impl Default for Symbol {
376    fn default() -> Self {
377        Symbol(NonZeroU32::MIN)
378    }
379}
380
381impl Symbol {
382    /// Returns the corresponding `Symbol` for the given `index`.
383    pub const fn new(index: u32) -> Self {
384        let index = index.saturating_add(1);
385        Self(match NonZeroU32::new(index) {
386            None => unreachable!(),
387            Some(x) => x,
388        })
389    }
390
391    /// Maps a string to its interned representation.
392    pub fn intern(string: &str) -> Self {
393        with_session_globals(|session_globals| session_globals.symbol_interner.intern(string))
394    }
395
396    /// Convert to effectively a `&'static str` given the `SessionGlobals`.
397    pub fn as_str<R>(self, s: &SessionGlobals, with: impl FnOnce(&str) -> R) -> R {
398        s.symbol_interner.get(self, with)
399    }
400
401    /// Converts this symbol to the raw index.
402    pub const fn as_u32(self) -> u32 {
403        self.0.get() - 1
404    }
405
406    fn serde_to_symbol<'de, D: Deserializer<'de>>(de: D) -> Result<NonZeroU32, D::Error> {
407        Ok(Symbol::intern(<&str>::deserialize(de)?).0)
408    }
409
410    fn serde_from_symbol<S: Serializer>(index: &NonZeroU32, ser: S) -> Result<S::Ok, S::Error> {
411        with_session_globals(|sg| Self(*index).as_str(sg, |s| ser.serialize_str(s)))
412    }
413}
414
415impl fmt::Debug for Symbol {
416    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
417        with_session_globals(|s| self.as_str(s, |s| fmt::Debug::fmt(s, f)))
418    }
419}
420
421impl fmt::Display for Symbol {
422    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
423        with_session_globals(|s| self.as_str(s, |s| fmt::Display::fmt(s, f)))
424    }
425}
426
427/// All the globals for a compiler sessions.
428pub struct SessionGlobals {
429    /// The interner for `Symbol`s used in the compiler.
430    symbol_interner: Interner,
431    /// The source map used in the compiler.
432    pub source_map: SourceMap,
433}
434
435impl Default for SessionGlobals {
436    fn default() -> Self {
437        Self { symbol_interner: Interner::prefilled(), source_map: SourceMap::default() }
438    }
439}
440
441scoped_tls::scoped_thread_local!(pub static SESSION_GLOBALS: SessionGlobals);
442
443/// Creates the session globals and then runs the closure `f`.
444#[inline]
445pub fn create_session_if_not_set_then<R>(f: impl FnOnce(&SessionGlobals) -> R) -> R {
446    if !SESSION_GLOBALS.is_set() {
447        let sg = SessionGlobals::default();
448        SESSION_GLOBALS.set(&sg, || SESSION_GLOBALS.with(f))
449    } else {
450        SESSION_GLOBALS.with(f)
451    }
452}
453
454/// Gives access to read or modify the session globals in `f`.
455#[inline]
456pub fn with_session_globals<R>(f: impl FnOnce(&SessionGlobals) -> R) -> R {
457    SESSION_GLOBALS.with(f)
458}
459
460/// An interned string,
461/// either prefilled "at compile time" (`Static`),
462/// or created at runtime (`Owned`).
463#[derive(Eq)]
464enum InternedStr {
465    /// String is stored "at compile time", i.e. prefilled.
466    Static(&'static str),
467    /// String is constructed and stored during runtime.
468    Owned(Box<str>),
469}
470
471impl Borrow<str> for InternedStr {
472    fn borrow(&self) -> &str {
473        self.deref()
474    }
475}
476
477impl Deref for InternedStr {
478    type Target = str;
479
480    fn deref(&self) -> &Self::Target {
481        match self {
482            Self::Static(s) => s,
483            Self::Owned(s) => s,
484        }
485    }
486}
487
488impl PartialEq for InternedStr {
489    fn eq(&self, other: &InternedStr) -> bool {
490        self.deref() == other.deref()
491    }
492}
493
494impl Hash for InternedStr {
495    fn hash<H: Hasher>(&self, state: &mut H) {
496        self.deref().hash(state);
497    }
498}
499
500/// The inner interner.
501/// This construction is used to get interior mutability in `Interner`.
502struct InnerInterner {
503    // /// Arena used to allocate the strings, giving us `&'static str`s from it.
504    // arena: DroplessArena,
505    /// Registration of strings and symbol index allocation is done in this set.
506    set: IndexSet<InternedStr, FxBuildHasher>,
507}
508
509/// A symbol-to-string interner.
510struct Interner {
511    inner: RefCell<InnerInterner>,
512}
513
514impl Interner {
515    /// Returns an interner prefilled with commonly used strings in Leo.
516    fn prefilled() -> Self {
517        Self::prefill(PRE_DEFINED)
518    }
519
520    /// Returns an interner prefilled with `init`.
521    fn prefill(init: &[&'static str]) -> Self {
522        let inner = InnerInterner {
523            // arena: <_>::default(),
524            set: init.iter().copied().map(InternedStr::Static).collect(),
525        };
526        Self { inner: RefCell::new(inner) }
527    }
528
529    /// Interns `string`, returning a `Symbol` corresponding to it.
530    fn intern(&self, string: &str) -> Symbol {
531        let InnerInterner { set } = &mut *self.inner.borrow_mut();
532
533        if let Some(sym) = set.get_index_of(string) {
534            // Already interned, return that symbol.
535            return Symbol::new(sym as u32);
536        }
537
538        Symbol::new(set.insert_full(InternedStr::Owned(string.into())).0 as u32)
539    }
540
541    /// Returns the corresponding string for the given symbol.
542    fn get<R>(&self, symbol: Symbol, with: impl FnOnce(&str) -> R) -> R {
543        let set = &self.inner.borrow().set;
544        with(set.get_index(symbol.as_u32() as usize).unwrap())
545    }
546}