Skip to main content

solar_sema/builtins/
mod.rs

1use crate::{
2    ast_lowering::resolve::{Declaration, Declarations},
3    hir,
4    ty::{Gcx, Ty, TyFnKind},
5};
6use solar_ast::StateMutability as SM;
7use solar_data_structures::map::FxHashMap;
8use solar_interface::{Span, Symbol, kw, sym};
9use std::sync::OnceLock;
10
11pub(crate) mod members;
12pub use members::{Member, MemberList};
13
14pub(crate) fn scopes() -> (Declarations, FxHashMap<Builtin, Declarations>) {
15    let global = declarations(Builtin::global());
16    let mut members_map = Builtin::iter()
17        .filter_map(|builtin| Some((builtin, declarations(builtin.members()?))))
18        .collect::<FxHashMap<_, _>>();
19    members_map.shrink_to_fit();
20    (global, members_map)
21}
22
23fn declarations(builtins: impl IntoIterator<Item = Builtin>) -> Declarations {
24    let mut declarations = Declarations::new();
25    for builtin in builtins {
26        let decl = Declaration { res: hir::Res::Builtin(builtin), span: Span::DUMMY };
27        declarations.declare_unchecked(builtin.name(), decl);
28    }
29    declarations
30}
31
32type Primitive = u8;
33
34macro_rules! declare_builtins {
35    (|$slf:ident, $gcx:ident| $($(#[$variant_attr:meta])* $variant_name:ident => $sym:ident::$name:ident => $ty:expr;)*) => {
36        /// A compiler builtin.
37        #[repr(u8)]
38        #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
39        pub enum Builtin {
40            $(
41                $(#[$variant_attr])*
42                $variant_name,
43            )*
44        }
45
46        impl Builtin {
47            // #[doc(hidden)]
48            // const ALL: [Self; Self::COUNT] = [$(Self::$variant_name,)*];
49            pub const COUNT: usize = 0 $(+ { let _ = Builtin::$variant_name; 1 })*;
50
51            /// Returns the symbol of the builtin.
52            pub fn name(self) -> Symbol {
53                match self {
54                    $(
55                        Builtin::$variant_name => $sym::$name,
56                    )*
57                }
58            }
59
60            /// Returns the cached type of the builtin.
61            pub fn ty<'gcx>($slf, $gcx: Gcx<'gcx>) -> Ty<'gcx> {
62                $gcx.type_of_builtin($slf)
63            }
64
65            pub(crate) fn ty_impl<'gcx>($slf, $gcx: Gcx<'gcx>) -> Ty<'gcx> {
66                match $slf {
67                    $(
68                        Builtin::$variant_name => $ty,
69                    )*
70                }
71            }
72        }
73    };
74}
75
76// https://docs.soliditylang.org/en/latest/units-and-global-variables.html
77// https://github.com/argotorg/solidity/blob/b136829e4998a9f0ebc6ca87b7ba45362fe83ba0/libsolidity/analysis/GlobalContext.cpp#L73
78// NOTE: Order matters, see functions below.
79declare_builtins! {
80    |self, gcx|
81
82    // Global
83    Blockhash              => kw::Blockhash
84                           => gcx.mk_builtin_fn(&[gcx.types.uint(256)], SM::View, &[gcx.types.fixed_bytes(32)]);
85    Blobhash               => kw::Blobhash
86                           => gcx.mk_builtin_fn(&[gcx.types.uint(256)], SM::View, &[gcx.types.fixed_bytes(32)]);
87
88    Gasleft                => sym::gasleft
89                           => gcx.mk_builtin_fn(&[], SM::View, &[gcx.types.uint(256)]);
90    Selfdestruct           => kw::Selfdestruct
91                           => gcx.mk_builtin_fn(&[gcx.types.address_payable], SM::NonPayable, &[]);
92
93    Assert                 => sym::assert
94                           => gcx.mk_builtin_fn(&[gcx.types.bool], SM::Pure, &[]);
95    Require                => sym::require
96                           => gcx.mk_builtin_fn(&[gcx.types.bool, gcx.mk_ty_variadic()], SM::Pure, &[]);
97    Revert                 => kw::Revert
98                           => gcx.mk_builtin_fn(&[], SM::Pure, &[]);
99    RevertMsg              => kw::Revert
100                           => gcx.mk_builtin_fn(&[gcx.types.string], SM::Pure, &[]);
101
102    AddMod                 => kw::Addmod
103                           => gcx.mk_builtin_fn(&[gcx.types.uint(256), gcx.types.uint(256), gcx.types.uint(256)], SM::Pure, &[gcx.types.uint(256)]);
104    MulMod                 => kw::Mulmod
105                           => gcx.mk_builtin_fn(&[gcx.types.uint(256), gcx.types.uint(256), gcx.types.uint(256)], SM::Pure, &[gcx.types.uint(256)]);
106
107    Keccak256              => kw::Keccak256
108                           => gcx.mk_builtin_fn(&[gcx.types.bytes_ref.memory], SM::Pure, &[gcx.types.fixed_bytes(32)]);
109    Sha256                 => sym::sha256
110                           => gcx.mk_builtin_fn(&[gcx.types.bytes_ref.memory], SM::Pure, &[gcx.types.fixed_bytes(32)]);
111    Ripemd160              => sym::ripemd160
112                           => gcx.mk_builtin_fn(&[gcx.types.bytes_ref.memory], SM::Pure, &[gcx.types.fixed_bytes(20)]);
113    EcRecover              => sym::ecrecover
114                           => gcx.mk_builtin_fn(&[gcx.types.fixed_bytes(32), gcx.types.uint(8), gcx.types.fixed_bytes(32), gcx.types.fixed_bytes(32)], SM::Pure, &[gcx.types.address]);
115
116    Erc7201                => sym::erc7201
117                           => gcx.mk_builtin_fn(&[gcx.types.string_ref.memory], SM::Pure, &[gcx.types.uint(256)]);
118
119    Block                  => sym::block
120                           => gcx.mk_builtin_mod(self);
121    Msg                    => sym::msg
122                           => gcx.mk_builtin_mod(self);
123    Tx                     => sym::tx
124                           => gcx.mk_builtin_mod(self);
125    Abi                    => sym::abi
126                           => gcx.mk_builtin_mod(self);
127
128    // Contract
129    This                   => sym::this   => unreachable!();
130    Super                  => sym::super_ => unreachable!();
131
132    // `block`
133    BlockCoinbase          => kw::Coinbase
134                           => gcx.types.address_payable;
135    BlockTimestamp         => kw::Timestamp
136                           => gcx.types.uint(256);
137    BlockDifficulty        => kw::Difficulty
138                           => gcx.types.uint(256);
139    BlockPrevrandao        => kw::Prevrandao
140                           => gcx.types.uint(256);
141    BlockNumber            => kw::Number
142                           => gcx.types.uint(256);
143    BlockGaslimit          => kw::Gaslimit
144                           => gcx.types.uint(256);
145    BlockChainid           => kw::Chainid
146                           => gcx.types.uint(256);
147    BlockBasefee           => kw::Basefee
148                           => gcx.types.uint(256);
149    BlockBlobbasefee       => kw::Blobbasefee
150                           => gcx.types.uint(256);
151
152    // `msg`
153    MsgSender              => sym::sender
154                           => gcx.types.address;
155    MsgGas                 => kw::Gas
156                           => gcx.types.uint(256);
157    MsgValue               => sym::value
158                           => gcx.types.uint(256);
159    MsgData                => sym::data
160                           => gcx.types.bytes_ref.calldata;
161    MsgSig                 => sym::sig
162                           => gcx.types.fixed_bytes(4);
163
164    // `tx`
165    TxOrigin               => kw::Origin
166                           => gcx.types.address;
167    TxGasPrice             => kw::Gasprice
168                           => gcx.types.uint(256);
169
170    // `abi`
171    AbiEncode              => sym::encode
172                           => gcx.mk_builtin_fn(&[gcx.mk_ty_variadic()], SM::Pure, &[gcx.types.bytes_ref.memory]);
173    AbiEncodePacked        => sym::encodePacked
174                           => gcx.mk_builtin_fn(&[gcx.mk_ty_variadic()], SM::Pure, &[gcx.types.bytes_ref.memory]);
175    AbiEncodeWithSelector  => sym::encodeWithSelector
176                           => gcx.mk_builtin_fn(&[gcx.types.fixed_bytes(4), gcx.mk_ty_variadic()], SM::Pure, &[gcx.types.bytes_ref.memory]);
177    AbiEncodeCall          => sym::encodeCall
178                           => gcx.mk_builtin_fn(&[gcx.mk_ty_variadic()], SM::Pure, &[gcx.types.bytes_ref.memory]);
179    AbiEncodeWithSignature => sym::encodeWithSignature
180                           => gcx.mk_builtin_fn(&[gcx.types.string_ref.memory, gcx.mk_ty_variadic()], SM::Pure, &[gcx.types.bytes_ref.memory]);
181    AbiDecode              => sym::decode
182                           => gcx.mk_builtin_fn(&[gcx.types.bytes_ref.memory, gcx.mk_ty_tuple(gcx.mk_tys(&[gcx.mk_ty_variadic()]))], SM::Pure, &[gcx.mk_ty_variadic()]);
183
184    // --- impls ---
185
186    AddressBalance         => kw::Balance
187                           => gcx.types.uint(256);
188    AddressCode            => sym::code
189                           => gcx.types.bytes_ref.memory;
190    AddressCodehash        => sym::codehash
191                           => gcx.types.fixed_bytes(32);
192    AddressCall            => kw::Call
193                           => gcx.mk_ty_fn_with_kind(TyFnKind::BareCall, &[gcx.types.bytes_ref.memory], SM::Payable, &[gcx.types.bool, gcx.types.bytes_ref.memory]);
194    AddressDelegatecall    => kw::Delegatecall
195                           => gcx.mk_ty_fn_with_kind(TyFnKind::BareDelegateCall, &[gcx.types.bytes_ref.memory], SM::NonPayable, &[gcx.types.bool, gcx.types.bytes_ref.memory]);
196    AddressStaticcall      => kw::Staticcall
197                           => gcx.mk_ty_fn_with_kind(TyFnKind::BareStaticCall, &[gcx.types.bytes_ref.memory], SM::View, &[gcx.types.bool, gcx.types.bytes_ref.memory]);
198
199    AddressPayableTransfer => sym::transfer
200                           => gcx.mk_builtin_fn(&[gcx.types.uint(256)], SM::NonPayable, &[]);
201    AddressPayableSend     => sym::send
202                           => gcx.mk_builtin_fn(&[gcx.types.uint(256)], SM::NonPayable, &[gcx.types.bool]);
203
204    FixedBytesLength       => sym::length
205                           => gcx.types.uint(8);
206
207    ArrayLength            => sym::length
208                           => gcx.types.uint(256);
209    ArrayPush0             => sym::push => unreachable!();
210    ArrayPush              => sym::push => unreachable!();
211    ArrayPop               => kw::Pop => unreachable!();
212
213    FunctionSelector       => sym::selector
214                           => gcx.types.fixed_bytes(4);
215
216    FunctionAddress        => kw::Address
217                           => gcx.types.address;
218
219    EventSelector          => sym::selector
220                           => gcx.types.fixed_bytes(32);
221
222    // `type(T)`
223    ContractCreationCode   => sym::creationCode
224                           => gcx.types.bytes_ref.memory;
225    ContractRuntimeCode    => sym::runtimeCode
226                           => gcx.types.bytes_ref.memory;
227    ContractName           => sym::name
228                           => gcx.types.string_ref.memory;
229    InterfaceId            => sym::interfaceId
230                           => gcx.types.fixed_bytes(4);
231    TypeMin                => sym::min => unreachable!();
232    TypeMax                => sym::max => unreachable!();
233
234    // `TyKind::Type` (`string.concat`, on the `string` type, not a string value)
235    UdvtWrap               => sym::wrap   => unreachable!();
236    UdvtUnwrap             => sym::unwrap => unreachable!();
237
238    StringConcat           => sym::concat
239                           => gcx.mk_builtin_fn(&[gcx.mk_ty_variadic()], SM::Pure, &[gcx.types.string_ref.memory]);
240
241    BytesConcat            => sym::concat
242                           => gcx.mk_builtin_fn(&[gcx.mk_ty_variadic()], SM::Pure, &[gcx.types.bytes_ref.memory]);
243
244    // Yul EVM builtins.
245    YulAdd                 => kw::Add              => gcx.mk_yul_builtin_fn(2, 1);
246    YulSub                 => kw::Sub              => gcx.mk_yul_builtin_fn(2, 1);
247    YulMul                 => kw::Mul              => gcx.mk_yul_builtin_fn(2, 1);
248    YulDiv                 => kw::Div              => gcx.mk_yul_builtin_fn(2, 1);
249    YulMod                 => kw::Mod              => gcx.mk_yul_builtin_fn(2, 1);
250    YulExp                 => kw::Exp              => gcx.mk_yul_builtin_fn(2, 1);
251    YulNot                 => kw::Not              => gcx.mk_yul_builtin_fn(1, 1);
252    YulAnd                 => kw::And              => gcx.mk_yul_builtin_fn(2, 1);
253    YulOr                  => kw::Or               => gcx.mk_yul_builtin_fn(2, 1);
254    YulXor                 => kw::Xor              => gcx.mk_yul_builtin_fn(2, 1);
255    YulShl                 => kw::Shl              => gcx.mk_yul_builtin_fn(2, 1);
256    YulShr                 => kw::Shr              => gcx.mk_yul_builtin_fn(2, 1);
257    YulSar                 => kw::Sar              => gcx.mk_yul_builtin_fn(2, 1);
258    YulStop                => kw::Stop             => gcx.mk_yul_builtin_fn(0, 0);
259    YulSdiv                => kw::Sdiv             => gcx.mk_yul_builtin_fn(2, 1);
260    YulSmod                => kw::Smod             => gcx.mk_yul_builtin_fn(2, 1);
261    YulLt                  => kw::Lt               => gcx.mk_yul_builtin_fn(2, 1);
262    YulGt                  => kw::Gt               => gcx.mk_yul_builtin_fn(2, 1);
263    YulSlt                 => kw::Slt              => gcx.mk_yul_builtin_fn(2, 1);
264    YulSgt                 => kw::Sgt              => gcx.mk_yul_builtin_fn(2, 1);
265    YulEq                  => kw::Eq               => gcx.mk_yul_builtin_fn(2, 1);
266    YulIszero              => kw::Iszero           => gcx.mk_yul_builtin_fn(1, 1);
267    YulByte                => kw::Byte             => gcx.mk_yul_builtin_fn(2, 1);
268    YulClz                 => kw::Clz              => gcx.mk_yul_builtin_fn(1, 1);
269    YulAddmod              => kw::Addmod           => gcx.mk_yul_builtin_fn(3, 1);
270    YulMulmod              => kw::Mulmod           => gcx.mk_yul_builtin_fn(3, 1);
271    YulSignextend          => kw::Signextend       => gcx.mk_yul_builtin_fn(2, 1);
272    YulKeccak256           => kw::Keccak256        => gcx.mk_yul_builtin_fn(2, 1);
273    YulAddress             => kw::Address          => gcx.mk_yul_builtin_fn(0, 1);
274    YulBalance             => kw::Balance          => gcx.mk_yul_builtin_fn(1, 1);
275    YulSelfbalance         => kw::Selfbalance      => gcx.mk_yul_builtin_fn(0, 1);
276    YulCaller              => kw::Caller           => gcx.mk_yul_builtin_fn(0, 1);
277    YulCallvalue           => kw::Callvalue        => gcx.mk_yul_builtin_fn(0, 1);
278    YulCalldataload        => kw::Calldataload     => gcx.mk_yul_builtin_fn(1, 1);
279    YulCalldatasize        => kw::Calldatasize     => gcx.mk_yul_builtin_fn(0, 1);
280    YulCalldatacopy        => kw::Calldatacopy     => gcx.mk_yul_builtin_fn(3, 0);
281    YulCodesize            => kw::Codesize         => gcx.mk_yul_builtin_fn(0, 1);
282    YulCodecopy            => kw::Codecopy         => gcx.mk_yul_builtin_fn(3, 0);
283    YulExtcodesize         => kw::Extcodesize      => gcx.mk_yul_builtin_fn(1, 1);
284    YulExtcodecopy         => kw::Extcodecopy      => gcx.mk_yul_builtin_fn(4, 0);
285    YulReturndatasize      => kw::Returndatasize   => gcx.mk_yul_builtin_fn(0, 1);
286    YulReturndatacopy      => kw::Returndatacopy   => gcx.mk_yul_builtin_fn(3, 0);
287    YulExtcodehash         => kw::Extcodehash      => gcx.mk_yul_builtin_fn(1, 1);
288    YulMload               => kw::Mload            => gcx.mk_yul_builtin_fn(1, 1);
289    YulMstore              => kw::Mstore           => gcx.mk_yul_builtin_fn(2, 0);
290    YulMstore8             => kw::Mstore8          => gcx.mk_yul_builtin_fn(2, 0);
291    YulSload               => kw::Sload            => gcx.mk_yul_builtin_fn(1, 1);
292    YulSstore              => kw::Sstore           => gcx.mk_yul_builtin_fn(2, 0);
293    YulTload               => kw::Tload            => gcx.mk_yul_builtin_fn(1, 1);
294    YulTstore              => kw::Tstore           => gcx.mk_yul_builtin_fn(2, 0);
295    YulMsize               => kw::Msize            => gcx.mk_yul_builtin_fn(0, 1);
296    YulGas                 => kw::Gas              => gcx.mk_yul_builtin_fn(0, 1);
297    YulLog0                => kw::Log0             => gcx.mk_yul_builtin_fn(2, 0);
298    YulLog1                => kw::Log1             => gcx.mk_yul_builtin_fn(3, 0);
299    YulLog2                => kw::Log2             => gcx.mk_yul_builtin_fn(4, 0);
300    YulLog3                => kw::Log3             => gcx.mk_yul_builtin_fn(5, 0);
301    YulLog4                => kw::Log4             => gcx.mk_yul_builtin_fn(6, 0);
302    YulCreate              => kw::Create           => gcx.mk_yul_builtin_fn(3, 1);
303    YulCreate2             => kw::Create2          => gcx.mk_yul_builtin_fn(4, 1);
304    YulCall                => kw::Call             => gcx.mk_yul_builtin_fn(7, 1);
305    YulCallcode            => kw::Callcode         => gcx.mk_yul_builtin_fn(7, 1);
306    YulDelegatecall        => kw::Delegatecall     => gcx.mk_yul_builtin_fn(6, 1);
307    YulStaticcall          => kw::Staticcall       => gcx.mk_yul_builtin_fn(6, 1);
308    YulExtcall             => kw::Extcall          => gcx.mk_yul_builtin_fn(4, 1);
309    YulExtdelegatecall     => kw::Extdelegatecall  => gcx.mk_yul_builtin_fn(3, 1);
310    YulExtstaticcall       => kw::Extstaticcall    => gcx.mk_yul_builtin_fn(3, 1);
311    YulReturn              => kw::Return           => gcx.mk_yul_builtin_fn(2, 0);
312    YulRevert              => kw::Revert           => gcx.mk_yul_builtin_fn(2, 0);
313    YulSelfdestruct        => kw::Selfdestruct     => gcx.mk_yul_builtin_fn(1, 0);
314    YulInvalid             => kw::Invalid          => gcx.mk_yul_builtin_fn(0, 0);
315    YulChainid             => kw::Chainid          => gcx.mk_yul_builtin_fn(0, 1);
316    YulBasefee             => kw::Basefee          => gcx.mk_yul_builtin_fn(0, 1);
317    YulBlobbasefee         => kw::Blobbasefee      => gcx.mk_yul_builtin_fn(0, 1);
318    YulBlobhash            => kw::Blobhash         => gcx.mk_yul_builtin_fn(1, 1);
319    YulCoinbase            => kw::Coinbase         => gcx.mk_yul_builtin_fn(0, 1);
320    YulDifficulty          => kw::Difficulty       => gcx.mk_yul_builtin_fn(0, 1);
321    YulPrevrandao          => kw::Prevrandao       => gcx.mk_yul_builtin_fn(0, 1);
322    YulGaslimit            => kw::Gaslimit         => gcx.mk_yul_builtin_fn(0, 1);
323    YulNumber              => kw::Number           => gcx.mk_yul_builtin_fn(0, 1);
324    YulTimestamp           => kw::Timestamp        => gcx.mk_yul_builtin_fn(0, 1);
325    YulGasprice            => kw::Gasprice         => gcx.mk_yul_builtin_fn(0, 1);
326    YulOrigin              => kw::Origin           => gcx.mk_yul_builtin_fn(0, 1);
327    YulBlockhash           => kw::Blockhash        => gcx.mk_yul_builtin_fn(1, 1);
328    YulPop                 => kw::Pop              => gcx.mk_yul_builtin_fn(1, 0);
329    YulMcopy               => kw::Mcopy            => gcx.mk_yul_builtin_fn(3, 0);
330}
331
332impl Builtin {
333    const FIRST_GLOBAL: usize = 0;
334    const LAST_GLOBAL: usize = Self::Abi as usize + 1;
335
336    const FIRST_BLOCK: usize = Self::BlockCoinbase as usize;
337    const LAST_BLOCK: usize = Self::BlockBlobbasefee as usize + 1;
338
339    const FIRST_MSG: usize = Self::MsgSender as usize;
340    const LAST_MSG: usize = Self::MsgSig as usize + 1;
341
342    const FIRST_TX: usize = Self::TxOrigin as usize;
343    const LAST_TX: usize = Self::TxGasPrice as usize + 1;
344
345    const FIRST_ABI: usize = Self::AbiEncode as usize;
346    const LAST_ABI: usize = Self::AbiDecode as usize + 1;
347
348    const FIRST_YUL: usize = Self::YulAdd as usize;
349    const LAST_YUL: usize = Self::YulMcopy as usize + 1;
350
351    /// Returns an iterator over all builtins.
352    #[inline]
353    pub fn iter() -> std::iter::Map<std::ops::Range<usize>, impl FnMut(usize) -> Self> {
354        (0..Self::COUNT).map(|i| Self::from_index(i).unwrap())
355    }
356
357    #[inline]
358    const fn from_index(i: usize) -> Option<Self> {
359        const {
360            assert!(Self::COUNT <= Primitive::MAX as usize);
361            assert!(size_of::<Self>() == 1);
362        }
363        if i < Self::COUNT {
364            // SAFETY:
365            //
366            // `Self` is a field-less, `repr(Primitive)` enum and therefore guaranteed
367            // to have the same size and alignment as `Primitive`.
368            //
369            // This branch ensures `i < Self::COUNT` where `Self::COUNT` is the
370            // number of variants in `Self`. The discriminants of `Self` are
371            // contiguous because no variant specifies a custom discriminant
372            // with `Variant = value`. This ensures that `i as Primitive` is
373            // a valid inhabitant of type `Self`.
374            Some(unsafe { std::mem::transmute::<Primitive, Self>(i as Primitive) })
375        } else {
376            None
377        }
378    }
379
380    /// Returns the global builtins.
381    pub fn global() -> impl ExactSizeIterator<Item = Self> + Clone {
382        Self::make_range_iter(Self::FIRST_GLOBAL..Self::LAST_GLOBAL)
383    }
384
385    /// Returns the builtin's members.
386    pub fn members(self) -> Option<impl ExactSizeIterator<Item = Self> + Clone> {
387        use Builtin::*;
388        Some(Self::make_range_iter(match self {
389            Block => Self::FIRST_BLOCK..Self::LAST_BLOCK,
390            Msg => Self::FIRST_MSG..Self::LAST_MSG,
391            Tx => Self::FIRST_TX..Self::LAST_TX,
392            Abi => Self::FIRST_ABI..Self::LAST_ABI,
393            _ => return None,
394        }))
395    }
396
397    /// Returns the Yul builtin with the given name.
398    pub fn from_yul_name(name: Symbol) -> Option<Self> {
399        static YUL_NAMES: OnceLock<FxHashMap<Symbol, Builtin>> = OnceLock::new();
400        YUL_NAMES
401            .get_or_init(|| {
402                Self::make_range_iter(Self::FIRST_YUL..Self::LAST_YUL)
403                    .map(|builtin| (builtin.name(), builtin))
404                    .collect()
405            })
406            .get(&name)
407            .copied()
408    }
409
410    #[inline]
411    fn make_range_iter(
412        range: std::ops::Range<usize>,
413    ) -> impl ExactSizeIterator<Item = Self> + Clone {
414        debug_assert!(range.start < Self::COUNT);
415        debug_assert!(range.end <= Self::COUNT);
416        (range.start as Primitive..range.end as Primitive)
417            .map(|idx| unsafe { Self::from_index(idx as usize).unwrap_unchecked() })
418    }
419}