1use crate::{SessionGlobals, Span};
2use solar_data_structures::{index::BaseIndex32, trustme};
3use solar_macros::symbols;
4use std::{cmp, fmt, hash, str};
5
6#[derive(Clone, Copy)]
8pub struct Ident {
9 pub name: Symbol,
11 pub span: Span,
13}
14
15impl Default for Ident {
16 #[inline]
17 fn default() -> Self {
18 Self::DUMMY
19 }
20}
21
22impl PartialEq for Ident {
23 #[inline]
24 fn eq(&self, rhs: &Self) -> bool {
25 self.name == rhs.name
26 }
27}
28
29impl Eq for Ident {}
30
31impl PartialOrd for Ident {
32 #[inline]
33 fn partial_cmp(&self, rhs: &Self) -> Option<cmp::Ordering> {
34 Some(self.cmp(rhs))
35 }
36}
37
38impl Ord for Ident {
39 #[inline]
40 fn cmp(&self, rhs: &Self) -> cmp::Ordering {
41 self.name.cmp(&rhs.name)
42 }
43}
44
45impl hash::Hash for Ident {
46 #[inline]
47 fn hash<H: hash::Hasher>(&self, state: &mut H) {
48 self.name.hash(state);
49 }
50}
51
52impl fmt::Debug for Ident {
53 #[inline]
54 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
55 fmt::Display::fmt(self, f)
56 }
57}
58
59impl fmt::Display for Ident {
60 #[inline]
61 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
62 self.name.fmt(f)
63 }
64}
65
66impl Ident {
67 pub const DUMMY: Self = Self::new(Symbol::DUMMY, Span::DUMMY);
69
70 #[inline]
72 pub const fn new(name: Symbol, span: Span) -> Self {
73 Self { name, span }
74 }
75
76 #[inline]
78 pub const fn with_dummy_span(name: Symbol) -> Self {
79 Self::new(name, Span::DUMMY)
80 }
81
82 #[allow(clippy::should_implement_trait)]
84 pub fn from_str(string: &str) -> Self {
85 Self::with_dummy_span(Symbol::intern(string))
86 }
87
88 pub fn from_str_and_span(string: &str, span: Span) -> Self {
90 Self::new(Symbol::intern(string), span)
91 }
92
93 #[inline]
95 #[allow(clippy::inherent_to_string_shadow_display)]
96 pub fn to_string(&self) -> String {
97 self.as_str().to_string()
98 }
99
100 pub fn as_str(&self) -> &str {
105 self.name.as_str()
106 }
107
108 #[inline]
110 pub fn is_used_keyword(self) -> bool {
111 self.name.is_used_keyword()
112 }
113
114 #[inline]
116 pub fn is_unused_keyword(self) -> bool {
117 self.name.is_unused_keyword()
118 }
119
120 #[inline]
122 pub fn is_weak_keyword(self) -> bool {
123 self.name.is_weak_keyword()
124 }
125
126 #[inline]
128 pub fn is_yul_keyword(self) -> bool {
129 self.name.is_yul_keyword()
130 }
131
132 #[inline]
134 pub fn is_yul_evm_builtin(self) -> bool {
135 self.name.is_yul_builtin()
136 }
137
138 #[inline]
141 pub fn is_reserved(self, yul: bool) -> bool {
142 self.name.is_reserved(yul)
143 }
144
145 #[inline]
148 pub fn is_non_reserved(self, yul: bool) -> bool {
149 self.name.is_non_reserved(yul)
150 }
151
152 #[inline]
156 pub fn is_elementary_type(self) -> bool {
157 self.name.is_elementary_type()
158 }
159
160 #[inline]
162 pub fn is_bool_lit(self) -> bool {
163 self.name.is_bool_lit()
164 }
165
166 #[inline]
168 pub fn is_location_specifier(self) -> bool {
169 self.name.is_location_specifier()
170 }
171
172 #[inline]
174 pub fn is_mutability_specifier(self) -> bool {
175 self.name.is_mutability_specifier()
176 }
177
178 #[inline]
180 pub fn is_visibility_specifier(self) -> bool {
181 self.name.is_visibility_specifier()
182 }
183}
184
185#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
190pub struct Symbol(BaseIndex32);
191
192impl Default for Symbol {
193 #[inline]
194 fn default() -> Self {
195 Self::DUMMY
196 }
197}
198
199impl Symbol {
200 pub const DUMMY: Self = kw::Empty;
202
203 const fn new(n: u32) -> Self {
204 Self(BaseIndex32::new(n))
205 }
206
207 pub fn intern(string: &str) -> Self {
209 SessionGlobals::with(|g| g.symbol_interner.intern(string))
210 }
211
212 #[inline]
214 #[allow(clippy::inherent_to_string_shadow_display)]
215 pub fn to_string(&self) -> String {
216 self.as_str().to_string()
217 }
218
219 pub fn as_str(&self) -> &str {
228 SessionGlobals::with(|g| unsafe { trustme::decouple_lt(g.symbol_interner.get(*self)) })
229 }
230
231 #[inline(always)]
233 pub const fn as_u32(self) -> u32 {
234 self.0.get()
235 }
236
237 #[inline]
241 pub fn is_used_keyword(self) -> bool {
242 self < kw::After
243 }
244
245 #[inline]
247 pub fn is_unused_keyword(self) -> bool {
248 self >= kw::After && self <= kw::Var
249 }
250
251 #[inline]
253 pub fn is_weak_keyword(self) -> bool {
254 self >= kw::Leave && self <= kw::Builtin
255 }
256
257 #[inline]
259 pub fn is_yul_keyword(self) -> bool {
260 matches!(
262 self,
263 kw::Function
264 | kw::Let
265 | kw::If
266 | kw::Switch
267 | kw::Case
268 | kw::Default
269 | kw::For
270 | kw::Break
271 | kw::Continue
272 | kw::Leave
273 | kw::True
274 | kw::False
275 )
276 }
277
278 #[inline]
280 pub fn is_yul_builtin(self) -> bool {
281 (self >= kw::Add && self <= kw::Xor)
282 | matches!(self, kw::Address | kw::Byte | kw::Return | kw::Revert)
283 }
284
285 #[inline]
288 pub fn is_reserved(self, yul: bool) -> bool {
289 if yul {
290 self.is_yul_keyword() | self.is_yul_builtin()
291 } else {
292 self.is_used_keyword() | self.is_unused_keyword()
293 }
294 }
295
296 #[inline]
299 pub fn is_non_reserved(self, yul: bool) -> bool {
300 !self.is_reserved(yul)
301 }
302
303 #[inline]
307 pub fn is_elementary_type(self) -> bool {
308 self >= kw::Int && self <= kw::UFixed
309 }
310
311 #[inline]
313 pub fn is_bool_lit(self) -> bool {
314 self == kw::False || self == kw::True
315 }
316
317 #[inline]
319 pub fn is_location_specifier(self) -> bool {
320 matches!(self, kw::Calldata | kw::Memory | kw::Storage)
321 }
322
323 #[inline]
325 pub fn is_mutability_specifier(self) -> bool {
326 matches!(self, kw::Immutable | kw::Constant)
327 }
328
329 #[inline]
331 pub fn is_visibility_specifier(self) -> bool {
332 matches!(self, kw::Public | kw::Private | kw::Internal | kw::External)
333 }
334
335 #[inline]
337 pub const fn is_preinterned(self) -> bool {
338 self.as_u32() < PREINTERNED_SYMBOLS_COUNT
339 }
340}
341
342impl fmt::Debug for Symbol {
343 #[inline]
344 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
345 fmt::Debug::fmt(self.as_str(), f)
346 }
347}
348
349impl fmt::Display for Symbol {
350 #[inline]
351 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
352 fmt::Display::fmt(self.as_str(), f)
353 }
354}
355
356type InternerInner = LassoInterner;
357
358pub(crate) struct Interner(InternerInner);
362
363impl Interner {
364 pub(crate) fn fresh() -> Self {
365 Self(InternerInner::fresh())
366 }
367
368 #[cfg(test)]
369 pub(crate) fn prefill(init: &[&'static str]) -> Self {
370 Self(InternerInner::prefill(init))
371 }
372
373 #[inline]
374 fn intern(&self, string: &str) -> Symbol {
375 self.0.intern(string)
376 }
377
378 #[inline]
379 fn get(&self, symbol: Symbol) -> &str {
380 self.0.get(symbol)
381 }
382}
383
384struct LassoInterner(lasso::ThreadedRodeo<Symbol, solar_data_structures::map::FxBuildHasher>);
386
387impl LassoInterner {
388 fn prefill(init: &[&'static str]) -> Self {
389 let capacity = if init.is_empty() {
390 Default::default()
391 } else {
392 let actual_string = init.len();
393 let strings = actual_string.next_power_of_two();
394 let actual_bytes = PREINTERNED_SYMBOLS_BYTES as usize;
395 let bytes = actual_bytes.next_power_of_two().max(4096);
396 trace!(strings, bytes, "prefill capacity");
397 lasso::Capacity::new(strings, std::num::NonZeroUsize::new(bytes).unwrap())
398 };
399 let rodeo = lasso::ThreadedRodeo::with_capacity_and_hasher(capacity, Default::default());
400 for &s in init {
401 rodeo.get_or_intern_static(s);
402 }
403 Self(rodeo)
404 }
405
406 #[inline]
407 fn intern(&self, string: &str) -> Symbol {
408 self.0.get_or_intern(string)
409 }
410
411 #[inline]
412 fn get(&self, symbol: Symbol) -> &str {
413 self.0.resolve(&symbol)
414 }
415}
416
417unsafe impl lasso::Key for Symbol {
418 #[inline]
419 fn into_usize(self) -> usize {
420 self.0.index()
421 }
422
423 #[inline]
424 fn try_from_usize(value: usize) -> Option<Self> {
425 BaseIndex32::try_from_usize(value).map(Self)
426 }
427}
428
429pub mod kw {
435 use crate::Symbol;
436
437 #[doc(inline)]
438 pub use super::kw_generated::*;
439
440 #[inline]
442 pub const fn boolean(b: bool) -> Symbol {
443 if b {
444 True
445 } else {
446 False
447 }
448 }
449
450 #[inline]
458 #[track_caller]
459 pub const fn int(n: u8) -> Symbol {
460 assert!(n <= 32);
461 Symbol::new(Int.as_u32() + n as u32)
462 }
463
464 #[inline]
472 #[track_caller]
473 pub const fn uint(n: u8) -> Symbol {
474 assert!(n <= 32);
475 Symbol::new(UInt.as_u32() + n as u32)
476 }
477
478 #[inline]
484 #[track_caller]
485 pub const fn fixed_bytes(n: u8) -> Symbol {
486 assert!(n > 0 && n <= 32);
487 Symbol::new(Bytes.as_u32() + n as u32)
488 }
489}
490
491pub mod sym {
497 use super::Symbol;
498
499 #[doc(inline)]
500 pub use super::sym_generated::*;
501
502 pub fn integer<N: TryInto<usize> + Copy + itoa::Integer>(n: N) -> Symbol {
506 if let Ok(idx @ 0..=9) = n.try_into() {
507 return Symbol::new(super::SYMBOL_DIGITS_BASE + idx as u32);
508 }
509 Symbol::intern(itoa::Buffer::new().format(n))
510 }
511}
512
513symbols! {
515 Keywords {
519 Empty: "",
521
522 Abstract: "abstract",
523 Anonymous: "anonymous",
524 As: "as",
525 Assembly: "assembly",
526 Break: "break",
527 Calldata: "calldata",
528 Catch: "catch",
529 Constant: "constant",
530 Constructor: "constructor",
531 Continue: "continue",
532 Contract: "contract",
533 Delete: "delete",
534 Do: "do",
535 Else: "else",
536 Emit: "emit",
537 Enum: "enum",
538 Event: "event",
539 External: "external",
540 Fallback: "fallback",
541 For: "for",
542 Function: "function",
543 Hex: "hex",
544 If: "if",
545 Immutable: "immutable",
546 Import: "import",
547 Indexed: "indexed",
548 Interface: "interface",
549 Internal: "internal",
550 Is: "is",
551 Library: "library",
552 Mapping: "mapping",
553 Memory: "memory",
554 Modifier: "modifier",
555 New: "new",
556 Override: "override",
557 Payable: "payable",
558 Pragma: "pragma",
559 Private: "private",
560 Public: "public",
561 Pure: "pure",
562 Receive: "receive",
563 Return: "return",
564 Returns: "returns",
565 Storage: "storage",
566 Struct: "struct",
567 Throw: "throw",
568 Try: "try",
569 Type: "type",
570 Unchecked: "unchecked",
571 Unicode: "unicode",
572 Using: "using",
573 View: "view",
574 Virtual: "virtual",
575 While: "while",
576
577 Int: "int",
579 Int8: "int8",
580 Int16: "int16",
581 Int24: "int24",
582 Int32: "int32",
583 Int40: "int40",
584 Int48: "int48",
585 Int56: "int56",
586 Int64: "int64",
587 Int72: "int72",
588 Int80: "int80",
589 Int88: "int88",
590 Int96: "int96",
591 Int104: "int104",
592 Int112: "int112",
593 Int120: "int120",
594 Int128: "int128",
595 Int136: "int136",
596 Int144: "int144",
597 Int152: "int152",
598 Int160: "int160",
599 Int168: "int168",
600 Int176: "int176",
601 Int184: "int184",
602 Int192: "int192",
603 Int200: "int200",
604 Int208: "int208",
605 Int216: "int216",
606 Int224: "int224",
607 Int232: "int232",
608 Int240: "int240",
609 Int248: "int248",
610 Int256: "int256",
611 UInt: "uint",
612 UInt8: "uint8",
613 UInt16: "uint16",
614 UInt24: "uint24",
615 UInt32: "uint32",
616 UInt40: "uint40",
617 UInt48: "uint48",
618 UInt56: "uint56",
619 UInt64: "uint64",
620 UInt72: "uint72",
621 UInt80: "uint80",
622 UInt88: "uint88",
623 UInt96: "uint96",
624 UInt104: "uint104",
625 UInt112: "uint112",
626 UInt120: "uint120",
627 UInt128: "uint128",
628 UInt136: "uint136",
629 UInt144: "uint144",
630 UInt152: "uint152",
631 UInt160: "uint160",
632 UInt168: "uint168",
633 UInt176: "uint176",
634 UInt184: "uint184",
635 UInt192: "uint192",
636 UInt200: "uint200",
637 UInt208: "uint208",
638 UInt216: "uint216",
639 UInt224: "uint224",
640 UInt232: "uint232",
641 UInt240: "uint240",
642 UInt248: "uint248",
643 UInt256: "uint256",
644 Bytes: "bytes",
645 Bytes1: "bytes1",
646 Bytes2: "bytes2",
647 Bytes3: "bytes3",
648 Bytes4: "bytes4",
649 Bytes5: "bytes5",
650 Bytes6: "bytes6",
651 Bytes7: "bytes7",
652 Bytes8: "bytes8",
653 Bytes9: "bytes9",
654 Bytes10: "bytes10",
655 Bytes11: "bytes11",
656 Bytes12: "bytes12",
657 Bytes13: "bytes13",
658 Bytes14: "bytes14",
659 Bytes15: "bytes15",
660 Bytes16: "bytes16",
661 Bytes17: "bytes17",
662 Bytes18: "bytes18",
663 Bytes19: "bytes19",
664 Bytes20: "bytes20",
665 Bytes21: "bytes21",
666 Bytes22: "bytes22",
667 Bytes23: "bytes23",
668 Bytes24: "bytes24",
669 Bytes25: "bytes25",
670 Bytes26: "bytes26",
671 Bytes27: "bytes27",
672 Bytes28: "bytes28",
673 Bytes29: "bytes29",
674 Bytes30: "bytes30",
675 Bytes31: "bytes31",
676 Bytes32: "bytes32",
677 String: "string",
678 Address: "address",
679 Bool: "bool",
680 Fixed: "fixed",
681 UFixed: "ufixed",
682
683 Wei: "wei",
685 Gwei: "gwei",
686 Ether: "ether",
687 Seconds: "seconds",
688 Minutes: "minutes",
689 Hours: "hours",
690 Days: "days",
691 Weeks: "weeks",
692 Years: "years",
693
694 False: "false",
696 True: "true",
697
698 After: "after",
700 Alias: "alias",
701 Apply: "apply",
702 Auto: "auto",
703 Byte: "byte",
704 Case: "case",
705 CopyOf: "copyof",
706 Default: "default",
707 Define: "define",
708 Final: "final",
709 Implements: "implements",
710 In: "in",
711 Inline: "inline",
712 Let: "let",
713 Macro: "macro",
714 Match: "match",
715 Mutable: "mutable",
716 NullLiteral: "null",
717 Of: "of",
718 Partial: "partial",
719 Promise: "promise",
720 Reference: "reference",
721 Relocatable: "relocatable",
722 Sealed: "sealed",
723 Sizeof: "sizeof",
724 Static: "static",
725 Supports: "supports",
726 Switch: "switch",
727 Typedef: "typedef",
728 TypeOf: "typeof",
729 Var: "var",
730
731 Leave: "leave",
736 Revert: "revert",
737
738 Add: "add",
744 Addmod: "addmod",
745 And: "and",
746 Balance: "balance",
747 Basefee: "basefee",
748 Blobbasefee: "blobbasefee",
749 Blobhash: "blobhash",
750 Blockhash: "blockhash",
751 Call: "call",
752 Callcode: "callcode",
753 Calldatacopy: "calldatacopy",
754 Calldataload: "calldataload",
755 Calldatasize: "calldatasize",
756 Caller: "caller",
757 Callvalue: "callvalue",
758 Chainid: "chainid",
759 Coinbase: "coinbase",
760 Create: "create",
761 Create2: "create2",
762 Delegatecall: "delegatecall",
763 Difficulty: "difficulty",
764 Div: "div",
765 Eq: "eq",
766 Exp: "exp",
767 Extcodecopy: "extcodecopy",
768 Extcodehash: "extcodehash",
769 Extcodesize: "extcodesize",
770 Gas: "gas",
771 Gaslimit: "gaslimit",
772 Gasprice: "gasprice",
773 Gt: "gt",
774 Invalid: "invalid",
775 Iszero: "iszero",
776 Keccak256: "keccak256",
777 Log0: "log0",
778 Log1: "log1",
779 Log2: "log2",
780 Log3: "log3",
781 Log4: "log4",
782 Lt: "lt",
783 Mcopy: "mcopy",
784 Mload: "mload",
785 Mod: "mod",
786 Msize: "msize",
787 Mstore: "mstore",
788 Mstore8: "mstore8",
789 Mul: "mul",
790 Mulmod: "mulmod",
791 Not: "not",
792 Number: "number",
793 Or: "or",
794 Origin: "origin",
795 Pop: "pop",
796 Prevrandao: "prevrandao",
797 Returndatacopy: "returndatacopy",
798 Returndatasize: "returndatasize",
799 Sar: "sar",
800 Sdiv: "sdiv",
801 Selfbalance: "selfbalance",
802 Selfdestruct: "selfdestruct",
803 Sgt: "sgt",
804 Shl: "shl",
805 Shr: "shr",
806 Signextend: "signextend",
807 Sload: "sload",
808 Slt: "slt",
809 Smod: "smod",
810 Sstore: "sstore",
811 Staticcall: "staticcall",
812 Stop: "stop",
813 Sub: "sub",
814 Timestamp: "timestamp",
815 Tload: "tload",
816 Tstore: "tstore",
817 Xor: "xor",
818
819 Class: "class",
821 Instantiation: "instantiation",
822 Integer: "Integer",
823 Itself: "itself",
824 StaticAssert: "static_assert",
825 Builtin: "__builtin",
826 ForAll: "forall",
827 }
828
829 Symbols {
848 X,
849 __tmp_struct,
850 abi,
851 abicoder,
852 assert,
853 at,
854 block,
855 code,
856 codehash,
857 concat,
858 creationCode,
859 data,
860 decode,
861 ecrecover,
862 encode,
863 encodeCall,
864 encodePacked,
865 encodeWithSelector,
866 encodeWithSignature,
867 error,
868 experimental,
869 from,
870 gasleft,
871 global,
872 interfaceId,
873 layout,
874 length,
875 max,
876 min,
877 msg,
878 name,
879 object,
880 push,
881 require,
882 ripemd160,
883 runtimeCode,
884 selector,
885 send,
886 sender,
887 sha256,
888 sig,
889 solidity,
890 super_: "super",
891 this,
892 transfer,
893 transient,
894 tx,
895 underscore: "_",
896 unwrap,
897 value,
898 wrap,
899 x,
900 }
901}
902
903#[cfg(test)]
904mod tests {
905 use super::*;
906
907 #[test]
908 fn interner_tests() {
909 let i = Interner::prefill(&[]);
910 assert_eq!(i.intern("dog"), Symbol::new(0));
912 assert_eq!(i.intern("dog"), Symbol::new(0));
914 assert_eq!(i.intern("cat"), Symbol::new(1));
916 assert_eq!(i.intern("cat"), Symbol::new(1));
917 assert_eq!(i.intern("dog"), Symbol::new(0));
919 }
920
921 #[test]
922 fn defaults() {
923 assert_eq!(Symbol::DUMMY, Symbol::new(0));
924 assert_eq!(Symbol::DUMMY, Symbol::default());
925 assert_eq!(Ident::DUMMY, Ident::new(Symbol::DUMMY, Span::DUMMY));
926 assert_eq!(Ident::DUMMY, Ident::default());
927
928 crate::enter(|| {
929 assert_eq!(Symbol::DUMMY.as_str(), "");
930 assert_eq!(Symbol::DUMMY.to_string(), "");
931 assert_eq!(Ident::DUMMY.as_str(), "");
932 assert_eq!(Ident::DUMMY.to_string(), "");
933 });
934 }
935}