diff --git a/crates/dojo/core-cairo-test/src/tests/meta/introspect.cairo b/crates/dojo/core-cairo-test/src/tests/meta/introspect.cairo index 478c7302f1..d0ffd64bbc 100644 --- a/crates/dojo/core-cairo-test/src/tests/meta/introspect.cairo +++ b/crates/dojo/core-cairo-test/src/tests/meta/introspect.cairo @@ -361,15 +361,74 @@ fn test_introspect_upgrade() { assert!(b.is_an_upgrade_of(@b)); } +#[test] +fn test_primitive_upgrade() { + let primitives = [ + 'bool', 'u8', 'u16', 'u32', 'usize', 'u64', 'u128', 'u256', 'i8', 'i16', 'i32', 'i64', + 'i128', 'felt252', 'ClassHash', 'ContractAddress', + ] + .span(); + + let mut allowed_upgrades: Span<(felt252, Span)> = [ + ('bool', [].span()), ('u8', ['u16', 'u32', 'usize', 'u64', 'u128', 'felt252'].span()), + ('u16', ['u32', 'usize', 'u64', 'u128', 'felt252'].span()), + ('u32', ['usize', 'u64', 'u128', 'felt252'].span()), + ('usize', ['u32', 'u64', 'u128', 'felt252'].span()), ('u64', ['u128', 'felt252'].span()), + ('u128', ['felt252'].span()), ('u256', [].span()), + ('i8', ['i16', 'i32', 'i64', 'i128', 'felt252'].span()), + ('i16', ['i32', 'i64', 'i128', 'felt252'].span()), + ('i32', ['i64', 'i128', 'felt252'].span()), ('i64', ['i128', 'felt252'].span()), + ('i128', ['felt252'].span()), ('felt252', ['ClassHash', 'ContractAddress'].span()), + ('ClassHash', ['felt252', 'ContractAddress'].span()), + ('ContractAddress', ['felt252', 'ClassHash'].span()), + ] + .span(); + + loop { + match allowed_upgrades.pop_front() { + Option::Some(( + src, allowed, + )) => { + for dest in primitives { + let expected = if src == dest { + true + } else { + let allowed = *allowed; + let mut i = 0; + + loop { + if i >= allowed.len() { + break false; + } + + if *allowed.at(i) == *dest { + break true; + } + + i += 1; + } + }; + + assert_eq!( + Ty::Primitive(*dest).is_an_upgrade_of(@Ty::Primitive(*src)), expected, + ); + } + }, + Option::None => { break; }, + }; + } +} + #[test] fn test_struct_upgrade() { let s = Struct { - name: 's', attrs: [ - 'one' - ].span(), children: [ + name: 's', + attrs: ['one'].span(), + children: [ Member { name: 'x', attrs: ['two'].span(), ty: Ty::Primitive('u8') }, - Member { name: 'y', attrs: ['three'].span(), ty: Ty::Primitive('u16') } - ].span() + Member { name: 'y', attrs: ['three'].span(), ty: Ty::Primitive('u16') }, + ] + .span(), }; // different name @@ -384,46 +443,67 @@ fn test_struct_upgrade() { // member name changed let mut upgraded = s; - upgraded.children = [ - Member { name: 'new', attrs: ['two'].span(), ty: Ty::Primitive('u8') }, - Member { name: 'y', attrs: ['three'].span(), ty: Ty::Primitive('u16') } - ].span(); + upgraded + .children = + [ + Member { name: 'new', attrs: ['two'].span(), ty: Ty::Primitive('u8') }, + Member { name: 'y', attrs: ['three'].span(), ty: Ty::Primitive('u16') }, + ] + .span(); assert!(!upgraded.is_an_upgrade_of(@s), "member name changed"); // member attr changed let mut upgraded = s; - upgraded.children = [ - Member { name: 'x', attrs: [].span(), ty: Ty::Primitive('u8') }, - Member { name: 'y', attrs: ['three'].span(), ty: Ty::Primitive('u16') } - ].span(); + upgraded + .children = + [ + Member { name: 'x', attrs: [].span(), ty: Ty::Primitive('u8') }, + Member { name: 'y', attrs: ['three'].span(), ty: Ty::Primitive('u16') }, + ] + .span(); assert!(!upgraded.is_an_upgrade_of(@s), "member attr changed"); + // allowed member change + let mut upgraded = s; + upgraded + .children = + [ + Member { name: 'x', attrs: ['two'].span(), ty: Ty::Primitive('u16') }, + Member { name: 'y', attrs: ['three'].span(), ty: Ty::Primitive('u16') }, + ] + .span(); + assert!(upgraded.is_an_upgrade_of(@s), "allowed member change"); + // wrong member change let mut upgraded = s; - upgraded.children = [ - Member { name: 'x', attrs: ['two'].span(), ty: Ty::Primitive('u8') }, - Member { name: 'y', attrs: ['three'].span(), ty: Ty::Primitive('u8') } - ].span(); + upgraded + .children = + [ + Member { name: 'x', attrs: ['two'].span(), ty: Ty::Primitive('u8') }, + Member { name: 'y', attrs: ['three'].span(), ty: Ty::Primitive('u8') }, + ] + .span(); assert!(!upgraded.is_an_upgrade_of(@s), "wrong member change"); // new member let mut upgraded = s; - upgraded.children = [ - Member { name: 'x', attrs: ['two'].span(), ty: Ty::Primitive('u8') }, - Member { name: 'y', attrs: ['three'].span(), ty: Ty::Primitive('u16') }, - Member { name: 'z', attrs: ['four'].span(), ty: Ty::Primitive('u32') } - ].span(); + upgraded + .children = + [ + Member { name: 'x', attrs: ['two'].span(), ty: Ty::Primitive('u8') }, + Member { name: 'y', attrs: ['three'].span(), ty: Ty::Primitive('u16') }, + Member { name: 'z', attrs: ['four'].span(), ty: Ty::Primitive('u32') }, + ] + .span(); assert!(upgraded.is_an_upgrade_of(@s), "new member"); } #[test] fn test_enum_upgrade() { let e = Enum { - name: 'e', attrs: [ - 'one' - ].span(), children: [ - ('x', Ty::Primitive('u8')), ('y', Ty::Primitive('u16')), - ].span() + name: 'e', + attrs: ['one'].span(), + children: [('x', Ty::Primitive('u8')), ('y', Ty::Primitive('u16'))].span(), }; // different name @@ -438,19 +518,25 @@ fn test_enum_upgrade() { // variant name changed let mut upgraded = e; - upgraded.children = [('new', Ty::Primitive('u8')), ('y', Ty::Primitive('u16')),].span(); + upgraded.children = [('new', Ty::Primitive('u8')), ('y', Ty::Primitive('u16'))].span(); assert!(upgraded.is_an_upgrade_of(@e), "variant name changed"); + // allowed variant change + let mut upgraded = e; + upgraded.children = [('x', Ty::Primitive('u16')), ('y', Ty::Primitive('u16'))].span(); + assert!(upgraded.is_an_upgrade_of(@e), "allowed variant change"); + // wrong variant change let mut upgraded = e; - upgraded.children = [('x', Ty::Primitive('u8')), ('y', Ty::Primitive('u8')),].span(); + upgraded.children = [('x', Ty::Primitive('u8')), ('y', Ty::Primitive('u8'))].span(); assert!(!upgraded.is_an_upgrade_of(@e), "wrong variant change"); // new member let mut upgraded = e; - upgraded.children = [ - ('x', Ty::Primitive('u8')), ('y', Ty::Primitive('u16')), ('z', Ty::Primitive('u32')) - ].span(); + upgraded + .children = + [('x', Ty::Primitive('u8')), ('y', Ty::Primitive('u16')), ('z', Ty::Primitive('u32'))] + .span(); assert!(upgraded.is_an_upgrade_of(@e), "new member"); } @@ -458,13 +544,17 @@ fn test_enum_upgrade() { fn test_tuple_upgrade() { let t = Ty::Tuple([Ty::Primitive('u8'), Ty::Primitive('u16')].span()); + // tuple item is upgradable + let upgraded = Ty::Tuple([Ty::Primitive('u16'), Ty::Primitive('u16')].span()); + assert!(upgraded.is_an_upgrade_of(@t)); + // tuple item is not upgradable let upgraded = Ty::Tuple([Ty::Primitive('bool'), Ty::Primitive('u16')].span()); assert!(!upgraded.is_an_upgrade_of(@t)); // tuple length changed let upgraded = Ty::Tuple( - [Ty::Primitive('u8'), Ty::Primitive('u16'), Ty::Primitive('u32')].span() + [Ty::Primitive('u8'), Ty::Primitive('u16'), Ty::Primitive('u32')].span(), ); assert!(!upgraded.is_an_upgrade_of(@t)); } @@ -473,6 +563,10 @@ fn test_tuple_upgrade() { fn test_array_upgrade() { let a = Ty::Array([Ty::Primitive('u8')].span()); + // array item is upgradable + let upgraded = Ty::Array([Ty::Primitive('u16')].span()); + assert!(upgraded.is_an_upgrade_of(@a)); + // array item is not upgradable let upgraded = Ty::Array([Ty::Primitive('bool')].span()); assert!(!upgraded.is_an_upgrade_of(@a)); diff --git a/crates/dojo/core/src/meta/introspect.cairo b/crates/dojo/core/src/meta/introspect.cairo index 00bf3625fa..2af944ae2e 100644 --- a/crates/dojo/core/src/meta/introspect.cairo +++ b/crates/dojo/core/src/meta/introspect.cairo @@ -39,35 +39,81 @@ pub trait TyCompareTrait { fn is_an_upgrade_of(self: @T, old: @T) -> bool; } +impl PrimitiveCompareImpl of TyCompareTrait { + fn is_an_upgrade_of(self: @felt252, old: @felt252) -> bool { + if self == old { + return true; + } + + let mut allowed_upgrades: Span<(felt252, Span)> = [ + ('bool', [].span()), ('u8', ['u16', 'u32', 'usize', 'u64', 'u128', 'felt252'].span()), + ('u16', ['u32', 'usize', 'u64', 'u128', 'felt252'].span()), + ('u32', ['usize', 'u64', 'u128', 'felt252'].span()), + ('usize', ['u32', 'u64', 'u128', 'felt252'].span()), + ('u64', ['u128', 'felt252'].span()), ('u128', ['felt252'].span()), ('u256', [].span()), + ('i8', ['i16', 'i32', 'i64', 'i128', 'felt252'].span()), + ('i16', ['i32', 'i64', 'i128', 'felt252'].span()), + ('i32', ['i64', 'i128', 'felt252'].span()), ('i64', ['i128', 'felt252'].span()), + ('i128', ['felt252'].span()), ('felt252', ['ClassHash', 'ContractAddress'].span()), + ('ClassHash', ['felt252', 'ContractAddress'].span()), + ('ContractAddress', ['felt252', 'ClassHash'].span()), + ] + .span(); + + loop { + match allowed_upgrades.pop_front() { + Option::Some(( + src, allowed, + )) => { + if src == old { + let mut i = 0; + break loop { + if i >= (*allowed).len() { + break false; + } + if (*allowed).at(i) == self { + break true; + } + i += 1; + }; + } + }, + Option::None => { break false; }, + } + } + } +} + impl TyCompareImpl of TyCompareTrait { fn is_an_upgrade_of(self: @Ty, old: @Ty) -> bool { match (self, old) { - (Ty::Primitive(n), Ty::Primitive(o)) => n == o, + (Ty::Primitive(n), Ty::Primitive(o)) => n.is_an_upgrade_of(o), (Ty::Struct(n), Ty::Struct(o)) => n.is_an_upgrade_of(o), (Ty::Array(n), Ty::Array(o)) => { (*n).at(0).is_an_upgrade_of((*o).at(0)) }, ( - Ty::Tuple(n), Ty::Tuple(o) + Ty::Tuple(n), Ty::Tuple(o), ) => { let n = *n; let o = *o; - if n.len() == o.len() { - let mut i = 0; - loop { - if i >= n.len() { - break true; - } - if !n.at(i).is_an_upgrade_of(o.at(i)) { - break false; - } - i += 1; + + if n.len() != o.len() { + return false; + } + + let mut i = 0; + loop { + if i >= n.len() { + break true; + } + if !n.at(i).is_an_upgrade_of(o.at(i)) { + break false; } - } else { - false + i += 1; } }, (Ty::ByteArray, Ty::ByteArray) => true, (Ty::Enum(n), Ty::Enum(o)) => n.is_an_upgrade_of(o), - _ => false + _ => false, } } }