1use super::Builtin;
2use crate::{
3 hir,
4 ty::{Gcx, Ty, TyFnPtr, TyKind},
5};
6use solar_ast::{DataLocation, ElementaryType, StateMutability as SM};
7use solar_data_structures::BumpExt;
8use solar_interface::{Symbol, kw, sym};
9
10pub type MemberList<'gcx> = &'gcx [Member<'gcx>];
11pub(crate) type MemberListOwned<'gcx> = Vec<Member<'gcx>>;
12
13pub(crate) fn members_of<'gcx>(gcx: Gcx<'gcx>, ty: Ty<'gcx>) -> MemberList<'gcx> {
14 let expected_ref = || unreachable!("members_of: type {ty:?} should be wrapped in Ref");
15 gcx.bump().alloc_vec(match ty.kind {
16 TyKind::Elementary(elementary_type) => match elementary_type {
17 ElementaryType::Address(false) => address(gcx).collect(),
18 ElementaryType::Address(true) => address_payable(gcx).collect(),
19 ElementaryType::Bool => Default::default(),
20 ElementaryType::String => Default::default(),
21 ElementaryType::Bytes => expected_ref(),
22 ElementaryType::Fixed(..) | ElementaryType::UFixed(..) => Default::default(),
23 ElementaryType::Int(_size) => Default::default(),
24 ElementaryType::UInt(_size) => Default::default(),
25 ElementaryType::FixedBytes(_size) => fixed_bytes(gcx),
26 },
27 TyKind::StringLiteral(_utf8, _size) => Default::default(),
28 TyKind::IntLiteral(_size) => Default::default(),
29 TyKind::Ref(inner, loc) => reference(gcx, ty, inner, loc),
30 TyKind::DynArray(_ty) => expected_ref(),
31 TyKind::Array(_ty, _len) => expected_ref(),
32 TyKind::Tuple(_tys) => Default::default(),
33 TyKind::Mapping(..) => Default::default(),
34 TyKind::FnPtr(f) => function(gcx, f),
35 TyKind::Contract(id) => contract(gcx, id),
36 TyKind::Struct(_id) => expected_ref(),
37 TyKind::Enum(_id) => Default::default(),
38 TyKind::Udvt(_ty, _id) => Default::default(),
39 TyKind::Error(_tys, _id) => Member::of_builtins(gcx, [Builtin::ErrorSelector]),
40 TyKind::Event(_tys, _id) => Member::of_builtins(gcx, [Builtin::EventSelector]),
41 TyKind::Module(id) => gcx.symbol_resolver.source_scopes[id]
42 .iter()
43 .flat_map(|(name, decls)| {
44 decls.iter().map(move |decl| Member::new(name, gcx.type_of_res(decl.res)))
45 })
46 .collect(),
47 TyKind::BuiltinModule(builtin) => builtin
48 .members()
49 .unwrap_or_else(|| panic!("builtin module {builtin:?} has no inner builtins"))
50 .map(|b| Member::of_builtin(gcx, b))
51 .collect(),
52 TyKind::Type(_ty) => type_type(gcx, ty),
53 TyKind::Meta(_ty) => meta(gcx, ty),
54 TyKind::Err(_guar) => Default::default(),
55 })
56}
57
58#[derive(Clone, Copy, Debug)]
59pub struct Member<'gcx> {
60 pub name: Symbol,
61 pub ty: Ty<'gcx>,
62 pub res: Option<hir::Res>,
63}
64
65impl<'gcx> Member<'gcx> {
66 pub fn new(name: Symbol, ty: Ty<'gcx>) -> Self {
67 Self { name, ty, res: None }
68 }
69
70 pub fn with_res(name: Symbol, ty: Ty<'gcx>, res: impl Into<hir::Res>) -> Self {
71 Self { name, ty, res: Some(res.into()) }
72 }
73
74 pub fn with_builtin(builtin: Builtin, ty: Ty<'gcx>) -> Self {
75 Self::with_res(builtin.name(), ty, builtin)
76 }
77
78 pub fn of_builtin(gcx: Gcx<'gcx>, builtin: Builtin) -> Self {
79 Self { name: builtin.name(), ty: builtin.ty(gcx), res: None }
80 }
81
82 pub fn of_builtins(
83 gcx: Gcx<'gcx>,
84 builtins: impl IntoIterator<Item = Builtin>,
85 ) -> MemberListOwned<'gcx> {
86 Self::of_builtins_iter(gcx, builtins).collect()
87 }
88
89 pub fn of_builtins_iter(
90 gcx: Gcx<'gcx>,
91 builtins: impl IntoIterator<Item = Builtin>,
92 ) -> impl Iterator<Item = Self> {
93 builtins.into_iter().map(move |builtin| Self::of_builtin(gcx, builtin))
94 }
95}
96
97fn address(gcx: Gcx<'_>) -> impl Iterator<Item = Member<'_>> {
98 Member::of_builtins_iter(
99 gcx,
100 [
101 Builtin::AddressBalance,
102 Builtin::AddressCode,
103 Builtin::AddressCodehash,
104 Builtin::AddressCall,
105 Builtin::AddressDelegatecall,
106 Builtin::AddressStaticcall,
107 ],
108 )
109}
110
111fn address_payable(gcx: Gcx<'_>) -> impl Iterator<Item = Member<'_>> {
112 address(gcx).chain(Member::of_builtins_iter(
113 gcx,
114 [Builtin::AddressPayableTransfer, Builtin::AddressPayableSend],
115 ))
116}
117
118fn fixed_bytes(gcx: Gcx<'_>) -> MemberListOwned<'_> {
119 Member::of_builtins(gcx, [Builtin::FixedBytesLength])
120}
121
122pub(crate) fn contract(gcx: Gcx<'_>, id: hir::ContractId) -> MemberListOwned<'_> {
123 let c = gcx.hir.contract(id);
124 if c.kind.is_library() {
125 return MemberListOwned::default();
126 }
127 gcx.interface_functions(id)
128 .iter()
129 .map(|f| {
130 let id = hir::ItemId::from(f.id);
131 Member::with_res(gcx.item_name(id).name, f.ty.as_externally_callable_function(gcx), id)
132 })
133 .collect()
134}
135
136fn function<'gcx>(gcx: Gcx<'gcx>, f: &'gcx TyFnPtr<'gcx>) -> MemberListOwned<'gcx> {
137 let _ = (gcx, f);
138 todo!()
139}
140
141fn reference<'gcx>(
142 gcx: Gcx<'gcx>,
143 this: Ty<'gcx>,
144 inner: Ty<'gcx>,
145 loc: DataLocation,
146) -> MemberListOwned<'gcx> {
147 match (&inner.kind, loc) {
148 (&TyKind::Struct(id), _) => {
149 let fields = gcx.hir.strukt(id).fields;
150 let tys = gcx.struct_field_types(id);
151 debug_assert_eq!(fields.len(), tys.len());
152 fields
153 .iter()
154 .zip(tys)
155 .map(|(&f, &ty)| Member::new(gcx.item_name(f).name, ty.with_loc(gcx, loc)))
156 .collect()
157 }
158 (
159 TyKind::DynArray(_) | TyKind::Elementary(ElementaryType::Bytes),
160 DataLocation::Storage,
161 ) => {
162 let inner = if let TyKind::DynArray(inner) = inner.kind {
163 inner
164 } else {
165 gcx.types.fixed_bytes(1)
166 };
167 vec![
168 Member::new(sym::length, gcx.types.uint(256)),
169 Member::new(sym::push, gcx.mk_builtin_fn(&[this, inner], SM::NonPayable, &[])),
170 Member::new(sym::push, gcx.mk_builtin_fn(&[this], SM::NonPayable, &[inner])),
171 Member::new(kw::Pop, gcx.mk_builtin_fn(&[this], SM::NonPayable, &[])),
172 ]
173 }
174 (
175 TyKind::Array(..) | TyKind::DynArray(_) | TyKind::Elementary(ElementaryType::Bytes),
176 _,
177 ) => array(gcx),
178 _ => Default::default(),
179 }
180}
181
182fn type_type<'gcx>(gcx: Gcx<'gcx>, ty: Ty<'gcx>) -> MemberListOwned<'gcx> {
184 match ty.kind {
185 TyKind::Contract(_) => Default::default(),
187 TyKind::Enum(id) => {
188 gcx.hir.enumm(id).variants.iter().map(|v| Member::new(v.name, ty)).collect()
189 }
190 TyKind::Udvt(inner, _id) => {
191 vec![
192 Member::with_builtin(
193 Builtin::UdvtWrap,
194 gcx.mk_builtin_fn(&[inner], SM::Pure, &[ty]),
195 ),
196 Member::with_builtin(
197 Builtin::UdvtUnwrap,
198 gcx.mk_builtin_fn(&[ty], SM::Pure, &[inner]),
199 ),
200 ]
201 }
202 TyKind::Elementary(ElementaryType::String) => string_ty(gcx),
203 TyKind::Elementary(ElementaryType::Bytes) => bytes_ty(gcx),
204 _ => Default::default(),
205 }
206}
207
208fn meta<'gcx>(gcx: Gcx<'gcx>, ty: Ty<'gcx>) -> MemberListOwned<'gcx> {
210 match ty.kind {
211 TyKind::Contract(id) => {
212 if gcx.hir.contract(id).can_be_deployed() {
213 type_contract(gcx)
214 } else {
215 type_interface(gcx)
216 }
217 }
218 TyKind::Elementary(ElementaryType::Int(_) | ElementaryType::UInt(_)) | TyKind::Enum(_) => {
219 vec![
220 Member::with_builtin(Builtin::TypeMin, ty),
221 Member::with_builtin(Builtin::TypeMax, ty),
222 ]
223 }
224 _ => Default::default(),
225 }
226}
227
228fn array(gcx: Gcx<'_>) -> MemberListOwned<'_> {
229 Member::of_builtins(gcx, [Builtin::ArrayLength])
230}
231
232fn string_ty(gcx: Gcx<'_>) -> MemberListOwned<'_> {
233 Member::of_builtins(gcx, [Builtin::StringConcat])
234}
235
236fn bytes_ty(gcx: Gcx<'_>) -> MemberListOwned<'_> {
237 Member::of_builtins(gcx, [Builtin::BytesConcat])
238}
239
240fn type_contract(gcx: Gcx<'_>) -> MemberListOwned<'_> {
241 Member::of_builtins(
242 gcx,
243 [Builtin::ContractCreationCode, Builtin::ContractRuntimeCode, Builtin::ContractName],
244 )
245}
246
247fn type_interface(gcx: Gcx<'_>) -> MemberListOwned<'_> {
248 Member::of_builtins(gcx, [Builtin::InterfaceId, Builtin::ContractName])
249}