diff --git a/CHANGELOG.md b/CHANGELOG.md index 3e11d2c11..615f8a67f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - The `isEmpty` extension function for the `Map` type: PR [#266](https://github.com/tact-lang/tact/pull/266) - The `pow2` power function with base 2: PR [#267](https://github.com/tact-lang/tact/pull/267) - The `try` and `try-catch` statements: PR [#212](https://github.com/tact-lang/tact/pull/212) +- The `del` method for the `Map` type: PR [#95](https://github.com/tact-lang/tact/pull/95) ### Changed diff --git a/src/abi/map.ts b/src/abi/map.ts index 0c7d0f8fa..b9c2f3732 100644 --- a/src/abi/map.ts +++ b/src/abi/map.ts @@ -325,6 +325,73 @@ export const MapFunctions: Map = new Map([ }, }, ], + [ + "del", + { + name: "del", + resolve(ctx, args, ref) { + // Check arguments + if (args.length !== 2) { + throwError("del expects one argument", ref); // Ignore self argument + } + const self = args[0]; + if (!self || self.kind !== "map") { + throwError("del expects a map as self argument", ref); // Should not happen + } + + // Check key type + if (args[1].kind !== "ref" || args[1].optional) { + throwError( + "del expects a direct type as first argument", + ref, + ); + } + if (args[1].name !== self.key) { + throwError( + `del expects a ${self.key} as first argument`, + ref, + ); + } + + // Returns boolean + return { kind: "ref", name: "Bool", optional: false }; + }, + generate: (ctx, args, exprs, ref) => { + if (args.length !== 2) { + throwError("del expects one argument", ref); // Ignore self argument + } + const self = args[0]; + if (!self || self.kind !== "map") { + throwError("del expects a map as self argument", ref); // Should not happen + } + + // Render expressions + const resolved = exprs.map((v) => writeExpression(v, ctx)); + + // Handle Int key + if (self.key === "Int") { + let bits = 257; + let kind = "int"; + if (self.keyAs && self.keyAs.startsWith("int")) { + bits = parseInt(self.keyAs.slice(3), 10); + } else if (self.keyAs && self.keyAs.startsWith("uint")) { + bits = parseInt(self.keyAs.slice(4), 10); + kind = "uint"; + } + ctx.used(`__tact_dict_delete_${kind}`); + return `${resolved[0]}~__tact_dict_delete_${kind}(${bits}, ${resolved[1]})`; + } + + // Handle Address key + if (self.key === "Address") { + ctx.used(`__tact_dict_delete`); + return `${resolved[0]}~__tact_dict_delete(267, ${resolved[1]})`; + } + + throwError(`del expects a map with Int keys`, ref); + }, + }, + ], [ "asCell", { diff --git a/src/generator/writers/__snapshots__/writeSerialization.spec.ts.snap b/src/generator/writers/__snapshots__/writeSerialization.spec.ts.snap index a736d34a2..4cfe9bd98 100644 --- a/src/generator/writers/__snapshots__/writeSerialization.spec.ts.snap +++ b/src/generator/writers/__snapshots__/writeSerialization.spec.ts.snap @@ -235,6 +235,30 @@ return __tact_create_address(chain, hash);", "name": "__tact_dict_delete", "signature": "(cell, int) __tact_dict_delete(cell dict, int key_len, slice index)", }, + { + "code": { + "code": "asm(index dict key_len) "DICTIDEL"", + "kind": "asm", + }, + "comment": null, + "context": "stdlib", + "depends": Set {}, + "flags": Set {}, + "name": "__tact_dict_delete_int", + "signature": "(cell, int) __tact_dict_delete_int(cell dict, int key_len, int index)", + }, + { + "code": { + "code": "asm(index dict key_len) "DICTUDEL"", + "kind": "asm", + }, + "comment": null, + "context": "stdlib", + "depends": Set {}, + "flags": Set {}, + "name": "__tact_dict_delete_uint", + "signature": "(cell, int) __tact_dict_delete_uint(cell dict, int key_len, int index)", + }, { "code": { "code": "asm(value index dict key_len) "DICTSETREF"", @@ -4275,6 +4299,30 @@ return __tact_create_address(chain, hash);", "name": "__tact_dict_delete", "signature": "(cell, int) __tact_dict_delete(cell dict, int key_len, slice index)", }, + { + "code": { + "code": "asm(index dict key_len) "DICTIDEL"", + "kind": "asm", + }, + "comment": null, + "context": "stdlib", + "depends": Set {}, + "flags": Set {}, + "name": "__tact_dict_delete_int", + "signature": "(cell, int) __tact_dict_delete_int(cell dict, int key_len, int index)", + }, + { + "code": { + "code": "asm(index dict key_len) "DICTUDEL"", + "kind": "asm", + }, + "comment": null, + "context": "stdlib", + "depends": Set {}, + "flags": Set {}, + "name": "__tact_dict_delete_uint", + "signature": "(cell, int) __tact_dict_delete_uint(cell dict, int key_len, int index)", + }, { "code": { "code": "asm(value index dict key_len) "DICTSETREF"", @@ -8315,6 +8363,30 @@ return __tact_create_address(chain, hash);", "name": "__tact_dict_delete", "signature": "(cell, int) __tact_dict_delete(cell dict, int key_len, slice index)", }, + { + "code": { + "code": "asm(index dict key_len) "DICTIDEL"", + "kind": "asm", + }, + "comment": null, + "context": "stdlib", + "depends": Set {}, + "flags": Set {}, + "name": "__tact_dict_delete_int", + "signature": "(cell, int) __tact_dict_delete_int(cell dict, int key_len, int index)", + }, + { + "code": { + "code": "asm(index dict key_len) "DICTUDEL"", + "kind": "asm", + }, + "comment": null, + "context": "stdlib", + "depends": Set {}, + "flags": Set {}, + "name": "__tact_dict_delete_uint", + "signature": "(cell, int) __tact_dict_delete_uint(cell dict, int key_len, int index)", + }, { "code": { "code": "asm(value index dict key_len) "DICTSETREF"", diff --git a/src/generator/writers/writeStdlib.ts b/src/generator/writers/writeStdlib.ts index 89d04d1bb..9d1e2de1b 100644 --- a/src/generator/writers/writeStdlib.ts +++ b/src/generator/writers/writeStdlib.ts @@ -167,6 +167,22 @@ export function writeStdlib(ctx: WriterContext) { ctx.asm(`asm(index dict key_len) "DICTDEL"`); }); + ctx.fun("__tact_dict_delete_int", () => { + ctx.signature( + `(cell, int) __tact_dict_delete_int(cell dict, int key_len, int index)`, + ); + ctx.context("stdlib"); + ctx.asm(`asm(index dict key_len) "DICTIDEL"`); + }); + + ctx.fun("__tact_dict_delete_uint", () => { + ctx.signature( + `(cell, int) __tact_dict_delete_uint(cell dict, int key_len, int index)`, + ); + ctx.context("stdlib"); + ctx.asm(`asm(index dict key_len) "DICTUDEL"`); + }); + ctx.fun("__tact_dict_set_ref", () => { ctx.signature( `((cell), ()) __tact_dict_set_ref(cell dict, int key_len, slice index, cell value)`, diff --git a/src/test/feature-map.spec.ts b/src/test/feature-map.spec.ts index fc98a570e..bcff9952f 100644 --- a/src/test/feature-map.spec.ts +++ b/src/test/feature-map.spec.ts @@ -550,4 +550,507 @@ describe("feature-map", () => { throw e; } }); + + it("should implement key deletion correctly", async () => { + jest.setTimeout(2 * 60000); + try { + // Init contract + const system = await ContractSystem.create(); + const treasure = system.treasure("treasure"); + const contract = system.open(await MapTestContract.fromInit()); + await contract.send(treasure, { value: toNano("10") }, null); + await system.run(); + + // Initial state + expect((await contract.getIntMap1()).size).toBe(0); + expect((await contract.getIntMap2()).size).toBe(0); + expect((await contract.getIntMap3()).size).toBe(0); + expect((await contract.getIntMap4()).size).toBe(0); + expect((await contract.getIntMap5()).size).toBe(0); + expect((await contract.getIntMap6_1()).size).toBe(0); + expect((await contract.getIntMap6_2()).size).toBe(0); + expect((await contract.getIntMap6_3()).size).toBe(0); + expect((await contract.getIntMap6_4()).size).toBe(0); + expect((await contract.getIntMap6_5()).size).toBe(0); + expect((await contract.getIntMap6_6()).size).toBe(0); + expect((await contract.getIntMap6_7()).size).toBe(0); + expect((await contract.getIntMap7_1()).size).toBe(0); + expect((await contract.getIntMap7_2()).size).toBe(0); + expect((await contract.getIntMap7_3()).size).toBe(0); + expect((await contract.getIntMap7_4()).size).toBe(0); + expect((await contract.getIntMap7_5()).size).toBe(0); + expect((await contract.getIntMap7_6()).size).toBe(0); + expect((await contract.getIntMap8_1()).size).toBe(0); + expect((await contract.getIntMap8_2()).size).toBe(0); + expect((await contract.getIntMap8_3()).size).toBe(0); + expect((await contract.getIntMap8_4()).size).toBe(0); + expect((await contract.getIntMap8_5()).size).toBe(0); + expect((await contract.getIntMap8_6()).size).toBe(0); + expect((await contract.getIntMap8_7()).size).toBe(0); + expect((await contract.getIntMap9_1()).size).toBe(0); + expect((await contract.getIntMap9_2()).size).toBe(0); + expect((await contract.getIntMap9_3()).size).toBe(0); + expect((await contract.getIntMap9_4()).size).toBe(0); + expect((await contract.getIntMap9_5()).size).toBe(0); + expect((await contract.getIntMap9_6()).size).toBe(0); + expect((await contract.getAddrMap1()).size).toBe(0); + expect((await contract.getAddrMap2()).size).toBe(0); + expect((await contract.getAddrMap3()).size).toBe(0); + expect((await contract.getAddrMap4()).size).toBe(0); + expect((await contract.getAddrMap6_1()).size).toBe(0); + expect((await contract.getAddrMap6_2()).size).toBe(0); + expect((await contract.getAddrMap6_3()).size).toBe(0); + expect((await contract.getAddrMap6_4()).size).toBe(0); + expect((await contract.getAddrMap6_5()).size).toBe(0); + expect((await contract.getAddrMap6_6()).size).toBe(0); + expect((await contract.getAddrMap6_7()).size).toBe(0); + expect((await contract.getAddrMap7_1()).size).toBe(0); + expect((await contract.getAddrMap7_2()).size).toBe(0); + expect((await contract.getAddrMap7_3()).size).toBe(0); + expect((await contract.getAddrMap7_4()).size).toBe(0); + expect((await contract.getAddrMap7_5()).size).toBe(0); + expect((await contract.getAddrMap7_6()).size).toBe(0); + + // Keys for test + const keys: bigint[] = []; + keys.push(1n); + keys.push(0n); + keys.push(-1n); + keys.push(10102312312312312312312n); + keys.push(-10102312312312312312312n); + for (const k of keys) { + // Check keys to be empty + expect(await contract.getIntMap1Value(k)).toBeNull(); + expect(await contract.getIntMap2Value(k)).toBeNull(); + expect(await contract.getIntMap3Value(k)).toBeNull(); + expect(await contract.getIntMap4Value(k)).toBeNull(); + + // Set keys + const valueInt = k * 10n; + const valueBool = k < 0n; + const addr = randomAddress(0, "addr-" + k.toString(10)); + const valueCell = beginCell().storeUint(123123, 128).endCell(); + const valueStruct: SomeStruct = { + $$type: "SomeStruct", + value: 10012312n, + }; + const valueAddr = randomAddress(0, "value-" + k.toString(10)); + const keySmall = k % 100n; + const keySmallAbs = (k > 0 ? k : -k) % 100n; + const valueSmall = k % 100n; + const valueSmallAbs = (k > 0 ? k : -k) % 100n; + await contract.send( + treasure, + { value: toNano(1) }, + { $$type: "SetIntMap1", key: k, value: valueInt }, + ); + await contract.send( + treasure, + { value: toNano(1) }, + { $$type: "SetIntMap2", key: k, value: valueBool }, + ); + await contract.send( + treasure, + { value: toNano(1) }, + { $$type: "SetIntMap3", key: k, value: valueCell }, + ); + await contract.send( + treasure, + { value: toNano(1) }, + { $$type: "SetIntMap4", key: k, value: valueStruct }, + ); + await contract.send( + treasure, + { value: toNano(1) }, + { $$type: "SetIntMap5", key: k, value: valueAddr }, + ); + await contract.send( + treasure, + { value: toNano(1) }, + { $$type: "SetIntMap6", key: keySmall, value: valueInt }, + ); + await contract.send( + treasure, + { value: toNano(1) }, + { + $$type: "SetUIntMap7", + key: keySmallAbs, + value: valueInt, + }, + ); + await contract.send( + treasure, + { value: toNano(1) }, + { $$type: "SetIntMap8", key: k, value: valueSmall }, + ); + await contract.send( + treasure, + { value: toNano(1) }, + { $$type: "SetUIntMap9", key: k, value: valueSmallAbs }, + ); + await contract.send( + treasure, + { value: toNano(1) }, + { $$type: "SetAddrMap1", key: addr, value: valueInt }, + ); + await contract.send( + treasure, + { value: toNano(1) }, + { $$type: "SetAddrMap2", key: addr, value: valueBool }, + ); + await contract.send( + treasure, + { value: toNano(1) }, + { $$type: "SetAddrMap3", key: addr, value: valueCell }, + ); + await contract.send( + treasure, + { value: toNano(1) }, + { $$type: "SetAddrMap4", key: addr, value: valueStruct }, + ); + await contract.send( + treasure, + { value: toNano(1) }, + { $$type: "SetAddrMap5", key: addr, value: valueAddr }, + ); + await contract.send( + treasure, + { value: toNano(1) }, + { $$type: "SetAddrMap6", key: addr, value: valueSmall }, + ); + await contract.send( + treasure, + { value: toNano(1) }, + { $$type: "SetAddrMap7", key: addr, value: valueSmallAbs }, + ); + await system.run(); + + // Check value set + expect(await contract.getIntMap1Value(k)).toBe(valueInt); + expect((await contract.getIntMap2Value(k))!).toBe(valueBool); + expect( + (await contract.getIntMap3Value(k))!.equals(valueCell), + ).toBe(true); + expect( + strEq((await contract.getIntMap4Value(k))!, valueStruct), + ).toBe(true); + expect( + (await contract.getIntMap5Value(k))!.equals(valueAddr), + ).toBe(true); + expect(await contract.getIntMap6_1Value(keySmall)).toBe( + valueInt, + ); + expect(await contract.getIntMap6_2Value(keySmall)).toBe( + valueInt, + ); + expect(await contract.getIntMap6_3Value(keySmall)).toBe( + valueInt, + ); + expect(await contract.getIntMap6_4Value(keySmall)).toBe( + valueInt, + ); + expect(await contract.getIntMap6_5Value(keySmall)).toBe( + valueInt, + ); + expect(await contract.getIntMap6_6Value(keySmall)).toBe( + valueInt, + ); + expect(await contract.getIntMap6_7Value(keySmall)).toBe( + valueInt, + ); + expect(await contract.getIntMap7_1Value(keySmallAbs)).toBe( + valueInt, + ); + expect(await contract.getIntMap7_2Value(keySmallAbs)).toBe( + valueInt, + ); + expect(await contract.getIntMap7_3Value(keySmallAbs)).toBe( + valueInt, + ); + expect(await contract.getIntMap7_4Value(keySmallAbs)).toBe( + valueInt, + ); + expect(await contract.getIntMap7_5Value(keySmallAbs)).toBe( + valueInt, + ); + expect(await contract.getIntMap7_6Value(keySmallAbs)).toBe( + valueInt, + ); + expect(await contract.getIntMap8_1Value(k)).toBe(valueSmall); + expect(await contract.getIntMap8_2Value(k)).toBe(valueSmall); + expect(await contract.getIntMap8_3Value(k)).toBe(valueSmall); + expect(await contract.getIntMap8_4Value(k)).toBe(valueSmall); + expect(await contract.getIntMap8_5Value(k)).toBe(valueSmall); + expect(await contract.getIntMap8_6Value(k)).toBe(valueSmall); + expect(await contract.getIntMap8_7Value(k)).toBe(valueSmall); + expect(await contract.getIntMap9_1Value(k)).toBe(valueSmallAbs); + expect(await contract.getIntMap9_2Value(k)).toBe(valueSmallAbs); + expect(await contract.getIntMap9_3Value(k)).toBe(valueSmallAbs); + expect(await contract.getIntMap9_4Value(k)).toBe(valueSmallAbs); + expect(await contract.getIntMap9_5Value(k)).toBe(valueSmallAbs); + expect(await contract.getIntMap9_6Value(k)).toBe(valueSmallAbs); + expect( + await contract.getIntMap10Value(keySmall, valueInt), + ).toBe(valueInt * 7n); + expect( + await contract.getIntMap11Value(keySmallAbs, valueInt), + ).toBe(valueInt * 6n); + expect(await contract.getIntMap12Value(k, valueSmall)).toBe( + valueSmall * 7n, + ); + expect(await contract.getIntMap13Value(k, valueSmallAbs)).toBe( + valueSmallAbs * 7n, + ); + expect(await contract.getAddrMap1Value(addr)).toBe(valueInt); + expect((await contract.getAddrMap2Value(addr))!).toBe( + valueBool, + ); + expect( + (await contract.getAddrMap3Value(addr))!.equals(valueCell), + ).toBe(true); + expect( + strEq( + (await contract.getAddrMap4Value(addr))!, + valueStruct, + ), + ).toBe(true); + expect( + (await contract.getAddrMap5Value(addr))!.equals(valueAddr), + ).toBe(true); + expect(await contract.getAddrMap6_1Value(addr)).toBe( + valueSmall, + ); + expect(await contract.getAddrMap6_2Value(addr)).toBe( + valueSmall, + ); + expect(await contract.getAddrMap6_3Value(addr)).toBe( + valueSmall, + ); + expect(await contract.getAddrMap6_4Value(addr)).toBe( + valueSmall, + ); + expect(await contract.getAddrMap6_5Value(addr)).toBe( + valueSmall, + ); + expect(await contract.getAddrMap6_6Value(addr)).toBe( + valueSmall, + ); + expect(await contract.getAddrMap6_7Value(addr)).toBe( + valueSmall, + ); + expect(await contract.getAddrMap7_1Value(addr)).toBe( + valueSmallAbs, + ); + expect(await contract.getAddrMap7_2Value(addr)).toBe( + valueSmallAbs, + ); + expect(await contract.getAddrMap7_3Value(addr)).toBe( + valueSmallAbs, + ); + expect(await contract.getAddrMap7_4Value(addr)).toBe( + valueSmallAbs, + ); + expect(await contract.getAddrMap7_5Value(addr)).toBe( + valueSmallAbs, + ); + expect(await contract.getAddrMap7_6Value(addr)).toBe( + valueSmallAbs, + ); + + // Sizes + expect((await contract.getIntMap1()).size).toBe(1); + expect((await contract.getIntMap2()).size).toBe(1); + expect((await contract.getIntMap3()).size).toBe(1); + expect((await contract.getIntMap4()).size).toBe(1); + expect((await contract.getIntMap5()).size).toBe(1); + expect((await contract.getIntMap6_1()).size).toBe(1); + expect((await contract.getIntMap6_2()).size).toBe(1); + expect((await contract.getIntMap6_3()).size).toBe(1); + expect((await contract.getIntMap6_4()).size).toBe(1); + expect((await contract.getIntMap6_5()).size).toBe(1); + expect((await contract.getIntMap6_6()).size).toBe(1); + expect((await contract.getIntMap6_7()).size).toBe(1); + expect((await contract.getIntMap7_1()).size).toBe(1); + expect((await contract.getIntMap7_2()).size).toBe(1); + expect((await contract.getIntMap7_3()).size).toBe(1); + expect((await contract.getIntMap7_4()).size).toBe(1); + expect((await contract.getIntMap7_5()).size).toBe(1); + expect((await contract.getIntMap7_6()).size).toBe(1); + expect((await contract.getIntMap8_1()).size).toBe(1); + expect((await contract.getIntMap8_2()).size).toBe(1); + expect((await contract.getIntMap8_3()).size).toBe(1); + expect((await contract.getIntMap8_4()).size).toBe(1); + expect((await contract.getIntMap8_5()).size).toBe(1); + expect((await contract.getIntMap8_6()).size).toBe(1); + expect((await contract.getIntMap8_7()).size).toBe(1); + expect((await contract.getIntMap9_1()).size).toBe(1); + expect((await contract.getIntMap9_2()).size).toBe(1); + expect((await contract.getIntMap9_3()).size).toBe(1); + expect((await contract.getIntMap9_4()).size).toBe(1); + expect((await contract.getIntMap9_5()).size).toBe(1); + expect((await contract.getIntMap9_6()).size).toBe(1); + expect((await contract.getAddrMap1()).size).toBe(1); + expect((await contract.getAddrMap2()).size).toBe(1); + expect((await contract.getAddrMap3()).size).toBe(1); + expect((await contract.getAddrMap4()).size).toBe(1); + + // Check .del return value + expect(await contract.getIntMap1Del(k)).toBe(true); + expect(await contract.getIntMap1Del(k + 1n)).toBe(false); + + // Clear keys + await contract.send( + treasure, + { value: toNano(1) }, + { $$type: "DelIntMap1", key: k }, + ); + await contract.send( + treasure, + { value: toNano(1) }, + { $$type: "DelIntMap2", key: k }, + ); + await contract.send( + treasure, + { value: toNano(1) }, + { $$type: "DelIntMap3", key: k }, + ); + await contract.send( + treasure, + { value: toNano(1) }, + { $$type: "DelIntMap4", key: k }, + ); + await contract.send( + treasure, + { value: toNano(1) }, + { $$type: "DelIntMap5", key: k }, + ); + await contract.send( + treasure, + { value: toNano(1) }, + { $$type: "DelIntMap6", key: keySmall }, + ); + await contract.send( + treasure, + { value: toNano(1) }, + { $$type: "DelUIntMap7", key: keySmallAbs }, + ); + await contract.send( + treasure, + { value: toNano(1) }, + { $$type: "DelIntMap8", key: k }, + ); + await contract.send( + treasure, + { value: toNano(1) }, + { $$type: "DelUIntMap9", key: k }, + ); + await contract.send( + treasure, + { value: toNano(1) }, + { $$type: "DelAddrMap1", key: addr }, + ); + await contract.send( + treasure, + { value: toNano(1) }, + { $$type: "DelAddrMap2", key: addr }, + ); + await contract.send( + treasure, + { value: toNano(1) }, + { $$type: "DelAddrMap3", key: addr }, + ); + await contract.send( + treasure, + { value: toNano(1) }, + { $$type: "DelAddrMap4", key: addr }, + ); + await contract.send( + treasure, + { value: toNano(1) }, + { $$type: "DelAddrMap5", key: addr }, + ); + await contract.send( + treasure, + { value: toNano(1) }, + { $$type: "DelAddrMap6", key: addr }, + ); + await contract.send( + treasure, + { value: toNano(1) }, + { $$type: "DelAddrMap7", key: addr }, + ); + await system.run(); + + // Check value cleared + expect(await contract.getIntMap1Value(k)).toBeNull(); + expect(await contract.getIntMap2Value(k)).toBeNull(); + expect(await contract.getIntMap3Value(k)).toBeNull(); + expect(await contract.getIntMap4Value(k)).toBeNull(); + expect(await contract.getIntMap5Value(k)).toBeNull(); + expect(await contract.getIntMap6_1Value(keySmall)).toBe(null); + expect(await contract.getIntMap6_2Value(keySmall)).toBe(null); + expect(await contract.getIntMap6_3Value(keySmall)).toBe(null); + expect(await contract.getIntMap6_4Value(keySmall)).toBe(null); + expect(await contract.getIntMap6_5Value(keySmall)).toBe(null); + expect(await contract.getIntMap6_6Value(keySmall)).toBe(null); + expect(await contract.getIntMap6_7Value(keySmall)).toBe(null); + expect(await contract.getIntMap7_1Value(keySmallAbs)).toBe( + null, + ); + expect(await contract.getIntMap7_2Value(keySmallAbs)).toBe( + null, + ); + expect(await contract.getIntMap7_3Value(keySmallAbs)).toBe( + null, + ); + expect(await contract.getIntMap7_4Value(keySmallAbs)).toBe( + null, + ); + expect(await contract.getIntMap7_5Value(keySmallAbs)).toBe( + null, + ); + expect(await contract.getIntMap7_6Value(keySmallAbs)).toBe( + null, + ); + expect(await contract.getIntMap8_1Value(k)).toBe(null); + expect(await contract.getIntMap8_2Value(k)).toBe(null); + expect(await contract.getIntMap8_3Value(k)).toBe(null); + expect(await contract.getIntMap8_4Value(k)).toBe(null); + expect(await contract.getIntMap8_5Value(k)).toBe(null); + expect(await contract.getIntMap8_6Value(k)).toBe(null); + expect(await contract.getIntMap8_7Value(k)).toBe(null); + expect(await contract.getIntMap9_1Value(k)).toBe(null); + expect(await contract.getIntMap9_2Value(k)).toBe(null); + expect(await contract.getIntMap9_3Value(k)).toBe(null); + expect(await contract.getIntMap9_4Value(k)).toBe(null); + expect(await contract.getIntMap9_5Value(k)).toBe(null); + expect(await contract.getIntMap9_6Value(k)).toBe(null); + expect(await contract.getAddrMap1Value(addr)).toBeNull(); + expect(await contract.getAddrMap2Value(addr)).toBeNull(); + expect(await contract.getAddrMap3Value(addr)).toBeNull(); + expect(await contract.getAddrMap4Value(addr)).toBeNull(); + expect(await contract.getAddrMap5Value(addr)).toBeNull(); + expect(await contract.getAddrMap6_1Value(addr)).toBe(null); + expect(await contract.getAddrMap6_2Value(addr)).toBe(null); + expect(await contract.getAddrMap6_3Value(addr)).toBe(null); + expect(await contract.getAddrMap6_4Value(addr)).toBe(null); + expect(await contract.getAddrMap6_5Value(addr)).toBe(null); + expect(await contract.getAddrMap6_6Value(addr)).toBe(null); + expect(await contract.getAddrMap6_7Value(addr)).toBe(null); + expect(await contract.getAddrMap7_1Value(addr)).toBe(null); + expect(await contract.getAddrMap7_2Value(addr)).toBe(null); + expect(await contract.getAddrMap7_3Value(addr)).toBe(null); + expect(await contract.getAddrMap7_4Value(addr)).toBe(null); + expect(await contract.getAddrMap7_5Value(addr)).toBe(null); + expect(await contract.getAddrMap7_6Value(addr)).toBe(null); + } + } catch (e) { + if (e instanceof ComputeError) { + if (e.logs) { + console.warn(e.logs); + } + } + throw e; + } + }); }); diff --git a/src/test/features/maps.tact b/src/test/features/maps.tact index 8d91e9644..7c45e6168 100644 --- a/src/test/features/maps.tact +++ b/src/test/features/maps.tact @@ -82,6 +82,70 @@ struct SomeStruct { value: Int; } +message DelIntMap1 { + key: Int; +} + +message DelIntMap2 { + key: Int; +} + +message DelIntMap3 { + key: Int; +} + +message DelIntMap4 { + key: Int; +} + +message DelIntMap5 { + key: Int; +} + +message DelIntMap6 { + key: Int; +} + +message DelUIntMap7 { + key: Int; +} + +message DelIntMap8 { + key: Int; +} + +message DelUIntMap9 { + key: Int; +} + +message DelAddrMap1 { + key: Address; +} + +message DelAddrMap2 { + key: Address; +} + +message DelAddrMap3 { + key: Address; +} + +message DelAddrMap4 { + key: Address; +} + +message DelAddrMap5 { + key: Address; +} + +message DelAddrMap6 { + key: Address; +} + +message DelAddrMap7 { + key: Address; +} + contract MapTestContract { init() { @@ -189,6 +253,65 @@ contract MapTestContract { self.intMap9_6.set(msg.key, msg.value); } + receive(msg: DelIntMap1) { + self.intMap1.del(msg.key); + } + + receive(msg: DelIntMap2) { + self.intMap2.del(msg.key); + } + + receive(msg: DelIntMap3) { + self.intMap3.del(msg.key); + } + + receive(msg: DelIntMap4) { + self.intMap4.del(msg.key); + } + + receive(msg: DelIntMap5) { + self.intMap5.del(msg.key); + } + + receive(msg: DelIntMap6) { + self.intMap6_1.del(msg.key); + self.intMap6_2.del(msg.key); + self.intMap6_3.del(msg.key); + self.intMap6_4.del(msg.key); + self.intMap6_5.del(msg.key); + self.intMap6_6.del(msg.key); + self.intMap6_6.del(msg.key); + self.intMap6_7.del(msg.key); + } + + receive(msg: DelUIntMap7) { + self.intMap7_1.del(msg.key); + self.intMap7_2.del(msg.key); + self.intMap7_3.del(msg.key); + self.intMap7_4.del(msg.key); + self.intMap7_5.del(msg.key); + self.intMap7_6.del(msg.key); + } + + receive(msg: DelIntMap8) { + self.intMap8_1.del(msg.key); + self.intMap8_2.del(msg.key); + self.intMap8_3.del(msg.key); + self.intMap8_4.del(msg.key); + self.intMap8_5.del(msg.key); + self.intMap8_6.del(msg.key); + self.intMap8_7.del(msg.key); + } + + receive(msg: DelUIntMap9) { + self.intMap9_1.del(msg.key); + self.intMap9_2.del(msg.key); + self.intMap9_3.del(msg.key); + self.intMap9_4.del(msg.key); + self.intMap9_5.del(msg.key); + self.intMap9_6.del(msg.key); + } + get fun intMap1(): map { return self.intMap1; } @@ -614,6 +737,45 @@ contract MapTestContract { self.addrMap7_6.set(msg.key, msg.value); } + receive(msg: DelAddrMap1) { + self.addrMap1.del(msg.key); + } + + receive(msg: DelAddrMap2) { + self.addrMap2.del(msg.key); + } + + receive(msg: DelAddrMap3) { + self.addrMap3.del(msg.key); + } + + receive(msg: DelAddrMap4) { + self.addrMap4.del(msg.key); + } + + receive(msg: DelAddrMap5) { + self.addrMap5.del(msg.key); + } + + receive(msg: DelAddrMap6) { + self.addrMap6_1.del(msg.key); + self.addrMap6_2.del(msg.key); + self.addrMap6_3.del(msg.key); + self.addrMap6_4.del(msg.key); + self.addrMap6_5.del(msg.key); + self.addrMap6_6.del(msg.key); + self.addrMap6_7.del(msg.key); + } + + receive(msg: DelAddrMap7) { + self.addrMap7_1.del(msg.key); + self.addrMap7_2.del(msg.key); + self.addrMap7_3.del(msg.key); + self.addrMap7_4.del(msg.key); + self.addrMap7_5.del(msg.key); + self.addrMap7_6.del(msg.key); + } + receive("reset") { self.addrMap1 = emptyMap(); self.addrMap2 = emptyMap(); @@ -779,4 +941,8 @@ contract MapTestContract { get fun intMap1IsEmpty(): Bool { return self.intMap1.isEmpty(); } + + get fun intMap1Del(key: Int): Bool { + return self.intMap1.del(key); + } } \ No newline at end of file diff --git a/src/types/__snapshots__/resolveStatements.spec.ts.snap b/src/types/__snapshots__/resolveStatements.spec.ts.snap index 7c4fa0168..99aa1e6e2 100644 --- a/src/types/__snapshots__/resolveStatements.spec.ts.snap +++ b/src/types/__snapshots__/resolveStatements.spec.ts.snap @@ -370,6 +370,16 @@ Line 14, col 26: " `; +exports[`resolveStatements should fail statements for case-37 1`] = ` +":6:5: Type mismatch: Bool is not assignable to +Line 6, col 5: + 5 | m.set(1, 2); +> 6 | return m.del(1); + ^~~~~~~~~~~~~~~~ + 7 | } +" +`; + exports[`resolveStatements should resolve statements for case-0 1`] = ` [ [ @@ -1027,3 +1037,72 @@ exports[`resolveStatements should resolve statements for case-14 1`] = ` ], ] `; + +exports[`resolveStatements should resolve statements for case-15 1`] = ` +[ + [ + "emptyMap()", + "", + ], + [ + "m", + "map", + ], + [ + "1", + "Int", + ], + [ + "2", + "Int", + ], + [ + "m.set(1, 2)", + "", + ], + [ + "m", + "map", + ], + [ + "3", + "Int", + ], + [ + "m.del(3)", + "Bool", + ], + [ + "emptyMap()", + "", + ], + [ + "m", + "map", + ], + [ + "1", + "Int", + ], + [ + "2", + "Int", + ], + [ + "m.set(1, 2)", + "", + ], + [ + "m", + "map", + ], + [ + "1", + "Int", + ], + [ + "m.del(1)", + "Bool", + ], +] +`; diff --git a/src/types/stmts-failed/case-37.tact b/src/types/stmts-failed/case-37.tact new file mode 100644 index 000000000..0057a2080 --- /dev/null +++ b/src/types/stmts-failed/case-37.tact @@ -0,0 +1,7 @@ +primitive Int; + +fun testFunction1() { + let m: map = emptyMap(); + m.set(1, 2); + return m.del(1); +} \ No newline at end of file diff --git a/src/types/stmts/case-15.tact b/src/types/stmts/case-15.tact new file mode 100644 index 000000000..810593ecf --- /dev/null +++ b/src/types/stmts/case-15.tact @@ -0,0 +1,14 @@ +primitive Int; +primitive Bool; + +fun testFunction1(): Bool { + let m: map = emptyMap(); + m.set(1, 2); + return m.del(3); +} + +fun testFunction2(): Bool { + let m: map = emptyMap(); + m.set(1, 2); + return m.del(1); +} \ No newline at end of file