From 2b2940330f5ad7becb05b703e3b76a83627ea98b Mon Sep 17 00:00:00 2001 From: Daniil Sedov Date: Fri, 16 Aug 2024 15:57:47 +0300 Subject: [PATCH] fix: allow returning `self` from getters (#666) --- CHANGELOG.md | 1 + cspell.json | 3 +- src/generator/createABI.ts | 10 +- src/generator/writeReport.ts | 5 +- src/generator/writers/resolveFuncTupleType.ts | 2 +- src/generator/writers/writeFunction.ts | 4 +- src/pipeline/build.ts | 2 +- src/storage/resolveAllocation.ts | 2 +- .../__snapshots__/constants.spec.ts.snap | 5 + .../external-fallbacks.spec.ts.snap | 15 + .../getter-names-conflict.spec.ts.snap | 299 ---------- .../__snapshots__/getters.spec.ts.snap | 531 ++++++++++++++++++ .../local-type-inference.spec.ts.snap | 5 + .../contracts/getter-names-conflict.tact | 16 - src/test/e2e-emulated/contracts/getters.tact | 49 ++ .../getter-names-conflict.spec.ts | 30 - src/test/e2e-emulated/getters.spec.ts | 56 ++ src/types/resolveABITypeRef.ts | 14 +- tact.config.json | 4 +- 19 files changed, 696 insertions(+), 357 deletions(-) delete mode 100644 src/test/e2e-emulated/__snapshots__/getter-names-conflict.spec.ts.snap create mode 100644 src/test/e2e-emulated/__snapshots__/getters.spec.ts.snap delete mode 100644 src/test/e2e-emulated/contracts/getter-names-conflict.tact create mode 100644 src/test/e2e-emulated/contracts/getters.tact delete mode 100644 src/test/e2e-emulated/getter-names-conflict.spec.ts create mode 100644 src/test/e2e-emulated/getters.spec.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 94f9236fd..31897fa99 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed +- Returning `self` from getters is now allowed: PR [#666](https://github.com/tact-lang/tact/pull/666) - Remainder fields in the middle of a struct are now forbidden: PR [#697](https://github.com/tact-lang/tact/pull/697) - Defining two native functions from the same FunC function now does not fail compilation: PR [#699](https://github.com/tact-lang/tact/pull/699) diff --git a/cspell.json b/cspell.json index d6f1c9a3a..e9d373075 100644 --- a/cspell.json +++ b/cspell.json @@ -80,7 +80,8 @@ "привет", "PUSHREF", "PUSHSLICE", - "SETINDEXVARQ" + "SETINDEXVARQ", + "gettest" ], "flagWords": [], "ignorePaths": [ diff --git a/src/generator/createABI.ts b/src/generator/createABI.ts index fc714e5e0..f4bcb133c 100644 --- a/src/generator/createABI.ts +++ b/src/generator/createABI.ts @@ -28,6 +28,12 @@ export function createABI(ctx: CompilerContext, name: string): ContractABI { header: t.header, fields: t.fields.map((v) => v.abi), }); + } else if (t.kind === "contract") { + types.push({ + name: t.name + "$Data", + header: t.header, + fields: t.fields.map((v) => v.abi), + }); } } @@ -119,11 +125,11 @@ export function createABI(ctx: CompilerContext, name: string): ContractABI { name: f.name, arguments: f.params.map((v) => ({ name: idText(v.name), - type: createABITypeRefFromTypeRef(v.type, v.loc), + type: createABITypeRefFromTypeRef(ctx, v.type, v.loc), })), returnType: f.returns.kind !== "void" - ? createABITypeRefFromTypeRef(f.returns, f.ast.loc) + ? createABITypeRefFromTypeRef(ctx, f.returns, f.ast.loc) : null, }); } diff --git a/src/generator/writeReport.ts b/src/generator/writeReport.ts index 71415df36..b6f580a85 100644 --- a/src/generator/writeReport.ts +++ b/src/generator/writeReport.ts @@ -20,7 +20,10 @@ export function writeReport(ctx: CompilerContext, pkg: PackageFileFormat) { w.write("Total Types: " + abi.types!.length); w.append(); for (const t of abi.types!) { - const tt = getType(ctx, t.name); + const tt = getType( + ctx, + t.name.endsWith("$Data") ? t.name.slice(0, -5) : t.name, + ); w.write(`## ${t.name}`); w.write(`TLB: \`${tt.tlb!}\``); w.write(`Signature: \`${tt.signature!}\``); diff --git a/src/generator/writers/resolveFuncTupleType.ts b/src/generator/writers/resolveFuncTupleType.ts index 04280a6c7..70483cebc 100644 --- a/src/generator/writers/resolveFuncTupleType.ts +++ b/src/generator/writers/resolveFuncTupleType.ts @@ -46,7 +46,7 @@ export function resolveFuncTupleType( } else { throw Error("Unknown primitive type: " + descriptor.name); } - } else if (descriptor.kind === "struct") { + } else if (descriptor.kind === "struct" || descriptor.kind === "contract") { return "tuple"; } diff --git a/src/generator/writers/writeFunction.ts b/src/generator/writers/writeFunction.ts index 00407e2df..d53f4c2ba 100644 --- a/src/generator/writers/writeFunction.ts +++ b/src/generator/writers/writeFunction.ts @@ -41,7 +41,7 @@ function unwrapExternal( ) { if (type.kind === "ref") { const t = getType(ctx.ctx, type.name); - if (t.kind === "struct") { + if (t.kind === "struct" || t.kind === "contract") { if (type.optional) { ctx.append( `${resolveFuncType(type, ctx)} ${targetName} = ${ops.typeFromOptTuple(t.name, ctx)}(${sourceName});`, @@ -658,7 +658,7 @@ export function writeGetter(f: FunctionDescription, ctx: WriterContext) { // Pack if needed if (f.returns.kind === "ref") { const t = getType(ctx.ctx, f.returns.name); - if (t.kind === "struct") { + if (t.kind === "struct" || t.kind === "contract") { if (f.returns.optional) { ctx.append( `return ${ops.typeToOptExternal(t.name, ctx)}(res);`, diff --git a/src/pipeline/build.ts b/src/pipeline/build.ts index 4d40063d1..3206164a7 100644 --- a/src/pipeline/build.ts +++ b/src/pipeline/build.ts @@ -297,7 +297,7 @@ export async function build(args: { kind: "direct", args: getType(ctx, contract).init!.params.map((v) => ({ name: idText(v.name), - type: createABITypeRefFromTypeRef(v.type, v.loc), + type: createABITypeRefFromTypeRef(ctx, v.type, v.loc), })), prefix: { bits: 1, diff --git a/src/storage/resolveAllocation.ts b/src/storage/resolveAllocation.ts index 70fa17fc4..a611e13ce 100644 --- a/src/storage/resolveAllocation.ts +++ b/src/storage/resolveAllocation.ts @@ -143,7 +143,7 @@ export function resolveAllocations(ctx: CompilerContext) { // Resolve opts const ops: AllocationOperation[] = []; for (const f of s.init.params) { - const abiType = createABITypeRefFromTypeRef(f.type, f.loc); + const abiType = createABITypeRefFromTypeRef(ctx, f.type, f.loc); ops.push({ name: idText(f.name), type: abiType, diff --git a/src/test/e2e-emulated/__snapshots__/constants.spec.ts.snap b/src/test/e2e-emulated/__snapshots__/constants.spec.ts.snap index df547581c..2fce1920f 100644 --- a/src/test/e2e-emulated/__snapshots__/constants.spec.ts.snap +++ b/src/test/e2e-emulated/__snapshots__/constants.spec.ts.snap @@ -725,6 +725,11 @@ ConstantTester { "header": null, "name": "T", }, + { + "fields": [], + "header": null, + "name": "ConstantTester$Data", + }, ], }, "address": kQDrQDJFeHEKbkV6TKFO-KhFpeMRJ3cEZgIrvDQYPUyzm4CN, diff --git a/src/test/e2e-emulated/__snapshots__/external-fallbacks.spec.ts.snap b/src/test/e2e-emulated/__snapshots__/external-fallbacks.spec.ts.snap index 60ad76185..e947c7f1c 100644 --- a/src/test/e2e-emulated/__snapshots__/external-fallbacks.spec.ts.snap +++ b/src/test/e2e-emulated/__snapshots__/external-fallbacks.spec.ts.snap @@ -257,6 +257,21 @@ exports[`strings should implement external fallbacks correctly 1`] = ` "header": 2412401973, "name": "Add", }, + { + "fields": [ + { + "name": "a", + "type": { + "format": 257, + "kind": "simple", + "optional": false, + "type": "int", + }, + }, + ], + "header": null, + "name": "ExternalFallbacksTester$Data", + }, ], } `; diff --git a/src/test/e2e-emulated/__snapshots__/getter-names-conflict.spec.ts.snap b/src/test/e2e-emulated/__snapshots__/getter-names-conflict.spec.ts.snap deleted file mode 100644 index 02f9615bf..000000000 --- a/src/test/e2e-emulated/__snapshots__/getter-names-conflict.spec.ts.snap +++ /dev/null @@ -1,299 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`getter-names-conflict should handle conflicts in getter names correctly 1`] = ` -Test { - "abi": { - "errors": { - "10": { - "message": "Dictionary error", - }, - "128": { - "message": "Null reference exception", - }, - "129": { - "message": "Invalid serialization prefix", - }, - "13": { - "message": "Out of gas error", - }, - "130": { - "message": "Invalid incoming message", - }, - "131": { - "message": "Constraints error", - }, - "132": { - "message": "Access denied", - }, - "133": { - "message": "Contract stopped", - }, - "134": { - "message": "Invalid argument", - }, - "135": { - "message": "Code of a contract was not found", - }, - "136": { - "message": "Invalid address", - }, - "137": { - "message": "Masterchain support is not enabled for this contract", - }, - "2": { - "message": "Stack underflow", - }, - "3": { - "message": "Stack overflow", - }, - "32": { - "message": "Method ID not found", - }, - "34": { - "message": "Action is invalid or not supported", - }, - "37": { - "message": "Not enough TON", - }, - "38": { - "message": "Not enough extra-currencies", - }, - "4": { - "message": "Integer overflow", - }, - "5": { - "message": "Integer out of expected range", - }, - "6": { - "message": "Invalid opcode", - }, - "7": { - "message": "Type check error", - }, - "8": { - "message": "Cell overflow", - }, - "9": { - "message": "Cell underflow", - }, - }, - "getters": [ - { - "arguments": [], - "name": "testGetter", - "returnType": { - "format": 257, - "kind": "simple", - "optional": false, - "type": "int", - }, - }, - { - "arguments": [], - "name": "test_getter", - "returnType": { - "format": 257, - "kind": "simple", - "optional": false, - "type": "int", - }, - }, - { - "arguments": [], - "name": "Test_getter", - "returnType": { - "format": 257, - "kind": "simple", - "optional": false, - "type": "int", - }, - }, - ], - "receivers": [ - { - "message": { - "kind": "empty", - }, - "receiver": "internal", - }, - ], - "types": [ - { - "fields": [ - { - "name": "code", - "type": { - "kind": "simple", - "optional": false, - "type": "cell", - }, - }, - { - "name": "data", - "type": { - "kind": "simple", - "optional": false, - "type": "cell", - }, - }, - ], - "header": null, - "name": "StateInit", - }, - { - "fields": [ - { - "name": "bounced", - "type": { - "kind": "simple", - "optional": false, - "type": "bool", - }, - }, - { - "name": "sender", - "type": { - "kind": "simple", - "optional": false, - "type": "address", - }, - }, - { - "name": "value", - "type": { - "format": 257, - "kind": "simple", - "optional": false, - "type": "int", - }, - }, - { - "name": "raw", - "type": { - "kind": "simple", - "optional": false, - "type": "slice", - }, - }, - ], - "header": null, - "name": "Context", - }, - { - "fields": [ - { - "name": "bounce", - "type": { - "kind": "simple", - "optional": false, - "type": "bool", - }, - }, - { - "name": "to", - "type": { - "kind": "simple", - "optional": false, - "type": "address", - }, - }, - { - "name": "value", - "type": { - "format": 257, - "kind": "simple", - "optional": false, - "type": "int", - }, - }, - { - "name": "mode", - "type": { - "format": 257, - "kind": "simple", - "optional": false, - "type": "int", - }, - }, - { - "name": "body", - "type": { - "kind": "simple", - "optional": true, - "type": "cell", - }, - }, - { - "name": "code", - "type": { - "kind": "simple", - "optional": true, - "type": "cell", - }, - }, - { - "name": "data", - "type": { - "kind": "simple", - "optional": true, - "type": "cell", - }, - }, - ], - "header": null, - "name": "SendParameters", - }, - ], - }, - "address": kQD80WcWxBhERIUXYb8hyvNv5hK-iToKR4Q5dEGUZ2NWl9Mx, - "init": { - "code": x{FF00F4A413F4BCF2C80B} - x{62_} - x{D001D0D3030171B0A301FA400120D74981010BBAF2E08820D70B0A208104FFBAF2D0898309BAF2E088545053036F04F86102F862DB3C59DB3CF2E08230C8F84301CC7F01CA00C9ED54} - x{ED44D0D401F863D20030916DE0F828D70B0A8309BAF2E089DB3C} - x{6D} - x{0192307FE07021D749C21F953020D70B1FDEC00001D749C121B0917FE070} - x{2_} - x{2_} - x{B9BDCDB3CDB3C31} - x{ED44D0D401F863D20030916DE0F828D70B0A8309BAF2E089DB3C} - x{6D} - x{73} - x{BBE1FDB3CDB3C31} - x{ED44D0D401F863D20030916DE0F828D70B0A8309BAF2E089DB3C} - x{6D} - x{71} - x{2_} - x{BB1A6DB3CDB3C31} - x{ED44D0D401F863D20030916DE0F828D70B0A8309BAF2E089DB3C} - x{6D} - x{72} - x{B82BEED44D0D20001}, - "data": x{4_} - x{C_} - x{A15891_} - x{FF00F4A413F4BCF2C80B} - x{62_} - x{D001D0D3030171B0A301FA400120D74981010BBAF2E08820D70B0A208104FFBAF2D0898309BAF2E088545053036F04F86102F862DB3C59DB3CF2E08230C8F84301CC7F01CA00C9ED54} - x{ED44D0D401F863D20030916DE0F828D70B0A8309BAF2E089DB3C} - x{6D} - x{0192307FE07021D749C21F953020D70B1FDEC00001D749C121B0917FE070} - x{2_} - x{2_} - x{B9BDCDB3CDB3C31} - x{ED44D0D401F863D20030916DE0F828D70B0A8309BAF2E089DB3C} - x{6D} - x{73} - x{BBE1FDB3CDB3C31} - x{ED44D0D401F863D20030916DE0F828D70B0A8309BAF2E089DB3C} - x{6D} - x{71} - x{2_} - x{BB1A6DB3CDB3C31} - x{ED44D0D401F863D20030916DE0F828D70B0A8309BAF2E089DB3C} - x{6D} - x{72} - x{B82BEED44D0D20001}, - }, -} -`; diff --git a/src/test/e2e-emulated/__snapshots__/getters.spec.ts.snap b/src/test/e2e-emulated/__snapshots__/getters.spec.ts.snap new file mode 100644 index 000000000..58dfbeca0 --- /dev/null +++ b/src/test/e2e-emulated/__snapshots__/getters.spec.ts.snap @@ -0,0 +1,531 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`getters should implement getters correctly 1`] = ` +Test { + "abi": { + "errors": { + "10": { + "message": "Dictionary error", + }, + "128": { + "message": "Null reference exception", + }, + "129": { + "message": "Invalid serialization prefix", + }, + "13": { + "message": "Out of gas error", + }, + "130": { + "message": "Invalid incoming message", + }, + "131": { + "message": "Constraints error", + }, + "132": { + "message": "Access denied", + }, + "133": { + "message": "Contract stopped", + }, + "134": { + "message": "Invalid argument", + }, + "135": { + "message": "Code of a contract was not found", + }, + "136": { + "message": "Invalid address", + }, + "137": { + "message": "Masterchain support is not enabled for this contract", + }, + "2": { + "message": "Stack underflow", + }, + "3": { + "message": "Stack overflow", + }, + "32": { + "message": "Method ID not found", + }, + "34": { + "message": "Action is invalid or not supported", + }, + "37": { + "message": "Not enough TON", + }, + "38": { + "message": "Not enough extra-currencies", + }, + "4": { + "message": "Integer overflow", + }, + "5": { + "message": "Integer out of expected range", + }, + "6": { + "message": "Invalid opcode", + }, + "7": { + "message": "Type check error", + }, + "8": { + "message": "Cell overflow", + }, + "9": { + "message": "Cell underflow", + }, + }, + "getters": [ + { + "arguments": [], + "name": "testGetter", + "returnType": { + "format": 257, + "kind": "simple", + "optional": false, + "type": "int", + }, + }, + { + "arguments": [], + "name": "test_getter", + "returnType": { + "format": 257, + "kind": "simple", + "optional": false, + "type": "int", + }, + }, + { + "arguments": [], + "name": "Test_getter", + "returnType": { + "format": 257, + "kind": "simple", + "optional": false, + "type": "int", + }, + }, + { + "arguments": [], + "name": "contractData", + "returnType": { + "kind": "simple", + "optional": false, + "type": "Test$Data", + }, + }, + { + "arguments": [ + { + "name": "s", + "type": { + "kind": "simple", + "optional": false, + "type": "S", + }, + }, + ], + "name": "structAsInput", + "returnType": { + "kind": "simple", + "optional": false, + "type": "S", + }, + }, + { + "arguments": [ + { + "name": "test", + "type": { + "kind": "simple", + "optional": false, + "type": "Test$Data", + }, + }, + ], + "name": "contractAsInput", + "returnType": { + "kind": "simple", + "optional": false, + "type": "Test$Data", + }, + }, + ], + "receivers": [ + { + "message": { + "kind": "typed", + "type": "SetIdAndData", + }, + "receiver": "internal", + }, + { + "message": { + "kind": "typed", + "type": "Deploy", + }, + "receiver": "internal", + }, + ], + "types": [ + { + "fields": [ + { + "name": "code", + "type": { + "kind": "simple", + "optional": false, + "type": "cell", + }, + }, + { + "name": "data", + "type": { + "kind": "simple", + "optional": false, + "type": "cell", + }, + }, + ], + "header": null, + "name": "StateInit", + }, + { + "fields": [ + { + "name": "bounced", + "type": { + "kind": "simple", + "optional": false, + "type": "bool", + }, + }, + { + "name": "sender", + "type": { + "kind": "simple", + "optional": false, + "type": "address", + }, + }, + { + "name": "value", + "type": { + "format": 257, + "kind": "simple", + "optional": false, + "type": "int", + }, + }, + { + "name": "raw", + "type": { + "kind": "simple", + "optional": false, + "type": "slice", + }, + }, + ], + "header": null, + "name": "Context", + }, + { + "fields": [ + { + "name": "bounce", + "type": { + "kind": "simple", + "optional": false, + "type": "bool", + }, + }, + { + "name": "to", + "type": { + "kind": "simple", + "optional": false, + "type": "address", + }, + }, + { + "name": "value", + "type": { + "format": 257, + "kind": "simple", + "optional": false, + "type": "int", + }, + }, + { + "name": "mode", + "type": { + "format": 257, + "kind": "simple", + "optional": false, + "type": "int", + }, + }, + { + "name": "body", + "type": { + "kind": "simple", + "optional": true, + "type": "cell", + }, + }, + { + "name": "code", + "type": { + "kind": "simple", + "optional": true, + "type": "cell", + }, + }, + { + "name": "data", + "type": { + "kind": "simple", + "optional": true, + "type": "cell", + }, + }, + ], + "header": null, + "name": "SendParameters", + }, + { + "fields": [ + { + "name": "queryId", + "type": { + "format": 64, + "kind": "simple", + "optional": false, + "type": "uint", + }, + }, + ], + "header": 2490013878, + "name": "Deploy", + }, + { + "fields": [ + { + "name": "queryId", + "type": { + "format": 64, + "kind": "simple", + "optional": false, + "type": "uint", + }, + }, + ], + "header": 2952335191, + "name": "DeployOk", + }, + { + "fields": [ + { + "name": "queryId", + "type": { + "format": 64, + "kind": "simple", + "optional": false, + "type": "uint", + }, + }, + { + "name": "cashback", + "type": { + "kind": "simple", + "optional": false, + "type": "address", + }, + }, + ], + "header": 1829761339, + "name": "FactoryDeploy", + }, + { + "fields": [ + { + "name": "a", + "type": { + "format": 257, + "kind": "simple", + "optional": false, + "type": "int", + }, + }, + { + "name": "b", + "type": { + "format": 257, + "kind": "simple", + "optional": false, + "type": "int", + }, + }, + ], + "header": null, + "name": "S", + }, + { + "fields": [ + { + "name": "id", + "type": { + "format": 257, + "kind": "simple", + "optional": false, + "type": "int", + }, + }, + { + "name": "data", + "type": { + "kind": "simple", + "optional": false, + "type": "cell", + }, + }, + ], + "header": 1746430141, + "name": "SetIdAndData", + }, + { + "fields": [ + { + "name": "id", + "type": { + "format": 32, + "kind": "simple", + "optional": false, + "type": "uint", + }, + }, + { + "name": "anotherData", + "type": { + "kind": "simple", + "optional": false, + "type": "cell", + }, + }, + ], + "header": null, + "name": "Test$Data", + }, + ], + }, + "address": kQBJzzDQ5H5TCORVona7ggMuJgyrJx9ZuZrwFUXXjw7LLq9e, + "init": { + "code": x{FF00F4A413F4BCF2C80B} + x{62_} + x{D001D0D3030171B0A301FA400120D74981010BBAF2E08820D70B0A208104FFBAF2D0898309BAF2E088545053036F04F86102F862DB3C5ADB3CF2E082C8F84301CC7F01CA005902CB1FCCC9ED54} + x{ED44D0D401F863D2000196D31FD4596C12E030F828D70B0A8309BAF2E089DB3C} + x{70C8C9} + x{0192307FE07021D749C21F953020D70B1FDE208210681868BDBA8E1A30D31F018210681868BDBAF2E081810101D700D4596C126C227FE08210946A98B6BA8EA7D31F018210946A98B6BAF2E081D33F0131C8018210AFF90F5758CB1FCB3FC9F84201706DDB3C7FE03070} + x{6D6D226EB3995B206EF2D0806F22019132E2102470030480425023DB3C} + x{C87101CA01500701CA007001CA02500520D74981010BBAF2E08820D70B0A208104FFBAF2D0898309BAF2E088CF165003FA027001CA68236EB3917F93246EB3E2973333017001CA00E30D216EB39C7F01CA0001206EF2D08001CC95317001CA00E2C901FB00} + x{7F01CA00C87001CA007001CA00246EB39D7F01CA0004206EF2D0805004CC9634037001CA00E2246EB39D7F01CA0004206EF2D0805004CC9634037001CA00E27001CA00027F01CA0002C958CC} + x{2_} + x{2_} + x{66_} + x{AFD8ED9E6D9E36114_} + x{ED44D0D401F863D2000196D31FD4596C12E030F828D70B0A8309BAF2E089DB3C} + x{70C8C9} + x{5C} + x{ADEE6D9E6D9E3610C_} + x{ED44D0D401F863D2000196D31FD4596C12E030F828D70B0A8309BAF2E089DB3C} + x{70C8C9} + x{73} + x{BBE1FDB3CDB3C6C21} + x{ED44D0D401F863D2000196D31FD4596C12E030F828D70B0A8309BAF2E089DB3C} + x{70C8C9} + x{71} + x{2_} + x{BB1A6DB3CDB3C6C21} + x{ED44D0D401F863D2000196D31FD4596C12E030F828D70B0A8309BAF2E089DB3C} + x{70C8C9} + x{72} + x{2_} + x{B457DDA89A1A40003_} + x{2_} + x{B2D85BC8B6CF169B08A_} + x{ED44D0D401F863D2000196D31FD4596C12E030F828D70B0A8309BAF2E089DB3C} + x{70C8C9} + x{B1B1DBC8B6CF169B08A_} + x{ED44D0D401F863D2000196D31FD4596C12E030F828D70B0A8309BAF2E089DB3C} + x{70C8C9}, + "data": x{4_} + x{C_} + x{A15891_} + x{FF00F4A413F4BCF2C80B} + x{62_} + x{D001D0D3030171B0A301FA400120D74981010BBAF2E08820D70B0A208104FFBAF2D0898309BAF2E088545053036F04F86102F862DB3C5ADB3CF2E082C8F84301CC7F01CA005902CB1FCCC9ED54} + x{ED44D0D401F863D2000196D31FD4596C12E030F828D70B0A8309BAF2E089DB3C} + x{70C8C9} + x{0192307FE07021D749C21F953020D70B1FDE208210681868BDBA8E1A30D31F018210681868BDBAF2E081810101D700D4596C126C227FE08210946A98B6BA8EA7D31F018210946A98B6BAF2E081D33F0131C8018210AFF90F5758CB1FCB3FC9F84201706DDB3C7FE03070} + x{6D6D226EB3995B206EF2D0806F22019132E2102470030480425023DB3C} + x{C87101CA01500701CA007001CA02500520D74981010BBAF2E08820D70B0A208104FFBAF2D0898309BAF2E088CF165003FA027001CA68236EB3917F93246EB3E2973333017001CA00E30D216EB39C7F01CA0001206EF2D08001CC95317001CA00E2C901FB00} + x{7F01CA00C87001CA007001CA00246EB39D7F01CA0004206EF2D0805004CC9634037001CA00E2246EB39D7F01CA0004206EF2D0805004CC9634037001CA00E27001CA00027F01CA0002C958CC} + x{2_} + x{2_} + x{66_} + x{AFD8ED9E6D9E36114_} + x{ED44D0D401F863D2000196D31FD4596C12E030F828D70B0A8309BAF2E089DB3C} + x{70C8C9} + x{5C} + x{ADEE6D9E6D9E3610C_} + x{ED44D0D401F863D2000196D31FD4596C12E030F828D70B0A8309BAF2E089DB3C} + x{70C8C9} + x{73} + x{BBE1FDB3CDB3C6C21} + x{ED44D0D401F863D2000196D31FD4596C12E030F828D70B0A8309BAF2E089DB3C} + x{70C8C9} + x{71} + x{2_} + x{BB1A6DB3CDB3C6C21} + x{ED44D0D401F863D2000196D31FD4596C12E030F828D70B0A8309BAF2E089DB3C} + x{70C8C9} + x{72} + x{2_} + x{B457DDA89A1A40003_} + x{2_} + x{B2D85BC8B6CF169B08A_} + x{ED44D0D401F863D2000196D31FD4596C12E030F828D70B0A8309BAF2E089DB3C} + x{70C8C9} + x{B1B1DBC8B6CF169B08A_} + x{ED44D0D401F863D2000196D31FD4596C12E030F828D70B0A8309BAF2E089DB3C} + x{70C8C9}, + }, +} +`; + +exports[`getters should implement getters correctly 2`] = ` +{ + "$$type": "S", + "a": 1n, + "b": 2n, +} +`; + +exports[`getters should implement getters correctly 3`] = ` +{ + "$$type": "Test$Data", + "anotherData": x{}, + "id": 0n, +} +`; + +exports[`getters should implement getters correctly 4`] = ` +{ + "$$type": "Test$Data", + "anotherData": x{000000000000007B}, + "id": 123n, +} +`; diff --git a/src/test/e2e-emulated/__snapshots__/local-type-inference.spec.ts.snap b/src/test/e2e-emulated/__snapshots__/local-type-inference.spec.ts.snap index f08d8ccac..227d4f5e2 100644 --- a/src/test/e2e-emulated/__snapshots__/local-type-inference.spec.ts.snap +++ b/src/test/e2e-emulated/__snapshots__/local-type-inference.spec.ts.snap @@ -468,6 +468,11 @@ exports[`local-type-inference should automatically set types for let statements "header": null, "name": "MyStruct", }, + { + "fields": [], + "header": null, + "name": "LocalTypeInferenceTester$Data", + }, ], } `; diff --git a/src/test/e2e-emulated/contracts/getter-names-conflict.tact b/src/test/e2e-emulated/contracts/getter-names-conflict.tact deleted file mode 100644 index bcd694034..000000000 --- a/src/test/e2e-emulated/contracts/getter-names-conflict.tact +++ /dev/null @@ -1,16 +0,0 @@ -contract Test { - receive () {} - - get fun testGetter(): Int { - return 1; - } - - get fun test_getter(): Int { - return 2; - } - - get fun Test_getter(): Int { - return 3; - } -} - diff --git a/src/test/e2e-emulated/contracts/getters.tact b/src/test/e2e-emulated/contracts/getters.tact new file mode 100644 index 000000000..0df021129 --- /dev/null +++ b/src/test/e2e-emulated/contracts/getters.tact @@ -0,0 +1,49 @@ +import "@stdlib/deploy"; + +struct S { + a: Int; + b: Int; +} + +message SetIdAndData { + id: Int; + data: Cell; +} + +contract Test with Deployable { + id: Int as uint32 = 0; + anotherData: Cell; + + init() { + self.anotherData = beginCell().endCell(); + } + + receive(msg: SetIdAndData) { + self.id = msg.id; + self.anotherData = msg.data; + } + + get fun testGetter(): Int { + return 1; + } + + get fun test_getter(): Int { + return 2; + } + + get fun Test_getter(): Int { + return 3; + } + + get fun contractData(): Test { + return self; + } + + get fun structAsInput(s: S): S { + return s; + } + + get fun contractAsInput(test: Test): Test { + return test; + } +} \ No newline at end of file diff --git a/src/test/e2e-emulated/getter-names-conflict.spec.ts b/src/test/e2e-emulated/getter-names-conflict.spec.ts deleted file mode 100644 index 9eda79803..000000000 --- a/src/test/e2e-emulated/getter-names-conflict.spec.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { toNano } from "@ton/core"; -import { ContractSystem } from "@tact-lang/emulator"; -import { __DANGER_resetNodeId } from "../../grammar/ast"; -import { - Test, - Test_getterMapping, -} from "./contracts/output/getter-names-conflict_Test"; - -describe("getter-names-conflict", () => { - beforeEach(() => { - __DANGER_resetNodeId(); - }); - it("should handle conflicts in getter names correctly", async () => { - // Init - const system = await ContractSystem.create(); - const treasure = system.treasure("treasure"); - const contract = system.open(await Test.fromInit()); - await contract.send(treasure, { value: toNano("10") }, null); - await system.run(); - expect(contract).toMatchSnapshot(); - - expect(await contract.getTestGetter()).toBe(1n); - expect(await contract.gettest_getter()).toBe(2n); - expect(await contract.getTest_getter()).toBe(3n); - - expect(Test_getterMapping["testGetter"]).toBe("getTestGetter"); - expect(Test_getterMapping["test_getter"]).toBe("gettest_getter"); - expect(Test_getterMapping["Test_getter"]).toBe("getTest_getter"); - }); -}); diff --git a/src/test/e2e-emulated/getters.spec.ts b/src/test/e2e-emulated/getters.spec.ts new file mode 100644 index 000000000..728befa00 --- /dev/null +++ b/src/test/e2e-emulated/getters.spec.ts @@ -0,0 +1,56 @@ +import { beginCell, toNano } from "@ton/core"; +import { ContractSystem } from "@tact-lang/emulator"; +import { __DANGER_resetNodeId } from "../../grammar/ast"; +import { Test, Test_getterMapping } from "./contracts/output/getters_Test"; + +describe("getters", () => { + beforeEach(() => { + __DANGER_resetNodeId(); + }); + it("should implement getters correctly", async () => { + // Init + const system = await ContractSystem.create(); + const treasure = system.treasure("treasure"); + const contract = system.open(await Test.fromInit()); + await contract.send( + treasure, + { value: toNano("10") }, + { + $$type: "Deploy", + queryId: 0n, + }, + ); + await system.run(); + expect(contract).toMatchSnapshot(); + + // Getter name conflicts + expect(await contract.getTestGetter()).toBe(1n); + expect(await contract.gettest_getter()).toBe(2n); + expect(await contract.getTest_getter()).toBe(3n); + + expect(Test_getterMapping["testGetter"]).toBe("getTestGetter"); + expect(Test_getterMapping["test_getter"]).toBe("gettest_getter"); + expect(Test_getterMapping["Test_getter"]).toBe("getTest_getter"); + + // Passing `S` struct to getter + expect( + await contract.getStructAsInput({ + $$type: "S", + a: 1n, + b: 2n, + }), + ).toMatchSnapshot(); + + // Returning `self` from getter + expect(await contract.getContractData()).toMatchSnapshot(); + + // Passing `Test` contract data to getter + expect( + await contract.getContractAsInput({ + $$type: "Test$Data", + id: 123n, + anotherData: beginCell().storeUint(123, 64).endCell(), + }), + ).toMatchSnapshot(); + }); +}); diff --git a/src/types/resolveABITypeRef.ts b/src/types/resolveABITypeRef.ts index f1aab7b67..7b9edbf62 100644 --- a/src/types/resolveABITypeRef.ts +++ b/src/types/resolveABITypeRef.ts @@ -20,6 +20,8 @@ import { throwInternalCompilerError, } from "../errors"; import { TypeRef } from "./types"; +import { CompilerContext } from "../context"; +import { getType } from "./resolveDescriptors"; type FormatDef = Record< string, @@ -376,6 +378,7 @@ export function resolveABIType(src: AstFieldDecl): ABITypeRef { } export function createABITypeRefFromTypeRef( + ctx: CompilerContext, src: TypeRef, loc: SrcInfo, ): ABITypeRef { @@ -412,7 +415,16 @@ export function createABITypeRefFromTypeRef( } // Structs - return { kind: "simple", type: src.name, optional: src.optional }; + const type = getType(ctx, src.name); + if (type.kind === "contract") { + return { + kind: "simple", + type: src.name + "$Data", + optional: src.optional, + }; + } else { + return { kind: "simple", type: src.name, optional: src.optional }; + } } if (src.kind === "map") { diff --git a/tact.config.json b/tact.config.json index ef70b2028..e7a51d39a 100644 --- a/tact.config.json +++ b/tact.config.json @@ -367,8 +367,8 @@ } }, { - "name": "getter-names-conflict", - "path": "./src/test/e2e-emulated/contracts/getter-names-conflict.tact", + "name": "getters", + "path": "./src/test/e2e-emulated/contracts/getters.tact", "output": "./src/test/e2e-emulated/contracts/output" }, {