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: do not allow assignments to constants #682

Merged
merged 1 commit into from
Aug 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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 CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Typechecking for optional types when the argument type is not an equality type: PR [#650](https://github.com/tact-lang/tact/pull/650)
- Getters now return flattened types for structs as before: PR [#679](https://github.com/tact-lang/tact/pull/679)
- New bindings cannot shadow global constants: PR [#680](https://github.com/tact-lang/tact/pull/680)
- Disallow using assignment operators on constants: PR [#682](https://github.com/tact-lang/tact/pull/682)

## [1.4.1] - 2024-07-26

Expand Down
105 changes: 105 additions & 0 deletions src/types/__snapshots__/resolveStatements.spec.ts.snap
Original file line number Diff line number Diff line change
@@ -1,5 +1,85 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`resolveStatements should fail statements for assign-augmented-const-contract 1`] = `
"<unknown>:8:9: Modifications of constant expressions are not allowed
Line 8, col 9:
7 | fun bar() {
> 8 | self.foo += 1 // <-- trying to assign to a constant
^~~~~~~~
9 | }
"
`;
exports[`resolveStatements should fail statements for assign-augmented-const-global 1`] = `
"<unknown>:6:5: Modifications of constant expressions are not allowed
Line 6, col 5:
5 | fun bar() {
> 6 | foo += 1 // <-- trying to assign to a constant
^~~
7 | }
"
`;
exports[`resolveStatements should fail statements for assign-augmented-const-struct-contract 1`] = `
"<unknown>:12:9: Modifications of constant expressions are not allowed
Line 12, col 9:
11 | self.baz.x += 1; // ok
> 12 | self.foo.y -= 1 // <-- trying to assign to a constant
^~~~~~~~~~
13 | }
"
`;
exports[`resolveStatements should fail statements for assign-augmented-const-struct-global 1`] = `
"<unknown>:10:5: Modifications of constant expressions are not allowed
Line 10, col 5:
9 | baz.x += 1; // ok
> 10 | foo.y -= 1 // <-- trying to assign to a constant
^~~~~
11 | }
"
`;
exports[`resolveStatements should fail statements for assign-const-contract 1`] = `
"<unknown>:8:9: Modifications of constant expressions are not allowed
Line 8, col 9:
7 | fun bar() {
> 8 | self.foo = 43 // <-- trying to assign to a constant
^~~~~~~~
9 | }
"
`;
exports[`resolveStatements should fail statements for assign-const-global 1`] = `
"<unknown>:6:5: Modifications of constant expressions are not allowed
Line 6, col 5:
5 | fun bar() {
> 6 | foo = 43 // <-- trying to assign to a constant
^~~
7 | }
"
`;
exports[`resolveStatements should fail statements for assign-const-struct-contract 1`] = `
"<unknown>:12:9: Modifications of constant expressions are not allowed
Line 12, col 9:
11 | self.baz.x = 1; // ok
> 12 | self.foo.y = 42 // <-- trying to assign to a constant
^~~~~~~~~~
13 | }
"
`;
exports[`resolveStatements should fail statements for assign-const-struct-global 1`] = `
"<unknown>:10:5: Modifications of constant expressions are not allowed
Line 10, col 5:
9 | baz.x = 1; // ok
> 10 | foo.y = 42 // <-- trying to assign to a constant
^~~~~
11 | }
"
`;
exports[`resolveStatements should fail statements for bounced-type-is-smaller 1`] = `
"<unknown>:23:22: Type bounced<"A"> does not have a field named "c"
Line 23, col 22:
Expand Down Expand Up @@ -1085,6 +1165,31 @@ Line 9, col 12:
"
`;
exports[`resolveStatements should resolve statements for assign-self-mutating-method 1`] = `
[
[
"self",
"IntWrapper",
],
[
"self.x",
"Int",
],
[
"42",
"Int",
],
[
"self",
"Int",
],
[
"42",
"Int",
],
]
`;
exports[`resolveStatements should resolve statements for contract-receiver-bounced 1`] = `
[
[
Expand Down
38 changes: 38 additions & 0 deletions src/types/resolveStatements.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import {
idText,
isWildcard,
selfId,
isSelfId,
eqNames,
} from "../grammar/ast";
import { isAssignable } from "./subtyping";
import {
Expand All @@ -18,6 +20,7 @@ import {
import {
getAllStaticFunctions,
getAllTypes,
getType,
hasStaticConstant,
resolveTypeRef,
} from "./resolveDescriptors";
Expand Down Expand Up @@ -183,6 +186,29 @@ function processCondition(
};
}

// Precondition: `self` here means a contract or a trait,
// and not a `self` parameter of a mutating method
function isLvalue(path: AstId[], ctx: CompilerContext): boolean {
const headId = path[0]!;
if (isSelfId(headId) && path.length > 1) {
// we can be dealing with a contract/trait constant `self.constFoo`
const selfTypeRef = getExpType(ctx, headId);
if (selfTypeRef.kind == "ref") {
const contractTypeDescription = getType(ctx, selfTypeRef.name);
return (
contractTypeDescription.constants.findIndex((constDescr) =>
eqNames(path[1]!, constDescr.name),
) === -1
);
} else {
return true;
}
} else {
// if the head path symbol is a global constant, then the whole path expression is a constant
return !hasStaticConstant(ctx, idText(headId));
}
}

function processStatements(
statements: AstStatement[],
sctx: StatementContext,
Expand Down Expand Up @@ -251,6 +277,12 @@ function processStatements(
s.path.loc,
);
}
if (!isLvalue(path, ctx)) {
throwCompilationError(
"Modifications of constant expressions are not allowed",
s.path.loc,
);
}

// Process expression
ctx = resolveExpression(s.expression, sctx, ctx);
Expand Down Expand Up @@ -289,6 +321,12 @@ function processStatements(
s.path.loc,
);
}
if (!isLvalue(path, ctx)) {
throwCompilationError(
"Modifications of constant expressions are not allowed",
s.path.loc,
);
}

// Process expression
ctx = resolveExpression(s.expression, sctx, ctx);
Expand Down
10 changes: 10 additions & 0 deletions src/types/stmts-failed/assign-augmented-const-contract.tact
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
primitive Int;
trait BaseTrait { }

contract Test {
const foo: Int = 42;

fun bar() {
self.foo += 1 // <-- trying to assign to a constant
}
}
7 changes: 7 additions & 0 deletions src/types/stmts-failed/assign-augmented-const-global.tact
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
primitive Int;

const foo: Int = 42;

fun bar() {
foo += 1 // <-- trying to assign to a constant
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
primitive Int;
trait BaseTrait { }

struct Foo {x: Int; y: Int}

contract Test {
const foo: Foo = Foo {x: 42, y: 43};
baz: Foo = Foo {x: 42, y: 43};

fun bar() {
self.baz.x += 1; // ok
self.foo.y -= 1 // <-- trying to assign to a constant
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
primitive Int;

struct Foo {x: Int; y: Int}

const foo: Foo = Foo {x: 42, y: 43};

fun bar() {
let baz: Foo = Foo {x: 42, y: 43};
baz.x += 1; // ok
foo.y -= 1 // <-- trying to assign to a constant
}
10 changes: 10 additions & 0 deletions src/types/stmts-failed/assign-const-contract.tact
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
primitive Int;
trait BaseTrait { }

contract Test {
const foo: Int = 42;

fun bar() {
self.foo = 43 // <-- trying to assign to a constant
}
}
7 changes: 7 additions & 0 deletions src/types/stmts-failed/assign-const-global.tact
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
primitive Int;

const foo: Int = 42;

fun bar() {
foo = 43 // <-- trying to assign to a constant
}
14 changes: 14 additions & 0 deletions src/types/stmts-failed/assign-const-struct-contract.tact
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
primitive Int;
trait BaseTrait { }

struct Foo {x: Int; y: Int}

contract Test {
const foo: Foo = Foo {x: 42, y: 43};
baz: Foo = Foo {x: 42, y: 43};

fun bar() {
self.baz.x = 1; // ok
self.foo.y = 42 // <-- trying to assign to a constant
}
}
11 changes: 11 additions & 0 deletions src/types/stmts-failed/assign-const-struct-global.tact
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
primitive Int;

struct Foo {x: Int; y: Int}

const foo: Foo = Foo {x: 42, y: 43};

fun bar() {
let baz: Foo = Foo {x: 42, y: 43};
baz.x = 1; // ok
foo.y = 42 // <-- trying to assign to a constant
}
11 changes: 11 additions & 0 deletions src/types/stmts/assign-self-mutating-method.tact
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
primitive Int;

struct IntWrapper { x: Int }

extends mutates fun foo(self: IntWrapper) {
self.x = 42
}

extends mutates fun bar(self: Int) {
self = 42
}
Loading