@@ -25,6 +25,39 @@ impl<'gcx> std::ops::Deref for Ty<'gcx> {
2525 }
2626}
2727
28+ #[ derive( Debug , Clone , Copy , PartialEq , Eq ) ]
29+ pub 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+
40+ impl TyConvertError {
41+ /// Returns the error message for this conversion error.
42+ pub fn message < ' gcx > ( self , from : Ty < ' gcx > , to : Ty < ' gcx > , gcx : Gcx < ' gcx > ) -> String {
43+ match self {
44+ Self :: NonDerivedContract => {
45+ format ! (
46+ "contract `{}` does not inherit from `{}`" ,
47+ from. display( gcx) ,
48+ to. display( gcx)
49+ )
50+ }
51+ Self :: InvalidConversion => {
52+ format ! ( "cannot convert `{}` to `{}`" , from. display( gcx) , to. display( gcx) )
53+ }
54+ Self :: Incompatible => {
55+ format ! ( "expected `{}`, found `{}`" , to. display( gcx) , from. display( gcx) )
56+ }
57+ }
58+ }
59+ }
60+
2861impl < ' gcx > Ty < ' gcx > {
2962 pub fn new ( gcx : Gcx < ' gcx > , kind : TyKind < ' gcx > ) -> Self {
3063 gcx. mk_ty ( kind)
@@ -382,12 +415,12 @@ impl<'gcx> Ty<'gcx> {
382415 pub fn common_type ( self , b : Self , gcx : Gcx < ' gcx > ) -> Option < Self > {
383416 let a = self ;
384417 if let Some ( a) = a. mobile ( gcx)
385- && b. convert_implicit_to ( a)
418+ && b. convert_implicit_to ( a, gcx )
386419 {
387420 return Some ( a) ;
388421 }
389422 if let Some ( b) = b. mobile ( gcx)
390- && a. convert_implicit_to ( b)
423+ && a. convert_implicit_to ( b, gcx )
391424 {
392425 return Some ( b) ;
393426 }
@@ -415,15 +448,18 @@ impl<'gcx> Ty<'gcx> {
415448 #[ inline]
416449 #[ doc( alias = "is_implicitly_convertible_to" ) ]
417450 #[ must_use]
418- pub fn convert_implicit_to ( self , other : Self ) -> bool {
419- self . try_convert_implicit_to ( other) . is_ok ( )
451+ pub fn convert_implicit_to ( self , other : Self , gcx : Gcx < ' gcx > ) -> bool {
452+ self . try_convert_implicit_to ( other, gcx ) . is_ok ( )
420453 }
421454
422455 /// Checks if the type is implicitly convertible to the given type.
423456 ///
424457 /// See: <https://docs.soliditylang.org/en/latest/types.html#implicit-conversions>
425- #[ allow( clippy:: result_unit_err) ]
426- pub fn try_convert_implicit_to ( self , other : Self ) -> Result < ( ) , ( ) > {
458+ pub fn try_convert_implicit_to (
459+ self ,
460+ other : Self ,
461+ gcx : Gcx < ' gcx > ,
462+ ) -> Result < ( ) , TyConvertError > {
427463 use ElementaryType :: * ;
428464 use TyKind :: * ;
429465
@@ -442,29 +478,29 @@ impl<'gcx> Ty<'gcx> {
442478 // Same location: allowed if base types match.
443479 ( DataLocation :: Memory , DataLocation :: Memory )
444480 | ( DataLocation :: Calldata , DataLocation :: Calldata ) => {
445- from_inner. try_convert_implicit_to ( to_inner)
481+ from_inner. try_convert_implicit_to ( to_inner, gcx )
446482 }
447483
448484 // storage -> storage: allowed (reference assignment).
449485 ( DataLocation :: Storage , DataLocation :: Storage ) => {
450- from_inner. try_convert_implicit_to ( to_inner)
486+ from_inner. try_convert_implicit_to ( to_inner, gcx )
451487 }
452488
453489 // calldata/storage -> memory: allowed (copy semantics).
454490 ( DataLocation :: Calldata , DataLocation :: Memory )
455491 | ( DataLocation :: Storage , DataLocation :: Memory ) => {
456- from_inner. try_convert_implicit_to ( to_inner)
492+ from_inner. try_convert_implicit_to ( to_inner, gcx )
457493 }
458494
459495 // memory/calldata -> storage: allowed (copy semantics).
460496 ( DataLocation :: Memory , DataLocation :: Storage )
461497 | ( DataLocation :: Calldata , DataLocation :: Storage ) => {
462- from_inner. try_convert_implicit_to ( to_inner)
498+ from_inner. try_convert_implicit_to ( to_inner, gcx )
463499 }
464500
465501 // storage -> calldata: never allowed.
466502 // memory -> calldata: never allowed.
467- _ => Result :: Err ( ( ) ) ,
503+ _ => Result :: Err ( TyConvertError :: Incompatible ) ,
468504 }
469505 }
470506
@@ -480,12 +516,22 @@ impl<'gcx> Ty<'gcx> {
480516 {
481517 Ok ( ( ) )
482518 } else {
483- Result :: Err ( ( ) )
519+ Result :: Err ( TyConvertError :: Incompatible )
520+ }
521+ }
522+
523+ // Contract -> Base contract (inheritance check)
524+ ( Contract ( self_contract_id) , Contract ( other_contract_id) ) => {
525+ let self_contract = gcx. hir . contract ( self_contract_id) ;
526+ if self_contract. linearized_bases . contains ( & other_contract_id) {
527+ Ok ( ( ) )
528+ } else {
529+ Result :: Err ( TyConvertError :: NonDerivedContract )
484530 }
485531 }
486532
487533 // TODO: more implicit conversions
488- _ => Result :: Err ( ( ) ) ,
534+ _ => Result :: Err ( TyConvertError :: Incompatible ) ,
489535 }
490536 }
491537
@@ -495,19 +541,22 @@ impl<'gcx> Ty<'gcx> {
495541 #[ inline]
496542 #[ doc( alias = "is_explicitly_convertible_to" ) ]
497543 #[ must_use]
498- pub fn convert_explicit_to ( self , other : Self ) -> bool {
499- self . try_convert_explicit_to ( other) . is_ok ( )
544+ pub fn convert_explicit_to ( self , other : Self , gcx : Gcx < ' gcx > ) -> bool {
545+ self . try_convert_explicit_to ( other, gcx ) . is_ok ( )
500546 }
501547
502548 /// Checks if the type is explicitly convertible to the given type.
503549 ///
504550 /// See: <https://docs.soliditylang.org/en/latest/types.html#explicit-conversions>
505- #[ allow( clippy:: result_unit_err) ]
506- pub fn try_convert_explicit_to ( self , other : Self ) -> Result < ( ) , ( ) > {
551+ pub fn try_convert_explicit_to (
552+ self ,
553+ other : Self ,
554+ gcx : Gcx < ' gcx > ,
555+ ) -> Result < ( ) , TyConvertError > {
507556 use ElementaryType :: * ;
508557 use TyKind :: * ;
509558
510- if self . try_convert_implicit_to ( other) . is_ok ( ) {
559+ if self . try_convert_implicit_to ( other, gcx ) . is_ok ( ) {
511560 return Ok ( ( ) ) ;
512561 }
513562 match ( self . kind , other. kind ) {
@@ -522,7 +571,7 @@ impl<'gcx> Ty<'gcx> {
522571 if matches ! ( ty. kind, Elementary ( Bytes ) ) {
523572 Ok ( ( ) )
524573 } else {
525- Result :: Err ( ( ) )
574+ Result :: Err ( TyConvertError :: InvalidConversion )
526575 }
527576 }
528577
@@ -545,7 +594,17 @@ impl<'gcx> Ty<'gcx> {
545594 // address -> address payable.
546595 ( Elementary ( Address ( false ) ) , Elementary ( Address ( true ) ) ) => Ok ( ( ) ) ,
547596
548- _ => Result :: Err ( ( ) ) ,
597+ // Contract -> Base contract (inheritance check)
598+ ( Contract ( self_contract_id) , Contract ( other_contract_id) ) => {
599+ let self_contract = gcx. hir . contract ( self_contract_id) ;
600+ if self_contract. linearized_bases . contains ( & other_contract_id) {
601+ Ok ( ( ) )
602+ } else {
603+ Result :: Err ( TyConvertError :: NonDerivedContract )
604+ }
605+ }
606+
607+ _ => Result :: Err ( TyConvertError :: InvalidConversion ) ,
549608 }
550609 }
551610
0 commit comments