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);