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#[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 Incompatible,
32
33 NonDerivedContract,
35
36 InvalidConversion,
38
39 AttachedFunction,
41
42 LiteralTooLarge,
44
45 ContractNotPayable,
47
48 AddressNotPayable,
50}
51
52#[derive(Debug, Clone, Copy, PartialEq, Eq)]
53pub(crate) enum SameSourceFileLevelUserTypeError {
54 NotUserDefined,
55 NotSameSourceFileLevel,
56}
57
58impl TyConvertError {
59 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 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 #[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 pub fn peel_refs(mut self) -> Self {
149 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 #[inline]
230 pub fn is_ref(self) -> bool {
231 matches!(self.kind, TyKind::Ref(..))
232 }
233
234 #[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 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 #[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 #[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 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 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 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 #[inline]
318 pub fn has_internal_function(self) -> bool {
319 self.flags.contains(TyFlags::HAS_INTERNAL_FN)
320 }
321
322 #[inline]
324 pub fn error_reported(self) -> Result<(), ErrorGuaranteed> {
325 if self.references_error() { Err(ErrorGuaranteed::new_unchecked()) } else { Ok(()) }
326 }
327
328 #[inline]
330 pub fn references_error(self) -> bool {
331 self.flags.contains(TyFlags::HAS_ERROR)
332 }
333
334 #[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 #[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 #[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 #[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 #[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 #[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 #[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 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 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 #[inline]
510 pub fn is_array(self) -> bool {
511 matches!(self.kind, TyKind::Array(..) | TyKind::DynArray(..))
512 }
513
514 #[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 #[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 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 pub fn is_fixed_bytes(self) -> bool {
558 matches!(self.kind, TyKind::Elementary(ElementaryType::FixedBytes(_)))
559 }
560
561 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 pub fn is_signed(self) -> bool {
572 matches!(
573 self.kind,
574 TyKind::Elementary(ElementaryType::Int(_)) | TyKind::IntLiteral(true, ..)
575 )
576 }
577
578 pub fn is_tuple(self) -> bool {
580 matches!(self.kind, TyKind::Tuple(..))
581 }
582
583 pub fn is_unit(self) -> bool {
585 matches!(self.kind, TyKind::Tuple([]))
586 }
587
588 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 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 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 #[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 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 (Elementary(Address(true)), Elementary(Address(false))) => Ok(()),
662
663 (Ref(from_inner, from_loc), Ref(to_inner, to_loc)) => {
666 match (from_loc, to_loc) {
667 (DataLocation::Memory, DataLocation::Memory)
669 | (DataLocation::Calldata, DataLocation::Calldata) => {
670 from_inner.try_convert_implicit_to(to_inner, gcx)
671 }
672
673 (DataLocation::Storage, DataLocation::Storage) => {
675 from_inner.try_convert_implicit_to(to_inner, gcx)
676 }
677
678 (DataLocation::Calldata, DataLocation::Memory)
680 | (DataLocation::Storage, DataLocation::Memory) => {
681 from_inner.try_convert_implicit_to(to_inner, gcx)
682 }
683
684 (DataLocation::Memory, DataLocation::Storage)
686 | (DataLocation::Calldata, DataLocation::Storage) => {
687 from_inner.try_convert_implicit_to(to_inner, gcx)
688 }
689
690 _ => Result::Err(TyConvertError::Incompatible),
693 }
694 }
695
696 (Ref(from_inner, _), to) if !matches!(to, Ref(..)) => {
702 from_inner.try_convert_implicit_to(other, gcx)
703 }
704
705 (Slice(underlying), _) if underlying == other => Ok(()),
710 (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(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 (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 (IntLiteral(neg, size, _), Elementary(UInt(target_size))) => {
763 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 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 (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 (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 (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 if from_fn.parameters != to_fn.parameters || from_fn.returns != to_fn.returns {
830 return Result::Err(TyConvertError::Incompatible);
831 }
832
833 if from_fn.is_declaration() && from_fn.function_id != to_fn.function_id {
836 return Result::Err(TyConvertError::Incompatible);
837 }
838
839 use StateMutability::*;
845 let mutability_ok = match (from_fn.state_mutability, to_fn.state_mutability) {
846 (a, b) if a == b => true,
847 (Pure, View) | (Pure, NonPayable) => true,
849 (View, NonPayable) => true,
851 (Payable, NonPayable) => true,
853 _ => false,
854 };
855 if mutability_ok { Ok(()) } else { Result::Err(TyConvertError::Incompatible) }
856 }
857
858 (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 #[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 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(_), _) if other.is_integer() => Ok(()),
907 (_, Enum(_)) if self.is_integer() => Ok(()),
908
909 (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 (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 (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 (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 (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 (Elementary(Address(false)), Elementary(Address(true))) => Ok(()),
952
953 (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(..)) => unreachable!(),
962
963 (Elementary(Int(_)), Elementary(Int(_))) => Ok(()),
965
966 (Elementary(UInt(_)), Elementary(UInt(_))) => Ok(()),
968
969 (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(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(_), Elementary(Address(false))) => Ok(()),
991 (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(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 (Elementary(Address(true)), Contract(_)) => Ok(()),
1009
1010 (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 (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 (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 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 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 (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 #[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 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 _ => self,
1115 })
1116 }
1117}
1118
1119pub 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#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1156#[non_exhaustive]
1157pub enum TyKind<'gcx> {
1158 Elementary(ElementaryType),
1160
1161 StringLiteral(bool, TypeSize),
1166
1167 IntLiteral(bool, TypeSize, Option<TypeSize>),
1170
1171 Ref(Ty<'gcx>, DataLocation),
1173
1174 DynArray(Ty<'gcx>),
1176
1177 Array(Ty<'gcx>, U256),
1179
1180 Slice(Ty<'gcx>),
1184
1185 Tuple(&'gcx [Ty<'gcx>]),
1187
1188 Mapping(Ty<'gcx>, Ty<'gcx>),
1190
1191 Fn(&'gcx TyFn<'gcx>),
1193
1194 Variadic,
1196
1197 Contract(hir::ContractId),
1199
1200 Super(hir::ContractId),
1202
1203 Struct(hir::StructId),
1207
1208 Enum(hir::EnumId),
1210
1211 Error(&'gcx [Ty<'gcx>], hir::ErrorId),
1213
1214 Event(&'gcx [Ty<'gcx>], hir::EventId),
1216
1217 Udvt(Ty<'gcx>, hir::UdvtId),
1219
1220 Module(hir::SourceId),
1222
1223 BuiltinModule(Builtin),
1225
1226 Type(Ty<'gcx>),
1229
1230 Meta(Ty<'gcx>),
1232
1233 Err(ErrorGuaranteed),
1235}
1236
1237#[derive(Debug, PartialEq, Eq, Hash)]
1238pub struct TyFn<'gcx> {
1239 pub kind: TyFnKind,
1241 pub parameters: &'gcx [Ty<'gcx>],
1243 pub returns: &'gcx [Ty<'gcx>],
1245 pub state_mutability: StateMutability,
1247 pub function_id: Option<hir::FunctionId>,
1249 pub attached: bool,
1251}
1252
1253#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1258pub enum TyFnKind {
1259 Internal,
1261 InternalWithSelector,
1265 External,
1267 Declaration,
1269 DelegateCall,
1273 BareCall,
1275 BareDelegateCall,
1277 BareStaticCall,
1279 Creation,
1281}
1282
1283impl<'gcx> TyFn<'gcx> {
1284 #[inline]
1286 pub fn kind(&self) -> TyFnKind {
1287 self.kind
1288 }
1289
1290 #[inline]
1292 pub fn is_internal(&self) -> bool {
1293 matches!(self.kind, TyFnKind::Internal | TyFnKind::InternalWithSelector)
1294 }
1295
1296 #[inline]
1298 pub fn is_external(&self) -> bool {
1299 self.kind == TyFnKind::External
1300 }
1301
1302 #[inline]
1304 pub fn is_declaration(&self) -> bool {
1305 self.kind == TyFnKind::Declaration
1306 }
1307
1308 #[inline]
1310 pub fn is_delegate_call(&self) -> bool {
1311 self.kind == TyFnKind::DelegateCall
1312 }
1313
1314 #[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 #[inline]
1325 pub fn is_creation(&self) -> bool {
1326 self.kind == TyFnKind::Creation
1327 }
1328
1329 #[inline]
1331 pub fn has_declaration(&self) -> bool {
1332 self.function_id.is_some()
1333 }
1334
1335 #[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 #[inline]
1349 pub fn has_address(&self) -> bool {
1350 self.is_external() && !self.attached
1351 }
1352
1353 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 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1362 pub struct TyFlags: u8 {
1363 const HAS_ERROR = 1 << 0;
1365 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}