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

Implement local type inference for let statements #198

Merged
merged 12 commits into from
Jun 14, 2024
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

- Trailing semicolons in struct and message declarations are optional now: PR [#395](https://github.com/tact-lang/tact/pull/395)
- Tests are refactored and renamed to convey the sense of what is being tested and to reduce the amount of merge conflicts during development: PR [#402](https://github.com/tact-lang/tact/pull/402)
- `let` statements can now be used without an explicit type declaration and determine the type automatically if it was not specified: PR [#198](https://github.com/tact-lang/tact/pull/198)

### Fixed

Expand Down
6 changes: 5 additions & 1 deletion src/generator/writers/writeFunction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,11 @@ export function writeStatement(
return;
} else if (f.kind === "statement_let") {
// Contract/struct case
const t = resolveTypeRef(ctx.ctx, f.type);
const t =
f.type === null
? getExpType(ctx.ctx, f.expression)
: resolveTypeRef(ctx.ctx, f.type);

if (t.kind === "ref") {
const tt = getType(ctx.ctx, t.name);
if (tt.kind === "contract" || tt.kind === "struct") {
Expand Down
3 changes: 1 addition & 2 deletions src/grammar/ast.ts
Original file line number Diff line number Diff line change
Expand Up @@ -418,7 +418,7 @@ export type ASTStatementLet = {
kind: "statement_let";
id: number;
name: string;
type: ASTTypeRef;
type: ASTTypeRef | null;
expression: ASTExpression;
ref: ASTRef;
};
Expand Down Expand Up @@ -729,7 +729,6 @@ export function traverse(node: ASTNode, callback: (node: ASTNode) => void) {
//

if (node.kind === "statement_let") {
traverse(node.type, callback);
traverse(node.expression, callback);
}
if (node.kind === "statement_return") {
Expand Down
1 change: 0 additions & 1 deletion src/grammar/clone.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ export function cloneNode<T extends ASTNode>(src: T): T {
} else if (src.kind === "statement_let") {
return cloneASTNode({
...src,
type: cloneASTNode(src.type),
anton-trunov marked this conversation as resolved.
Show resolved Hide resolved
expression: cloneNode(src.expression),
});
} else if (src.kind === "statement_condition") {
Expand Down
2 changes: 1 addition & 1 deletion src/grammar/grammar.ohm
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ Tact {

StatementBlock = "{" Statement* "}"

StatementLet = let id ":" Type "=" Expression ";"
StatementLet = let id (":" Type)? "=" Expression ";"

StatementReturn = return Expression? ";"

Expand Down
2 changes: 1 addition & 1 deletion src/grammar/grammar.ts
anton-trunov marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -577,7 +577,7 @@ semantics.addOperation<ASTNode>("astOfStatement", {
return createNode({
kind: "statement_let",
name: id.sourceString,
type: type.astOfType(),
type: unwrapOptNode(type, (t) => t.astOfType()),
expression: expression.astOfExpression(),
ref: createRef(this),
});
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,318 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`local-type-inference should automatically set types for let statements 1`] = `
{
"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": "test1",
"returnType": {
"format": 257,
"kind": "simple",
"optional": false,
"type": "int",
},
},
{
"arguments": [],
"name": "test2",
"returnType": {
"format": 257,
"kind": "simple",
"optional": false,
"type": "int",
},
},
{
"arguments": [],
"name": "test3",
"returnType": {
"kind": "simple",
"optional": false,
"type": "address",
},
},
{
"arguments": [],
"name": "test4",
"returnType": {
"kind": "simple",
"optional": false,
"type": "address",
},
},
{
"arguments": [],
"name": "test5",
"returnType": {
"kind": "simple",
"optional": false,
"type": "bool",
},
},
],
"receivers": [
{
"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",
},
],
}
`;
Loading
Loading