From 8578be7b36592d3e8c2c698fc06fb26f04f324f4 Mon Sep 17 00:00:00 2001 From: jeshecdom <jeshecdom@hotmail.com> Date: Thu, 9 Jan 2025 17:44:54 +0100 Subject: [PATCH] Final changes after review. --- src/abi/global.ts | 132 ++++++++++-------- src/constEval.ts | 8 +- src/generator/writers/writeExpression.spec.ts | 2 +- src/generator/writers/writeExpression.ts | 13 +- src/grammar/ast.ts | 15 +- src/grammar/test/expr-is-value.spec.ts | 19 +-- src/interpreter.ts | 114 ++++++++++----- src/optimizer/associative.ts | 115 ++++++++++----- src/optimizer/test/partial-eval.spec.ts | 10 +- src/pipeline/precompile.ts | 6 +- src/prettyPrinter.ts | 3 +- src/storage/resolveAllocation.spec.ts | 4 +- .../const-eval-failed.spec.ts | 2 +- .../resolveDescriptors.spec.ts.snap | 4 +- src/types/resolveDescriptors.spec.ts | 4 +- src/types/resolveDescriptors.ts | 17 +-- src/types/resolveErrors.ts | 29 ++-- src/types/resolveExpression.ts | 25 ++-- src/types/resolveSignatures.ts | 9 +- src/types/resolveStatements.spec.ts | 6 +- src/types/resolveStatements.ts | 11 +- 21 files changed, 340 insertions(+), 208 deletions(-) diff --git a/src/abi/global.ts b/src/abi/global.ts index 1cd8201a2..4bd5ff988 100644 --- a/src/abi/global.ts +++ b/src/abi/global.ts @@ -4,25 +4,22 @@ import { writeAddress, writeCell, writeSlice, + writeString, } from "../generator/writers/writeConstant"; -import { - writeExpression, - writeValue, -} from "../generator/writers/writeExpression"; -import { TactConstEvalError, throwCompilationError } from "../errors"; -import { evalConstantExpression } from "../constEval"; +import { writeExpression } from "../generator/writers/writeExpression"; +import { throwCompilationError } from "../errors"; import { getErrorId } from "../types/resolveErrors"; import { AbiFunction } from "./AbiFunction"; import { sha256_sync } from "@ton/crypto"; import path from "path"; import { cwd } from "process"; import { posixNormalize } from "../utils/filePath"; -import { ensureSimplifiedString } from "../interpreter"; -import { getAstFactory } from "../grammar/ast"; -import { getAstUtil } from "../optimizer/util"; -import { dummySrcInfo } from "../grammar"; - -const util = getAstUtil(getAstFactory()); +import { + ensureSimplifiedString, + ensureString, + interpretEscapeSequences, +} from "../interpreter"; +import { isLiteral } from "../grammar/ast"; export const GlobalFunctions: Map<string, AbiFunction> = new Map([ [ @@ -58,10 +55,14 @@ export const GlobalFunctions: Map<string, AbiFunction> = new Map([ ref, ); } - const str = ensureSimplifiedString( - evalConstantExpression(resolved[0]!, ctx.ctx), + const resolved0 = resolved[0]!; + // FIXME: When optimizer step added, change the following line to: + // const str = ensureSimplifiedString(resolved0).value; + const str = interpretEscapeSequences( + ensureString(resolved0).value, + resolved0.loc, ); - return toNano(str.value).toString(10); + return toNano(str).toString(10); }, }, ], @@ -111,10 +112,14 @@ export const GlobalFunctions: Map<string, AbiFunction> = new Map([ ref, ); } - const str = ensureSimplifiedString( - evalConstantExpression(resolved[1]!, ctx.ctx), + const resolved1 = resolved[1]!; + // FIXME: When optimizer step added, change the following line to: + // const str = ensureSimplifiedString(resolved1).value; + const str = interpretEscapeSequences( + ensureString(resolved1).value, + resolved1.loc, ); - return `throw_unless(${getErrorId(str.value, ctx.ctx)}, ${writeExpression(resolved[0]!, ctx)})`; + return `throw_unless(${getErrorId(str, ctx.ctx)}, ${writeExpression(resolved[0]!, ctx)})`; }, }, ], @@ -151,9 +156,13 @@ export const GlobalFunctions: Map<string, AbiFunction> = new Map([ ref, ); } - const str = ensureSimplifiedString( - evalConstantExpression(resolved[0]!, ctx.ctx), - ).value; + const resolved0 = resolved[0]!; + // FIXME: When optimizer step added, change the following line to: + // const str = ensureSimplifiedString(resolved0).value; + const str = interpretEscapeSequences( + ensureString(resolved0).value, + resolved0.loc, + ); let address: Address; try { address = Address.parse(str); @@ -203,9 +212,13 @@ export const GlobalFunctions: Map<string, AbiFunction> = new Map([ } // Load cell data - const str = ensureSimplifiedString( - evalConstantExpression(resolved[0]!, ctx.ctx), - ).value; + const resolved0 = resolved[0]!; + // FIXME: When optimizer step added, change the following line to: + // const str = ensureSimplifiedString(resolved0).value; + const str = interpretEscapeSequences( + ensureString(resolved0).value, + resolved0.loc, + ); let c: Cell; try { c = Cell.fromBase64(str); @@ -241,13 +254,9 @@ export const GlobalFunctions: Map<string, AbiFunction> = new Map([ : "unknown"; const lineCol = ref.interval.getLineAndColumn(); const debugPrint1 = `File ${filePath}:${lineCol.lineNum}:${lineCol.colNum}:`; - const debugPrint2 = writeValue( - util.makeSimplifiedStringLiteral( - ref.interval.contents, - dummySrcInfo, - ), - ctx, - ); + const contentsId = writeString(ref.interval.contents, ctx); + ctx.used(contentsId); + const debugPrint2 = `${contentsId}()`; if (arg0.kind === "map") { const exp = writeExpression(resolved[0]!, ctx); @@ -368,23 +377,12 @@ export const GlobalFunctions: Map<string, AbiFunction> = new Map([ // String case if (arg0.name === "String") { - let str: string | undefined; + const resolved0 = resolved[0]!; - // Try const-eval - try { - str = ensureSimplifiedString( - evalConstantExpression(resolved[0]!, ctx.ctx), - ).value; - } catch (error) { - if ( - !(error instanceof TactConstEvalError) || - error.fatal - ) - throw error; - } - - // If const-eval did succeed - if (str !== undefined) { + if (isLiteral(resolved0)) { + // FIXME: This one does not need fixing, because it is carried out inside a "isLiteral" check. + // Remove this comment once the optimization step is added + const str = ensureSimplifiedString(resolved0).value; return BigInt( "0x" + sha256_sync(str).toString("hex"), ).toString(10); @@ -437,9 +435,13 @@ export const GlobalFunctions: Map<string, AbiFunction> = new Map([ } // Load slice data - const str = ensureSimplifiedString( - evalConstantExpression(resolved[0]!, ctx.ctx), - ).value; + const resolved0 = resolved[0]!; + // FIXME: When optimizer step added, change the following line to: + // const str = ensureSimplifiedString(resolved0).value; + const str = interpretEscapeSequences( + ensureString(resolved0).value, + resolved0.loc, + ); let c: Cell; try { c = Cell.fromBase64(str); @@ -488,9 +490,13 @@ export const GlobalFunctions: Map<string, AbiFunction> = new Map([ } // Load slice data - const str = ensureSimplifiedString( - evalConstantExpression(resolved[0]!, ctx.ctx), - ).value; + const resolved0 = resolved[0]!; + // FIXME: When optimizer step added, change the following line to: + // const str = ensureSimplifiedString(resolved0).value; + const str = interpretEscapeSequences( + ensureString(resolved0).value, + resolved0.loc, + ); let c: Cell; try { c = beginCell().storeBuffer(Buffer.from(str)).endCell(); @@ -533,9 +539,13 @@ export const GlobalFunctions: Map<string, AbiFunction> = new Map([ } // Load slice data - const str = ensureSimplifiedString( - evalConstantExpression(resolved[0]!, ctx.ctx), - ).value; + const resolved0 = resolved[0]!; + // FIXME: When optimizer step added, change the following line to: + // const str = ensureSimplifiedString(resolved0).value; + const str = interpretEscapeSequences( + ensureString(resolved0).value, + resolved0.loc, + ); if (str.length > 32) { throwCompilationError( @@ -577,9 +587,13 @@ export const GlobalFunctions: Map<string, AbiFunction> = new Map([ } // Load slice data - const str = ensureSimplifiedString( - evalConstantExpression(resolved[0]!, ctx.ctx), - ).value; + const resolved0 = resolved[0]!; + // FIXME: When optimizer step added, change the following line to: + // const str = ensureSimplifiedString(resolved0).value; + const str = interpretEscapeSequences( + ensureString(resolved0).value, + resolved0.loc, + ); return `"${str}"c`; }, diff --git a/src/constEval.ts b/src/constEval.ts index 856a8024b..37914006e 100644 --- a/src/constEval.ts +++ b/src/constEval.ts @@ -50,7 +50,7 @@ export const getOptimizer = (util: AstUtil) => { const simplOperand = partiallyEvalExpression(operand, ctx); if (isLiteral(simplOperand)) { - const result = evalUnaryOp(op, simplOperand, source); + const result = evalUnaryOp(op, simplOperand, source, util); return result; } else { const newAst = util.makeUnaryExpression(op, simplOperand); @@ -94,6 +94,7 @@ export const getOptimizer = (util: AstUtil) => { } }, source, + util, ); return result; @@ -130,7 +131,7 @@ export const getOptimizer = (util: AstUtil) => { ctx: CompilerContext, interpreterConfig?: InterpreterConfig, ): AstExpression { - const interpreter = new Interpreter(ctx, interpreterConfig); + const interpreter = new Interpreter(util, ctx, interpreterConfig); switch (ast.kind) { case "id": try { @@ -210,9 +211,10 @@ export const getOptimizer = (util: AstUtil) => { export function evalConstantExpression( ast: AstExpression, ctx: CompilerContext, + util: AstUtil, interpreterConfig?: InterpreterConfig, ): AstLiteral { - const interpreter = new Interpreter(ctx, interpreterConfig); + const interpreter = new Interpreter(util, ctx, interpreterConfig); const result = interpreter.interpretExpression(ast); return result; } diff --git a/src/generator/writers/writeExpression.spec.ts b/src/generator/writers/writeExpression.spec.ts index 2b12b8d95..b7b2c0caa 100644 --- a/src/generator/writers/writeExpression.spec.ts +++ b/src/generator/writers/writeExpression.spec.ts @@ -79,7 +79,7 @@ describe("writeExpression", () => { getParser(ast, defaultParser), ); ctx = resolveDescriptors(ctx, ast); - ctx = resolveStatements(ctx); + ctx = resolveStatements(ctx, ast); const main = getStaticFunction(ctx, "main"); if (main.ast.kind !== "function_def") { throw Error("Unexpected function kind"); diff --git a/src/generator/writers/writeExpression.ts b/src/generator/writers/writeExpression.ts index b5b4493d1..553763f44 100644 --- a/src/generator/writers/writeExpression.ts +++ b/src/generator/writers/writeExpression.ts @@ -3,6 +3,7 @@ import { AstId, AstLiteral, eqNames, + getAstFactory, idText, tryExtractPath, } from "../../grammar/ast"; @@ -40,8 +41,9 @@ import { } from "./writeConstant"; import { ops } from "./ops"; import { writeCastedExpression } from "./writeFunction"; -import { evalConstantExpression } from "../../constEval"; import { isLvalue } from "../../types/resolveStatements"; +import { evalConstantExpression } from "../../constEval"; +import { getAstUtil } from "../../optimizer/util"; function isNull(wCtx: WriterContext, expr: AstExpression): boolean { return getExpType(wCtx.ctx, expr).kind === "null"; @@ -171,12 +173,19 @@ export function writePathExpression(path: AstId[]): string { export function writeExpression(f: AstExpression, wCtx: WriterContext): string { // literals and constant expressions are covered here + + // FIXME: Once optimization step is added, remove this try and replace it with this + // conditional: + // if (isLiteral(f)) { + // return writeValue(f, wCtx); + // } try { + const util = getAstUtil(getAstFactory()); // Let us put a limit of 2 ^ 12 = 4096 iterations on loops to increase compiler responsiveness. // If a loop takes more than such number of iterations, the interpreter will fail evaluation. // I think maxLoopIterations should be a command line option in case a user wants to wait more // during evaluation. - const value = evalConstantExpression(f, wCtx.ctx, { + const value = evalConstantExpression(f, wCtx.ctx, util, { maxLoopIterations: 2n ** 12n, }); return writeValue(value, wCtx); diff --git a/src/grammar/ast.ts b/src/grammar/ast.ts index e2bf6aba5..f95f674ec 100644 --- a/src/grammar/ast.ts +++ b/src/grammar/ast.ts @@ -999,11 +999,18 @@ function eqArrays<T>( } export function isLiteral(ast: AstExpression): ast is AstLiteral { - return checkLiteral(ast, () => true, () => false); + return checkLiteral( + ast, + () => true, + () => false, + ); } -export function checkLiteral<T>(ast: AstExpression, t: (node: AstLiteral) => T, -f: (node: Exclude<AstExpression, AstLiteral>) => T): T { +export function checkLiteral<T>( + ast: AstExpression, + t: (node: AstLiteral) => T, + f: (node: Exclude<AstExpression, AstLiteral>) => T, +): T { switch (ast.kind) { case "null": case "boolean": @@ -1032,5 +1039,3 @@ f: (node: Exclude<AstExpression, AstLiteral>) => T): T { throwInternalCompilerError("Unrecognized expression kind"); } } - - diff --git a/src/grammar/test/expr-is-value.spec.ts b/src/grammar/test/expr-is-value.spec.ts index eca90d2ca..5ec49dd7e 100644 --- a/src/grammar/test/expr-is-value.spec.ts +++ b/src/grammar/test/expr-is-value.spec.ts @@ -4,19 +4,20 @@ import { getAstFactory, isLiteral } from "../ast"; import { getParser } from "../"; import { defaultParser } from "../grammar"; -const valueExpressions: string[] = [ - "1", - "true", - "false", - "null", +const valueExpressions: string[] = ["1", "true", "false", "null"]; + +const notValueExpressions: string[] = [ + "g", + + // Raw strings are not literals: they need to go through the interpreter to get transformed into simplified strings, which are literals. + '"one"', + + // Even if these three struct instances have literal fields, raw struct instances are not literals because they need to go through + // the interpreter to get transformed into struct values. "Test {f1: 0, f2: true}", "Test {f1: 0, f2: true, f3: null}", "Test {f1: Test2 {c:0}, f2: true}", -]; -const notValueExpressions: string[] = [ - "g", - '"one"', // A raw string cannot be determined to be a literal because it is not possible to know if some of its characters are already escaped or not "Test {f1: 0, f2: b}", "Test {f1: a, f2: true}", "f(1)", diff --git a/src/interpreter.ts b/src/interpreter.ts index cb2a4bb11..c0d930e41 100644 --- a/src/interpreter.ts +++ b/src/interpreter.ts @@ -65,7 +65,7 @@ import { idText, isSelfId, } from "./grammar/ast"; -import { divFloor, getAstUtil, modFloor } from "./optimizer/util"; +import { AstUtil, divFloor, modFloor } from "./optimizer/util"; import { getStaticConstant, getStaticFunction, @@ -89,7 +89,7 @@ const maxRepeatStatement: bigint = 2n ** 31n - 1n; // Util factory methods // FIXME: pass util as argument -const util = getAstUtil(getAstFactory()); +//const util = getAstUtil(getAstFactory()); // Throws a non-fatal const-eval error, in the sense that const-eval as a compiler // optimization cannot be applied, e.g. to `let`-statements. @@ -118,10 +118,10 @@ type EvalResult = | { kind: "ok"; value: AstLiteral } | { kind: "error"; message: string }; -export function ensureInt(val: AstLiteral): AstNumber { +export function ensureInt(val: AstExpression): AstNumber { if (val.kind !== "number") { throwErrorConstEval( - `integer expected, but got '${showValue(val)}'`, + `integer expected, but got expression of kind '${val.kind}'`, val.loc, ); } @@ -148,7 +148,7 @@ function ensureArgumentForEquality(val: AstLiteral): AstLiteral { return val; case "struct_value": throwErrorConstEval( - `struct ${showValue(val)} cannot be argument to == operator`, + `struct ${showValue(val)} cannot be an argument to == operator`, val.loc, ); break; @@ -157,10 +157,10 @@ function ensureArgumentForEquality(val: AstLiteral): AstLiteral { } } -function ensureRepeatInt(val: AstLiteral): AstNumber { +function ensureRepeatInt(val: AstExpression): AstNumber { if (val.kind !== "number") { throwErrorConstEval( - `integer expected, but got '${showValue(val)}'`, + `integer expected, but got expression of kind '${val.kind}'`, val.loc, ); } @@ -168,26 +168,38 @@ function ensureRepeatInt(val: AstLiteral): AstNumber { return val; } else { throwErrorConstEval( - `repeat argument must be a number between -2^256 (inclusive) and 2^31 - 1 (inclusive)`, + `repeat argument '${showValue(val)}' must be a number between -2^256 (inclusive) and 2^31 - 1 (inclusive)`, val.loc, ); } } -export function ensureBoolean(val: AstLiteral): AstBoolean { +export function ensureBoolean(val: AstExpression): AstBoolean { if (val.kind !== "boolean") { throwErrorConstEval( - `boolean expected, but got '${showValue(val)}'`, + `boolean expected, but got expression of kind '${val.kind}'`, val.loc, ); } return val; } -export function ensureSimplifiedString(val: AstLiteral): AstSimplifiedString { +export function ensureString(val: AstExpression): AstString { + if (val.kind !== "string") { + throwErrorConstEval( + `string expected, but got expression of kind '${val.kind}'`, + val.loc, + ); + } + return val; +} + +export function ensureSimplifiedString( + val: AstExpression, +): AstSimplifiedString { if (val.kind !== "simplified_string") { throwErrorConstEval( - `string expected, but got '${showValue(val)}'`, + `simplified string expected, but got expression of kind '${val.kind}'`, val.loc, ); } @@ -220,6 +232,7 @@ export function evalUnaryOp( op: AstUnaryOperation, valOperand: AstLiteral, source: SrcInfo, + util: AstUtil, ): AstLiteral { switch (op) { case "+": @@ -257,6 +270,7 @@ export function evalBinaryOp( valLeft: AstLiteral, valRightContinuation: () => AstLiteral, // It needs to be a continuation, because some binary operators short-circuit source: SrcInfo, + util: AstUtil, ): AstLiteral { switch (op) { case "+": { @@ -411,7 +425,7 @@ export function evalBinaryOp( const valR_ = ensureArgumentForEquality(valR); // Changed to equality testing (instead of ===) because cells, slices, address are equal by hashing - const result = eqExpressions(valLeft_, valR_); + const result = eqExpressions(valLeft_, valR_); return util.makeBooleanLiteral(result, source); } case "!=": { @@ -433,7 +447,7 @@ export function evalBinaryOp( const valR_ = ensureArgumentForEquality(valR); // Changed to equality testing (instead of ===) because cells, slices are equal by hashing - const result = !eqExpressions(valLeft_, valR_); + const result = !eqExpressions(valLeft_, valR_); return util.makeBooleanLiteral(result, source); } case "&&": { @@ -451,7 +465,7 @@ export function evalBinaryOp( } } -function interpretEscapeSequences( +export function interpretEscapeSequences( stringLiteral: string, source: SrcInfo, ): string { @@ -677,12 +691,14 @@ export function parseAndEvalExpression( sourceCode: string, ast: FactoryAst = getAstFactory(), parser: Parser = getParser(ast, defaultParser), + util: AstUtil, ): EvalResult { try { const ast = parser.parseExpression(sourceCode); const constEvalResult = evalConstantExpression( ast, new CompilerContext(), + util, ); return { kind: "ok", value: constEvalResult }; } catch (error) { @@ -743,14 +759,17 @@ export class Interpreter { private envStack: EnvironmentStack; private context: CompilerContext; private config: InterpreterConfig; + private util: AstUtil; constructor( + util: AstUtil, context: CompilerContext = new CompilerContext(), config: InterpreterConfig = defaultInterpreterConfig, ) { this.envStack = new EnvironmentStack(); this.context = context; this.config = config; + this.util = util; } public interpretModuleItem(ast: AstModuleItem): void { @@ -915,7 +934,7 @@ export class Interpreter { const comment = ensureSimplifiedString( this.interpretExpression(ast.self), ).value; - return util.makeCommentLiteral(comment, ast.loc); + return this.util.makeCommentLiteral(comment, ast.loc); } default: throwNonFatalErrorConstEval( @@ -945,7 +964,7 @@ export class Interpreter { } public interpretString(ast: AstString): AstSimplifiedString { - return util.makeSimplifiedStringLiteral( + return this.util.makeSimplifiedStringLiteral( interpretEscapeSequences(ast.value, ast.loc), ast.loc, ); @@ -981,20 +1000,26 @@ export class Interpreter { if (ast.operand.kind === "number" && ast.op === "-") { // emulating negative integer literals return ensureInt( - util.makeNumberLiteral(-ast.operand.value, ast.loc), + this.util.makeNumberLiteral(-ast.operand.value, ast.loc), ); } const valOperand = this.interpretExpression(ast.operand); - return evalUnaryOp(ast.op, valOperand, ast.loc); + return evalUnaryOp(ast.op, valOperand, ast.loc, this.util); } public interpretBinaryOp(ast: AstOpBinary): AstLiteral { const valLeft = this.interpretExpression(ast.left); const valRightContinuation = () => this.interpretExpression(ast.right); - return evalBinaryOp(ast.op, valLeft, valRightContinuation, ast.loc); + return evalBinaryOp( + ast.op, + valLeft, + valRightContinuation, + ast.loc, + this.util, + ); } public interpretConditional(ast: AstConditional): AstLiteral { @@ -1020,7 +1045,10 @@ export class Interpreter { resultMap.set(field.name, field.default); } else { if (field.type.kind === "ref" && field.type.optional) { - resultMap.set(field.name, util.makeNullLiteral(ast.loc)); + resultMap.set( + field.name, + this.util.makeNullLiteral(ast.loc), + ); } } } @@ -1042,7 +1070,7 @@ export class Interpreter { ); if (typeof sourceField !== "undefined") { structValueFields.push( - util.makeStructFieldValue( + this.util.makeStructFieldValue( fieldName, fieldValue, sourceField.loc, @@ -1051,12 +1079,16 @@ export class Interpreter { } else { // Use as source code location the entire struct structValueFields.push( - util.makeStructFieldValue(fieldName, fieldValue, ast.loc), + this.util.makeStructFieldValue( + fieldName, + fieldValue, + ast.loc, + ), ); } } - return util.makeStructValue(structValueFields, ast.type, ast.loc); + return this.util.makeStructValue(structValueFields, ast.type, ast.loc); } public interpretStructValue(ast: AstStructValue): AstStructValue { @@ -1131,7 +1163,7 @@ export class Interpreter { ); try { return ensureInt( - util.makeNumberLiteral( + this.util.makeNumberLiteral( BigInt(toNano(tons.value).toString(10)), ast.loc, ), @@ -1162,7 +1194,9 @@ export class Interpreter { } try { const result = valBase.value ** valExp.value; - return ensureInt(util.makeNumberLiteral(result, ast.loc)); + return ensureInt( + this.util.makeNumberLiteral(result, ast.loc), + ); } catch (e) { if (e instanceof RangeError) { // even TS bigint type cannot hold it @@ -1187,7 +1221,9 @@ export class Interpreter { } try { const result = 2n ** valExponent.value; - return ensureInt(util.makeNumberLiteral(result, ast.loc)); + return ensureInt( + this.util.makeNumberLiteral(result, ast.loc), + ); } catch (e) { if (e instanceof RangeError) { // even TS bigint type cannot hold it @@ -1209,14 +1245,14 @@ export class Interpreter { ); } const str = ensureSimplifiedString(expr); - return util.makeNumberLiteral( + return this.util.makeNumberLiteral( BigInt("0x" + sha256_sync(str.value).toString("hex")), ast.loc, ); } case "emptyMap": { ensureFunArity(0, ast.args, ast.loc); - return util.makeNullLiteral(ast.loc); + return this.util.makeNullLiteral(ast.loc); } case "cell": { @@ -1225,7 +1261,7 @@ export class Interpreter { this.interpretExpression(ast.args[0]!), ); try { - return util.makeCellLiteral( + return this.util.makeCellLiteral( Cell.fromBase64(str.value), ast.loc, ); @@ -1244,7 +1280,7 @@ export class Interpreter { this.interpretExpression(ast.args[0]!), ); try { - return util.makeSliceLiteral( + return this.util.makeSliceLiteral( Cell.fromBase64(str.value).asSlice(), ast.loc, ); @@ -1307,7 +1343,7 @@ export class Interpreter { } // Return the constructed slice - return util.makeSliceLiteral( + return this.util.makeSliceLiteral( beginCell().storeBits(bits).endCell().asSlice(), ast.loc, ); @@ -1332,7 +1368,10 @@ export class Interpreter { ast.loc, ); } - return util.makeNumberLiteral(BigInt("0x" + hex), ast.loc); + return this.util.makeNumberLiteral( + BigInt("0x" + hex), + ast.loc, + ); } break; case "crc32": @@ -1341,7 +1380,7 @@ export class Interpreter { const str = ensureSimplifiedString( this.interpretExpression(ast.args[0]!), ); - return util.makeNumberLiteral( + return this.util.makeNumberLiteral( BigInt(crc32.str(str.value) >>> 0), ast.loc, ); // >>> 0 converts to unsigned @@ -1364,7 +1403,7 @@ export class Interpreter { ast.loc, ); } - return util.makeAddressLiteral(address, ast.loc); + return this.util.makeAddressLiteral(address, ast.loc); } catch (_) { throwErrorConstEval( `invalid address encoding: ${showValue(str)}`, @@ -1390,7 +1429,7 @@ export class Interpreter { ast.loc, ); } - return util.makeAddressLiteral( + return this.util.makeAddressLiteral( new Address(Number(wc), addr), ast.loc, ); @@ -1502,7 +1541,7 @@ export class Interpreter { // The function does not return a value. // We rely on the typechecker so that the function is called as a statement. // Hence, we can return a dummy null, since the null will be discarded anyway. - return util.makeNullLiteral(dummySrcInfo); + return this.util.makeNullLiteral(dummySrcInfo); } }, { names: paramNames, values: argValues }, @@ -1633,6 +1672,7 @@ export class Interpreter { currentPathValue, updateVal, ast.loc, + this.util, ); this.envStack.updateBinding(idText(ast.path), newVal); } else { diff --git a/src/optimizer/associative.ts b/src/optimizer/associative.ts index 5779c1a34..bb8da62dd 100644 --- a/src/optimizer/associative.ts +++ b/src/optimizer/associative.ts @@ -32,13 +32,6 @@ type Transform = ( s: SrcInfo, ) => TransformData; -/* A simple wrapper function to transform the right value in a binary operator to a continuation - so that we can call the evaluation function in the interpreter module -function evalBinaryOp(op: AstBinaryOperation, valL: AstLiteral, valR: AstLiteral): AstLiteral { - return interpreterModule.evalBinaryOp(op, valL, () => valR); -} -*/ - abstract class AssociativeRewriteRule extends Rule { // An entry (op, S) in the map means "operator op associates with all operators in set S", // mathematically: all op2 \in S. (a op b) op2 c = a op (b op2 c) @@ -168,6 +161,7 @@ export class AssociativeRule1 extends AllowableOpRule { c1, () => c2, topLevelNode.loc, + util, ); // The final expression is @@ -221,6 +215,7 @@ export class AssociativeRule1 extends AllowableOpRule { c1, () => c2, topLevelNode.loc, + util, ); // The current expression could be either @@ -278,6 +273,7 @@ export class AssociativeRule1 extends AllowableOpRule { c2, () => c1, topLevelNode.loc, + util, ); // The current expression could be either @@ -335,6 +331,7 @@ export class AssociativeRule1 extends AllowableOpRule { c1, () => c2, topLevelNode.loc, + util, ); // The final expression is @@ -633,7 +630,7 @@ export class AssociativeRule3 extends Rule { (x1, c1, c2, util, s) => { // final expression: x1 + (c1 + c2) const val_ = iM.ensureInt( - iM.evalBinaryOp("+", c1, () => c2, s), + iM.evalBinaryOp("+", c1, () => c2, s, util), ); const c1_ = iM.ensureInt(c1); return { @@ -657,7 +654,7 @@ export class AssociativeRule3 extends Rule { (x1, c1, c2, util, s) => { // final expression: x1 + (c1 - c2) const val_ = iM.ensureInt( - iM.evalBinaryOp("-", c1, () => c2, s), + iM.evalBinaryOp("-", c1, () => c2, s, util), ); const c1_ = iM.ensureInt(c1); return { @@ -686,7 +683,7 @@ export class AssociativeRule3 extends Rule { (x1, c1, c2, util, s) => { // final expression x1 - (c1 - c2) const val_ = iM.ensureInt( - iM.evalBinaryOp("-", c1, () => c2, s), + iM.evalBinaryOp("-", c1, () => c2, s, util), ); const c1_ = iM.ensureInt(c1); return { @@ -710,7 +707,7 @@ export class AssociativeRule3 extends Rule { (x1, c1, c2, util, s) => { // final expression x1 - (c1 + c2) const val_ = iM.ensureInt( - iM.evalBinaryOp("+", c1, () => c2, s), + iM.evalBinaryOp("+", c1, () => c2, s, util), ); const c1_ = iM.ensureInt(c1); return { @@ -739,7 +736,7 @@ export class AssociativeRule3 extends Rule { (x1, c1, c2, util, s) => { // final expression x1 * (c1 * c2) const val_ = iM.ensureInt( - iM.evalBinaryOp("*", c1, () => c2, s), + iM.evalBinaryOp("*", c1, () => c2, s, util), ); const c1_ = iM.ensureInt(c1); return { @@ -767,7 +764,13 @@ export class AssociativeRule3 extends Rule { // original expression: (x1 && c1) && c2 (x1, c1, c2, util, s) => { // final expression x1 && (c1 && c2) - const val_ = iM.evalBinaryOp("&&", c1, () => c2, s); + const val_ = iM.evalBinaryOp( + "&&", + c1, + () => c2, + s, + util, + ); return { simplifiedExpression: util.makeBinaryExpression( "&&", @@ -789,7 +792,13 @@ export class AssociativeRule3 extends Rule { // original expression: (x1 || c1) || c2 (x1, c1, c2, util, s) => { // final expression x1 || (c1 || c2) - const val_ = iM.evalBinaryOp("||", c1, () => c2, s); + const val_ = iM.evalBinaryOp( + "||", + c1, + () => c2, + s, + util, + ); return { simplifiedExpression: util.makeBinaryExpression( "||", @@ -826,7 +835,7 @@ export class AssociativeRule3 extends Rule { (x1, c1, c2, util, s) => { // final expression (c2 + c1) + x1 const val_ = iM.ensureInt( - iM.evalBinaryOp("+", c2, () => c1, s), + iM.evalBinaryOp("+", c2, () => c1, s, util), ); const c1_ = iM.ensureInt(c1); return { @@ -850,7 +859,7 @@ export class AssociativeRule3 extends Rule { (x1, c1, c2, util, s) => { // final expression (c2 + c1) - x1 const val_ = iM.ensureInt( - iM.evalBinaryOp("+", c2, () => c1, s), + iM.evalBinaryOp("+", c2, () => c1, s, util), ); const c1_ = iM.ensureInt(c1); return { @@ -879,7 +888,7 @@ export class AssociativeRule3 extends Rule { (x1, c1, c2, util, s) => { // final expression (c2 - c1) - x1 const val_ = iM.ensureInt( - iM.evalBinaryOp("-", c2, () => c1, s), + iM.evalBinaryOp("-", c2, () => c1, s, util), ); const c1_ = iM.ensureInt(c1); return { @@ -903,7 +912,7 @@ export class AssociativeRule3 extends Rule { (x1, c1, c2, util, s) => { // final expression (c2 - c1) + x1 const val_ = iM.ensureInt( - iM.evalBinaryOp("-", c2, () => c1, s), + iM.evalBinaryOp("-", c2, () => c1, s, util), ); const c1_ = iM.ensureInt(c1); return { @@ -933,7 +942,7 @@ export class AssociativeRule3 extends Rule { (x1, c1, c2, util, s) => { // final expression (c2 * c1) * x1 const val_ = iM.ensureInt( - iM.evalBinaryOp("*", c2, () => c1, s), + iM.evalBinaryOp("*", c2, () => c1, s, util), ); const c1_ = iM.ensureInt(c1); return { @@ -962,7 +971,13 @@ export class AssociativeRule3 extends Rule { // original expression: c2 && (c1 && x1) (x1, c1, c2, util, s) => { // final expression (c2 && c1) && x1 - const val_ = iM.evalBinaryOp("&&", c2, () => c1, s); + const val_ = iM.evalBinaryOp( + "&&", + c2, + () => c1, + s, + util, + ); return { simplifiedExpression: util.makeBinaryExpression( "&&", @@ -985,7 +1000,13 @@ export class AssociativeRule3 extends Rule { // original expression: c2 || (c1 || x1) (x1, c1, c2, util, s) => { // final expression (c2 || c1) || x1 - const val_ = iM.evalBinaryOp("||", c2, () => c1, s); + const val_ = iM.evalBinaryOp( + "||", + c2, + () => c1, + s, + util, + ); return { simplifiedExpression: util.makeBinaryExpression( "||", @@ -1022,7 +1043,7 @@ export class AssociativeRule3 extends Rule { (x1, c1, c2, util, s) => { // final expression x1 + (c2 + c1) const val_ = iM.ensureInt( - iM.evalBinaryOp("+", c2, () => c1, s), + iM.evalBinaryOp("+", c2, () => c1, s, util), ); const c1_ = iM.ensureInt(c1); return { @@ -1046,7 +1067,7 @@ export class AssociativeRule3 extends Rule { (x1, c1, c2, util, s) => { // final expression x1 - (c1 - c2) const val_ = iM.ensureInt( - iM.evalBinaryOp("-", c1, () => c2, s), + iM.evalBinaryOp("-", c1, () => c2, s, util), ); const c1_ = iM.ensureInt(c1); return { @@ -1075,7 +1096,7 @@ export class AssociativeRule3 extends Rule { (x1, c1, c2, util, s) => { // final expression (c2 - c1) - x1 const val_ = iM.ensureInt( - iM.evalBinaryOp("-", c2, () => c1, s), + iM.evalBinaryOp("-", c2, () => c1, s, util), ); const c1_ = iM.ensureInt(c1); return { @@ -1099,7 +1120,7 @@ export class AssociativeRule3 extends Rule { (x1, c1, c2, util, s) => { // final expression (c2 + c1) - x1 const val_ = iM.ensureInt( - iM.evalBinaryOp("+", c2, () => c1, s), + iM.evalBinaryOp("+", c2, () => c1, s, util), ); const c1_ = iM.ensureInt(c1); return { @@ -1129,7 +1150,7 @@ export class AssociativeRule3 extends Rule { (x1, c1, c2, util, s) => { // Final expression x1 * (c2 * c1) const val_ = iM.ensureInt( - iM.evalBinaryOp("*", c2, () => c1, s), + iM.evalBinaryOp("*", c2, () => c1, s, util), ); const c1_ = iM.ensureInt(c1); return { @@ -1155,7 +1176,13 @@ export class AssociativeRule3 extends Rule { "&&", // original expression: c2 && (x1 && c1) (x1, c1, c2, util, s) => { - const val_ = iM.evalBinaryOp("&&", c2, () => c1, s); + const val_ = iM.evalBinaryOp( + "&&", + c2, + () => c1, + s, + util, + ); const c1_ = iM.ensureBoolean(c1); const c2_ = iM.ensureBoolean(c2); let final_expr; @@ -1193,7 +1220,13 @@ export class AssociativeRule3 extends Rule { "||", // original expression: c2 || (x1 || c1) (x1, c1, c2, util, s) => { - const val_ = iM.evalBinaryOp("||", c2, () => c1, s); + const val_ = iM.evalBinaryOp( + "||", + c2, + () => c1, + s, + util, + ); const c1_ = iM.ensureBoolean(c1); const c2_ = iM.ensureBoolean(c2); let final_expr; @@ -1246,7 +1279,7 @@ export class AssociativeRule3 extends Rule { (x1, c1, c2, util, s) => { // Final expression (c1 + c2) + x1 const val_ = iM.ensureInt( - iM.evalBinaryOp("+", c1, () => c2, s), + iM.evalBinaryOp("+", c1, () => c2, s, util), ); const c1_ = iM.ensureInt(c1); return { @@ -1270,7 +1303,7 @@ export class AssociativeRule3 extends Rule { (x1, c1, c2, util, s) => { // Final expression (c1 - c2) + x1 const val_ = iM.ensureInt( - iM.evalBinaryOp("-", c1, () => c2, s), + iM.evalBinaryOp("-", c1, () => c2, s, util), ); const c1_ = iM.ensureInt(c1); return { @@ -1299,7 +1332,7 @@ export class AssociativeRule3 extends Rule { (x1, c1, c2, util, s) => { // Final expression (c1 + c2) - x1 const val_ = iM.ensureInt( - iM.evalBinaryOp("+", c1, () => c2, s), + iM.evalBinaryOp("+", c1, () => c2, s, util), ); const c1_ = iM.ensureInt(c1); return { @@ -1323,7 +1356,7 @@ export class AssociativeRule3 extends Rule { (x1, c1, c2, util, s) => { // Final expression (c1 - c2) - x1 const val_ = iM.ensureInt( - iM.evalBinaryOp("-", c1, () => c2, s), + iM.evalBinaryOp("-", c1, () => c2, s, util), ); const c1_ = iM.ensureInt(c1); return { @@ -1352,7 +1385,7 @@ export class AssociativeRule3 extends Rule { (x1, c1, c2, util, s) => { // Final expression (c1 * c2) * x1 const val_ = iM.ensureInt( - iM.evalBinaryOp("*", c1, () => c2, s), + iM.evalBinaryOp("*", c1, () => c2, s, util), ); const c1_ = iM.ensureInt(c1); return { @@ -1379,7 +1412,13 @@ export class AssociativeRule3 extends Rule { "&&", // original expression: (c1 && x1) && c2 (x1, c1, c2, util, s) => { - const val_ = iM.evalBinaryOp("&&", c1, () => c2, s); + const val_ = iM.evalBinaryOp( + "&&", + c1, + () => c2, + s, + util, + ); const c1_ = iM.ensureBoolean(c1); const c2_ = iM.ensureBoolean(c2); let final_expr; @@ -1417,7 +1456,13 @@ export class AssociativeRule3 extends Rule { "||", // original expression: (c1 || x1) || c2 (x1, c1, c2, util, s) => { - const val_ = iM.evalBinaryOp("||", c1, () => c2, s); + const val_ = iM.evalBinaryOp( + "||", + c1, + () => c2, + s, + util, + ); const c1_ = iM.ensureBoolean(c1); const c2_ = iM.ensureBoolean(c2); let final_expr; diff --git a/src/optimizer/test/partial-eval.spec.ts b/src/optimizer/test/partial-eval.spec.ts index 8dd4ed529..34cb418b5 100644 --- a/src/optimizer/test/partial-eval.spec.ts +++ b/src/optimizer/test/partial-eval.spec.ts @@ -352,10 +352,9 @@ function testExpressionWithOptimizer( // The reason for doing this is that the partial evaluator will actually simplify constant // expressions. So, when comparing for equality of expressions, we also need to simplify // constant expressions. -function dummyEval( - ast: AstExpression, - { cloneNode }: FactoryAst, -): AstExpression { +function dummyEval(ast: AstExpression, astFactory: FactoryAst): AstExpression { + const cloneNode = astFactory.cloneNode; + const util = getAstUtil(astFactory); const recurse = (ast: AstExpression): AstExpression => { switch (ast.kind) { case "null": @@ -395,7 +394,7 @@ function dummyEval( const newNode = cloneNode(ast); newNode.operand = recurse(ast.operand); if (isLiteral(newNode.operand)) { - return evalUnaryOp(ast.op, newNode.operand, ast.loc); + return evalUnaryOp(ast.op, newNode.operand, ast.loc, util); } return newNode; } @@ -410,6 +409,7 @@ function dummyEval( newNode.left, () => valR, ast.loc, + util, ); } return newNode; diff --git a/src/pipeline/precompile.ts b/src/pipeline/precompile.ts index 8695f5c14..7c233f9c6 100644 --- a/src/pipeline/precompile.ts +++ b/src/pipeline/precompile.ts @@ -30,16 +30,16 @@ export function precompile( ctx = resolveDescriptors(ctx, ast); // This creates TLB-style type definitions - ctx = resolveSignatures(ctx); + ctx = resolveSignatures(ctx, ast); // This creates allocations for all defined types ctx = resolveAllocations(ctx); // This checks and resolves all statements - ctx = resolveStatements(ctx); + ctx = resolveStatements(ctx, ast); // This extracts error messages - ctx = resolveErrors(ctx); + ctx = resolveErrors(ctx, ast); // Prepared context return ctx; diff --git a/src/prettyPrinter.ts b/src/prettyPrinter.ts index fc430065b..6724fbf76 100644 --- a/src/prettyPrinter.ts +++ b/src/prettyPrinter.ts @@ -186,7 +186,8 @@ export const ppAstBoolean = ({ value }: A.AstBoolean) => value.toString(); export const ppAstId = ({ text }: A.AstId) => text; export const ppAstNull = (_expr: A.AstNull) => "null"; export const ppAstString = ({ value }: A.AstString) => `"${value}"`; -export const ppAstCommentValue = ({ value }: A.AstCommentValue) => JSON.stringify(value); +export const ppAstCommentValue = ({ value }: A.AstCommentValue) => + JSON.stringify(value); export const ppAstSimplifiedString = ({ value }: A.AstSimplifiedString) => JSON.stringify(value); export const ppAstAddress = ({ value }: A.AstAddress) => diff --git a/src/storage/resolveAllocation.spec.ts b/src/storage/resolveAllocation.spec.ts index e784bb90c..1f01614e6 100644 --- a/src/storage/resolveAllocation.spec.ts +++ b/src/storage/resolveAllocation.spec.ts @@ -75,8 +75,8 @@ describe("resolveAllocation", () => { getParser(ast, defaultParser), ); ctx = resolveDescriptors(ctx, ast); - ctx = resolveSignatures(ctx); - ctx = resolveStatements(ctx); + ctx = resolveSignatures(ctx, ast); + ctx = resolveStatements(ctx, ast); ctx = resolveAllocations(ctx); expect(getAllocations(ctx)).toMatchSnapshot(); }); diff --git a/src/test/compilation-failed/const-eval-failed.spec.ts b/src/test/compilation-failed/const-eval-failed.spec.ts index 61f60ab21..6d073e539 100644 --- a/src/test/compilation-failed/const-eval-failed.spec.ts +++ b/src/test/compilation-failed/const-eval-failed.spec.ts @@ -156,7 +156,7 @@ describe("fail-const-eval", () => { itShouldNotCompile({ testName: "const-eval-repeat-upper-bound", errorMessage: - "Cannot evaluate expression to a constant: repeat argument must be a number between -2^256 (inclusive) and 2^31 - 1 (inclusive)", + "Cannot evaluate expression to a constant: repeat argument '2147483648' must be a number between -2^256 (inclusive) and 2^31 - 1 (inclusive)", }); itShouldNotCompile({ testName: "const-eval-ascii-overflow", diff --git a/src/types/__snapshots__/resolveDescriptors.spec.ts.snap b/src/types/__snapshots__/resolveDescriptors.spec.ts.snap index 42b324859..adcfecf6e 100644 --- a/src/types/__snapshots__/resolveDescriptors.spec.ts.snap +++ b/src/types/__snapshots__/resolveDescriptors.spec.ts.snap @@ -1915,7 +1915,7 @@ exports[`resolveDescriptors should resolve descriptors for const-decl-struct-wit }, }, "default": { - "id": 1, + "id": 24, "kind": "null", "loc": s: Int?, }, @@ -13431,7 +13431,7 @@ exports[`resolveDescriptors should resolve descriptors for struct-decl-non-rec-t }, }, "default": { - "id": 2, + "id": 20, "kind": "null", "loc": value: SomeStruct?, }, diff --git a/src/types/resolveDescriptors.spec.ts b/src/types/resolveDescriptors.spec.ts index c662b9cf7..2c5dc177e 100644 --- a/src/types/resolveDescriptors.spec.ts +++ b/src/types/resolveDescriptors.spec.ts @@ -30,7 +30,7 @@ describe("resolveDescriptors", () => { ); ctx = featureEnable(ctx, "external"); ctx = resolveDescriptors(ctx, Ast); - ctx = resolveSignatures(ctx); + ctx = resolveSignatures(ctx, Ast); expect(getAllTypes(ctx)).toMatchSnapshot(); expect(getAllStaticFunctions(ctx)).toMatchSnapshot(); }); @@ -47,7 +47,7 @@ describe("resolveDescriptors", () => { ctx = featureEnable(ctx, "external"); expect(() => { ctx = resolveDescriptors(ctx, Ast); - ctx = resolveSignatures(ctx); + ctx = resolveSignatures(ctx, Ast); }).toThrowErrorMatchingSnapshot(); }); } diff --git a/src/types/resolveDescriptors.ts b/src/types/resolveDescriptors.ts index 03f50b657..bd82e7ade 100644 --- a/src/types/resolveDescriptors.ts +++ b/src/types/resolveDescriptors.ts @@ -18,7 +18,6 @@ import { AstTypeId, AstAsmFunctionDef, FactoryAst, - getAstFactory, } from "../grammar/ast"; import { traverse } from "../grammar/iterators"; import { @@ -58,9 +57,7 @@ import { ItemOrigin } from "../grammar"; import { getExpType, resolveExpression } from "./resolveExpression"; import { emptyContext } from "./resolveStatements"; import { isAssignable } from "./subtyping"; -import { getAstUtil } from "../optimizer/util"; - -const util = getAstUtil(getAstFactory()); +import { AstUtil, getAstUtil } from "../optimizer/util"; const store = createContextStore<TypeDescription>(); const staticFunctionsStore = createContextStore<FunctionDescription>(); @@ -294,7 +291,7 @@ export function resolveDescriptors(ctx: CompilerContext, Ast: FactoryAst) { const staticFunctions: Map<string, FunctionDescription> = new Map(); const staticConstants: Map<string, ConstantDescription> = new Map(); const ast = getRawAST(ctx); - const util = getAstUtil(Ast); // FIXME: Pass it where needed + const util = getAstUtil(Ast); // // Register types @@ -1975,7 +1972,7 @@ export function resolveDescriptors(ctx: CompilerContext, Ast: FactoryAst) { } // A pass that initializes constants and default field values - ctx = initializeConstantsAndDefaultContractAndStructFields(ctx); + ctx = initializeConstantsAndDefaultContractAndStructFields(ctx, util); // detect self-referencing or mutually-recursive types checkRecursiveTypes(ctx); @@ -2123,6 +2120,7 @@ function checkInitializerType( function initializeConstants( constants: ConstantDescription[], ctx: CompilerContext, + util: AstUtil, ): CompilerContext { for (const constant of constants) { if (constant.ast.kind === "constant_def") { @@ -2136,6 +2134,7 @@ function initializeConstants( constant.value = evalConstantExpression( constant.ast.initializer, ctx, + util, ); } } @@ -2144,6 +2143,7 @@ function initializeConstants( function initializeConstantsAndDefaultContractAndStructFields( ctx: CompilerContext, + util: AstUtil, ): CompilerContext { for (const aggregateTy of getAllTypes(ctx)) { switch (aggregateTy.kind) { @@ -2165,6 +2165,7 @@ function initializeConstantsAndDefaultContractAndStructFields( field.default = evalConstantExpression( field.ast.initializer, ctx, + util, ); } else { // if a field has optional type and it is missing an explicit initializer @@ -2179,7 +2180,7 @@ function initializeConstantsAndDefaultContractAndStructFields( // constants need to be processed after structs because // see more detail below - ctx = initializeConstants(aggregateTy.constants, ctx); + ctx = initializeConstants(aggregateTy.constants, ctx, util); } break; } @@ -2189,7 +2190,7 @@ function initializeConstantsAndDefaultContractAndStructFields( // constants need to be processed after structs because // constants might use default field values: `const x: Int = S{}.f`, where `struct S {f: Int = 42}` // and the default field values are filled in during struct field initializers processing - ctx = initializeConstants(getAllStaticConstants(ctx), ctx); + ctx = initializeConstants(getAllStaticConstants(ctx), ctx, util); return ctx; } diff --git a/src/types/resolveErrors.ts b/src/types/resolveErrors.ts index d0bab9036..4a4bf9986 100644 --- a/src/types/resolveErrors.ts +++ b/src/types/resolveErrors.ts @@ -1,6 +1,6 @@ import { sha256_sync } from "@ton/crypto"; import { CompilerContext, createContextStore } from "../context"; -import { AstNode, isRequire } from "../grammar/ast"; +import { AstNode, FactoryAst, isRequire } from "../grammar/ast"; import { traverse } from "../grammar/iterators"; import { evalConstantExpression } from "../constEval"; import { throwInternalCompilerError } from "../errors"; @@ -10,6 +10,7 @@ import { getAllStaticConstants, } from "./resolveDescriptors"; import { ensureSimplifiedString } from "../interpreter"; +import { AstUtil, getAstUtil } from "../optimizer/util"; type Exception = { value: string; id: number }; @@ -23,14 +24,18 @@ function exceptionId(src: string): number { return (stringId(src) % 63000) + 1000; } -function resolveStringsInAST(ast: AstNode, ctx: CompilerContext) { +function resolveStringsInAST( + ast: AstNode, + ctx: CompilerContext, + util: AstUtil, +) { traverse(ast, (node) => { if (node.kind === "static_call" && isRequire(node.function)) { if (node.args.length !== 2) { return; } const resolved = ensureSimplifiedString( - evalConstantExpression(node.args[1]!, ctx), + evalConstantExpression(node.args[1]!, ctx, util), ).value; if (!exceptions.get(ctx, resolved)) { const id = exceptionId(resolved); @@ -50,42 +55,44 @@ function resolveStringsInAST(ast: AstNode, ctx: CompilerContext) { return ctx; } -export function resolveErrors(ctx: CompilerContext) { +export function resolveErrors(ctx: CompilerContext, Ast: FactoryAst) { + const util = getAstUtil(Ast); + // Process all static functions for (const f of getAllStaticFunctions(ctx)) { - ctx = resolveStringsInAST(f.ast, ctx); + ctx = resolveStringsInAST(f.ast, ctx, util); } // Process all static constants for (const f of getAllStaticConstants(ctx)) { - ctx = resolveStringsInAST(f.ast, ctx); + ctx = resolveStringsInAST(f.ast, ctx, util); } // Process all types for (const t of getAllTypes(ctx)) { // Process fields for (const f of t.fields) { - ctx = resolveStringsInAST(f.ast, ctx); + ctx = resolveStringsInAST(f.ast, ctx, util); } // Process constants for (const f of t.constants) { - ctx = resolveStringsInAST(f.ast, ctx); + ctx = resolveStringsInAST(f.ast, ctx, util); } // Process init if (t.init) { - ctx = resolveStringsInAST(t.init.ast, ctx); + ctx = resolveStringsInAST(t.init.ast, ctx, util); } // Process receivers for (const f of t.receivers) { - ctx = resolveStringsInAST(f.ast, ctx); + ctx = resolveStringsInAST(f.ast, ctx, util); } // Process functions for (const f of t.functions.values()) { - ctx = resolveStringsInAST(f.ast, ctx); + ctx = resolveStringsInAST(f.ast, ctx, util); } } diff --git a/src/types/resolveExpression.ts b/src/types/resolveExpression.ts index c0254257d..cb625f639 100644 --- a/src/types/resolveExpression.ts +++ b/src/types/resolveExpression.ts @@ -15,12 +15,6 @@ import { eqNames, idText, isWildcard, - AstAddress, - AstCell, - AstSlice, - AstSimplifiedString, - AstCommentValue, - AstStructValue, } from "../grammar/ast"; import { idTextErr, throwCompilationError } from "../errors"; import { CompilerContext, createContextStore } from "../context"; @@ -32,7 +26,7 @@ import { hasStaticConstant, hasStaticFunction, } from "./resolveDescriptors"; -import { printTypeRef, showValue, TypeRef, typeRefEquals } from "./types"; +import { printTypeRef, TypeRef, typeRefEquals } from "./types"; import { StatementContext } from "./resolveStatements"; import { MapFunctions } from "../abi/map"; import { GlobalFunctions } from "../abi/global"; @@ -771,13 +765,16 @@ export function resolveExpression( case "string": { return resolveStringLiteral(exp, sctx, ctx); } - case "address": - case "cell": - case "slice": - case "simplified_string": - case "comment_value": - case "struct_value": - throwInternalCompilerError(`Expression kind ${exp.kind} should not happen here`); + case "address": + case "cell": + case "slice": + case "simplified_string": + case "comment_value": + case "struct_value": + throwInternalCompilerError( + `Expression kind ${exp.kind} should not happen here`, + ); + break; case "struct_instance": { return resolveStructNew(exp, sctx, ctx); } diff --git a/src/types/resolveSignatures.ts b/src/types/resolveSignatures.ts index 0b47c87a6..b5f5ec72f 100644 --- a/src/types/resolveSignatures.ts +++ b/src/types/resolveSignatures.ts @@ -14,14 +14,17 @@ import { ReceiverDescription, } from "./types"; import { throwCompilationError } from "../errors"; -import { AstNumber, AstReceiver } from "../grammar/ast"; +import { AstNumber, AstReceiver, FactoryAst } from "../grammar/ast"; import { commentPseudoOpcode } from "../generator/writers/writeRouter"; import { sha256_sync } from "@ton/crypto"; import { dummySrcInfo } from "../grammar"; import { ensureInt } from "../interpreter"; import { evalConstantExpression } from "../constEval"; +import { getAstUtil } from "../optimizer/util"; + +export function resolveSignatures(ctx: CompilerContext, Ast: FactoryAst) { + const util = getAstUtil(Ast); -export function resolveSignatures(ctx: CompilerContext) { const signatures: Map< string, { signature: string; tlb: string; id: AstNumber | null } @@ -203,7 +206,7 @@ export function resolveSignatures(ctx: CompilerContext) { // ``` // WILL NOT result in error const opCode = ensureInt( - evalConstantExpression(t.ast.opcode, ctx), + evalConstantExpression(t.ast.opcode, ctx, util), ).value; if (opCode === 0n) { throwConstEvalError( diff --git a/src/types/resolveStatements.spec.ts b/src/types/resolveStatements.spec.ts index 90c2c0d81..1cb1e39a8 100644 --- a/src/types/resolveStatements.spec.ts +++ b/src/types/resolveStatements.spec.ts @@ -21,7 +21,7 @@ describe("resolveStatements", () => { ); ctx = featureEnable(ctx, "external"); ctx = resolveDescriptors(ctx, Ast); - ctx = resolveStatements(ctx); + ctx = resolveStatements(ctx, Ast); expect(getAllExpressionTypes(ctx)).toMatchSnapshot(); }); } @@ -36,7 +36,9 @@ describe("resolveStatements", () => { ); ctx = featureEnable(ctx, "external"); ctx = resolveDescriptors(ctx, Ast); - expect(() => resolveStatements(ctx)).toThrowErrorMatchingSnapshot(); + expect(() => + resolveStatements(ctx, Ast), + ).toThrowErrorMatchingSnapshot(); }); } }); diff --git a/src/types/resolveStatements.ts b/src/types/resolveStatements.ts index 7a1a0e4dc..39d01d67c 100644 --- a/src/types/resolveStatements.ts +++ b/src/types/resolveStatements.ts @@ -9,6 +9,7 @@ import { selfId, isSelfId, eqNames, + FactoryAst, } from "../grammar/ast"; import { isAssignable } from "./subtyping"; import { @@ -31,6 +32,7 @@ import { evalConstantExpression } from "../constEval"; import { ensureInt } from "../interpreter"; import { crc16 } from "../utils/crc16"; import { SrcInfo } from "../grammar"; +import { AstUtil, getAstUtil } from "../optimizer/util"; export type StatementContext = { root: SrcInfo; @@ -798,7 +800,9 @@ function processFunctionBody( return res.ctx; } -export function resolveStatements(ctx: CompilerContext) { +export function resolveStatements(ctx: CompilerContext, Ast: FactoryAst) { + const util = getAstUtil(Ast); + // Process all static functions for (const f of getAllStaticFunctions(ctx)) { if (f.ast.kind === "function_def") { @@ -953,7 +957,7 @@ export function resolveStatements(ctx: CompilerContext) { // Check for collisions in getter method IDs if (f.isGetter) { - const methodId = getMethodId(f, ctx, sctx); + const methodId = getMethodId(f, ctx, sctx, util); const existing = methodIds.get(methodId); if (existing) { throwCompilationError( @@ -1016,6 +1020,7 @@ function getMethodId( funcDescr: FunctionDescription, ctx: CompilerContext, sctx: StatementContext, + util: AstUtil, ): number { const optMethodId = funcDescr.ast.attributes.find( (attr) => attr.type === "get", @@ -1032,7 +1037,7 @@ function getMethodId( } const methodId = ensureInt( - evalConstantExpression(optMethodId, ctx), + evalConstantExpression(optMethodId, ctx, util), ).value; checkMethodId(methodId, optMethodId.loc); return Number(methodId);