Skip to main content

solar_sema/ty/
ty.rs

1use super::{Gcx, Recursiveness, print::TySolcPrinter};
2use crate::{builtins::Builtin, hir};
3use alloy_primitives::U256;
4use solar_ast::{DataLocation, ElementaryType, StateMutability, TypeSize};
5use solar_data_structures::{Interned, fmt};
6use solar_interface::diagnostics::ErrorGuaranteed;
7use std::{borrow::Borrow, hash::Hash, ops::ControlFlow};
8
9/// An interned type.
10#[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
28#[derive(Debug, Clone, Copy, PartialEq, Eq)]
29pub enum TyConvertError {
30    /// Generic incompatibility (fallback).
31    Incompatible,
32
33    /// Contract doesn't inherit from target contract.
34    NonDerivedContract,
35
36    /// Invalid conversion between types.
37    InvalidConversion,
38
39    /// Attached function cannot be converted to an unattached function pointer.
40    AttachedFunction,
41
42    /// Literal is larger than the target type.
43    LiteralTooLarge,
44
45    /// Contract cannot be converted to address payable because it cannot receive ether.
46    ContractNotPayable,
47
48    /// Non-payable address cannot be converted to a contract that can receive ether.
49    AddressNotPayable,
50}
51
52#[derive(Debug, Clone, Copy, PartialEq, Eq)]
53pub(crate) enum SameSourceFileLevelUserTypeError {
54    NotUserDefined,
55    NotSameSourceFileLevel,
56}
57
58impl TyConvertError {
59    /// Returns the error message for this conversion error.
60    pub fn message<'gcx>(self, from: Ty<'gcx>, to: Ty<'gcx>, gcx: Gcx<'gcx>) -> String {
61        match self {
62            Self::NonDerivedContract => {
63                format!(
64                    "contract `{}` does not inherit from `{}`",
65                    from.display(gcx),
66                    to.display(gcx)
67                )
68            }
69            Self::InvalidConversion => {
70                format!("cannot convert `{}` to `{}`", from.display(gcx), to.display(gcx))
71            }
72            Self::Incompatible => {
73                format!("expected `{}`, found `{}`", to.display(gcx), from.display(gcx))
74            }
75            Self::AttachedFunction => {
76                "attached functions cannot be converted into unattached functions".to_string()
77            }
78            Self::LiteralTooLarge => {
79                format!(
80                    "literal `{}` is larger than the type `{}`",
81                    from.display(gcx),
82                    to.display(gcx)
83                )
84            }
85            Self::ContractNotPayable => {
86                format!(
87                    "cannot convert `{}` to `address payable` because it has no receive function or payable fallback",
88                    from.display(gcx)
89                )
90            }
91            Self::AddressNotPayable => {
92                format!(
93                    "cannot convert non-payable `address` to `{}` because it has a receive function or payable fallback",
94                    to.display(gcx)
95                )
96            }
97        }
98    }
99}
100
101impl<'gcx> Ty<'gcx> {
102    pub fn new(gcx: Gcx<'gcx>, kind: TyKind<'gcx>) -> Self {
103        gcx.mk_ty(kind)
104    }
105
106    /// Displays the type for human-readable diagnostics.
107    pub fn display(self, gcx: Gcx<'gcx>) -> impl fmt::Display + use<'gcx> {
108        fmt::from_fn(move |f| TySolcPrinter::new(gcx, f).data_locations(true).print(self))
109    }
110
111    #[doc(alias = "with_location")]
112    pub fn with_loc(self, gcx: Gcx<'gcx>, loc: DataLocation) -> Self {
113        let mut ty = self;
114        if let TyKind::Ref(inner, l2) = self.kind {
115            if l2 == loc {
116                return self;
117            }
118            ty = inner;
119        }
120        Self::new(gcx, TyKind::Ref(ty, loc))
121    }
122
123    #[doc(alias = "with_location_if_reference")]
124    pub fn with_loc_if_ref(self, gcx: Gcx<'gcx>, loc: DataLocation) -> Self {
125        if self.peel_refs().is_reference_type() {
126            return self.with_loc(gcx, loc);
127        }
128        self
129    }
130
131    pub fn with_loc_if_ref_opt(self, gcx: Gcx<'gcx>, loc: Option<DataLocation>) -> Self {
132        if let Some(loc) = loc {
133            return self.with_loc_if_ref(gcx, loc);
134        }
135        self
136    }
137
138    /// Returns the location of the type if it is a reference.
139    #[doc(alias = "location")]
140    pub fn loc(self) -> Option<DataLocation> {
141        match self.kind {
142            TyKind::Ref(_, loc) => Some(loc),
143            _ => None,
144        }
145    }
146
147    /// Peels `Ref` layers from the type, returning the inner type.
148    pub fn peel_refs(mut self) -> Self {
149        // There shouldn't be any double references so we can avoid using a loop here.
150        if let TyKind::Ref(inner, _) = self.kind {
151            self = inner;
152        }
153        debug_assert!(!self.is_ref(), "double reference type found");
154        self
155    }
156
157    pub fn as_externally_callable_function(self, in_library: bool, gcx: Gcx<'gcx>) -> Self {
158        let TyKind::Fn(f) = self.kind else { return self };
159        let kind = if in_library { TyFnKind::DelegateCall } else { f.kind };
160        let is_calldata = |param: &Ty<'_>| param.is_ref_at(DataLocation::Calldata);
161        let parameters = self.parameters().unwrap_or_default();
162        let returns = self.returns().unwrap_or_default();
163        let any_parameter = parameters.iter().any(is_calldata);
164        let any_return = returns.iter().any(is_calldata);
165        if !any_parameter && !any_return && f.kind == kind {
166            return self;
167        }
168        let parameters = if any_parameter {
169            gcx.mk_ty_iter(parameters.iter().map(|param| {
170                if is_calldata(param) { param.with_loc(gcx, DataLocation::Memory) } else { *param }
171            }))
172        } else {
173            parameters
174        };
175        let returns = if any_return {
176            gcx.mk_ty_iter(returns.iter().map(|ret| {
177                if is_calldata(ret) { ret.with_loc(gcx, DataLocation::Memory) } else { *ret }
178            }))
179        } else {
180            returns
181        };
182        gcx.mk_ty_fn(TyFn {
183            kind,
184            parameters,
185            returns,
186            state_mutability: f.state_mutability,
187            function_id: f.function_id,
188            attached: false,
189        })
190    }
191
192    pub fn as_attached_function(self, gcx: Gcx<'gcx>) -> Self {
193        let TyKind::Fn(f) = self.kind else { return self };
194        if f.attached {
195            return self;
196        }
197        gcx.mk_ty_fn(TyFn {
198            kind: f.kind,
199            parameters: f.parameters,
200            returns: f.returns,
201            state_mutability: f.state_mutability,
202            function_id: f.function_id,
203            attached: true,
204        })
205    }
206
207    pub fn make_ref(self, gcx: Gcx<'gcx>, loc: DataLocation) -> Self {
208        if self.is_ref_at(loc) {
209            return self;
210        }
211        Self::new(gcx, TyKind::Ref(self, loc))
212    }
213
214    pub fn make_type_type(self, gcx: Gcx<'gcx>) -> Self {
215        if let TyKind::Type(_) = self.kind {
216            return self;
217        }
218        Self::new(gcx, TyKind::Type(self))
219    }
220
221    pub fn make_meta(self, gcx: Gcx<'gcx>) -> Self {
222        if let TyKind::Meta(_) = self.kind {
223            return self;
224        }
225        Self::new(gcx, TyKind::Meta(self))
226    }
227
228    /// Returns `true` if the type is a reference.
229    #[inline]
230    pub fn is_ref(self) -> bool {
231        matches!(self.kind, TyKind::Ref(..))
232    }
233
234    /// Returns `true` if the type is a reference to the given location.
235    #[inline]
236    #[doc(alias = "is_reference_with_location")]
237    pub fn is_ref_at(self, loc: DataLocation) -> bool {
238        matches!(self.kind, TyKind::Ref(_, l) if l == loc)
239    }
240
241    /// Returns `true` if the type is a reference to the given location.
242    pub fn data_stored_in(self, loc: DataLocation) -> bool {
243        match self.kind {
244            TyKind::Ref(_, l) => l == loc,
245            TyKind::Mapping(..) => loc == DataLocation::Storage,
246            _ => false,
247        }
248    }
249
250    /// Returns `true` if the type is a value type.
251    ///
252    /// Reference: <https://docs.soliditylang.org/en/latest/types.html#value-types>
253    #[inline]
254    pub fn is_value_type(self) -> bool {
255        match self.kind {
256            TyKind::Elementary(t) => t.is_value_type(),
257            TyKind::Contract(_)
258            | TyKind::Super(_)
259            | TyKind::Fn(_)
260            | TyKind::Enum(_)
261            | TyKind::Udvt(..) => true,
262            _ => false,
263        }
264    }
265
266    /// Returns `true` if the type is a reference type.
267    #[inline]
268    pub fn is_reference_type(self) -> bool {
269        match self.kind {
270            TyKind::Elementary(t) => t.is_reference_type(),
271            TyKind::Struct(_)
272            | TyKind::Array(..)
273            | TyKind::DynArray(_)
274            | TyKind::Slice(_)
275            | TyKind::Mapping(..) => true,
276            _ => false,
277        }
278    }
279
280    /// Returns `true` if the type is recursive.
281    pub fn is_recursive(self, gcx: Gcx<'gcx>) -> bool {
282        self.visit_with_structs(gcx, &mut |ty| match ty.kind {
283            TyKind::Struct(id)
284                if matches!(gcx.struct_recursiveness(id), Recursiveness::Recursive) =>
285            {
286                ControlFlow::Break(())
287            }
288            _ => ControlFlow::Continue(()),
289        })
290        .is_break()
291    }
292
293    /// Returns `true` if this type contains a mapping.
294    pub fn has_mapping(self, gcx: Gcx<'gcx>) -> bool {
295        self.visit_with_structs(gcx, &mut |ty| {
296            if matches!(ty.kind, TyKind::Mapping(..)) {
297                ControlFlow::Break(())
298            } else {
299                ControlFlow::Continue(())
300            }
301        })
302        .is_break()
303    }
304
305    /// Returns `true` if this type contains a library contract type.
306    pub fn contains_library(self, gcx: Gcx<'gcx>) -> bool {
307        self.visit_with_structs(gcx, &mut |ty| match ty.kind {
308            TyKind::Contract(id) if gcx.hir.contract(id).kind.is_library() => {
309                ControlFlow::Break(())
310            }
311            _ => ControlFlow::Continue(()),
312        })
313        .is_break()
314    }
315
316    /// Returns `true` if this type contains a non-public (internal/private) function type.
317    #[inline]
318    pub fn has_internal_function(self) -> bool {
319        self.flags.contains(TyFlags::HAS_INTERNAL_FN)
320    }
321
322    /// Returns `Err(guar)` if this type contains an error.
323    #[inline]
324    pub fn error_reported(self) -> Result<(), ErrorGuaranteed> {
325        if self.references_error() { Err(ErrorGuaranteed::new_unchecked()) } else { Ok(()) }
326    }
327
328    /// Returns `true` if this type contains an error.
329    #[inline]
330    pub fn references_error(self) -> bool {
331        self.flags.contains(TyFlags::HAS_ERROR)
332    }
333
334    /// Returns `true` if this type can be part of an externally callable function.
335    #[inline]
336    pub fn can_be_exported(self, gcx: Gcx<'gcx>) -> bool {
337        !(self.is_recursive(gcx)
338            || self.has_mapping(gcx)
339            || self.has_internal_function()
340            || self.references_error())
341    }
342
343    /// Returns the parameter types of the type.
344    #[inline]
345    pub fn parameters(self) -> Option<&'gcx [Self]> {
346        Some(match self.kind {
347            TyKind::Fn(f) => f.parameters,
348            TyKind::Event(tys, _) | TyKind::Error(tys, _) => tys,
349            _ => return None,
350        })
351    }
352
353    /// Returns the return types of the type.
354    #[inline]
355    pub fn returns(self) -> Option<&'gcx [Self]> {
356        Some(match self.kind {
357            TyKind::Fn(f) => f.returns,
358            _ => return None,
359        })
360    }
361
362    /// Returns the state mutability of the type.
363    #[inline]
364    pub fn state_mutability(self) -> Option<StateMutability> {
365        match self.kind {
366            TyKind::Fn(f) => Some(f.state_mutability),
367            _ => None,
368        }
369    }
370
371    /// Returns the function ID if this is a function type with a specific function.
372    #[inline]
373    pub fn function_id(self) -> Option<hir::FunctionId> {
374        match self.kind {
375            TyKind::Fn(f) => f.function_id,
376            _ => None,
377        }
378    }
379
380    /// Returns the HIR item ID associated with this type, if any.
381    #[inline]
382    pub fn item_id(self) -> Option<hir::ItemId> {
383        Some(match self.kind {
384            TyKind::Fn(f) => f.function_id?.into(),
385            TyKind::Contract(id) => id.into(),
386            TyKind::Struct(id) => id.into(),
387            TyKind::Enum(id) => id.into(),
388            TyKind::Error(_, id) => id.into(),
389            TyKind::Event(_, id) => id.into(),
390            TyKind::Udvt(_, id) => id.into(),
391            _ => return None,
392        })
393    }
394
395    /// Returns the source ID where this type's HIR item is defined.
396    #[inline]
397    pub fn item_source(self, gcx: Gcx<'gcx>) -> Option<hir::SourceId> {
398        self.peel_refs().item_id().map(|id| gcx.hir.item(id).source())
399    }
400
401    pub(crate) fn same_source_file_level_user_type(
402        self,
403        gcx: Gcx<'gcx>,
404        source: hir::SourceId,
405    ) -> Result<(), SameSourceFileLevelUserTypeError> {
406        let Some(id @ (hir::ItemId::Struct(_) | hir::ItemId::Enum(_) | hir::ItemId::Udvt(_))) =
407            self.peel_refs().item_id()
408        else {
409            return Err(SameSourceFileLevelUserTypeError::NotUserDefined);
410        };
411        let item = gcx.hir.item(id);
412        if item.source() == source && item.contract().is_none() {
413            Ok(())
414        } else {
415            Err(SameSourceFileLevelUserTypeError::NotSameSourceFileLevel)
416        }
417    }
418
419    /// Visits the type and its subtypes.
420    pub fn visit<T>(self, f: &mut impl FnMut(Self) -> ControlFlow<T>) -> ControlFlow<T> {
421        f(self)?;
422        match self.kind {
423            TyKind::Elementary(_)
424            | TyKind::StringLiteral(..)
425            | TyKind::IntLiteral(..)
426            | TyKind::Contract(_)
427            | TyKind::Super(_)
428            | TyKind::Fn(_)
429            | TyKind::Enum(_)
430            | TyKind::Module(_)
431            | TyKind::BuiltinModule(_)
432            | TyKind::Variadic
433            | TyKind::Struct(_)
434            | TyKind::Err(_) => ControlFlow::Continue(()),
435
436            TyKind::Ref(ty, _)
437            | TyKind::DynArray(ty)
438            | TyKind::Array(ty, _)
439            | TyKind::Slice(ty)
440            | TyKind::Udvt(ty, _)
441            | TyKind::Type(ty)
442            | TyKind::Meta(ty) => ty.visit(f),
443
444            TyKind::Error(list, _) | TyKind::Event(list, _) | TyKind::Tuple(list) => {
445                for ty in list {
446                    ty.visit(f)?;
447                }
448                ControlFlow::Continue(())
449            }
450
451            TyKind::Mapping(k, v) => {
452                k.visit(f)?;
453                v.visit(f)
454            }
455        }
456    }
457
458    /// Visits the type, its subtypes, and non-recursive struct fields.
459    fn visit_with_structs<T>(
460        self,
461        gcx: Gcx<'gcx>,
462        f: &mut impl FnMut(Self) -> ControlFlow<T>,
463    ) -> ControlFlow<T> {
464        f(self)?;
465        match self.kind {
466            TyKind::Struct(id) => {
467                if let Recursiveness::None = gcx.struct_recursiveness(id) {
468                    for &ty in gcx.struct_field_types(id) {
469                        ty.visit_with_structs(gcx, f)?;
470                    }
471                }
472                ControlFlow::Continue(())
473            }
474            TyKind::Elementary(_)
475            | TyKind::StringLiteral(..)
476            | TyKind::IntLiteral(..)
477            | TyKind::Contract(_)
478            | TyKind::Super(_)
479            | TyKind::Fn(_)
480            | TyKind::Enum(_)
481            | TyKind::Module(_)
482            | TyKind::BuiltinModule(_)
483            | TyKind::Variadic
484            | TyKind::Err(_) => ControlFlow::Continue(()),
485
486            TyKind::Ref(ty, _)
487            | TyKind::DynArray(ty)
488            | TyKind::Array(ty, _)
489            | TyKind::Slice(ty)
490            | TyKind::Udvt(ty, _)
491            | TyKind::Type(ty)
492            | TyKind::Meta(ty) => ty.visit_with_structs(gcx, f),
493
494            TyKind::Error(list, _) | TyKind::Event(list, _) | TyKind::Tuple(list) => {
495                for ty in list {
496                    ty.visit_with_structs(gcx, f)?;
497                }
498                ControlFlow::Continue(())
499            }
500
501            TyKind::Mapping(k, v) => {
502                k.visit_with_structs(gcx, f)?;
503                v.visit_with_structs(gcx, f)
504            }
505        }
506    }
507
508    /// Returns `true` if the type is an array.
509    #[inline]
510    pub fn is_array(self) -> bool {
511        matches!(self.kind, TyKind::Array(..) | TyKind::DynArray(..))
512    }
513
514    /// Returns `true` if the type is an array-like type.
515    ///
516    /// This is either an array or bytes/string.
517    #[inline]
518    pub fn is_array_like(&self) -> bool {
519        self.is_array()
520            || matches!(
521                self.kind,
522                TyKind::Elementary(ElementaryType::Bytes | ElementaryType::String)
523            )
524    }
525
526    /// Returns `true` if the type is sliceable.
527    ///
528    /// This is either an array, bytes, string, or slice.
529    #[inline]
530    pub fn is_sliceable(self) -> bool {
531        let inner = self.peel_refs();
532        inner.is_array_like() || matches!(inner.kind, TyKind::Slice(..))
533    }
534
535    /// Returns `true` if the type is dynamically sized.
536    pub fn is_dynamically_sized(self) -> bool {
537        matches!(
538            self.kind,
539            TyKind::Elementary(ElementaryType::Bytes | ElementaryType::String)
540                | TyKind::DynArray(..)
541                | TyKind::Slice(..)
542        )
543    }
544
545    pub fn is_dynamically_encoded(self, gcx: Gcx<'gcx>) -> bool {
546        match self.kind {
547            TyKind::Struct(id) => {
548                self.is_recursive(gcx)
549                    || gcx.struct_field_types(id).iter().any(|ty| ty.is_dynamically_encoded(gcx))
550            }
551            TyKind::Array(element, _) => element.is_dynamically_encoded(gcx),
552            _ => self.is_dynamically_sized(),
553        }
554    }
555
556    /// Returns `true` if the type is a fixed-size byte array.
557    pub fn is_fixed_bytes(self) -> bool {
558        matches!(self.kind, TyKind::Elementary(ElementaryType::FixedBytes(_)))
559    }
560
561    /// Returns `true` if the type is an integer, including literals.
562    pub fn is_integer(self) -> bool {
563        matches!(
564            self.kind,
565            TyKind::Elementary(hir::ElementaryType::Int(_) | hir::ElementaryType::UInt(_))
566                | TyKind::IntLiteral(..)
567        )
568    }
569
570    /// Returns `true` if the type is a signed integer, including negative literals.
571    pub fn is_signed(self) -> bool {
572        matches!(
573            self.kind,
574            TyKind::Elementary(ElementaryType::Int(_)) | TyKind::IntLiteral(true, ..)
575        )
576    }
577
578    /// Returns `true` if the type is a tuple.
579    pub fn is_tuple(self) -> bool {
580        matches!(self.kind, TyKind::Tuple(..))
581    }
582
583    /// Returns `true` if the type is the unit type `()`.
584    pub fn is_unit(self) -> bool {
585        matches!(self.kind, TyKind::Tuple([]))
586    }
587
588    /// Returns `true` if the type can be used for variables.
589    pub fn nameable(self) -> bool {
590        matches!(
591            self.kind,
592            TyKind::Elementary(_)
593                | TyKind::Array(..)
594                | TyKind::DynArray(_)
595                | TyKind::Contract(_)
596                | TyKind::Struct(_)
597                | TyKind::Enum(_)
598                | TyKind::Udvt(..)
599                | TyKind::Mapping(..)
600        )
601    }
602
603    /// Returns the common type between the two types.
604    pub fn common_type(self, b: Self, gcx: Gcx<'gcx>) -> Option<Self> {
605        let a = self;
606        if let Some(a) = a.mobile(gcx)
607            && b.convert_implicit_to(a, gcx)
608        {
609            return Some(a);
610        }
611        if let Some(b) = b.mobile(gcx)
612            && a.convert_implicit_to(b, gcx)
613        {
614            return Some(b);
615        }
616        None
617    }
618
619    /// Returns the base type, if any.
620    pub fn base_type(self, gcx: Gcx<'gcx>) -> Option<Self> {
621        let loc = self.loc();
622        match self.peel_refs().kind {
623            TyKind::Array(base, _) | TyKind::DynArray(base) => {
624                Some(base.with_loc_if_ref_opt(gcx, loc))
625            }
626            TyKind::Slice(arr) => arr.with_loc_if_ref_opt(gcx, loc).base_type(gcx),
627            TyKind::Elementary(ElementaryType::Bytes | ElementaryType::String) => {
628                Some(gcx.types.fixed_bytes(1))
629            }
630            _ => None,
631        }
632    }
633
634    /// Returns `true` if the type is implicitly convertible to the given type.
635    ///
636    /// Prefer using [`Ty::try_convert_implicit_to`] if you need to handle the error case.
637    #[inline]
638    #[doc(alias = "is_implicitly_convertible_to")]
639    #[must_use]
640    pub fn convert_implicit_to(self, other: Self, gcx: Gcx<'gcx>) -> bool {
641        self.try_convert_implicit_to(other, gcx).is_ok()
642    }
643
644    /// Checks if the type is implicitly convertible to the given type.
645    ///
646    /// See: <https://docs.soliditylang.org/en/latest/types.html#implicit-conversions>
647    pub fn try_convert_implicit_to(
648        self,
649        other: Self,
650        gcx: Gcx<'gcx>,
651    ) -> Result<(), TyConvertError> {
652        use ElementaryType::*;
653        use TyKind::*;
654
655        if self == other || self.references_error() || other.references_error() {
656            return Ok(());
657        }
658
659        match (self.kind, other.kind) {
660            // address payable -> address.
661            (Elementary(Address(true)), Elementary(Address(false))) => Ok(()),
662
663            // Reference type location coercion rules.
664            // See: <https://docs.soliditylang.org/en/latest/types.html#data-location-and-assignment-behaviour>
665            (Ref(from_inner, from_loc), Ref(to_inner, to_loc)) => {
666                match (from_loc, to_loc) {
667                    // Same location: allowed if base types match.
668                    (DataLocation::Memory, DataLocation::Memory)
669                    | (DataLocation::Calldata, DataLocation::Calldata) => {
670                        from_inner.try_convert_implicit_to(to_inner, gcx)
671                    }
672
673                    // storage -> storage: allowed (reference assignment).
674                    (DataLocation::Storage, DataLocation::Storage) => {
675                        from_inner.try_convert_implicit_to(to_inner, gcx)
676                    }
677
678                    // calldata/storage -> memory: allowed (copy semantics).
679                    (DataLocation::Calldata, DataLocation::Memory)
680                    | (DataLocation::Storage, DataLocation::Memory) => {
681                        from_inner.try_convert_implicit_to(to_inner, gcx)
682                    }
683
684                    // memory/calldata -> storage: allowed (copy semantics).
685                    (DataLocation::Memory, DataLocation::Storage)
686                    | (DataLocation::Calldata, DataLocation::Storage) => {
687                        from_inner.try_convert_implicit_to(to_inner, gcx)
688                    }
689
690                    // storage -> calldata: never allowed.
691                    // memory -> calldata: never allowed.
692                    _ => Result::Err(TyConvertError::Incompatible),
693                }
694            }
695
696            // A reference type (in any data location) is convertible to its
697            // location-less base type. This arises for mapping keys, whose key
698            // type carries no location: e.g. indexing `mapping(string => ...)`
699            // with a `string memory`/`string calldata` value, or the implicit
700            // public getter whose key parameter is `string calldata`.
701            (Ref(from_inner, _), to) if !matches!(to, Ref(..)) => {
702                from_inner.try_convert_implicit_to(other, gcx)
703            }
704
705            // Array slices are implicitly convertible to arrays of their underlying element type.
706            // Note: conversion to storage pointers is NOT allowed.
707            // See: <https://docs.soliditylang.org/en/latest/types.html#array-slices>
708            // `T[] loc slice` -> `T[] loc`
709            (Slice(underlying), _) if underlying == other => Ok(()),
710            // `T[] loc slice` -> `T[] memory`
711            (Slice(underlying), Ref(other_inner, DataLocation::Memory)) => {
712                if let Ref(self_inner, _) = underlying.kind
713                    && self_inner == other_inner
714                {
715                    Ok(())
716                } else {
717                    Result::Err(TyConvertError::Incompatible)
718                }
719            }
720
721            // Contract -> Base contract (inheritance check)
722            (Contract(self_contract_id), Contract(other_contract_id)) => {
723                let self_contract = gcx.hir.contract(self_contract_id);
724                if self_contract.linearized_bases.contains(&other_contract_id) {
725                    Ok(())
726                } else {
727                    Result::Err(TyConvertError::NonDerivedContract)
728                }
729            }
730            (Super(_), _) | (_, Super(_)) => Result::Err(TyConvertError::Incompatible),
731
732            // byte literal -> bytesN/bytes
733            // See: <https://docs.soliditylang.org/en/latest/types.html#index-34>
734            (StringLiteral(_, _), Elementary(Bytes)) => Ok(()),
735            (StringLiteral(_, _), Ref(inner, DataLocation::Memory))
736                if matches!(inner.kind, Elementary(Bytes)) =>
737            {
738                Ok(())
739            }
740            (StringLiteral(true, _), Elementary(String)) => Ok(()),
741            (StringLiteral(true, _), Ref(inner, DataLocation::Memory))
742                if matches!(inner.kind, Elementary(String)) =>
743            {
744                Ok(())
745            }
746            (StringLiteral(_, size_from), Elementary(FixedBytes(size_to))) => {
747                if size_from.bytes() <= size_to.bytes() {
748                    Ok(())
749                } else {
750                    Result::Err(TyConvertError::LiteralTooLarge)
751                }
752            }
753            (IntLiteral(_, _, Some(TypeSize::ZERO)), Elementary(FixedBytes(_))) => Ok(()),
754            (IntLiteral(false, _, Some(size_from)), Elementary(FixedBytes(size_to)))
755                if size_from == size_to =>
756            {
757                Ok(())
758            }
759
760            // Integer literals can coerce to typed integers if they fit.
761            // Non-negative literals can coerce to both uint and int types.
762            (IntLiteral(neg, size, _), Elementary(UInt(target_size))) => {
763                // Unsigned: reject negative, check size fits
764                if neg {
765                    Result::Err(TyConvertError::Incompatible)
766                } else if size.bits() <= target_size.bits() {
767                    Ok(())
768                } else {
769                    Result::Err(TyConvertError::Incompatible)
770                }
771            }
772            (IntLiteral(neg, size, _), Elementary(Int(target_size))) => {
773                // Signed: non-negative values need strict inequality since they use the
774                // positive range [0, 2^(N-1)-1]. Negative values use <= since negative
775                // int_literal[N] can fit in int(N) (e.g., -128 needs 8 bits, fits in int8).
776                if neg {
777                    if size.bits() <= target_size.bits() {
778                        Ok(())
779                    } else {
780                        Result::Err(TyConvertError::Incompatible)
781                    }
782                } else if size.bits() < target_size.bits() {
783                    Ok(())
784                } else {
785                    Result::Err(TyConvertError::Incompatible)
786                }
787            }
788
789            // Integer width conversions: smaller int -> larger int (same signedness).
790            // See: <https://docs.soliditylang.org/en/latest/types.html#implicit-conversions>
791            (Elementary(UInt(from_size)), Elementary(UInt(to_size))) => {
792                if from_size.bits() <= to_size.bits() {
793                    Ok(())
794                } else {
795                    Result::Err(TyConvertError::Incompatible)
796                }
797            }
798            (Elementary(Int(from_size)), Elementary(Int(to_size))) => {
799                if from_size.bits() <= to_size.bits() {
800                    Ok(())
801                } else {
802                    Result::Err(TyConvertError::Incompatible)
803                }
804            }
805
806            // FixedBytes size conversions: smaller bytesN -> larger bytesN (right-padded with
807            // zeros). See: <https://docs.soliditylang.org/en/latest/types.html#implicit-conversions>
808            (Elementary(FixedBytes(from_size)), Elementary(FixedBytes(to_size))) => {
809                if from_size.bytes() <= to_size.bytes() {
810                    Ok(())
811                } else {
812                    Result::Err(TyConvertError::Incompatible)
813                }
814            }
815
816            // Function pointer conversions.
817            // See: <https://docs.soliditylang.org/en/latest/types.html#function-types>
818            (Fn(from_fn), Fn(to_fn)) => {
819                if from_fn.attached != to_fn.attached {
820                    if from_fn.attached {
821                        return Result::Err(TyConvertError::AttachedFunction);
822                    }
823                    return Result::Err(TyConvertError::Incompatible);
824                }
825                if from_fn.kind != to_fn.kind && !(from_fn.is_internal() && to_fn.is_internal()) {
826                    return Result::Err(TyConvertError::Incompatible);
827                }
828                // Parameter and return types must match exactly (no implicit conversion).
829                if from_fn.parameters != to_fn.parameters || from_fn.returns != to_fn.returns {
830                    return Result::Err(TyConvertError::Incompatible);
831                }
832
833                // Declaration function values name a concrete declaration accessed through a
834                // contract type and are only compatible with that same declaration.
835                if from_fn.is_declaration() && from_fn.function_id != to_fn.function_id {
836                    return Result::Err(TyConvertError::Incompatible);
837                }
838
839                // State mutability compatibility:
840                // - pure can convert to view or non-payable (more restrictive -> less restrictive)
841                // - view can convert to non-payable
842                // - payable can convert to non-payable (you can pay 0)
843                // - non-payable cannot convert to payable
844                use StateMutability::*;
845                let mutability_ok = match (from_fn.state_mutability, to_fn.state_mutability) {
846                    (a, b) if a == b => true,
847                    // pure is the most restrictive, can convert to anything except payable.
848                    (Pure, View) | (Pure, NonPayable) => true,
849                    // view can convert to non-payable.
850                    (View, NonPayable) => true,
851                    // payable can convert to non-payable.
852                    (Payable, NonPayable) => true,
853                    _ => false,
854                };
855                if mutability_ok { Ok(()) } else { Result::Err(TyConvertError::Incompatible) }
856            }
857
858            // Tuple conversions: element-wise implicit conversion with same length.
859            // See: <https://docs.soliditylang.org/en/latest/types.html#tuple-types>
860            (Tuple(from_tys), Tuple(to_tys)) => {
861                if from_tys.len() != to_tys.len() {
862                    return Result::Err(TyConvertError::Incompatible);
863                }
864                for (&from_ty, &to_ty) in std::iter::zip(from_tys, to_tys) {
865                    from_ty.try_convert_implicit_to(to_ty, gcx)?;
866                }
867                Ok(())
868            }
869
870            _ => Result::Err(TyConvertError::Incompatible),
871        }
872    }
873
874    /// Returns `true` if the type is explicitly convertible to the given type.
875    ///
876    /// Prefer using [`Ty::try_convert_explicit_to`] if you need to handle the error case.
877    #[inline]
878    #[doc(alias = "is_explicitly_convertible_to")]
879    #[must_use]
880    pub fn convert_explicit_to(self, other: Self, gcx: Gcx<'gcx>) -> bool {
881        self.try_convert_explicit_to(other, gcx).is_ok()
882    }
883
884    /// Checks if the type is explicitly convertible to the given type.
885    ///
886    /// Explicit conversions are a superset of implicit conversions.
887    ///
888    /// See: <https://docs.soliditylang.org/en/latest/types.html#explicit-conversions>
889    fn can_convert_explicit_to(self, other: Self, gcx: Gcx<'gcx>) -> Result<(), TyConvertError> {
890        use ElementaryType::*;
891        use TyKind::*;
892
893        macro_rules! unreachable {
894            () => {
895                gcx.dcx()
896                    .bug(format!("unreachable explicit conversion from `{self:?}` to `{other:?}`"))
897                    .emit()
898            };
899        }
900
901        if self.try_convert_implicit_to(other, gcx).is_ok() {
902            return Ok(());
903        }
904        match (self.kind, other.kind) {
905            // Enum <-> all integer types.
906            (Enum(_), _) if other.is_integer() => Ok(()),
907            (_, Enum(_)) if self.is_integer() => Ok(()),
908
909            // bytes/FixedBytes to FixedBytes: always allowed (any size).
910            // Smaller to larger right-pads with zeros, larger to smaller truncates on the right.
911            (Elementary(FixedBytes(_)), Elementary(FixedBytes(_))) => Ok(()),
912            (Ref(ty, _), Elementary(FixedBytes(_))) => {
913                if matches!(ty.kind, Elementary(Bytes)) {
914                    Ok(())
915                } else {
916                    Result::Err(TyConvertError::InvalidConversion)
917                }
918            }
919
920            // A calldata `bytes` slice (`data[i:j]`) converts like `bytes`: to
921            // fixed-bytes, `bytes`, or `string`.
922            (Slice(underlying), other_kind)
923                if matches!(underlying.peel_refs().kind, Elementary(Bytes)) =>
924            {
925                match other_kind {
926                    Elementary(FixedBytes(_) | Bytes | String) => Ok(()),
927                    Ref(inner, _) if matches!(inner.kind, Elementary(Bytes | String)) => Ok(()),
928                    _ => Result::Err(TyConvertError::InvalidConversion),
929                }
930            }
931
932            (Ref(from_inner, _), _) if from_inner == other && other.is_reference_type() => Ok(()),
933
934            // FixedBytes <-> UInt: same size only (signed integers not allowed).
935            (Elementary(FixedBytes(size_from)), Elementary(UInt(size_to)))
936            | (Elementary(UInt(size_from)), Elementary(FixedBytes(size_to)))
937                if size_from == size_to =>
938            {
939                Ok(())
940            }
941
942            // address <-> bytes20.
943            (Elementary(Address(false)), Elementary(FixedBytes(s))) if s.bytes() == 20 => Ok(()),
944            (Elementary(FixedBytes(s)), Elementary(Address(false))) if s.bytes() == 20 => Ok(()),
945
946            // address <-> uint160.
947            (Elementary(Address(false)), Elementary(UInt(s))) if s.bits() == 160 => Ok(()),
948            (Elementary(UInt(s)), Elementary(Address(false))) if s.bits() == 160 => Ok(()),
949
950            // address -> address payable.
951            (Elementary(Address(false)), Elementary(Address(true))) => Ok(()),
952
953            // Integer literals -> address.
954            (IntLiteral(_, _, Some(TypeSize::ZERO)), Elementary(Address(_))) => Ok(()),
955            (IntLiteral(false, size, _), Elementary(Address(false))) if size.bits() <= 160 => {
956                Ok(())
957            }
958
959            // IntLiteral -> IntLiteral: explicit conversion to a literal type shouldn't be
960            // possible.
961            (IntLiteral(..), IntLiteral(..)) => unreachable!(),
962
963            // Int <-> Int: any size allowed (only width changes, sign stays same).
964            (Elementary(Int(_)), Elementary(Int(_))) => Ok(()),
965
966            // UInt <-> UInt: any size allowed (only width changes, sign stays same).
967            (Elementary(UInt(_)), Elementary(UInt(_))) => Ok(()),
968
969            // Int <-> UInt: same size only (prevents multi-aspect conversion).
970            // This enforces the Solidity 0.8.0+ restriction: cannot change both sign and width.
971            (Elementary(Int(size_from)), Elementary(UInt(size_to)))
972            | (Elementary(UInt(size_from)), Elementary(Int(size_to)))
973                if size_from == size_to =>
974            {
975                Ok(())
976            }
977
978            // Contract -> Base contract (inheritance check)
979            (Contract(self_contract_id), Contract(other_contract_id)) => {
980                let self_contract = gcx.hir.contract(self_contract_id);
981                if self_contract.linearized_bases.contains(&other_contract_id) {
982                    Ok(())
983                } else {
984                    Result::Err(TyConvertError::NonDerivedContract)
985                }
986            }
987            (Super(_), _) | (_, Super(_)) => Result::Err(TyConvertError::InvalidConversion),
988
989            // Contract -> address (always allowed)
990            (Contract(_), Elementary(Address(false))) => Ok(()),
991            // Library type name -> address.
992            (Type(library), Elementary(Address(false))) if matches!(library.kind, Contract(id) if gcx.hir.contract(id).kind.is_library()) => {
993                Ok(())
994            }
995
996            // Contract -> address payable (only if contract can receive ether)
997            (Contract(contract_id), Elementary(Address(true))) => {
998                let contract = gcx.hir.contract(contract_id);
999
1000                if hir::can_receive_ether(contract, gcx) {
1001                    Ok(())
1002                } else {
1003                    Result::Err(TyConvertError::ContractNotPayable)
1004                }
1005            }
1006
1007            // Address payable -> Contract (always allowed)
1008            (Elementary(Address(true)), Contract(_)) => Ok(()),
1009
1010            // Address (non-payable) -> Contract (only if contract cannot receive ether)
1011            (Elementary(Address(false)), Contract(contract_id)) => {
1012                let contract = gcx.hir.contract(contract_id);
1013
1014                if hir::can_receive_ether(contract, gcx) {
1015                    Result::Err(TyConvertError::AddressNotPayable)
1016                } else {
1017                    Ok(())
1018                }
1019            }
1020
1021            // bytes <-> string (explicit conversion).
1022            // See: https://docs.soliditylang.org/en/latest/types.html#explicit-conversions
1023            // When target is Ref with location, locations must match.
1024            (Ref(from_inner, from_loc), Ref(to_inner, to_loc)) if from_loc == to_loc => {
1025                match (from_inner.kind, to_inner.kind) {
1026                    (Elementary(Bytes), Elementary(String)) => Ok(()),
1027                    (Elementary(String), Elementary(Bytes)) => Ok(()),
1028                    _ => Result::Err(TyConvertError::InvalidConversion),
1029                }
1030            }
1031            // When target is unlocated (e.g., `bytes(s)` where s is `string memory`),
1032            // the location is inherited from the source.
1033            (Ref(from_inner, _), Elementary(Bytes))
1034                if matches!(from_inner.kind, Elementary(String)) =>
1035            {
1036                Ok(())
1037            }
1038            (Ref(from_inner, _), Elementary(String))
1039                if matches!(from_inner.kind, Elementary(Bytes)) =>
1040            {
1041                Ok(())
1042            }
1043
1044            _ => Result::Err(TyConvertError::InvalidConversion),
1045        }
1046    }
1047
1048    /// Performs an explicit type conversion, returning the result type.
1049    ///
1050    /// For most conversions this is `other`, but for bytes <-> string with unlocated target,
1051    /// the result type inherits the source's data location.
1052    ///
1053    /// See: <https://docs.soliditylang.org/en/latest/types.html#explicit-conversions>
1054    pub fn try_convert_explicit_to(
1055        self,
1056        other: Self,
1057        gcx: Gcx<'gcx>,
1058    ) -> Result<Self, TyConvertError> {
1059        self.can_convert_explicit_to(other, gcx)?;
1060
1061        // Handle special cases where unlocated reference targets get a data location.
1062        use ElementaryType::*;
1063        use TyKind::*;
1064        Ok(match (self.kind, other.kind) {
1065            (StringLiteral(..), Elementary(Bytes)) => gcx.types.bytes_ref.memory,
1066            (StringLiteral(true, _), Elementary(String)) => gcx.types.string_ref.memory,
1067            (Ref(from_inner, loc), _) if from_inner == other && other.is_reference_type() => {
1068                other.with_loc(gcx, loc)
1069            }
1070            (Ref(from_inner, loc), Elementary(Bytes))
1071                if matches!(from_inner.kind, Elementary(String)) =>
1072            {
1073                gcx.types.bytes.with_loc(gcx, loc)
1074            }
1075            (Ref(from_inner, loc), Elementary(String))
1076                if matches!(from_inner.kind, Elementary(Bytes)) =>
1077            {
1078                gcx.types.string.with_loc(gcx, loc)
1079            }
1080            // A calldata `bytes` slice converted to `bytes`/`string` stays in
1081            // calldata (it is a view), so the result is `bytes`/`string calldata`.
1082            (Slice(_), Elementary(Bytes)) => gcx.types.bytes.with_loc(gcx, DataLocation::Calldata),
1083            (Slice(_), Elementary(String)) => {
1084                gcx.types.string.with_loc(gcx, DataLocation::Calldata)
1085            }
1086            _ => other,
1087        })
1088    }
1089
1090    /// Returns the mobile (in contrast to static) type corresponding to the given type.
1091    #[doc(alias = "mobile_type")]
1092    pub fn mobile(self, gcx: Gcx<'gcx>) -> Option<Self> {
1093        Some(match self.kind {
1094            TyKind::IntLiteral(false, size, _) => gcx.types.uint_(size),
1095            TyKind::IntLiteral(true, size, _) => gcx.types.int_(size),
1096            TyKind::StringLiteral(..) => gcx.types.string_ref.memory,
1097            // TODO: basetype.is_dynamically_encoded
1098            TyKind::Slice(ty)
1099                if ty.data_stored_in(DataLocation::Calldata) && ty.is_dynamically_sized() =>
1100            {
1101                ty
1102            }
1103            TyKind::Tuple(tys) => {
1104                let tys = tys.iter().map(|ty| ty.mobile(gcx)).collect::<Option<Vec<_>>>()?;
1105                gcx.mk_ty_tuple(gcx.mk_tys(&tys))
1106            }
1107            TyKind::Error(..)
1108            | TyKind::Event(..)
1109            | TyKind::Module(..)
1110            | TyKind::BuiltinModule(..)
1111            | TyKind::Type(_)
1112            | TyKind::Meta(_) => return None,
1113            // TODO: functions
1114            _ => self,
1115        })
1116    }
1117}
1118
1119/// The interned data of a type.
1120pub struct TyData<'gcx> {
1121    pub kind: TyKind<'gcx>,
1122    pub flags: TyFlags,
1123}
1124
1125impl<'gcx> Borrow<TyKind<'gcx>> for &TyData<'gcx> {
1126    #[inline]
1127    fn borrow(&self) -> &TyKind<'gcx> {
1128        &self.kind
1129    }
1130}
1131
1132impl PartialEq for TyData<'_> {
1133    #[inline]
1134    fn eq(&self, other: &Self) -> bool {
1135        self.kind == other.kind
1136    }
1137}
1138
1139impl Eq for TyData<'_> {}
1140
1141impl std::hash::Hash for TyData<'_> {
1142    #[inline]
1143    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
1144        self.kind.hash(state);
1145    }
1146}
1147
1148impl fmt::Debug for TyData<'_> {
1149    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1150        self.kind.fmt(f)
1151    }
1152}
1153
1154/// The kind of a type.
1155#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1156#[non_exhaustive]
1157pub enum TyKind<'gcx> {
1158    /// An elementary/primitive type.
1159    Elementary(ElementaryType),
1160
1161    /// Any string literal. Contains `(is_valid_utf8(s), min(s.len(), 32))`.
1162    /// - all string literals can coerce to `bytes`
1163    /// - only valid UTF-8 string literals can coerce to `string`
1164    /// - only string literals with `len <= N` can coerce to `bytesN`
1165    StringLiteral(bool, TypeSize),
1166
1167    /// Any integer or fixed-point number literal.
1168    /// Contains `(negative, minimum bits, compatible fixed-bytes size)`.
1169    IntLiteral(bool, TypeSize, Option<TypeSize>),
1170
1171    /// A reference to another type which lives in the data location.
1172    Ref(Ty<'gcx>, DataLocation),
1173
1174    /// Dynamic array: `T[]`.
1175    DynArray(Ty<'gcx>),
1176
1177    /// Fixed-size array: `T[N]`.
1178    Array(Ty<'gcx>, U256),
1179
1180    /// Array slice: result of `expr[1:2]`.
1181    ///
1182    /// Holds the underlying array type it is slicing (which can also be string/bytes).
1183    Slice(Ty<'gcx>),
1184
1185    /// Tuple: `(T1, T2, ...)`.
1186    Tuple(&'gcx [Ty<'gcx>]),
1187
1188    /// Mapping: `mapping(K => V)`.
1189    Mapping(Ty<'gcx>, Ty<'gcx>),
1190
1191    /// Function pointer: `function(...) returns (...)`.
1192    Fn(&'gcx TyFn<'gcx>),
1193
1194    /// Variadic function parameter.
1195    Variadic,
1196
1197    /// Contract.
1198    Contract(hir::ContractId),
1199
1200    /// The `super` type for a contract.
1201    Super(hir::ContractId),
1202
1203    /// A struct.
1204    ///
1205    /// Cannot contain the types of its fields because it can be recursive.
1206    Struct(hir::StructId),
1207
1208    /// An enum.
1209    Enum(hir::EnumId),
1210
1211    /// A custom error.
1212    Error(&'gcx [Ty<'gcx>], hir::ErrorId),
1213
1214    /// An event.
1215    Event(&'gcx [Ty<'gcx>], hir::EventId),
1216
1217    /// A user-defined value type. `Ty` can only be `Elementary`.
1218    Udvt(Ty<'gcx>, hir::UdvtId),
1219
1220    /// A source imported as a module: `import "path" as Module;`.
1221    Module(hir::SourceId),
1222
1223    /// Builtin module.
1224    BuiltinModule(Builtin),
1225
1226    /// The self-referential type, e.g. `Enum` in `Enum.Variant`.
1227    /// Corresponds to `TypeType` in solc.
1228    Type(Ty<'gcx>),
1229
1230    /// The meta type: `type(<inner_type>)`.
1231    Meta(Ty<'gcx>),
1232
1233    /// An invalid type. Silences further errors.
1234    Err(ErrorGuaranteed),
1235}
1236
1237#[derive(Debug, PartialEq, Eq, Hash)]
1238pub struct TyFn<'gcx> {
1239    /// The semantic kind of this function value.
1240    pub kind: TyFnKind,
1241    /// The parameter types.
1242    pub parameters: &'gcx [Ty<'gcx>],
1243    /// The return types.
1244    pub returns: &'gcx [Ty<'gcx>],
1245    /// The function state mutability.
1246    pub state_mutability: StateMutability,
1247    /// The declaration this function value refers to, if known.
1248    pub function_id: Option<hir::FunctionId>,
1249    /// Whether the first parameter is bound to a receiver in member-access syntax.
1250    pub attached: bool,
1251}
1252
1253/// The semantic kind of a function value.
1254///
1255/// This mirrors the solc `FunctionType::Kind` variants that are relevant to the current type
1256/// system.
1257#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1258pub enum TyFnKind {
1259    /// An ordinary internal function value.
1260    Internal,
1261    /// An internal function value for a public function accessed through a qualified name.
1262    ///
1263    /// It is callable as an internal function, but also has a `.selector` member.
1264    InternalWithSelector,
1265    /// An ordinary external function value.
1266    External,
1267    /// A function declaration accessed through a contract type, e.g. `C.f`.
1268    Declaration,
1269    /// A public library function accessed through the library type, e.g. `L.f`.
1270    ///
1271    /// It has a `.selector` member, but is not assignable to ordinary function types.
1272    DelegateCall,
1273    /// A low-level `address.call` function.
1274    BareCall,
1275    /// A low-level `address.delegatecall` function.
1276    BareDelegateCall,
1277    /// A low-level `address.staticcall` function.
1278    BareStaticCall,
1279    /// A contract creation function, e.g. `new C`.
1280    Creation,
1281}
1282
1283impl<'gcx> TyFn<'gcx> {
1284    /// Returns the semantic kind of this function value.
1285    #[inline]
1286    pub fn kind(&self) -> TyFnKind {
1287        self.kind
1288    }
1289
1290    /// Returns whether this is an internal function value.
1291    #[inline]
1292    pub fn is_internal(&self) -> bool {
1293        matches!(self.kind, TyFnKind::Internal | TyFnKind::InternalWithSelector)
1294    }
1295
1296    /// Returns whether this is an external function value.
1297    #[inline]
1298    pub fn is_external(&self) -> bool {
1299        self.kind == TyFnKind::External
1300    }
1301
1302    /// Returns whether this is a contract-type function declaration value.
1303    #[inline]
1304    pub fn is_declaration(&self) -> bool {
1305        self.kind == TyFnKind::Declaration
1306    }
1307
1308    /// Returns whether this is a public library function value.
1309    #[inline]
1310    pub fn is_delegate_call(&self) -> bool {
1311        self.kind == TyFnKind::DelegateCall
1312    }
1313
1314    /// Returns whether this is a low-level call function.
1315    #[inline]
1316    pub fn is_bare_call(&self) -> bool {
1317        matches!(
1318            self.kind,
1319            TyFnKind::BareCall | TyFnKind::BareDelegateCall | TyFnKind::BareStaticCall
1320        )
1321    }
1322
1323    /// Returns whether this is a contract creation function.
1324    #[inline]
1325    pub fn is_creation(&self) -> bool {
1326        self.kind == TyFnKind::Creation
1327    }
1328
1329    /// Returns whether this function value has a known declaration.
1330    #[inline]
1331    pub fn has_declaration(&self) -> bool {
1332        self.function_id.is_some()
1333    }
1334
1335    /// Returns whether this function value has a `.selector` member.
1336    #[inline]
1337    pub fn has_selector(&self) -> bool {
1338        matches!(
1339            self.kind,
1340            TyFnKind::InternalWithSelector
1341                | TyFnKind::External
1342                | TyFnKind::Declaration
1343                | TyFnKind::DelegateCall
1344        )
1345    }
1346
1347    /// Returns whether this function value has an `.address` member.
1348    #[inline]
1349    pub fn has_address(&self) -> bool {
1350        self.is_external() && !self.attached
1351    }
1352
1353    /// Returns an iterator over all the types in the function value.
1354    pub fn tys(&self) -> impl DoubleEndedIterator<Item = Ty<'gcx>> + Clone {
1355        self.parameters.iter().copied().chain(self.returns.iter().copied())
1356    }
1357}
1358
1359bitflags::bitflags! {
1360    /// [`Ty`] flags.
1361    #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1362    pub struct TyFlags: u8 {
1363        /// Whether an error is reachable.
1364        const HAS_ERROR    = 1 << 0;
1365        /// Whether this type contains a non-public (internal/private) function type.
1366        const HAS_INTERNAL_FN = 1 << 1;
1367    }
1368}
1369
1370impl TyFlags {
1371    pub(super) fn calculate(ty: &TyKind<'_>) -> Self {
1372        let mut flags = Self::empty();
1373        flags.add_ty_kind(ty);
1374        flags
1375    }
1376
1377    fn add_ty_kind(&mut self, ty: &TyKind<'_>) {
1378        match *ty {
1379            TyKind::Elementary(_)
1380            | TyKind::StringLiteral(..)
1381            | TyKind::IntLiteral(..)
1382            | TyKind::Contract(_)
1383            | TyKind::Super(_)
1384            | TyKind::Enum(_)
1385            | TyKind::Struct(_)
1386            | TyKind::Module(_)
1387            | TyKind::BuiltinModule(_)
1388            | TyKind::Variadic => {}
1389
1390            TyKind::Fn(f) => {
1391                if f.is_internal() {
1392                    self.add(Self::HAS_INTERNAL_FN);
1393                }
1394            }
1395
1396            TyKind::Ref(ty, _)
1397            | TyKind::DynArray(ty)
1398            | TyKind::Array(ty, _)
1399            | TyKind::Slice(ty)
1400            | TyKind::Udvt(ty, _)
1401            | TyKind::Type(ty)
1402            | TyKind::Meta(ty) => self.add_ty(ty),
1403
1404            TyKind::Error(list, _) | TyKind::Event(list, _) | TyKind::Tuple(list) => {
1405                self.add_tys(list)
1406            }
1407
1408            TyKind::Mapping(k, v) => {
1409                self.add_ty(k);
1410                self.add_ty(v);
1411            }
1412
1413            TyKind::Err(_) => self.add(Self::HAS_ERROR),
1414        }
1415    }
1416
1417    #[inline]
1418    fn add(&mut self, other: Self) {
1419        *self |= other;
1420    }
1421
1422    #[inline]
1423    fn add_ty(&mut self, ty: Ty<'_>) {
1424        self.add(ty.flags);
1425    }
1426
1427    #[inline]
1428    fn add_tys(&mut self, tys: &[Ty<'_>]) {
1429        for &ty in tys {
1430            self.add_ty(ty);
1431        }
1432    }
1433}