Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: various bugs in traits #1591

Merged
merged 22 commits into from
Feb 5, 2025
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
030b3af
fix: support overriding constant and methods of BaseTrait
i582 Jan 27, 2025
f7f3a90
revert comment
i582 Jan 27, 2025
9a8552f
added CHANGELOG.md entry
i582 Jan 27, 2025
439c5a2
remove unnecessary code
i582 Jan 27, 2025
3da1825
missed `;`
i582 Jan 27, 2025
8b61245
Merge branch 'main' into pmakhnev/constants-override-in-traits
i582 Jan 27, 2025
27a2010
Merge branch 'main' into pmakhnev/constants-override-in-traits
i582 Jan 27, 2025
842c1c7
Merge branch 'main' into pmakhnev/constants-override-in-traits
i582 Jan 28, 2025
e72a705
Merge branch 'main' into pmakhnev/constants-override-in-traits
anton-trunov Jan 29, 2025
065723a
Merge branch 'main' into pmakhnev/constants-override-in-traits
anton-trunov Jan 31, 2025
50478e8
Merge branch 'main' into pmakhnev/constants-override-in-traits
i582 Feb 4, 2025
544d342
fix: forbid traits inherit implicitly from BaseTrait
i582 Feb 4, 2025
91ea3ff
added note in documentation
i582 Feb 4, 2025
805d3e6
added entry to CHANGELOG.md
i582 Feb 4, 2025
72cb3a9
review fixes
i582 Feb 4, 2025
ab222e3
Merge branch 'main' into pmakhnev/constants-override-in-traits
i582 Feb 5, 2025
11c883e
Merge branch 'main' into pmakhnev/constants-override-in-traits
i582 Feb 5, 2025
3b351ed
check that the overridden constant has a super constant
i582 Feb 5, 2025
e4e96d2
added entry to CHANGELOG.md
i582 Feb 5, 2025
e254a9c
updated snapshots
i582 Feb 5, 2025
822eb8e
Merge branch 'main' into pmakhnev/constants-override-in-traits
i582 Feb 5, 2025
37bb315
inline isOverride
i582 Feb 5, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions dev-docs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Forbid read and write to self in contract init function: PR [#1482](https://github.com/tact-lang/tact/pull/1482)
- Support for using a constant within another constant and for the default value of a struct field before constant declaration: PR [#1478](https://github.com/tact-lang/tact/pull/1478)
- Incorrect call generation to a mutation function: PR [#1608](https://github.com/tact-lang/tact/pull/1608)
- Support overriding constants and methods of BaseTrait: PR [#1591](https://github.com/tact-lang/tact/pull/1591)
- Allow constant/trait constants depend on each other: PR [#1622](https://github.com/tact-lang/tact/pull/1622)

### Docs
Expand Down
36 changes: 36 additions & 0 deletions src/test/e2e-emulated/base-trait-constant-override-1.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { toNano } from "@ton/core";
import { Blockchain, SandboxContract, TreasuryContract } from "@ton/sandbox";
import { TraitsConstantContract } from "./contracts/output/base-trait-constant-override-1_TraitsConstantContract";
import "@ton/test-utils";

describe("base-trait-constant-override-1", () => {
let blockchain: Blockchain;
let treasure: SandboxContract<TreasuryContract>;
let contract: SandboxContract<TraitsConstantContract>;

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

contract = blockchain.openContract(
await TraitsConstantContract.fromInit(),
);

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

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

it("should override constant correctly", async () => {
expect(await contract.getConstant()).toEqual(100n);
});
});
36 changes: 36 additions & 0 deletions src/test/e2e-emulated/base-trait-constant-override-2.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { toNano } from "@ton/core";
import { Blockchain, SandboxContract, TreasuryContract } from "@ton/sandbox";
import { TraitsConstantContract } from "./contracts/output/base-trait-constant-override-2_TraitsConstantContract";
import "@ton/test-utils";

describe("base-trait-constant-override-2", () => {
let blockchain: Blockchain;
let treasure: SandboxContract<TreasuryContract>;
let contract: SandboxContract<TraitsConstantContract>;

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

contract = blockchain.openContract(
await TraitsConstantContract.fromInit(),
);

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

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

it("should override constant correctly", async () => {
expect(await contract.getConstant()).toEqual(10000n);
});
});
36 changes: 36 additions & 0 deletions src/test/e2e-emulated/base-trait-function-override.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { toNano } from "@ton/core";
import { Blockchain, SandboxContract, TreasuryContract } from "@ton/sandbox";
import { BaseTraitsFunctionContract } from "./contracts/output/base-trait-function-override_BaseTraitsFunctionContract";
import "@ton/test-utils";

describe("base-trait-function-override", () => {
let blockchain: Blockchain;
let treasure: SandboxContract<TreasuryContract>;
let contract: SandboxContract<BaseTraitsFunctionContract>;

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

contract = blockchain.openContract(
await BaseTraitsFunctionContract.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 override function correctly", async () => {
expect(await contract.getValue()).toEqual(1000n);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
trait OverrideBaseTraitConstant {
override const storageReserve: Int = 100;
}

contract TraitsConstantContract with OverrideBaseTraitConstant {
receive() {
// deploy
}

get fun constant(): Int {
return self.storageReserve;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
trait OverrideBaseTraitConstant {
override const storageReserve: Int = 100;
}

trait OverrideBaseTraitConstantWithVirtual {
override virtual const storageReserve: Int = 100;
}

trait OverrideOverridden with OverrideBaseTraitConstantWithVirtual {
override const storageReserve: Int = 10000;
}

contract TraitsConstantContract with OverrideOverridden {
receive() {
// deploy
}

get fun constant(): Int {
return self.storageReserve;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
trait OverrideBaseTraitFunction {
value: Int;

override fun notify(body: Cell?) {
self.value = 1000;
}
}

contract BaseTraitsFunctionContract with OverrideBaseTraitFunction {
value: Int = 0;

receive() {
// deploy
}

get fun value(): Int {
self.notify(null);
return self.value;
}
}
13 changes: 11 additions & 2 deletions src/test/e2e-emulated/contracts/traits.tact
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,16 @@ trait Laika with Dog {
}
}

contract LaikaContract with Laika {
// empty contract, we just inherit everything from the traits
trait WithConstant {
virtual const foo: Int = 10;
}

trait IWantToOverrideConstant with WithConstant {
override const foo: Int = 100;
}

contract LaikaContract with Laika, IWantToOverrideConstant {
get fun fooConstant(): Int {
return self.foo;
}
}
5 changes: 5 additions & 0 deletions src/test/e2e-emulated/traits.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,9 @@ describe("traits", () => {
// Check the contract's behavior after deployment
expect(await contract.getSay()).toBe("I am a Laika and I say Woof");
});

it("should override constant correctly", async () => {
// Check the contract's behavior after deployment
expect(await contract.getFooConstant()).toBe(100n);
});
});
50 changes: 34 additions & 16 deletions src/types/resolveDescriptors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -588,14 +588,6 @@ export function resolveDescriptors(ctx: CompilerContext, Ast: FactoryAst) {
traitDecl.loc,
);
}
if (
traitDecl.attributes.find((v) => v.type === "override")
) {
throwCompilationError(
`Trait constant cannot be overridden`,
traitDecl.loc,
);
}
types
.get(idText(a.name))!
.constants.push(buildConstantDescription(traitDecl));
Expand Down Expand Up @@ -1689,10 +1681,17 @@ export function resolveDescriptors(ctx: CompilerContext, Ast: FactoryAst) {
funInContractOrTrait.ast.loc,
);
}
throwCompilationError(
`Function "${traitFunction.name}" is already defined in trait "${inheritedTrait.name}"`,
funInContractOrTrait.ast.loc,
);

if (
traitFunction.ast.attributes.find(
(v) => v.type === "override",
) === undefined
) {
throwCompilationError(
`Function "${traitFunction.name}" is already defined in trait "${inheritedTrait.name}"`,
funInContractOrTrait.ast.loc,
);
}
}

// Register function
Expand Down Expand Up @@ -1755,10 +1754,17 @@ export function resolveDescriptors(ctx: CompilerContext, Ast: FactoryAst) {
constInContractOrTrait.ast.loc,
);
}
throwCompilationError(
`Constant "${traitConstant.name}" is already defined in trait "${inheritedTrait.name}"`,
constInContractOrTrait.ast.loc,
);

if (
traitConstant.ast.attributes.find(
(v) => v.type === "override",
) === undefined
) {
throwCompilationError(
`Constant "${traitConstant.name}" is already defined in trait "${inheritedTrait.name}"`,
constInContractOrTrait.ast.loc,
);
}
}
const contractField = contractOrTrait.fields.find(
(v) => v.name === traitConstant.name,
Expand All @@ -1771,6 +1777,18 @@ export function resolveDescriptors(ctx: CompilerContext, Ast: FactoryAst) {
);
}

if (
traitConstant.ast.attributes.find(
(v) => v.type === "override",
)
) {
// remove overridden constant
contractOrTrait.constants =
contractOrTrait.constants.filter(
(c) => c.name !== traitConstant.name,
);
}

// Register constant
contractOrTrait.constants.push({
...traitConstant,
Expand Down