use crate::{
ast_lowering::resolve::{Declaration, Declarations},
hir,
ty::{Gcx, Ty, TyFnKind},
};
use solar_ast::StateMutability as SM;
use solar_data_structures::map::FxHashMap;
use solar_interface::{Span, Symbol, kw, sym};
use std::sync::OnceLock;
pub(crate) mod members;
pub use members::{Member, MemberList};
pub(crate) fn scopes() -> (Declarations, FxHashMap<Builtin, Declarations>) {
let global = declarations(Builtin::global());
let mut members_map = Builtin::iter()
.filter_map(|builtin| Some((builtin, declarations(builtin.members()?))))
.collect::<FxHashMap<_, _>>();
members_map.shrink_to_fit();
(global, members_map)
}
fn declarations(builtins: impl IntoIterator<Item = Builtin>) -> Declarations {
let mut declarations = Declarations::new();
for builtin in builtins {
let decl = Declaration { res: hir::Res::Builtin(builtin), span: Span::DUMMY };
declarations.declare_unchecked(builtin.name(), decl);
}
declarations
}
type Primitive = u8;
macro_rules! declare_builtins {
(|$slf:ident, $gcx:ident| $($(#[$variant_attr:meta])* $variant_name:ident => $sym:ident::$name:ident => $ty:expr;)*) => {
#[repr(u8)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum Builtin {
$(
$(#[$variant_attr])*
$variant_name,
)*
}
impl Builtin {
pub const COUNT: usize = 0 $(+ { let _ = Builtin::$variant_name; 1 })*;
pub fn name(self) -> Symbol {
match self {
$(
Builtin::$variant_name => $sym::$name,
)*
}
}
pub fn ty<'gcx>($slf, $gcx: Gcx<'gcx>) -> Ty<'gcx> {
$gcx.type_of_builtin($slf)
}
pub(crate) fn ty_impl<'gcx>($slf, $gcx: Gcx<'gcx>) -> Ty<'gcx> {
match $slf {
$(
Builtin::$variant_name => $ty,
)*
}
}
}
};
}
declare_builtins! {
|self, gcx|
Blockhash => kw::Blockhash
=> gcx.mk_builtin_fn(&[gcx.types.uint(256)], SM::View, &[gcx.types.fixed_bytes(32)]);
Blobhash => kw::Blobhash
=> gcx.mk_builtin_fn(&[gcx.types.uint(256)], SM::View, &[gcx.types.fixed_bytes(32)]);
Gasleft => sym::gasleft
=> gcx.mk_builtin_fn(&[], SM::View, &[gcx.types.uint(256)]);
Selfdestruct => kw::Selfdestruct
=> gcx.mk_builtin_fn(&[gcx.types.address_payable], SM::NonPayable, &[]);
Assert => sym::assert
=> gcx.mk_builtin_fn(&[gcx.types.bool], SM::Pure, &[]);
Require => sym::require
=> gcx.mk_builtin_fn(&[gcx.types.bool, gcx.mk_ty_variadic()], SM::Pure, &[]);
Revert => kw::Revert
=> gcx.mk_builtin_fn(&[], SM::Pure, &[]);
RevertMsg => kw::Revert
=> gcx.mk_builtin_fn(&[gcx.types.string], SM::Pure, &[]);
AddMod => kw::Addmod
=> gcx.mk_builtin_fn(&[gcx.types.uint(256), gcx.types.uint(256), gcx.types.uint(256)], SM::Pure, &[gcx.types.uint(256)]);
MulMod => kw::Mulmod
=> gcx.mk_builtin_fn(&[gcx.types.uint(256), gcx.types.uint(256), gcx.types.uint(256)], SM::Pure, &[gcx.types.uint(256)]);
Keccak256 => kw::Keccak256
=> gcx.mk_builtin_fn(&[gcx.types.bytes_ref.memory], SM::Pure, &[gcx.types.fixed_bytes(32)]);
Sha256 => sym::sha256
=> gcx.mk_builtin_fn(&[gcx.types.bytes_ref.memory], SM::Pure, &[gcx.types.fixed_bytes(32)]);
Ripemd160 => sym::ripemd160
=> gcx.mk_builtin_fn(&[gcx.types.bytes_ref.memory], SM::Pure, &[gcx.types.fixed_bytes(20)]);
EcRecover => sym::ecrecover
=> 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]);
Erc7201 => sym::erc7201
=> gcx.mk_builtin_fn(&[gcx.types.string_ref.memory], SM::Pure, &[gcx.types.uint(256)]);
Block => sym::block
=> gcx.mk_builtin_mod(self);
Msg => sym::msg
=> gcx.mk_builtin_mod(self);
Tx => sym::tx
=> gcx.mk_builtin_mod(self);
Abi => sym::abi
=> gcx.mk_builtin_mod(self);
This => sym::this => unreachable!();
Super => sym::super_ => unreachable!();
BlockCoinbase => kw::Coinbase
=> gcx.types.address_payable;
BlockTimestamp => kw::Timestamp
=> gcx.types.uint(256);
BlockDifficulty => kw::Difficulty
=> gcx.types.uint(256);
BlockPrevrandao => kw::Prevrandao
=> gcx.types.uint(256);
BlockNumber => kw::Number
=> gcx.types.uint(256);
BlockGaslimit => kw::Gaslimit
=> gcx.types.uint(256);
BlockChainid => kw::Chainid
=> gcx.types.uint(256);
BlockBasefee => kw::Basefee
=> gcx.types.uint(256);
BlockBlobbasefee => kw::Blobbasefee
=> gcx.types.uint(256);
MsgSender => sym::sender
=> gcx.types.address;
MsgGas => kw::Gas
=> gcx.types.uint(256);
MsgValue => sym::value
=> gcx.types.uint(256);
MsgData => sym::data
=> gcx.types.bytes_ref.calldata;
MsgSig => sym::sig
=> gcx.types.fixed_bytes(4);
TxOrigin => kw::Origin
=> gcx.types.address;
TxGasPrice => kw::Gasprice
=> gcx.types.uint(256);
AbiEncode => sym::encode
=> gcx.mk_builtin_fn(&[gcx.mk_ty_variadic()], SM::Pure, &[gcx.types.bytes_ref.memory]);
AbiEncodePacked => sym::encodePacked
=> gcx.mk_builtin_fn(&[gcx.mk_ty_variadic()], SM::Pure, &[gcx.types.bytes_ref.memory]);
AbiEncodeWithSelector => sym::encodeWithSelector
=> gcx.mk_builtin_fn(&[gcx.types.fixed_bytes(4), gcx.mk_ty_variadic()], SM::Pure, &[gcx.types.bytes_ref.memory]);
AbiEncodeCall => sym::encodeCall
=> gcx.mk_builtin_fn(&[gcx.mk_ty_variadic()], SM::Pure, &[gcx.types.bytes_ref.memory]);
AbiEncodeWithSignature => sym::encodeWithSignature
=> gcx.mk_builtin_fn(&[gcx.types.string_ref.memory, gcx.mk_ty_variadic()], SM::Pure, &[gcx.types.bytes_ref.memory]);
AbiDecode => sym::decode
=> 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()]);
AddressBalance => kw::Balance
=> gcx.types.uint(256);
AddressCode => sym::code
=> gcx.types.bytes_ref.memory;
AddressCodehash => sym::codehash
=> gcx.types.fixed_bytes(32);
AddressCall => kw::Call
=> gcx.mk_ty_fn_with_kind(TyFnKind::BareCall, &[gcx.types.bytes_ref.memory], SM::Payable, &[gcx.types.bool, gcx.types.bytes_ref.memory]);
AddressDelegatecall => kw::Delegatecall
=> gcx.mk_ty_fn_with_kind(TyFnKind::BareDelegateCall, &[gcx.types.bytes_ref.memory], SM::NonPayable, &[gcx.types.bool, gcx.types.bytes_ref.memory]);
AddressStaticcall => kw::Staticcall
=> gcx.mk_ty_fn_with_kind(TyFnKind::BareStaticCall, &[gcx.types.bytes_ref.memory], SM::View, &[gcx.types.bool, gcx.types.bytes_ref.memory]);
AddressPayableTransfer => sym::transfer
=> gcx.mk_builtin_fn(&[gcx.types.uint(256)], SM::NonPayable, &[]);
AddressPayableSend => sym::send
=> gcx.mk_builtin_fn(&[gcx.types.uint(256)], SM::NonPayable, &[gcx.types.bool]);
FixedBytesLength => sym::length
=> gcx.types.uint(8);
ArrayLength => sym::length
=> gcx.types.uint(256);
ArrayPush0 => sym::push => unreachable!();
ArrayPush => sym::push => unreachable!();
ArrayPop => kw::Pop => unreachable!();
FunctionSelector => sym::selector
=> gcx.types.fixed_bytes(4);
FunctionAddress => kw::Address
=> gcx.types.address;
EventSelector => sym::selector
=> gcx.types.fixed_bytes(32);
ContractCreationCode => sym::creationCode
=> gcx.types.bytes_ref.memory;
ContractRuntimeCode => sym::runtimeCode
=> gcx.types.bytes_ref.memory;
ContractName => sym::name
=> gcx.types.string_ref.memory;
InterfaceId => sym::interfaceId
=> gcx.types.fixed_bytes(4);
TypeMin => sym::min => unreachable!();
TypeMax => sym::max => unreachable!();
UdvtWrap => sym::wrap => unreachable!();
UdvtUnwrap => sym::unwrap => unreachable!();
StringConcat => sym::concat
=> gcx.mk_builtin_fn(&[gcx.mk_ty_variadic()], SM::Pure, &[gcx.types.string_ref.memory]);
BytesConcat => sym::concat
=> gcx.mk_builtin_fn(&[gcx.mk_ty_variadic()], SM::Pure, &[gcx.types.bytes_ref.memory]);
YulAdd => kw::Add => gcx.mk_yul_builtin_fn(2, 1);
YulSub => kw::Sub => gcx.mk_yul_builtin_fn(2, 1);
YulMul => kw::Mul => gcx.mk_yul_builtin_fn(2, 1);
YulDiv => kw::Div => gcx.mk_yul_builtin_fn(2, 1);
YulMod => kw::Mod => gcx.mk_yul_builtin_fn(2, 1);
YulExp => kw::Exp => gcx.mk_yul_builtin_fn(2, 1);
YulNot => kw::Not => gcx.mk_yul_builtin_fn(1, 1);
YulAnd => kw::And => gcx.mk_yul_builtin_fn(2, 1);
YulOr => kw::Or => gcx.mk_yul_builtin_fn(2, 1);
YulXor => kw::Xor => gcx.mk_yul_builtin_fn(2, 1);
YulShl => kw::Shl => gcx.mk_yul_builtin_fn(2, 1);
YulShr => kw::Shr => gcx.mk_yul_builtin_fn(2, 1);
YulSar => kw::Sar => gcx.mk_yul_builtin_fn(2, 1);
YulStop => kw::Stop => gcx.mk_yul_builtin_fn(0, 0);
YulSdiv => kw::Sdiv => gcx.mk_yul_builtin_fn(2, 1);
YulSmod => kw::Smod => gcx.mk_yul_builtin_fn(2, 1);
YulLt => kw::Lt => gcx.mk_yul_builtin_fn(2, 1);
YulGt => kw::Gt => gcx.mk_yul_builtin_fn(2, 1);
YulSlt => kw::Slt => gcx.mk_yul_builtin_fn(2, 1);
YulSgt => kw::Sgt => gcx.mk_yul_builtin_fn(2, 1);
YulEq => kw::Eq => gcx.mk_yul_builtin_fn(2, 1);
YulIszero => kw::Iszero => gcx.mk_yul_builtin_fn(1, 1);
YulByte => kw::Byte => gcx.mk_yul_builtin_fn(2, 1);
YulClz => kw::Clz => gcx.mk_yul_builtin_fn(1, 1);
YulAddmod => kw::Addmod => gcx.mk_yul_builtin_fn(3, 1);
YulMulmod => kw::Mulmod => gcx.mk_yul_builtin_fn(3, 1);
YulSignextend => kw::Signextend => gcx.mk_yul_builtin_fn(2, 1);
YulKeccak256 => kw::Keccak256 => gcx.mk_yul_builtin_fn(2, 1);
YulAddress => kw::Address => gcx.mk_yul_builtin_fn(0, 1);
YulBalance => kw::Balance => gcx.mk_yul_builtin_fn(1, 1);
YulSelfbalance => kw::Selfbalance => gcx.mk_yul_builtin_fn(0, 1);
YulCaller => kw::Caller => gcx.mk_yul_builtin_fn(0, 1);
YulCallvalue => kw::Callvalue => gcx.mk_yul_builtin_fn(0, 1);
YulCalldataload => kw::Calldataload => gcx.mk_yul_builtin_fn(1, 1);
YulCalldatasize => kw::Calldatasize => gcx.mk_yul_builtin_fn(0, 1);
YulCalldatacopy => kw::Calldatacopy => gcx.mk_yul_builtin_fn(3, 0);
YulCodesize => kw::Codesize => gcx.mk_yul_builtin_fn(0, 1);
YulCodecopy => kw::Codecopy => gcx.mk_yul_builtin_fn(3, 0);
YulExtcodesize => kw::Extcodesize => gcx.mk_yul_builtin_fn(1, 1);
YulExtcodecopy => kw::Extcodecopy => gcx.mk_yul_builtin_fn(4, 0);
YulReturndatasize => kw::Returndatasize => gcx.mk_yul_builtin_fn(0, 1);
YulReturndatacopy => kw::Returndatacopy => gcx.mk_yul_builtin_fn(3, 0);
YulExtcodehash => kw::Extcodehash => gcx.mk_yul_builtin_fn(1, 1);
YulMload => kw::Mload => gcx.mk_yul_builtin_fn(1, 1);
YulMstore => kw::Mstore => gcx.mk_yul_builtin_fn(2, 0);
YulMstore8 => kw::Mstore8 => gcx.mk_yul_builtin_fn(2, 0);
YulSload => kw::Sload => gcx.mk_yul_builtin_fn(1, 1);
YulSstore => kw::Sstore => gcx.mk_yul_builtin_fn(2, 0);
YulTload => kw::Tload => gcx.mk_yul_builtin_fn(1, 1);
YulTstore => kw::Tstore => gcx.mk_yul_builtin_fn(2, 0);
YulMsize => kw::Msize => gcx.mk_yul_builtin_fn(0, 1);
YulGas => kw::Gas => gcx.mk_yul_builtin_fn(0, 1);
YulLog0 => kw::Log0 => gcx.mk_yul_builtin_fn(2, 0);
YulLog1 => kw::Log1 => gcx.mk_yul_builtin_fn(3, 0);
YulLog2 => kw::Log2 => gcx.mk_yul_builtin_fn(4, 0);
YulLog3 => kw::Log3 => gcx.mk_yul_builtin_fn(5, 0);
YulLog4 => kw::Log4 => gcx.mk_yul_builtin_fn(6, 0);
YulCreate => kw::Create => gcx.mk_yul_builtin_fn(3, 1);
YulCreate2 => kw::Create2 => gcx.mk_yul_builtin_fn(4, 1);
YulCall => kw::Call => gcx.mk_yul_builtin_fn(7, 1);
YulCallcode => kw::Callcode => gcx.mk_yul_builtin_fn(7, 1);
YulDelegatecall => kw::Delegatecall => gcx.mk_yul_builtin_fn(6, 1);
YulStaticcall => kw::Staticcall => gcx.mk_yul_builtin_fn(6, 1);
YulExtcall => kw::Extcall => gcx.mk_yul_builtin_fn(4, 1);
YulExtdelegatecall => kw::Extdelegatecall => gcx.mk_yul_builtin_fn(3, 1);
YulExtstaticcall => kw::Extstaticcall => gcx.mk_yul_builtin_fn(3, 1);
YulReturn => kw::Return => gcx.mk_yul_builtin_fn(2, 0);
YulRevert => kw::Revert => gcx.mk_yul_builtin_fn(2, 0);
YulSelfdestruct => kw::Selfdestruct => gcx.mk_yul_builtin_fn(1, 0);
YulInvalid => kw::Invalid => gcx.mk_yul_builtin_fn(0, 0);
YulChainid => kw::Chainid => gcx.mk_yul_builtin_fn(0, 1);
YulBasefee => kw::Basefee => gcx.mk_yul_builtin_fn(0, 1);
YulBlobbasefee => kw::Blobbasefee => gcx.mk_yul_builtin_fn(0, 1);
YulBlobhash => kw::Blobhash => gcx.mk_yul_builtin_fn(1, 1);
YulCoinbase => kw::Coinbase => gcx.mk_yul_builtin_fn(0, 1);
YulDifficulty => kw::Difficulty => gcx.mk_yul_builtin_fn(0, 1);
YulPrevrandao => kw::Prevrandao => gcx.mk_yul_builtin_fn(0, 1);
YulGaslimit => kw::Gaslimit => gcx.mk_yul_builtin_fn(0, 1);
YulNumber => kw::Number => gcx.mk_yul_builtin_fn(0, 1);
YulTimestamp => kw::Timestamp => gcx.mk_yul_builtin_fn(0, 1);
YulGasprice => kw::Gasprice => gcx.mk_yul_builtin_fn(0, 1);
YulOrigin => kw::Origin => gcx.mk_yul_builtin_fn(0, 1);
YulBlockhash => kw::Blockhash => gcx.mk_yul_builtin_fn(1, 1);
YulPop => kw::Pop => gcx.mk_yul_builtin_fn(1, 0);
YulMcopy => kw::Mcopy => gcx.mk_yul_builtin_fn(3, 0);
}
impl Builtin {
const FIRST_GLOBAL: usize = 0;
const LAST_GLOBAL: usize = Self::Abi as usize + 1;
const FIRST_BLOCK: usize = Self::BlockCoinbase as usize;
const LAST_BLOCK: usize = Self::BlockBlobbasefee as usize + 1;
const FIRST_MSG: usize = Self::MsgSender as usize;
const LAST_MSG: usize = Self::MsgSig as usize + 1;
const FIRST_TX: usize = Self::TxOrigin as usize;
const LAST_TX: usize = Self::TxGasPrice as usize + 1;
const FIRST_ABI: usize = Self::AbiEncode as usize;
const LAST_ABI: usize = Self::AbiDecode as usize + 1;
const FIRST_YUL: usize = Self::YulAdd as usize;
const LAST_YUL: usize = Self::YulMcopy as usize + 1;
#[inline]
pub fn iter() -> std::iter::Map<std::ops::Range<usize>, impl FnMut(usize) -> Self> {
(0..Self::COUNT).map(|i| Self::from_index(i).unwrap())
}
#[inline]
const fn from_index(i: usize) -> Option<Self> {
const {
assert!(Self::COUNT <= Primitive::MAX as usize);
assert!(size_of::<Self>() == 1);
}
if i < Self::COUNT {
Some(unsafe { std::mem::transmute::<Primitive, Self>(i as Primitive) })
} else {
None
}
}
pub fn global() -> impl ExactSizeIterator<Item = Self> + Clone {
Self::make_range_iter(Self::FIRST_GLOBAL..Self::LAST_GLOBAL)
}
pub fn members(self) -> Option<impl ExactSizeIterator<Item = Self> + Clone> {
use Builtin::*;
Some(Self::make_range_iter(match self {
Block => Self::FIRST_BLOCK..Self::LAST_BLOCK,
Msg => Self::FIRST_MSG..Self::LAST_MSG,
Tx => Self::FIRST_TX..Self::LAST_TX,
Abi => Self::FIRST_ABI..Self::LAST_ABI,
_ => return None,
}))
}
pub fn from_yul_name(name: Symbol) -> Option<Self> {
static YUL_NAMES: OnceLock<FxHashMap<Symbol, Builtin>> = OnceLock::new();
YUL_NAMES
.get_or_init(|| {
Self::make_range_iter(Self::FIRST_YUL..Self::LAST_YUL)
.map(|builtin| (builtin.name(), builtin))
.collect()
})
.get(&name)
.copied()
}
#[inline]
fn make_range_iter(
range: std::ops::Range<usize>,
) -> impl ExactSizeIterator<Item = Self> + Clone {
debug_assert!(range.start < Self::COUNT);
debug_assert!(range.end <= Self::COUNT);
(range.start as Primitive..range.end as Primitive)
.map(|idx| unsafe { Self::from_index(idx as usize).unwrap_unchecked() })
}
}