Skip to content

Commit

Permalink
fix(codegen): self argument in optional struct methods
Browse files Browse the repository at this point in the history
  • Loading branch information
anton-trunov committed Jan 4, 2025
1 parent 06cb9f4 commit 667a67e
Show file tree
Hide file tree
Showing 6 changed files with 68 additions and 6 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- TypeScript wrappers generation for messages with single quote: PR [#1106](https://github.com/tact-lang/tact/pull/1106)
- `foreach` loops now properly handle `as coins` map value serialization type: PR [#1186](https://github.com/tact-lang/tact/pull/1186)
- The typechecker now rejects integer map key types with variable width (`coins`, `varint16`, `varint32`, `varuint16`, `varuint32`): PR [#1276](https://github.com/tact-lang/tact/pull/1276)
- Code generation for `self` argument in optional struct methods: PR [#XXX](https://github.com/tact-lang/tact/pull/XXX)

### Docs

Expand Down
3 changes: 1 addition & 2 deletions src/generator/writers/writeExpression.ts
Original file line number Diff line number Diff line change
Expand Up @@ -624,8 +624,7 @@ export function writeExpression(f: AstExpression, wCtx: WriterContext): string {
}
}

// Render
const s = writeExpression(f.self, wCtx);
const s = writeCastedExpression(f.self, methodDescr.self!, wCtx);
if (methodDescr.isMutating) {
// check if it's an l-value
const path = tryExtractPath(f.self);
Expand Down
12 changes: 8 additions & 4 deletions src/generator/writers/writeFunction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -527,8 +527,10 @@ function writeCondition(
}

export function writeFunction(f: FunctionDescription, ctx: WriterContext) {
// Resolve self
const self = f.self?.kind === "ref" ? getType(ctx.ctx, f.self.name) : null;
const [self, isSelfOpt] =
f.self?.kind === "ref"
? [getType(ctx.ctx, f.self.name), f.self.optional]
: [null, false];

// Write function header
let returns: string = resolveFuncType(f.returns, ctx);
Expand All @@ -546,7 +548,9 @@ export function writeFunction(f: FunctionDescription, ctx: WriterContext) {
// Resolve function descriptor
const params: string[] = [];
if (self) {
params.push(resolveFuncType(self, ctx) + " " + funcIdOf("self"));
params.push(
resolveFuncType(self, ctx, isSelfOpt) + " " + funcIdOf("self"),
);
}
for (const a of f.params) {
params.push(resolveFuncType(a.type, ctx) + " " + funcIdOf(a.name));
Expand Down Expand Up @@ -619,7 +623,7 @@ export function writeFunction(f: FunctionDescription, ctx: WriterContext) {
}
ctx.body(() => {
// Unpack self
if (self) {
if (self && !isSelfOpt) {
ctx.append(
`var (${resolveFuncTypeUnpack(self, funcIdOf("self"), ctx)}) = ${funcIdOf("self")};`,
);
Expand Down
18 changes: 18 additions & 0 deletions src/test/e2e-emulated/contracts/non-mutating-methods.tact
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
struct SomeStruct { i: Int; b: Bool }

extends fun equal(self: SomeStruct?, other: SomeStruct?): Bool {
if (self == null && other == null) { return true }
if (self == null || other == null) { return false }
return self!!.i == other!!.i && self!!.b == other!!.b;
}

contract Tester {
receive() { }

get fun test1(): Bool {
let s1 = SomeStruct {i: 42, b: true};
let s2 = SomeStruct {i: 42, b: false};
let s3: SomeStruct? = null;
return s1.equal(s1) && !s1.equal(s2) && !s3.equal(s2);
}
}
35 changes: 35 additions & 0 deletions src/test/e2e-emulated/non-mutating-methods.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { toNano } from "@ton/core";
import { Blockchain, SandboxContract, TreasuryContract } from "@ton/sandbox";
import { Tester } from "./contracts/output/non-mutating-methods_Tester";
import "@ton/test-utils";

describe("bugs", () => {
let blockchain: Blockchain;
let treasure: SandboxContract<TreasuryContract>;
let contract: SandboxContract<Tester>;

beforeEach(async () => {
blockchain = await Blockchain.create();
blockchain.verbosity.print = false;
treasure = await blockchain.treasury("treasure");

contract = blockchain.openContract(await Tester.fromInit());

const deployResult = await contract.send(
treasure.getSender(),
{ value: toNano("10") },
null,
);

expect(deployResult.transactions).toHaveTransaction({
from: treasure.address,
to: contract.address,
success: true,
deploy: true,
});
});

it("should implement non-mutating method chaining correctly", async () => {
expect(await contract.getTest1()).toStrictEqual(true);
});
});
5 changes: 5 additions & 0 deletions tact.config.json
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,11 @@
"path": "./src/test/e2e-emulated/contracts/mutating-methods.tact",
"output": "./src/test/e2e-emulated/contracts/output"
},
{
"name": "non-mutating-methods",
"path": "./src/test/e2e-emulated/contracts/non-mutating-methods.tact",
"output": "./src/test/e2e-emulated/contracts/output"
},
{
"name": "underscore-variable",
"path": "./src/test/e2e-emulated/contracts/underscore-variable.tact",
Expand Down

0 comments on commit 667a67e

Please sign in to comment.