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

Expand reduceBool and add reduceSlice #197

Closed
wants to merge 4 commits into from
Closed
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 @@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed
- Update the `dump` function to handle addresses: PR [#175](https://github.com/tact-lang/tact/pull/175)
- The implicit empty `init` function is now present by default in the contract if not declared: PR [#167](https://github.com/tact-lang/tact/pull/167)
- Improved `Bool` reduction in constant expressions: PR [#197](https://github.com/tact-lang/tact/pull/197)

### Fixed

Expand Down
80 changes: 80 additions & 0 deletions src/test/features/constants.tact
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,22 @@ contract ConstantTester {
const something6: Int = 10 * 1;
const something7: Int = 10 >> 1;
const something8: Int = (2 + 4) & 4;
const something9: Bool = true && false;
const something10: Bool = true || false;
const something11: Bool = !true;
const something12: Bool = true == false;
const something13: Bool = true != false;
const something14: Bool = !(true == false);
const something15: Bool = 5 > 5;
const something16: Bool = 5 >= 5;
const something17: Bool = "Hello" == "Hello";
const something18: Bool = "Hello" != "Hello";
const something19: Bool = "Hello".asCell() == "Hello".asCell();
const something20: Bool = "Hello".asCell() != "Hello".asCell();
const something21: Bool = newAddress(0, 0x606813c5f6a76175eae668630c6d8ffe229543610e3d204db245dd51f9ba0503) == newAddress(0, 0x606813c5f6a76175eae668630c6d8ffe229543610e3d204db245dd51f9ba0503);
const something22: Bool = newAddress(0, 0x606813c5f6a76175eae668630c6d8ffe229543610e3d204db245dd51f9ba0503) != newAddress(0, 0x606813c5f6a76175eae668630c6d8ffe229543610e3d204db245dd51f9ba0503);
const something23: Bool = "Hello".asCell() == "Hello".asCell();
const something24: Bool = "Hello".asCell() != "Hello".asCell();

init() {

Expand Down Expand Up @@ -49,4 +65,68 @@ contract ConstantTester {
get fun globalConst(): Int {
return someGlobalConst;
}

get fun something9(): Bool {
return self.something9;
}

get fun something10(): Bool {
return self.something10;
}

get fun something11(): Bool {
return self.something11;
}

get fun something12(): Bool {
return self.something12;
}

get fun something13(): Bool {
return self.something13;
}

get fun something14(): Bool {
return self.something14;
}

get fun something15(): Bool {
return self.something15;
}

get fun something16(): Bool {
return self.something16;
}

get fun something17(): Bool {
return self.something17;
}

get fun something18(): Bool {
return self.something18;
}

get fun something19(): Bool {
return self.something19;
}

get fun something20(): Bool {
return self.something20;
}

get fun something21(): Bool {
return self.something21;
}

get fun something22(): Bool {
return self.something22;
}

get fun something23(): Bool {
return self.something23;
}

get fun something24(): Bool {
return self.something24;
}
}
142 changes: 108 additions & 34 deletions src/types/resolveConstantValue.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,22 @@
import { Address, Cell, toNano } from "@ton/core";
import { Address, Cell, Slice, toNano } from "@ton/core";
import { enabledMasterchain } from "../config/features";
import { CompilerContext } from "../context";
import { ASTExpression, throwError } from "../grammar/ast";
import { ASTExpression, ASTOpCallStatic, throwError } from "../grammar/ast";
import { printTypeRef, TypeRef } from "./types";
import { sha256_sync } from "@ton/crypto";
import { getExpType } from "./resolveExpression";

function isSlice(ast: ASTExpression): boolean {
return (ast.kind === 'op_static_call') && (ast.name === 'slice') && (ast.args.length === 1);
}

function isCell(ast: ASTExpression): boolean {
return (ast.kind === 'op_static_call') && (ast.name === 'cell') && (ast.args.length === 1);
}

function isAddress(ast: ASTExpression): boolean {
return (ast.kind === 'op_static_call') && (ast.name === 'address') && (ast.args.length === 1);
}

function reduceInt(ast: ASTExpression): bigint {
if (ast.kind === 'number') {
Expand Down Expand Up @@ -59,22 +72,70 @@ function reduceInt(ast: ASTExpression): bigint {
throwError('Cannot reduce expression to a constant integer', ast.ref);
}

function reduceBool(ast: ASTExpression): boolean {
function reduceBool(ast: ASTExpression, ctx: CompilerContext): boolean {
if (ast.kind === 'boolean') {
return ast.value;
}
if (ast.kind === 'op_unary') {
if (ast.op === '!') {
return !reduceBool(ast.right);
return !reduceBool(ast.right, ctx);
}
}
if (ast.kind === 'op_binary') {
if (ast.op === '&&') {
return reduceBool(ast.left) && reduceBool(ast.right);
return reduceBool(ast.left, ctx) && reduceBool(ast.right, ctx);
} else if (ast.op === '||') {
return reduceBool(ast.left) || reduceBool(ast.right);
return reduceBool(ast.left, ctx) || reduceBool(ast.right, ctx);
} else {
const leftType = getExpType(ctx, ast.left);
const rightType = getExpType(ctx, ast.right);
if (ast.op === '>') {
return reduceInt(ast.left) > reduceInt(ast.right);
} else if (ast.op === '<') {
return reduceInt(ast.left) < reduceInt(ast.right);
} else if (ast.op === '>=') {
return reduceInt(ast.left) >= reduceInt(ast.right);
} else if (ast.op === '<=') {
return reduceInt(ast.left) <= reduceInt(ast.right);
} else if (ast.op === '==') {
if (leftType.kind === 'ref' && rightType.kind === 'ref') {
Gusarich marked this conversation as resolved.
Show resolved Hide resolved
if (leftType.name === 'Address' && rightType.name === 'Address') {
return reduceAddress(ast.left, ctx).equals(reduceAddress(ast.right, ctx));
} else if (leftType.name === 'Cell' && rightType.name === 'Cell') {
return reduceCell(ast.left).equals(reduceCell(ast.right));
} else if (leftType.name === 'String' && rightType.name === 'String') {
return reduceString(ast.left) === reduceString(ast.right);
} else if (leftType.name === 'Int' && rightType.name === 'Int') {
return reduceInt(ast.left) === reduceInt(ast.right);
} else if (leftType.name === 'Bool' && rightType.name === 'Bool') {
return reduceBool(ast.left, ctx) === reduceBool(ast.right, ctx);
} else if (leftType.name === 'Slice' && rightType.name === 'Slice') {
return reduceSlice(ast.left).asCell().equals(reduceSlice(ast.right).asCell());
}
} else if (leftType.kind === 'null' && rightType.kind === 'null') {
return true;
Gusarich marked this conversation as resolved.
Show resolved Hide resolved
}
} else if (ast.op === '!=') {
if (leftType.kind === 'ref' && rightType.kind === 'ref') {
if (leftType.name === 'Address' && rightType.name === 'Address') {
return !reduceAddress(ast.left, ctx).equals(reduceAddress(ast.right, ctx));
} else if (leftType.name === 'Cell' && rightType.name === 'Cell') {
return !reduceCell(ast.left).equals(reduceCell(ast.right));
} else if (leftType.name === 'String' && rightType.name === 'String') {
return reduceString(ast.left) !== reduceString(ast.right);
} else if (leftType.name === 'Int' && rightType.name === 'Int') {
return reduceInt(ast.left) !== reduceInt(ast.right);
} else if (leftType.name === 'Bool' && rightType.name === 'Bool') {
return reduceBool(ast.left, ctx) !== reduceBool(ast.right, ctx);
} else if (leftType.name === 'Slice' && rightType.name === 'Slice') {
return !reduceSlice(ast.left).asCell().equals(reduceSlice(ast.right).asCell());
}
} else if (leftType.kind === 'null' && rightType.kind === 'null') {
return false;
}
}
return true;
}
// TODO: More cases
}

throwError('Cannot reduce expression to a constant boolean', ast.ref);
Expand All @@ -88,44 +149,57 @@ function reduceString(ast: ASTExpression): string {
}

function reduceAddress(ast: ASTExpression, ctx: CompilerContext): Address {
if (ast.kind === 'op_static_call') {
if (ast.name === 'address') {
if (ast.args.length === 1) {
const str = reduceString(ast.args[0]);
const address = Address.parse(str);
if (address.workChain !== 0 && address.workChain !== -1) {
throwError(`Address ${str} invalid address`, ast.ref);
}
if (!enabledMasterchain(ctx)) {
if (address.workChain !== 0) {
throwError(`Address ${str} from masterchain are not enabled for this contract`, ast.ref);
}
}
return address;
if (isAddress(ast)) {
ast = ast as ASTOpCallStatic;
const str = reduceString(ast.args[0]);
const address = Address.parse(str);
if (address.workChain !== 0 && address.workChain !== -1) {
throwError(`Address ${str} invalid address`, ast.ref);
}
if (!enabledMasterchain(ctx)) {
if (address.workChain !== 0) {
throwError(
`Address ${str} from masterchain are not enabled for this contract`,
ast.ref
);
}
}
return address;
}
throwError('Cannot reduce expression to a constant Address', ast.ref);
}

function reduceCell(ast: ASTExpression): Cell {
if (ast.kind === 'op_static_call') {
if (ast.name === 'cell') {
if (ast.args.length === 1) {
const str = reduceString(ast.args[0]);
let c: Cell;
try {
c = Cell.fromBase64(str);
} catch (e) {
throwError(`Invalid cell ${str}`, ast.ref);
}
return c;
}
if (isCell(ast)) {
ast = ast as ASTOpCallStatic;
const str = reduceString(ast.args[0]);
let c: Cell;
try {
c = Cell.fromBase64(str);
} catch (e) {
throwError(`Invalid cell ${str}`, ast.ref);
}
return c;
}

throwError('Cannot reduce expression to a constant Cell', ast.ref);
}

function reduceSlice(ast: ASTExpression): Slice {
if (isSlice(ast)) {
ast = ast as ASTOpCallStatic;
const str = reduceString(ast.args[0]);
let c: Cell;
try {
c = Cell.fromBase64(str);
} catch (e) {
throwError(`Invalid cell ${str}`, ast.ref);
}
return c.asSlice();
}
throwError('Cannot reduce expression to a constant Slice', ast.ref);
}

export function resolveConstantValue(type: TypeRef, ast: ASTExpression | null, ctx: CompilerContext) {
if (ast === null) {
return undefined;
Expand All @@ -149,7 +223,7 @@ export function resolveConstantValue(type: TypeRef, ast: ASTExpression | null, c

// Handle bool
if (type.name === 'Bool') {
return reduceBool(ast);
return reduceBool(ast, ctx);
}

// Handle string
Expand Down
Loading