solar_sema/builtins/
mod.rs

1use crate::{
2    ast_lowering::resolve::{Declaration, Declarations},
3    hir,
4    ty::{Gcx, Ty},
5};
6use solar_ast::StateMutability as SM;
7use solar_interface::{Span, Symbol, kw, sym};
8
9pub(crate) mod members;
10pub use members::{Member, MemberList};
11
12pub(crate) fn scopes() -> (Declarations, Box<[Option<Declarations>; Builtin::COUNT]>) {
13    let global = declarations(Builtin::global());
14    let members_map = Box::new(std::array::from_fn(|i| {
15        Some(declarations(Builtin::from_index(i).unwrap().members()?))
16    }));
17    (global, members_map)
18}
19
20fn declarations(builtins: impl IntoIterator<Item = Builtin>) -> Declarations {
21    let mut declarations = Declarations::new();
22    for builtin in builtins {
23        let decl = Declaration { res: hir::Res::Builtin(builtin), span: Span::DUMMY };
24        declarations.declare_unchecked(builtin.name(), decl);
25    }
26    declarations
27}
28
29type Primitive = u8;
30
31macro_rules! declare_builtins {
32    (|$gcx:ident| $($(#[$variant_attr:meta])* $variant_name:ident => $sym:ident::$name:ident => $ty:expr;)*) => {
33        /// A compiler builtin.
34        #[repr(u8)]
35        #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
36        pub enum Builtin {
37            $(
38                $(#[$variant_attr])*
39                $variant_name,
40            )*
41        }
42
43        impl Builtin {
44            // #[doc(hidden)]
45            // const ALL: [Self; Self::COUNT] = [$(Self::$variant_name,)*];
46            pub const COUNT: usize = 0 $(+ { let _ = Builtin::$variant_name; 1 })*;
47
48            /// Returns the symbol of the builtin.
49            pub fn name(self) -> Symbol {
50                match self {
51                    $(
52                        Builtin::$variant_name => $sym::$name,
53                    )*
54                }
55            }
56
57            /// Returns the type of the builtin.
58            pub fn ty(self, $gcx: Gcx<'_>) -> Ty<'_> {
59                match self {
60                    $(
61                        Builtin::$variant_name => $ty,
62                    )*
63                }
64            }
65        }
66    };
67}
68
69// https://docs.soliditylang.org/en/latest/units-and-global-variables.html
70// https://github.com/argotorg/solidity/blob/b136829e4998a9f0ebc6ca87b7ba45362fe83ba0/libsolidity/analysis/GlobalContext.cpp#L73
71// NOTE: Order matters, see functions below.
72declare_builtins! {
73    |gcx|
74
75    // Global
76    Blockhash              => kw::Blockhash
77                           => gcx.mk_builtin_fn(&[gcx.types.uint(256)], SM::View, &[gcx.types.fixed_bytes(32)]);
78    Blobhash               => kw::Blobhash
79                           => gcx.mk_builtin_fn(&[gcx.types.uint(256)], SM::View, &[gcx.types.fixed_bytes(32)]);
80
81    Gasleft                => sym::gasleft
82                           => gcx.mk_builtin_fn(&[], SM::View, &[gcx.types.uint(256)]);
83    Selfdestruct           => kw::Selfdestruct
84                           => gcx.mk_builtin_fn(&[gcx.types.address_payable], SM::NonPayable, &[]);
85
86    Assert                 => sym::assert
87                           => gcx.mk_builtin_fn(&[gcx.types.bool], SM::Pure, &[]);
88    Require                => sym::require
89                           => gcx.mk_builtin_fn(&[gcx.types.bool], SM::Pure, &[]);
90    RequireMsg             => sym::require
91                           => gcx.mk_builtin_fn(&[gcx.types.bool, gcx.types.string_ref.memory], SM::Pure, &[]);
92    // RequireErr             => sym::require
93    //                        => gcx.mk_builtin_fn(&[gcx.types.bool, gcx.type_of()], SM::Pure, &[]);
94    Revert                 => kw::Revert
95                           => gcx.mk_builtin_fn(&[], SM::Pure, &[]);
96    RevertMsg              => kw::Revert
97                           => gcx.mk_builtin_fn(&[gcx.types.string], SM::Pure, &[]);
98
99    AddMod                 => kw::Addmod
100                           => gcx.mk_builtin_fn(&[gcx.types.uint(256), gcx.types.uint(256), gcx.types.uint(256)], SM::Pure, &[gcx.types.uint(256)]);
101    MulMod                 => kw::Mulmod
102                           => gcx.mk_builtin_fn(&[gcx.types.uint(256), gcx.types.uint(256), gcx.types.uint(256)], SM::Pure, &[gcx.types.uint(256)]);
103
104    Keccak256              => kw::Keccak256
105                           => gcx.mk_builtin_fn(&[gcx.types.bytes_ref.memory], SM::Pure, &[gcx.types.fixed_bytes(32)]);
106    Sha256                 => sym::sha256
107                           => gcx.mk_builtin_fn(&[gcx.types.bytes_ref.memory], SM::Pure, &[gcx.types.fixed_bytes(32)]);
108    Ripemd160              => sym::ripemd160
109                           => gcx.mk_builtin_fn(&[gcx.types.bytes_ref.memory], SM::Pure, &[gcx.types.fixed_bytes(20)]);
110    EcRecover              => sym::ecrecover
111                           => 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]);
112
113    Block                  => sym::block
114                           => gcx.mk_builtin_mod(Self::Block);
115    Msg                    => sym::msg
116                           => gcx.mk_builtin_mod(Self::Msg);
117    Tx                     => sym::tx
118                           => gcx.mk_builtin_mod(Self::Tx);
119    Abi                    => sym::abi
120                           => gcx.mk_builtin_mod(Self::Abi);
121
122    // Contract
123    This                   => sym::this   => unreachable!();
124    Super                  => sym::super_ => unreachable!();
125
126    // `block`
127    BlockCoinbase          => kw::Coinbase
128                           => gcx.types.address_payable;
129    BlockTimestamp         => kw::Timestamp
130                           => gcx.types.uint(256);
131    BlockDifficulty        => kw::Difficulty
132                           => gcx.types.uint(256);
133    BlockPrevrandao        => kw::Prevrandao
134                           => gcx.types.uint(256);
135    BlockNumber            => kw::Number
136                           => gcx.types.uint(256);
137    BlockGaslimit          => kw::Gaslimit
138                           => gcx.types.uint(256);
139    BlockChainid           => kw::Chainid
140                           => gcx.types.uint(256);
141    BlockBasefee           => kw::Basefee
142                           => gcx.types.uint(256);
143    BlockBlobbasefee       => kw::Blobbasefee
144                           => gcx.types.uint(256);
145
146    // `msg`
147    MsgSender              => sym::sender
148                           => gcx.types.address;
149    MsgGas                 => kw::Gas
150                           => gcx.types.uint(256);
151    MsgValue               => sym::value
152                           => gcx.types.uint(256);
153    MsgData                => sym::data
154                           => gcx.types.bytes_ref.calldata;
155    MsgSig                 => sym::sig
156                           => gcx.types.fixed_bytes(4);
157
158    // `tx`
159    TxOrigin               => kw::Origin
160                           => gcx.types.address;
161    TxGasPrice             => kw::Gasprice
162                           => gcx.types.uint(256);
163
164    // `abi`
165    // TODO                => `(T...) pure returns(bytes memory)`
166    AbiEncode              => sym::encode
167                           => gcx.mk_builtin_fn(&[], SM::Pure, &[gcx.types.bytes_ref.memory]);
168    // TODO                => `(T...) pure returns(bytes memory)`
169    AbiEncodePacked        => sym::encodePacked
170                           => gcx.mk_builtin_fn(&[], SM::Pure, &[gcx.types.bytes_ref.memory]);
171    // TODO                => `(bytes4, T...) pure returns(bytes memory)`
172    AbiEncodeWithSelector  => sym::encodeWithSelector
173                           => gcx.mk_builtin_fn(&[], SM::Pure, &[gcx.types.bytes_ref.memory]);
174    // TODO                => `(F, T...) pure returns(bytes memory)`
175    AbiEncodeCall          => sym::encodeCall
176                           => gcx.mk_builtin_fn(&[], SM::Pure, &[gcx.types.bytes_ref.memory]);
177    // TODO                => `(string memory, T...) pure returns(bytes memory)`
178    AbiEncodeWithSignature => sym::encodeWithSignature
179                           => gcx.mk_builtin_fn(&[], SM::Pure, &[gcx.types.bytes_ref.memory]);
180    // TODO                => `(bytes memory, (T...)) pure returns(T...)`
181    AbiDecode              => sym::decode
182                           => gcx.mk_builtin_fn(&[], SM::Pure, &[]);
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_builtin_fn(&[gcx.types.bytes_ref.memory], SM::View, &[gcx.types.bytes_ref.memory]);
194    AddressDelegatecall    => kw::Delegatecall
195                           => gcx.mk_builtin_fn(&[gcx.types.bytes_ref.memory], SM::View, &[gcx.types.bytes_ref.memory]);
196    AddressStaticcall      => kw::Staticcall
197                           => gcx.mk_builtin_fn(&[gcx.types.bytes_ref.memory], SM::View, &[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
210    ErrorSelector          => sym::selector
211                           => gcx.types.fixed_bytes(4);
212
213    EventSelector          => sym::selector
214                           => gcx.types.fixed_bytes(32);
215
216    // `type(T)`
217    ContractCreationCode   => sym::creationCode
218                           => gcx.types.bytes_ref.memory;
219    ContractRuntimeCode    => sym::runtimeCode
220                           => gcx.types.bytes_ref.memory;
221    ContractName           => sym::name
222                           => gcx.types.string_ref.memory;
223    InterfaceId            => sym::interfaceId
224                           => gcx.types.fixed_bytes(4);
225    TypeMin                => sym::min => unreachable!();
226    TypeMax                => sym::max => unreachable!();
227
228    // `TyKind::Type` (`string.concat`, on the `string` type, not a string value)
229    UdvtWrap               => sym::wrap   => unreachable!();
230    UdvtUnwrap             => sym::unwrap => unreachable!();
231
232    // TODO                => `(string memory...) pure returns(string memory)`
233    StringConcat           => sym::concat
234                           => gcx.mk_builtin_fn(&[], SM::Pure, &[gcx.types.string_ref.memory]);
235
236    // TODO                => `(bytes memory...) pure returns(bytes memory)`
237    BytesConcat            => sym::concat
238                           => gcx.mk_builtin_fn(&[], SM::Pure, &[gcx.types.bytes_ref.memory]);
239}
240
241impl Builtin {
242    const FIRST_GLOBAL: usize = 0;
243    const LAST_GLOBAL: usize = Self::Abi as usize + 1;
244
245    const FIRST_BLOCK: usize = Self::BlockCoinbase as usize;
246    const LAST_BLOCK: usize = Self::BlockBlobbasefee as usize + 1;
247
248    const FIRST_MSG: usize = Self::MsgSender as usize;
249    const LAST_MSG: usize = Self::MsgSig as usize + 1;
250
251    const FIRST_TX: usize = Self::TxOrigin as usize;
252    const LAST_TX: usize = Self::TxGasPrice as usize + 1;
253
254    const FIRST_ABI: usize = Self::AbiEncode as usize;
255    const LAST_ABI: usize = Self::AbiDecode as usize + 1;
256
257    /// Returns an iterator over all builtins.
258    #[inline]
259    pub fn iter() -> std::iter::Map<std::ops::Range<usize>, impl FnMut(usize) -> Self> {
260        (0..Self::COUNT).map(|i| Self::from_index(i).unwrap())
261    }
262
263    #[inline]
264    const fn from_index(i: usize) -> Option<Self> {
265        const {
266            assert!(Self::COUNT <= Primitive::MAX as usize);
267            assert!(size_of::<Self>() == 1);
268        }
269        if i < Self::COUNT {
270            // SAFETY:
271            //
272            // `Self` is a field-less, `repr(Primitive)` enum and therefore guaranteed
273            // to have the same size and alignment as `Primitive`.
274            //
275            // This branch ensures `i < Self::COUNT` where `Self::COUNT` is the
276            // number of variants in `Self`. The discriminants of `Self` are
277            // contiguous because no variant specifies a custom discriminant
278            // with `Variant = value`. This ensures that `i as Primitive` is
279            // a valid inhabitant of type `Self`.
280            Some(unsafe { std::mem::transmute::<Primitive, Self>(i as Primitive) })
281        } else {
282            None
283        }
284    }
285
286    /// Returns the global builtins.
287    pub fn global() -> impl ExactSizeIterator<Item = Self> + Clone {
288        Self::make_range_iter(Self::FIRST_GLOBAL..Self::LAST_GLOBAL)
289    }
290
291    /// Returns the builtin's members.
292    pub fn members(self) -> Option<impl ExactSizeIterator<Item = Self> + Clone> {
293        use Builtin::*;
294        Some(Self::make_range_iter(match self {
295            Block => Self::FIRST_BLOCK..Self::LAST_BLOCK,
296            Msg => Self::FIRST_MSG..Self::LAST_MSG,
297            Tx => Self::FIRST_TX..Self::LAST_TX,
298            Abi => Self::FIRST_ABI..Self::LAST_ABI,
299            _ => return None,
300        }))
301    }
302
303    #[inline]
304    fn make_range_iter(
305        range: std::ops::Range<usize>,
306    ) -> impl ExactSizeIterator<Item = Self> + Clone {
307        debug_assert!(range.start < Self::COUNT);
308        debug_assert!(range.end < Self::COUNT);
309        (range.start as Primitive..range.end as Primitive)
310            .map(|idx| unsafe { Self::from_index(idx as usize).unwrap_unchecked() })
311    }
312}