1use super::{Gcx, Recursiveness, abi::TySolcPrinter};
2use crate::{builtins::Builtin, hir};
3use alloy_primitives::U256;
4use solar_ast::{DataLocation, ElementaryType, StateMutability, TypeSize, Visibility};
5use solar_data_structures::{Interned, fmt};
6use solar_interface::diagnostics::ErrorGuaranteed;
7use std::{borrow::Borrow, hash::Hash, ops::ControlFlow};
8
9#[derive(Clone, Copy, PartialEq, Eq, Hash)]
11pub struct Ty<'gcx>(pub(super) Interned<'gcx, TyData<'gcx>>);
12
13impl fmt::Debug for Ty<'_> {
14 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
15 self.0.fmt(f)
16 }
17}
18
19impl<'gcx> std::ops::Deref for Ty<'gcx> {
20 type Target = &'gcx TyData<'gcx>;
21
22 #[inline(always)]
23 fn deref(&self) -> &Self::Target {
24 &self.0.0
25 }
26}
27
28impl<'gcx> Ty<'gcx> {
29 pub fn new(gcx: Gcx<'gcx>, kind: TyKind<'gcx>) -> Self {
30 gcx.mk_ty(kind)
31 }
32
33 pub fn with_loc(self, gcx: Gcx<'gcx>, loc: DataLocation) -> Self {
35 let mut ty = self;
36 if let TyKind::Ref(inner, l2) = self.kind {
37 if l2 == loc {
38 return self;
39 }
40 ty = inner;
41 }
42 Self::new(gcx, TyKind::Ref(ty, loc))
43 }
44
45 pub fn peel_refs(self) -> Self {
47 let mut ty = self;
48 while let TyKind::Ref(inner, _) = ty.kind {
49 ty = inner;
50 }
51 ty
52 }
53
54 pub fn as_externally_callable_function(self, gcx: Gcx<'gcx>) -> Self {
55 let is_calldata = |param: &Ty<'_>| param.is_ref_at(DataLocation::Calldata);
56 let parameters = self.parameters().unwrap_or_default();
57 let returns = self.returns().unwrap_or_default();
58 let any_parameter = parameters.iter().any(is_calldata);
59 let any_return = returns.iter().any(is_calldata);
60 if !any_parameter && !any_return {
61 return self;
62 }
63 gcx.mk_ty_fn_ptr(TyFnPtr {
64 parameters: if any_parameter {
65 gcx.mk_ty_iter(parameters.iter().map(|param| {
66 if is_calldata(param) {
67 param.with_loc(gcx, DataLocation::Memory)
68 } else {
69 *param
70 }
71 }))
72 } else {
73 parameters
74 },
75 returns: if any_return {
76 gcx.mk_ty_iter(returns.iter().map(|ret| {
77 if is_calldata(ret) { ret.with_loc(gcx, DataLocation::Memory) } else { *ret }
78 }))
79 } else {
80 returns
81 },
82 state_mutability: self.state_mutability().unwrap_or(StateMutability::NonPayable),
83 visibility: self.visibility().unwrap_or(Visibility::Public),
84 })
85 }
86
87 pub fn make_ref(self, gcx: Gcx<'gcx>, loc: DataLocation) -> Self {
88 if self.is_ref_at(loc) {
89 return self;
90 }
91 Self::new(gcx, TyKind::Ref(self, loc))
92 }
93
94 pub fn make_type_type(self, gcx: Gcx<'gcx>) -> Self {
95 if let TyKind::Type(_) = self.kind {
96 return self;
97 }
98 Self::new(gcx, TyKind::Type(self))
99 }
100
101 pub fn make_meta(self, gcx: Gcx<'gcx>) -> Self {
102 if let TyKind::Meta(_) = self.kind {
103 return self;
104 }
105 Self::new(gcx, TyKind::Meta(self))
106 }
107
108 #[inline]
110 pub fn is_ref(self) -> bool {
111 matches!(self.kind, TyKind::Ref(..))
112 }
113
114 #[inline]
116 pub fn is_ref_at(self, loc: DataLocation) -> bool {
117 matches!(self.kind, TyKind::Ref(_, l) if l == loc)
118 }
119
120 #[inline]
124 pub fn is_value_type(self) -> bool {
125 match self.kind {
126 TyKind::Elementary(t) => t.is_value_type(),
127 TyKind::Contract(_) | TyKind::FnPtr(_) | TyKind::Enum(_) | TyKind::Udvt(..) => true,
128 _ => false,
129 }
130 }
131
132 #[inline]
134 pub fn is_reference_type(self) -> bool {
135 match self.kind {
136 TyKind::Elementary(t) => t.is_reference_type(),
137 TyKind::Struct(_) | TyKind::Array(..) | TyKind::DynArray(_) => true,
138 _ => false,
139 }
140 }
141
142 pub fn is_recursive(self) -> bool {
144 self.flags.contains(TyFlags::IS_RECURSIVE)
145 }
146
147 pub fn has_mapping(self) -> bool {
149 self.flags.contains(TyFlags::HAS_MAPPING)
150 }
151
152 pub fn has_error(self) -> Result<(), ErrorGuaranteed> {
154 if self.flags.contains(TyFlags::HAS_ERROR) {
155 Err(ErrorGuaranteed::new_unchecked())
156 } else {
157 Ok(())
158 }
159 }
160
161 #[inline]
163 pub fn can_be_exported(self) -> bool {
164 !(self.is_recursive() || self.has_mapping() || self.has_error().is_err())
165 }
166
167 #[inline]
169 pub fn parameters(self) -> Option<&'gcx [Self]> {
170 Some(match self.kind {
171 TyKind::FnPtr(f) => f.parameters,
172 TyKind::Event(tys, _) | TyKind::Error(tys, _) => tys,
173 _ => return None,
174 })
175 }
176
177 #[inline]
179 pub fn returns(self) -> Option<&'gcx [Self]> {
180 Some(match self.kind {
181 TyKind::FnPtr(f) => f.returns,
182 _ => return None,
183 })
184 }
185
186 #[inline]
188 pub fn state_mutability(self) -> Option<StateMutability> {
189 match self.kind {
190 TyKind::FnPtr(f) => Some(f.state_mutability),
191 _ => None,
192 }
193 }
194
195 #[inline]
197 pub fn visibility(self) -> Option<Visibility> {
198 match self.kind {
199 TyKind::FnPtr(f) => Some(f.visibility),
200 _ => None,
201 }
202 }
203
204 pub fn visit<T>(self, f: &mut impl FnMut(Self) -> ControlFlow<T>) -> ControlFlow<T> {
206 f(self)?;
207 match self.kind {
208 TyKind::Elementary(_)
209 | TyKind::StringLiteral(..)
210 | TyKind::IntLiteral(_)
211 | TyKind::Contract(_)
212 | TyKind::FnPtr(_)
213 | TyKind::Enum(_)
214 | TyKind::Module(_)
215 | TyKind::BuiltinModule(_)
216 | TyKind::Struct(_)
217 | TyKind::Err(_) => ControlFlow::Continue(()),
218
219 TyKind::Ref(ty, _)
220 | TyKind::DynArray(ty)
221 | TyKind::Array(ty, _)
222 | TyKind::Udvt(ty, _)
223 | TyKind::Type(ty)
224 | TyKind::Meta(ty) => ty.visit(f),
225
226 TyKind::Error(list, _) | TyKind::Event(list, _) | TyKind::Tuple(list) => {
227 for ty in list {
228 ty.visit(f)?;
229 }
230 ControlFlow::Continue(())
231 }
232
233 TyKind::Mapping(k, v) => {
234 k.visit(f)?;
235 v.visit(f)
236 }
237 }
238 }
239
240 pub fn display(self, gcx: Gcx<'gcx>) -> impl fmt::Display + use<'gcx> {
242 fmt::from_fn(move |f| TySolcPrinter::new(gcx, f).data_locations(true).print(self))
243 }
244}
245
246pub struct TyData<'gcx> {
248 pub kind: TyKind<'gcx>,
249 pub flags: TyFlags,
250}
251
252impl<'gcx> Borrow<TyKind<'gcx>> for &TyData<'gcx> {
253 #[inline]
254 fn borrow(&self) -> &TyKind<'gcx> {
255 &self.kind
256 }
257}
258
259impl PartialEq for TyData<'_> {
260 #[inline]
261 fn eq(&self, other: &Self) -> bool {
262 self.kind == other.kind
263 }
264}
265
266impl Eq for TyData<'_> {}
267
268impl std::hash::Hash for TyData<'_> {
269 #[inline]
270 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
271 self.kind.hash(state);
272 }
273}
274
275impl fmt::Debug for TyData<'_> {
276 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
277 self.kind.fmt(f)
278 }
279}
280
281#[derive(Debug, PartialEq, Eq, Hash)]
283#[non_exhaustive]
284pub enum TyKind<'gcx> {
285 Elementary(ElementaryType),
287
288 StringLiteral(bool, TypeSize),
293
294 IntLiteral(TypeSize),
296
297 Ref(Ty<'gcx>, DataLocation),
299
300 DynArray(Ty<'gcx>),
302
303 Array(Ty<'gcx>, U256),
305
306 Tuple(&'gcx [Ty<'gcx>]),
308
309 Mapping(Ty<'gcx>, Ty<'gcx>),
311
312 FnPtr(&'gcx TyFnPtr<'gcx>),
314
315 Contract(hir::ContractId),
317
318 Struct(hir::StructId),
322
323 Enum(hir::EnumId),
325
326 Error(&'gcx [Ty<'gcx>], hir::ErrorId),
328
329 Event(&'gcx [Ty<'gcx>], hir::EventId),
331
332 Udvt(Ty<'gcx>, hir::UdvtId),
334
335 Module(hir::SourceId),
337
338 BuiltinModule(Builtin),
340
341 Type(Ty<'gcx>),
344
345 Meta(Ty<'gcx>),
347
348 Err(ErrorGuaranteed),
350}
351
352#[derive(Debug, PartialEq, Eq, Hash)]
353pub struct TyFnPtr<'gcx> {
354 pub parameters: &'gcx [Ty<'gcx>],
355 pub returns: &'gcx [Ty<'gcx>],
356 pub state_mutability: StateMutability,
357 pub visibility: Visibility,
358}
359
360impl<'gcx> TyFnPtr<'gcx> {
361 pub fn tys(&self) -> impl DoubleEndedIterator<Item = Ty<'gcx>> + Clone {
363 self.parameters.iter().copied().chain(self.returns.iter().copied())
364 }
365}
366
367bitflags::bitflags! {
368 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
370 pub struct TyFlags: u8 {
371 const IS_RECURSIVE = 1 << 0;
373 const HAS_MAPPING = 1 << 1;
375 const HAS_ERROR = 1 << 2;
377 }
378}
379
380impl TyFlags {
381 pub(super) fn calculate<'gcx>(gcx: Gcx<'gcx>, ty: &TyKind<'gcx>) -> Self {
382 let mut flags = Self::empty();
383 flags.add_ty_kind(gcx, ty);
384 flags
385 }
386
387 fn add_ty_kind<'gcx>(&mut self, gcx: Gcx<'gcx>, ty: &TyKind<'gcx>) {
388 match *ty {
389 TyKind::Elementary(_)
390 | TyKind::StringLiteral(..)
391 | TyKind::IntLiteral(_)
392 | TyKind::Contract(_)
393 | TyKind::FnPtr(_)
394 | TyKind::Enum(_)
395 | TyKind::Module(_)
396 | TyKind::BuiltinModule(_) => {}
397
398 TyKind::Ref(ty, _)
399 | TyKind::DynArray(ty)
400 | TyKind::Array(ty, _)
401 | TyKind::Udvt(ty, _)
402 | TyKind::Type(ty)
403 | TyKind::Meta(ty) => self.add_ty(ty),
404
405 TyKind::Error(list, _) | TyKind::Event(list, _) | TyKind::Tuple(list) => {
406 self.add_tys(list)
407 }
408
409 TyKind::Mapping(k, v) => {
410 self.add_ty(k);
411 self.add_ty(v);
412 self.add(Self::HAS_MAPPING);
413 }
414
415 TyKind::Struct(id) => match gcx.struct_recursiveness(id) {
416 Recursiveness::None => self.add_tys(gcx.struct_field_types(id)),
417 Recursiveness::Recursive => {
418 self.add(Self::IS_RECURSIVE);
419 }
420 Recursiveness::Infinite(_guar) => {
421 self.add(Self::HAS_ERROR);
422 }
423 },
424
425 TyKind::Err(_) => self.add(Self::HAS_ERROR),
426 }
427 }
428
429 #[inline]
430 fn add(&mut self, other: Self) {
431 *self |= other;
432 }
433
434 #[inline]
435 fn add_ty(&mut self, ty: Ty<'_>) {
436 self.add(ty.flags);
437 }
438
439 #[inline]
440 fn add_tys(&mut self, tys: &[Ty<'_>]) {
441 for &ty in tys {
442 self.add_ty(ty);
443 }
444 }
445}