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