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
356pub(crate) struct Interner {
360 inner: inturn::Interner<Symbol, solar_data_structures::map::FxBuildHasher>,
361}
362
363impl Interner {
364 pub(crate) fn fresh() -> Self {
365 Self::prefill(PREINTERNED)
366 }
367
368 pub(crate) fn prefill(init: &[&'static str]) -> Self {
369 let mut inner =
370 inturn::Interner::with_capacity_and_hasher(init.len() * 4, Default::default());
371 inner.intern_many_mut_static(init.iter().copied());
372 Self { inner }
373 }
374
375 #[inline]
376 fn intern(&self, string: &str) -> Symbol {
377 self.inner.intern(string)
378 }
379
380 #[inline]
381 fn get(&self, symbol: Symbol) -> &str {
382 self.inner.resolve(symbol)
383 }
384
385 fn trace_stats(&mut self) {
386 if enabled!(tracing::Level::TRACE) {
387 self.trace_stats_impl();
388 }
389 }
390
391 #[inline(never)]
392 fn trace_stats_impl(&mut self) {
393 let mut lengths = self.inner.iter().map(|(_, s)| s.len()).collect::<Vec<_>>();
394 lengths.sort_unstable();
395 let len = lengths.len();
396 assert!(len > 0);
397 let bytes = lengths.iter().copied().sum::<usize>();
398 trace!(
399 preinterned=PREINTERNED_SYMBOLS_COUNT,
400 len,
401 bytes,
402 max=lengths.last().copied().unwrap_or(0),
403 mean=%format!("{:.2}", (bytes as f64 / len as f64)),
404 median=%format!("{:.2}", if len.is_multiple_of(2) {
405 (lengths[len / 2 - 1] + lengths[len / 2]) as f64 / 2.0
406 } else {
407 lengths[len / 2] as f64
408 }),
409 "Interner stats",
410 );
411 }
412}
413
414impl inturn::InternerSymbol for Symbol {
415 #[inline]
416 fn try_from_usize(n: usize) -> Option<Self> {
417 BaseIndex32::try_from_usize(n).map(Self)
418 }
419
420 #[inline]
421 fn to_usize(self) -> usize {
422 self.0.index()
423 }
424}
425
426impl Drop for Interner {
427 fn drop(&mut self) {
428 self.trace_stats();
429 }
430}
431
432pub mod kw {
438 use crate::Symbol;
439
440 #[doc(inline)]
441 pub use super::kw_generated::*;
442
443 #[inline]
445 pub const fn boolean(b: bool) -> Symbol {
446 if b { True } else { False }
447 }
448
449 #[inline]
457 #[track_caller]
458 pub const fn int(n: u8) -> Symbol {
459 assert!(n <= 32);
460 Symbol::new(Int.as_u32() + n as u32)
461 }
462
463 #[inline]
471 #[track_caller]
472 pub const fn uint(n: u8) -> Symbol {
473 assert!(n <= 32);
474 Symbol::new(UInt.as_u32() + n as u32)
475 }
476
477 #[inline]
483 #[track_caller]
484 pub const fn fixed_bytes(n: u8) -> Symbol {
485 assert!(n > 0 && n <= 32);
486 Symbol::new(Bytes.as_u32() + n as u32)
487 }
488}
489
490pub mod sym {
496 use super::Symbol;
497
498 #[doc(inline)]
499 pub use super::sym_generated::*;
500
501 pub fn integer<N: TryInto<usize> + Copy + itoa::Integer>(n: N) -> Symbol {
505 if let Ok(idx @ 0..=9) = n.try_into() {
506 return Symbol::new(super::SYMBOL_DIGITS_BASE + idx as u32);
507 }
508 Symbol::intern(itoa::Buffer::new().format(n))
509 }
510}
511
512symbols! {
514 Keywords {
518 Empty: "",
520
521 Abstract: "abstract",
522 Anonymous: "anonymous",
523 As: "as",
524 Assembly: "assembly",
525 Break: "break",
526 Calldata: "calldata",
527 Catch: "catch",
528 Constant: "constant",
529 Constructor: "constructor",
530 Continue: "continue",
531 Contract: "contract",
532 Delete: "delete",
533 Do: "do",
534 Else: "else",
535 Emit: "emit",
536 Enum: "enum",
537 Event: "event",
538 External: "external",
539 Fallback: "fallback",
540 For: "for",
541 Function: "function",
542 Hex: "hex",
543 If: "if",
544 Immutable: "immutable",
545 Import: "import",
546 Indexed: "indexed",
547 Interface: "interface",
548 Internal: "internal",
549 Is: "is",
550 Library: "library",
551 Mapping: "mapping",
552 Memory: "memory",
553 Modifier: "modifier",
554 New: "new",
555 Override: "override",
556 Payable: "payable",
557 Pragma: "pragma",
558 Private: "private",
559 Public: "public",
560 Pure: "pure",
561 Receive: "receive",
562 Return: "return",
563 Returns: "returns",
564 Storage: "storage",
565 Struct: "struct",
566 Throw: "throw",
567 Try: "try",
568 Type: "type",
569 Unchecked: "unchecked",
570 Unicode: "unicode",
571 Using: "using",
572 View: "view",
573 Virtual: "virtual",
574 While: "while",
575
576 Int: "int",
578 Int8: "int8",
579 Int16: "int16",
580 Int24: "int24",
581 Int32: "int32",
582 Int40: "int40",
583 Int48: "int48",
584 Int56: "int56",
585 Int64: "int64",
586 Int72: "int72",
587 Int80: "int80",
588 Int88: "int88",
589 Int96: "int96",
590 Int104: "int104",
591 Int112: "int112",
592 Int120: "int120",
593 Int128: "int128",
594 Int136: "int136",
595 Int144: "int144",
596 Int152: "int152",
597 Int160: "int160",
598 Int168: "int168",
599 Int176: "int176",
600 Int184: "int184",
601 Int192: "int192",
602 Int200: "int200",
603 Int208: "int208",
604 Int216: "int216",
605 Int224: "int224",
606 Int232: "int232",
607 Int240: "int240",
608 Int248: "int248",
609 Int256: "int256",
610 UInt: "uint",
611 UInt8: "uint8",
612 UInt16: "uint16",
613 UInt24: "uint24",
614 UInt32: "uint32",
615 UInt40: "uint40",
616 UInt48: "uint48",
617 UInt56: "uint56",
618 UInt64: "uint64",
619 UInt72: "uint72",
620 UInt80: "uint80",
621 UInt88: "uint88",
622 UInt96: "uint96",
623 UInt104: "uint104",
624 UInt112: "uint112",
625 UInt120: "uint120",
626 UInt128: "uint128",
627 UInt136: "uint136",
628 UInt144: "uint144",
629 UInt152: "uint152",
630 UInt160: "uint160",
631 UInt168: "uint168",
632 UInt176: "uint176",
633 UInt184: "uint184",
634 UInt192: "uint192",
635 UInt200: "uint200",
636 UInt208: "uint208",
637 UInt216: "uint216",
638 UInt224: "uint224",
639 UInt232: "uint232",
640 UInt240: "uint240",
641 UInt248: "uint248",
642 UInt256: "uint256",
643 Bytes: "bytes",
644 Bytes1: "bytes1",
645 Bytes2: "bytes2",
646 Bytes3: "bytes3",
647 Bytes4: "bytes4",
648 Bytes5: "bytes5",
649 Bytes6: "bytes6",
650 Bytes7: "bytes7",
651 Bytes8: "bytes8",
652 Bytes9: "bytes9",
653 Bytes10: "bytes10",
654 Bytes11: "bytes11",
655 Bytes12: "bytes12",
656 Bytes13: "bytes13",
657 Bytes14: "bytes14",
658 Bytes15: "bytes15",
659 Bytes16: "bytes16",
660 Bytes17: "bytes17",
661 Bytes18: "bytes18",
662 Bytes19: "bytes19",
663 Bytes20: "bytes20",
664 Bytes21: "bytes21",
665 Bytes22: "bytes22",
666 Bytes23: "bytes23",
667 Bytes24: "bytes24",
668 Bytes25: "bytes25",
669 Bytes26: "bytes26",
670 Bytes27: "bytes27",
671 Bytes28: "bytes28",
672 Bytes29: "bytes29",
673 Bytes30: "bytes30",
674 Bytes31: "bytes31",
675 Bytes32: "bytes32",
676 String: "string",
677 Address: "address",
678 Bool: "bool",
679 Fixed: "fixed",
680 UFixed: "ufixed",
681
682 Wei: "wei",
684 Gwei: "gwei",
685 Ether: "ether",
686 Seconds: "seconds",
687 Minutes: "minutes",
688 Hours: "hours",
689 Days: "days",
690 Weeks: "weeks",
691 Years: "years",
692
693 False: "false",
695 True: "true",
696
697 After: "after",
699 Alias: "alias",
700 Apply: "apply",
701 Auto: "auto",
702 Byte: "byte",
703 Case: "case",
704 CopyOf: "copyof",
705 Default: "default",
706 Define: "define",
707 Final: "final",
708 Implements: "implements",
709 In: "in",
710 Inline: "inline",
711 Let: "let",
712 Macro: "macro",
713 Match: "match",
714 Mutable: "mutable",
715 NullLiteral: "null",
716 Of: "of",
717 Partial: "partial",
718 Promise: "promise",
719 Reference: "reference",
720 Relocatable: "relocatable",
721 Sealed: "sealed",
722 Sizeof: "sizeof",
723 Static: "static",
724 Supports: "supports",
725 Switch: "switch",
726 Typedef: "typedef",
727 TypeOf: "typeof",
728 Var: "var",
729
730 Leave: "leave",
735 Revert: "revert",
736
737 Add: "add",
743 Addmod: "addmod",
744 And: "and",
745 Balance: "balance",
746 Basefee: "basefee",
747 Blobbasefee: "blobbasefee",
748 Blobhash: "blobhash",
749 Blockhash: "blockhash",
750 Call: "call",
751 Callcode: "callcode",
752 Calldatacopy: "calldatacopy",
753 Calldataload: "calldataload",
754 Calldatasize: "calldatasize",
755 Caller: "caller",
756 Callvalue: "callvalue",
757 Chainid: "chainid",
758 Coinbase: "coinbase",
759 Create: "create",
760 Create2: "create2",
761 Delegatecall: "delegatecall",
762 Difficulty: "difficulty",
763 Div: "div",
764 Eq: "eq",
765 Exp: "exp",
766 Extcodecopy: "extcodecopy",
767 Extcodehash: "extcodehash",
768 Extcodesize: "extcodesize",
769 Gas: "gas",
770 Gaslimit: "gaslimit",
771 Gasprice: "gasprice",
772 Gt: "gt",
773 Invalid: "invalid",
774 Iszero: "iszero",
775 Keccak256: "keccak256",
776 Log0: "log0",
777 Log1: "log1",
778 Log2: "log2",
779 Log3: "log3",
780 Log4: "log4",
781 Lt: "lt",
782 Mcopy: "mcopy",
783 Mload: "mload",
784 Mod: "mod",
785 Msize: "msize",
786 Mstore: "mstore",
787 Mstore8: "mstore8",
788 Mul: "mul",
789 Mulmod: "mulmod",
790 Not: "not",
791 Number: "number",
792 Or: "or",
793 Origin: "origin",
794 Pop: "pop",
795 Prevrandao: "prevrandao",
796 Returndatacopy: "returndatacopy",
797 Returndatasize: "returndatasize",
798 Sar: "sar",
799 Sdiv: "sdiv",
800 Selfbalance: "selfbalance",
801 Selfdestruct: "selfdestruct",
802 Sgt: "sgt",
803 Shl: "shl",
804 Shr: "shr",
805 Signextend: "signextend",
806 Sload: "sload",
807 Slt: "slt",
808 Smod: "smod",
809 Sstore: "sstore",
810 Staticcall: "staticcall",
811 Stop: "stop",
812 Sub: "sub",
813 Timestamp: "timestamp",
814 Tload: "tload",
815 Tstore: "tstore",
816 Xor: "xor",
817
818 Class: "class",
820 Instantiation: "instantiation",
821 Integer: "Integer",
822 Itself: "itself",
823 StaticAssert: "static_assert",
824 Builtin: "__builtin",
825 ForAll: "forall",
826 }
827
828 Symbols {
847 X,
848 __tmp_struct,
849 abi,
850 abicoder,
851 assert,
852 at,
853 block,
854 code,
855 codehash,
856 concat,
857 creationCode,
858 data,
859 decode,
860 ecrecover,
861 encode,
862 encodeCall,
863 encodePacked,
864 encodeWithSelector,
865 encodeWithSignature,
866 error,
867 experimental,
868 from,
869 gasleft,
870 global,
871 interfaceId,
872 layout,
873 length,
874 max,
875 min,
876 msg,
877 name,
878 object,
879 push,
880 require,
881 ripemd160,
882 runtimeCode,
883 selector,
884 send,
885 sender,
886 sha256,
887 sig,
888 solidity,
889 super_: "super",
890 this,
891 transfer,
892 transient,
893 tx,
894 underscore: "_",
895 unwrap,
896 value,
897 wrap,
898 x,
899 }
900}
901
902#[cfg(test)]
903mod tests {
904 use super::*;
905
906 #[test]
907 fn interner_tests() {
908 let i = Interner::prefill(&[]);
909 assert_eq!(i.intern("dog"), Symbol::new(0));
911 assert_eq!(i.intern("dog"), Symbol::new(0));
913 assert_eq!(i.intern("cat"), Symbol::new(1));
915 assert_eq!(i.intern("cat"), Symbol::new(1));
916 assert_eq!(i.intern("dog"), Symbol::new(0));
918 }
919
920 #[test]
921 fn defaults() {
922 assert_eq!(Symbol::DUMMY, Symbol::new(0));
923 assert_eq!(Symbol::DUMMY, Symbol::default());
924 assert_eq!(Ident::DUMMY, Ident::new(Symbol::DUMMY, Span::DUMMY));
925 assert_eq!(Ident::DUMMY, Ident::default());
926
927 crate::enter(|| {
928 assert_eq!(Symbol::DUMMY.as_str(), "");
929 assert_eq!(Symbol::DUMMY.to_string(), "");
930 assert_eq!(Ident::DUMMY.as_str(), "");
931 assert_eq!(Ident::DUMMY.to_string(), "");
932 });
933 }
934}