From 04db484dcc4e663258ee8b421c20e12072c3452a Mon Sep 17 00:00:00 2001 From: cs01 Date: Fri, 6 Mar 2026 23:54:31 -0800 Subject: [PATCH 1/7] refactor: use discriminated union narrowing in closure-analyzer if/else alternate branch --- src/codegen/infrastructure/closure-analyzer.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/codegen/infrastructure/closure-analyzer.ts b/src/codegen/infrastructure/closure-analyzer.ts index 0dd2c09f..4d7373f0 100644 --- a/src/codegen/infrastructure/closure-analyzer.ts +++ b/src/codegen/infrastructure/closure-analyzer.ts @@ -309,9 +309,8 @@ export class ClosureAnalyzer { this.walkBlock(s.consequent); } if (s.alternate) { - const alt = s.alternate as { type: string }; - if (alt.type === "if") { - this.walkStatement(s.alternate as Statement); + if (s.alternate.type === "if") { + this.walkStatement(s.alternate); } else { this.walkBlock(s.alternate as BlockStatement); } From afb55b09be22756eadb2e722612d3bcd5ce285e5 Mon Sep 17 00:00:00 2001 From: cs01 Date: Sat, 7 Mar 2026 00:19:29 -0800 Subject: [PATCH 2/7] refactor: convert integer-analysis to class to eliminate object[] workaround and anonymous casts --- .../infrastructure/integer-analysis.ts | 168 ++++++++---------- src/codegen/llvm-generator.ts | 2 +- 2 files changed, 73 insertions(+), 97 deletions(-) diff --git a/src/codegen/infrastructure/integer-analysis.ts b/src/codegen/infrastructure/integer-analysis.ts index 79a80779..97c2d93a 100644 --- a/src/codegen/infrastructure/integer-analysis.ts +++ b/src/codegen/infrastructure/integer-analysis.ts @@ -1,119 +1,95 @@ // Static analysis to find variables safe to keep as native i64 instead of double. // Used by both function-level and global-level codegen to enable integer optimization. -// -// NOTE: The parameter uses `object[]` instead of `Statement[]` because `Statement` -// is a union type, and standalone functions with union-type parameters cause codegen -// issues in the native compiler (the type name gets emitted literally). Using `object[]` -// ensures the ObjectArray is passed through correctly. -// Returns true if the expression is an integer literal. -function isIntegerLiteral(val: object): boolean { - const expr = val as { type: string; value?: number }; - if (!expr || !expr.type) return false; - if (expr.type !== "number") return false; - const v = expr.value; - if (v === null || v === undefined) return false; - return v % 1 === 0; -} - -function getBlockStatements(block: object): object[] { - const b = block as { type: string; statements: object[] }; - return b.statements; -} +import type { + Expression, + Statement, + AssignmentStatement, + VariableDeclaration, + WhileStatement, + IfStatement, + ForStatement, + NumberNode, +} from "../../ast/types.js"; -function collectNestedAssignments(stmts: object[], out: object[]): void { - for (let i = 0; i < stmts.length; i++) { - const stmt = stmts[i]; - if (!stmt) continue; - const stmtBase = stmt as { type: string }; - if (!stmtBase.type) continue; +class IntegerAnalyzer { + private isIntegerLiteral(val: Expression): boolean { + if (val.type !== "number") return false; + return (val as NumberNode).value % 1 === 0; + } - if (stmtBase.type === "assignment") { - out.push(stmt); - } else if (stmtBase.type === "while" || stmtBase.type === "do_while") { - // WhileStatement: { type, condition, body, loc } — body at field index 2 - const asWhile = stmt as { type: string; condition: object; body: object }; - collectNestedAssignments(getBlockStatements(asWhile.body), out); - } else if (stmtBase.type === "if") { - // IfStatement: { type, condition, thenBlock, elseBlock, loc } — thenBlock=2, elseBlock=3 - const asIf = stmt as { - type: string; - condition: object; - thenBlock: object; - elseBlock: object; - }; - collectNestedAssignments(getBlockStatements(asIf.thenBlock), out); - if (asIf.elseBlock) { - collectNestedAssignments(getBlockStatements(asIf.elseBlock), out); + private collectNestedAssignments(stmts: Statement[], out: AssignmentStatement[]): void { + for (const stmt of stmts) { + if (stmt.type === "assignment") { + out.push(stmt as AssignmentStatement); + } else if (stmt.type === "while" || stmt.type === "do_while") { + const loop = stmt as WhileStatement; + this.collectNestedAssignments(loop.body.statements, out); + } else if (stmt.type === "if") { + const ifStmt = stmt as IfStatement; + this.collectNestedAssignments(ifStmt.thenBlock.statements, out); + if (ifStmt.elseBlock) { + this.collectNestedAssignments(ifStmt.elseBlock.statements, out); + } + } else if (stmt.type === "for") { + const forStmt = stmt as ForStatement; + this.collectNestedAssignments(forStmt.body.statements, out); } - } else if (stmtBase.type === "for") { - // ForStatement: { type, init, condition, update, body, loc } — body at field index 4 - const asFor = stmt as { - type: string; - init: object; - condition: object; - update: object; - body: object; - }; - collectNestedAssignments(getBlockStatements(asFor.body), out); } } -} -export function findI64EligibleVariables(statements: object[]): string[] { - if (!statements || !statements.length) return []; - const len = statements.length; + findI64EligibleVariables(statements: Statement[]): string[] { + if (!statements || !statements.length) return []; - const candidates: string[] = []; - const isConst: boolean[] = []; + const candidates: string[] = []; + const isConst: boolean[] = []; - // Pass 1: Collect variables initialized with integer literals - for (let i = 0; i < len; i++) { - const stmt = statements[i]; - if (!stmt) continue; - const stmtTyped = stmt as { type: string; kind?: string; name?: string; value?: unknown }; - if (!stmtTyped.type) continue; - if (stmtTyped.type !== "variable_declaration") continue; - if (!stmtTyped.value || !stmtTyped.name) continue; - if (isIntegerLiteral(stmtTyped.value)) { - candidates.push(stmtTyped.name); - isConst.push(stmtTyped.kind === "const"); + // Pass 1: Collect variables initialized with integer literals + for (const stmt of statements) { + if (stmt.type !== "variable_declaration") continue; + const varDecl = stmt as VariableDeclaration; + if (!varDecl.value) continue; + if (this.isIntegerLiteral(varDecl.value)) { + candidates.push(varDecl.name); + isConst.push(varDecl.kind === "const"); + } } - } - if (candidates.length === 0) return []; + if (candidates.length === 0) return []; - // Pass 2: Scan all assignments (including inside loops/branches) to demote - // variables that are ever assigned a non-integer value. - const isDemoted: boolean[] = []; - for (let k = 0; k < candidates.length; k++) { - isDemoted.push(false); - } + // Pass 2: Scan all assignments (including inside loops/branches) to demote + // variables that are ever assigned a non-integer value. + const isDemoted: boolean[] = []; + for (let k = 0; k < candidates.length; k++) { + isDemoted.push(false); + } - const allAssignments: object[] = []; - collectNestedAssignments(statements, allAssignments); + const allAssignments: AssignmentStatement[] = []; + this.collectNestedAssignments(statements, allAssignments); - for (let i = 0; i < allAssignments.length; i++) { - const stmt = allAssignments[i]; - const stmtTyped = stmt as { type: string; name?: string; value?: unknown }; - if (!stmtTyped.name || !stmtTyped.value) continue; - for (let j = 0; j < candidates.length; j++) { - if (candidates[j] === stmtTyped.name) { - if (isConst[j]) break; - if (!isIntegerLiteral(stmtTyped.value)) { - isDemoted[j] = true; + for (const stmt of allAssignments) { + for (let j = 0; j < candidates.length; j++) { + if (candidates[j] === stmt.name) { + if (isConst[j]) break; + if (!this.isIntegerLiteral(stmt.value)) { + isDemoted[j] = true; + } + break; } - break; } } - } - // Build result: candidates minus demoted - const result: string[] = []; - for (let i = 0; i < candidates.length; i++) { - if (!isDemoted[i]) { - result.push(candidates[i]); + // Build result: candidates minus demoted + const result: string[] = []; + for (let i = 0; i < candidates.length; i++) { + if (!isDemoted[i]) { + result.push(candidates[i]); + } } + return result; } - return result; +} + +export function findI64EligibleVariables(statements: Statement[]): string[] { + return new IntegerAnalyzer().findI64EligibleVariables(statements); } diff --git a/src/codegen/llvm-generator.ts b/src/codegen/llvm-generator.ts index 589c65f6..d8d7ebc9 100644 --- a/src/codegen/llvm-generator.ts +++ b/src/codegen/llvm-generator.ts @@ -1812,7 +1812,7 @@ export class LLVMGenerator extends BaseGenerator implements IGeneratorContext { return ir; } const items = this.ast.topLevelStatements; - const i64Eligible = findI64EligibleVariables(this.ast.topLevelStatements as object[]); + const i64Eligible = findI64EligibleVariables(this.ast.topLevelStatements); for (let stmtIdx = 0; stmtIdx < totalCount; stmtIdx++) { const stmt = items[stmtIdx] as { type: string; From ba30966b3a4870eebbb85ff5bdceb6be6515e2f5 Mon Sep 17 00:00:00 2001 From: cs01 Date: Sat, 7 Mar 2026 06:49:42 -0800 Subject: [PATCH 3/7] fix: add missing setUsesOs to IGeneratorContext and LLVMGenerator --- src/codegen/infrastructure/generator-context.ts | 2 ++ src/codegen/llvm-generator.ts | 1 + 2 files changed, 3 insertions(+) diff --git a/src/codegen/infrastructure/generator-context.ts b/src/codegen/infrastructure/generator-context.ts index 5688d9a9..5909f66c 100644 --- a/src/codegen/infrastructure/generator-context.ts +++ b/src/codegen/infrastructure/generator-context.ts @@ -767,6 +767,7 @@ export interface IGeneratorContext { setUsesTreeSitter(value: boolean): void; setUsesSqlite(value: boolean): void; setUsesCurl(value: boolean): void; + setUsesOs(value: boolean): void; setUsesUvHrtime(value: boolean): void; setUsesConsoleTime(value: boolean): void; setUsesArraySort(value: boolean): void; @@ -1195,6 +1196,7 @@ export class MockGeneratorContext implements IGeneratorContext { setUsesCurl(value: boolean): void { this.usesCurl = value ? 1 : 0; } + setUsesOs(_value: boolean): void {} setUsesUvHrtime(value: boolean): void { this.usesUvHrtime = value ? 1 : 0; } diff --git a/src/codegen/llvm-generator.ts b/src/codegen/llvm-generator.ts index d8d7ebc9..a73f5a45 100644 --- a/src/codegen/llvm-generator.ts +++ b/src/codegen/llvm-generator.ts @@ -1009,6 +1009,7 @@ export class LLVMGenerator extends BaseGenerator implements IGeneratorContext { public setUsesCurl(value: boolean): void { this.usesCurl = value ? 1 : 0; } + public setUsesOs(_value: boolean): void {} public getUsesCurl(): boolean { return this.usesCurl !== 0; } From 2ff0e6f8453ea490eadbc2b346f0b3e46eda4f5d Mon Sep 17 00:00:00 2001 From: cs01 Date: Sat, 7 Mar 2026 07:26:39 -0800 Subject: [PATCH 4/7] refactor: remove as { type: string } intermediate casts across codegen --- src/codegen/expressions/arrow-functions.ts | 73 +++++++------------ src/codegen/expressions/calls.ts | 6 +- src/codegen/expressions/method-calls.ts | 1 - src/codegen/expressions/operators/binary.ts | 6 +- src/codegen/expressions/orchestrator.ts | 52 +++++++------ src/codegen/expressions/templates.ts | 3 +- .../infrastructure/assignment-generator.ts | 6 +- .../infrastructure/closure-analyzer.ts | 14 ++-- .../infrastructure/function-generator.ts | 6 +- .../type-resolver/type-resolver.ts | 17 ++--- .../infrastructure/variable-allocator.ts | 17 ++--- src/codegen/llvm-generator.ts | 73 ++++++++----------- src/codegen/statements/control-flow.ts | 5 +- src/codegen/types/collections/array.ts | 3 +- src/codegen/types/objects/class.ts | 5 +- 15 files changed, 119 insertions(+), 168 deletions(-) diff --git a/src/codegen/expressions/arrow-functions.ts b/src/codegen/expressions/arrow-functions.ts index 91824540..9e4f285d 100644 --- a/src/codegen/expressions/arrow-functions.ts +++ b/src/codegen/expressions/arrow-functions.ts @@ -22,6 +22,7 @@ import { ReturnStatement, IfStatement, Expression, + ConditionalExpressionNode, } from "../../ast/types.js"; import { ClosureAnalyzer, @@ -264,18 +265,17 @@ export class ArrowFunctionExpressionGenerator extends BaseGenerator { } private inferReturnTypeFromBody(body: ArrowFunctionNode["body"]): string | null { - const bodyTyped = body as { type: string }; - if (bodyTyped.type === "object") { + if (body.type === "object") { return "object"; } if ( - bodyTyped.type === "string" || - bodyTyped.type === "string_literal" || - bodyTyped.type === "template_literal" + body.type === "string" || + (body.type as string) === "string_literal" || + body.type === "template_literal" ) { return "string"; } - if (bodyTyped.type === "binary") { + if (body.type === "binary") { const binExpr = body as BinaryNode; if (binExpr.op === "+") { const leftType = this.inferReturnTypeFromBody(binExpr.left as ArrowFunctionNode["body"]); @@ -285,74 +285,57 @@ export class ArrowFunctionExpressionGenerator extends BaseGenerator { } } } - if (bodyTyped.type === "array") { + if (body.type === "array") { return "array"; } - if (bodyTyped.type === "conditional") { - const condTyped = body as { - type: string; - condition: unknown; - consequent: unknown; - alternate: unknown; - }; - const consequent = condTyped.consequent; - const alternate = condTyped.alternate; - if (consequent) { - const consequentTyped = consequent as { type: string }; - if (consequentTyped.type === "object") { - return "object"; - } - if (consequentTyped.type === "string_literal") { - return "string"; - } + if (body.type === "conditional") { + const cond = body as ConditionalExpressionNode; + if (cond.consequent.type === "object") { + return "object"; } - if (alternate) { - const alternateTyped = alternate as { type: string }; - if (alternateTyped.type === "object") { - return "object"; - } + if ((cond.consequent.type as string) === "string_literal") { + return "string"; + } + if (cond.alternate.type === "object") { + return "object"; } } - if (bodyTyped.type === "block") { + if (body.type === "block") { const blockTyped = body as BlockStatement; const blockStatements = blockTyped.statements; if (blockStatements) { for (let i = 0; i < blockStatements.length; i++) { const stmt = blockStatements[i]; - const stmtBase = stmt as { type: string }; - if (stmtBase.type === "return") { + if (stmt.type === "return") { const stmtTyped = stmt as ReturnStatement; if (stmtTyped.value) { - const returnValueTyped = stmtTyped.value as { type: string }; - if (returnValueTyped.type === "object") { + if (stmtTyped.value.type === "object") { return "object"; } if ( - returnValueTyped.type === "string" || - returnValueTyped.type === "string_literal" || - returnValueTyped.type === "template_literal" + stmtTyped.value.type === "string" || + (stmtTyped.value.type as string) === "string_literal" || + stmtTyped.value.type === "template_literal" ) { return "string"; } } } - if (stmtBase.type === "if") { + if (stmt.type === "if") { const ifStmt = stmt as IfStatement; const thenBlock = ifStmt.thenBlock; const thenBlockStatements = thenBlock ? thenBlock.statements : null; if (thenBlockStatements) { for (let j = 0; j < thenBlockStatements.length; j++) { - const innerStmt = thenBlockStatements[j]; - const innerStmtTyped = innerStmt as ReturnStatement; + const innerStmtTyped = thenBlockStatements[j] as ReturnStatement; if (innerStmtTyped.type === "return" && innerStmtTyped.value) { - const innerReturnValueTyped = innerStmtTyped.value as { type: string }; - if (innerReturnValueTyped.type === "object") { + if (innerStmtTyped.value.type === "object") { return "object"; } if ( - innerReturnValueTyped.type === "string" || - innerReturnValueTyped.type === "string_literal" || - innerReturnValueTyped.type === "template_literal" + innerStmtTyped.value.type === "string" || + (innerStmtTyped.value.type as string) === "string_literal" || + innerStmtTyped.value.type === "template_literal" ) { return "string"; } diff --git a/src/codegen/expressions/calls.ts b/src/codegen/expressions/calls.ts index 9444de72..f77b95ad 100644 --- a/src/codegen/expressions/calls.ts +++ b/src/codegen/expressions/calls.ts @@ -136,15 +136,13 @@ export class CallExpressionGenerator { // Handle test() - built-in test runner (only when called with string + arrow/function callback) if (expr.name === "test" && expr.args.length >= 2) { - const secondArg = expr.args[1] as { type: string }; - if (secondArg.type === "arrow_function" || secondArg.type === "variable") { + if (expr.args[1].type === "arrow_function" || expr.args[1].type === "variable") { return this.generateTest(expr, params); } } if (expr.name === "describe" && expr.args.length >= 2) { - const secondArg = expr.args[1] as { type: string }; - if (secondArg.type === "arrow_function" || secondArg.type === "variable") { + if (expr.args[1].type === "arrow_function" || expr.args[1].type === "variable") { return this.generateDescribe(expr, params); } } diff --git a/src/codegen/expressions/method-calls.ts b/src/codegen/expressions/method-calls.ts index bca37b49..10d7e0a9 100644 --- a/src/codegen/expressions/method-calls.ts +++ b/src/codegen/expressions/method-calls.ts @@ -467,7 +467,6 @@ export class MethodCallGenerator { return this.generateOptionalMethodCall(expr, params); } - const objBase = expr.object as { type: string }; const method = expr.method; // Handle Promise static methods (Promise.resolve, Promise.reject, Promise.all) diff --git a/src/codegen/expressions/operators/binary.ts b/src/codegen/expressions/operators/binary.ts index 590f0836..2f25d276 100644 --- a/src/codegen/expressions/operators/binary.ts +++ b/src/codegen/expressions/operators/binary.ts @@ -296,10 +296,8 @@ export class BinaryExpressionGenerator { leftExpr: Expression, rightExpr: Expression, ): string { - const leftExprTyped = leftExpr as { type: string }; - const rightExprTyped = rightExpr as { type: string }; - const leftIsNullish = leftExprTyped.type === "null" || leftExprTyped.type === "undefined"; - const rightIsNullish = rightExprTyped.type === "null" || rightExprTyped.type === "undefined"; + const leftIsNullish = leftExpr.type === "null" || leftExpr.type === "undefined"; + const rightIsNullish = rightExpr.type === "null" || rightExpr.type === "undefined"; if (leftIsNullish || rightIsNullish) { return this.generatePointerNullComparison(op, leftValue, rightValue); } diff --git a/src/codegen/expressions/orchestrator.ts b/src/codegen/expressions/orchestrator.ts index a7af1c05..16da1ea3 100644 --- a/src/codegen/expressions/orchestrator.ts +++ b/src/codegen/expressions/orchestrator.ts @@ -118,8 +118,7 @@ export class ExpressionGenerator { * Delegates to appropriate sub-generator based on expression type */ generate(expr: Expression, params: string[]): string { - const exprTyped = expr as { type: string }; - if (!exprTyped.type || exprTyped.type.length === 0) { + if (!expr.type || expr.type.length === 0) { // Hard error: expressions must have a type. An empty type indicates a parser // or AST construction bug. Previously this silently generated a null pointer, // which LLVM -O2 could exploit as UB to prune unrelated code paths. @@ -129,37 +128,37 @@ export class ExpressionGenerator { ); } // Literals - if (exprTyped.type === "number") { + if (expr.type === "number") { const numExpr = expr as NumberNode; return this.literalGen.generateNumber(numExpr.value); } - if (exprTyped.type === "boolean") { + if (expr.type === "boolean") { const boolExpr = expr as BooleanNode; return this.literalGen.generateBoolean(boolExpr.value); } - if (exprTyped.type === "string") { + if (expr.type === "string") { const strExpr = expr as StringNode; return this.literalGen.generateString(strExpr.value); } - if (exprTyped.type === "null" || exprTyped.type === "undefined") { + if (expr.type === "null" || expr.type === "undefined") { this.ctx.setVariableType("null", "i8*"); return "null"; } - if (exprTyped.type.indexOf("spread:") === 0) { - const varName = exprTyped.type.substr(7); + if (expr.type.indexOf("spread:") === 0) { + const varName = expr.type.substr(7); return this.variableGen.generate(varName); } - if (exprTyped.type === "regex") { + if (expr.type === "regex") { const regexExpr = expr as RegexNode; return this.literalGen.generateRegex(regexExpr.pattern, regexExpr.flags); } - if (exprTyped.type === "array") { + if (expr.type === "array") { return this.literalGen.generateArray(expr as ArrayNode, params); } @@ -180,45 +179,45 @@ export class ExpressionGenerator { return this.literalGen.generateNew(newExpr.className, newExpr.args, params, newExpr.typeArgs); } - if (exprTyped.type === "this") { + if (expr.type === "this") { return this.literalGen.generateThis(); } // Variables - if (exprTyped.type === "variable") { + if (expr.type === "variable") { const varExpr = expr as VariableNode; return this.variableGen.generate(varExpr.name); } // Unary operators - if (exprTyped.type === "unary") { + if (expr.type === "unary") { const unaryExpr = expr as UnaryNode; return this.unaryGen.generate(unaryExpr.op, unaryExpr.operand, params); } // Binary operators - if (exprTyped.type === "binary") { + if (expr.type === "binary") { const binExpr = expr as BinaryNode; return this.binaryGen.generate(binExpr.op, binExpr.left, binExpr.right, params); } // Call expressions - if (exprTyped.type === "call") { + if (expr.type === "call") { return this.callGen.generate(expr as CallNode, params); } // Index access - if (exprTyped.type === "index_access") { + if (expr.type === "index_access") { return this.indexAccessGen.generate(expr as IndexAccessNode, params); } // Member access - if (exprTyped.type === "member_access") { + if (expr.type === "member_access") { return this.memberAccessGen.generate(expr as MemberAccessNode, params); } // Arrow functions - if (exprTyped.type === "arrow_function") { + if (expr.type === "arrow_function") { const scopeVarsResult = this.ctx.symbolTable.getScopeVarsArraysForClosure(); const scopeVarsTyped = scopeVarsResult as { names: string[]; @@ -282,22 +281,22 @@ export class ExpressionGenerator { } // Conditional (ternary) expressions - if (exprTyped.type === "conditional") { + if (expr.type === "conditional") { return this.conditionalGen.generate(expr as ConditionalExpressionNode, params); } // Template literals - if (exprTyped.type === "template_literal") { + if (expr.type === "template_literal") { return this.templateLiteralGen.generate(expr as TemplateLiteralNode, params); } // Method calls - if (exprTyped.type === "method_call") { + if (expr.type === "method_call") { return this.methodCallGen.generate(expr as MethodCallNode, params); } // Await expressions - if (exprTyped.type === "await") { + if (expr.type === "await") { const awaitExpr = expr as AwaitExpressionNode; const promiseReg = this.generate(awaitExpr.argument, params); const valueReg = this.ctx.nextTemp(); @@ -311,10 +310,9 @@ export class ExpressionGenerator { // When the inner expression is a variable, record its name so that // allocateDeclaredInterface can inherit the source variable's field order // (the asserted type may reorder fields relative to the object literal layout). - if (exprTyped.type === "type_assertion") { + if (expr.type === "type_assertion") { const assertExpr = expr as TypeAssertionNode; - const innerBase = assertExpr.expression as { type: string }; - if (innerBase.type === "variable") { + if (assertExpr.expression.type === "variable") { const innerVar = assertExpr.expression as VariableNode; this.ctx.setLastTypeAssertionSourceVar(innerVar.name); } else { @@ -324,14 +322,14 @@ export class ExpressionGenerator { } // Index access assignment (arr[i] = value) - if (exprTyped.type === "index_access_assignment") { + if (expr.type === "index_access_assignment") { return this.indexAccessGen.generateAssignment(expr as IndexAccessAssignmentNode, params); } // Hard error: unsupported expression types must not silently produce null pointers. // A null here would be UB that LLVM -O2 can exploit to prune unrelated code. this.ctx.emitError( - "unsupported expression type: " + exprTyped.type, + "unsupported expression type: " + expr.type, (expr as { loc?: { line: number; column: number } }).loc, ); } diff --git a/src/codegen/expressions/templates.ts b/src/codegen/expressions/templates.ts index 528e1441..662bf34c 100644 --- a/src/codegen/expressions/templates.ts +++ b/src/codegen/expressions/templates.ts @@ -77,9 +77,8 @@ export class TemplateLiteralGenerator { partValue = this.ctx.stringGen.doCreateStringConstant(partAsObj.value || ""); } else { const exprPart = part as Expression; - const exprPartObj = exprPart as { type: string }; const exprValue = this.ctx.generateExpression(exprPart, params); - if (exprPartObj.type === "boolean") { + if (exprPart.type === "boolean") { partValue = this.booleanToString(exprValue); } else if ( this.ctx.isStringExpression(exprPart) || diff --git a/src/codegen/infrastructure/assignment-generator.ts b/src/codegen/infrastructure/assignment-generator.ts index 679f87b8..3865696e 100644 --- a/src/codegen/infrastructure/assignment-generator.ts +++ b/src/codegen/infrastructure/assignment-generator.ts @@ -261,7 +261,8 @@ export class AssignmentGenerator { this.ctx.setCurrentDeclaredSetType(undefined); let instancePtr: string | null = null; - const objType = object.type; + const objectTyped = object as { type: string }; + const objType = objectTyped.type; if (objType === "variable") { const varName = (object as VariableNode).name; if (this.ctx.symbolTable.isClass(varName)) { @@ -570,7 +571,8 @@ export class AssignmentGenerator { let arrayType = "%StringArray"; const currentClass = this.ctx.getCurrentClassName(); - if (arrayExpr.object.type === "this" && currentClass) { + const arrayExprObj = arrayExpr.object; + if (arrayExprObj.type === "this" && currentClass) { const fieldInfo = this.ctx.classGenGetFieldInfo(currentClass, arrayExpr.property); if (fieldInfo) { const fi = fieldInfo as { index: number; type: string; tsType: string }; diff --git a/src/codegen/infrastructure/closure-analyzer.ts b/src/codegen/infrastructure/closure-analyzer.ts index 4d7373f0..7abe0174 100644 --- a/src/codegen/infrastructure/closure-analyzer.ts +++ b/src/codegen/infrastructure/closure-analyzer.ts @@ -324,8 +324,7 @@ export class ClosureAnalyzer { if (s.init) this.walkStatement(s.init); if (s.condition) this.walkExpression(s.condition); if (s.update) { - const upd = s.update as { type: string }; - if (upd.type) { + if (s.update.type) { this.walkStatement(s.update as Statement); } else { this.walkExpression(s.update as Expression); @@ -365,8 +364,7 @@ export class ClosureAnalyzer { } private walkExpression(expr: Expression): void { - const exprTyped = expr as { type: string }; - const exprType = exprTyped.type; + const exprType = expr.type; if (exprType === "variable") { const e = expr as VariableExpr; @@ -412,9 +410,8 @@ export class ClosureAnalyzer { const e = expr as TemplateLiteralExpr; for (let _pti = 0; _pti < e.parts.length; _pti++) { const part = e.parts[_pti]; - const partAsObj = part as { type: string }; - if (partAsObj.type && partAsObj.type !== "string") { - this.walkExpression(part as Expression); + if (typeof part !== "string") { + this.walkExpression(part); } } } else if (exprType === "arrow_function") { @@ -424,8 +421,7 @@ export class ClosureAnalyzer { for (let _ppi = 0; _ppi < e.params.length; _ppi++) { this.declaredVars.add(e.params[_ppi]); } - const bodyTyped = e.body as { type: string }; - if (bodyTyped.type === "block") { + if (e.body.type === "block") { this.walkBlock(e.body as BlockStatement); } else { this.walkExpression(e.body as Expression); diff --git a/src/codegen/infrastructure/function-generator.ts b/src/codegen/infrastructure/function-generator.ts index c83e84f3..f2f3e19d 100644 --- a/src/codegen/infrastructure/function-generator.ts +++ b/src/codegen/infrastructure/function-generator.ts @@ -729,7 +729,7 @@ export class FunctionGenerator { if (!block) return false; if (!block.statements) return false; for (let i = 0; i < block.statements.length; i++) { - const stmt = block.statements[i] as { type: string }; + const stmt = block.statements[i]; if (!stmt) continue; if (stmt.type === "return") { return true; @@ -755,7 +755,7 @@ export class FunctionGenerator { if (!caseItem) continue; if (!caseItem.consequent) continue; for (let k = 0; k < caseItem.consequent.length; k++) { - const consequentStmt = caseItem.consequent[k] as { type: string }; + const consequentStmt = caseItem.consequent[k]; if (!consequentStmt) continue; if (consequentStmt.type === "return") return true; } @@ -773,7 +773,7 @@ export class FunctionGenerator { if (!block) return false; if (!block.statements) return false; for (let i = 0; i < block.statements.length; i++) { - const stmt = block.statements[i] as { type: string }; + const stmt = block.statements[i]; if (!stmt) continue; if (stmt.type === "try") return true; if (stmt.type === "if") { diff --git a/src/codegen/infrastructure/type-resolver/type-resolver.ts b/src/codegen/infrastructure/type-resolver/type-resolver.ts index 8d18839d..a555afb2 100644 --- a/src/codegen/infrastructure/type-resolver/type-resolver.ts +++ b/src/codegen/infrastructure/type-resolver/type-resolver.ts @@ -1112,21 +1112,18 @@ export class TypeResolver { if (method !== "find") return null; - const arrayExpr = methodCall.object as { type: string }; - - if (arrayExpr.type === "member_access") { - const memberAccess = arrayExpr as MemberAccessNode; + if (methodCall.object.type === "member_access") { + const memberAccess = methodCall.object as MemberAccessNode; const propertyName = memberAccess.property; let objectInfo: | { ptr: string; keys: string[]; types: string[]; tsTypes?: string[] } | undefined; - const memberObj = memberAccess.object as { type: string }; - if (memberObj.type === "variable") { - const varName = (memberObj as VariableNode).name; + if (memberAccess.object.type === "variable") { + const varName = (memberAccess.object as VariableNode).name; objectInfo = this.ctx.symbolTable.getObjectInfo(varName); - } else if (memberObj.type === "member_access" || memberObj.type === "this") { + } else if (memberAccess.object.type === "member_access" || memberAccess.object.type === "this") { const arrayType = this.resolveMemberAccessArrayType(memberAccess); if (arrayType) { return this.getInterfaceMetadata(arrayType); @@ -1150,8 +1147,8 @@ export class TypeResolver { return this.getInterfaceMetadata(elementType); } - if (arrayExpr.type === "variable") { - const varExpr = arrayExpr as VariableNode; + if (methodCall.object.type === "variable") { + const varExpr = methodCall.object as VariableNode; const varName = varExpr.name; const objArrayMeta = this.ctx.symbolTable.getObjectArrayMetadata(varName); if (objArrayMeta) { diff --git a/src/codegen/infrastructure/variable-allocator.ts b/src/codegen/infrastructure/variable-allocator.ts index 91018f9f..97f80229 100644 --- a/src/codegen/infrastructure/variable-allocator.ts +++ b/src/codegen/infrastructure/variable-allocator.ts @@ -269,11 +269,9 @@ export class VariableAllocator { } private getGenericMethodReturnError(expr: Expression, varName: string): string | null { - const e = expr as { type: string }; - if (e.type !== "method_call") return null; + if (expr.type !== "method_call") return null; const methodExpr = expr as MethodCallNode; - const objBase = methodExpr.object as { type: string }; - if (objBase.type !== "variable") return null; + if (methodExpr.object.type !== "variable") return null; const objName = (methodExpr.object as VariableNode).name; const className = this.ctx.symbolTable.getConcreteClass(objName); if (!className) return null; @@ -304,8 +302,7 @@ export class VariableAllocator { } private resolveGenericCallReturnType(expr: Expression): string | null { - const e = expr as { type: string }; - if (e.type !== "call") return null; + if (expr.type !== "call") return null; const callNode = expr as CallNode; if (!callNode.typeArgs || callNode.typeArgs.length === 0) return null; const ast = this.ctx.getAst(); @@ -709,8 +706,7 @@ export class VariableAllocator { const stmtValue = stmt.value!; - const stmtValueBase = stmtValue as { type: string }; - if (stmtValueBase.type === "new") { + if (stmtValue.type === "new") { const newNode = stmtValue as NewNode; if (newNode.className === "URL") { this.allocateUrl(stmt, params); @@ -1828,8 +1824,7 @@ export class VariableAllocator { let setTypeInfoResult = this.parseSetType(stmt.declaredType); if (!setTypeInfoResult && stmt.value) { - const valueBase = stmt.value as { type: string }; - if (valueBase.type === "new") { + if (stmt.value.type === "new") { const newExpr = stmt.value as { type: string; className: string; @@ -1839,7 +1834,7 @@ export class VariableAllocator { if (newExpr.className === "Set" && newExpr.typeArgs && newExpr.typeArgs.length > 0) { setTypeInfoResult = { valueType: newExpr.typeArgs[0] }; } - } else if (valueBase.type === "set") { + } else if (stmt.value.type === "set") { const setExpr = stmt.value as { valueType?: string }; if (setExpr.valueType) { setTypeInfoResult = { valueType: setExpr.valueType }; diff --git a/src/codegen/llvm-generator.ts b/src/codegen/llvm-generator.ts index a73f5a45..0337515b 100644 --- a/src/codegen/llvm-generator.ts +++ b/src/codegen/llvm-generator.ts @@ -922,9 +922,7 @@ export class LLVMGenerator extends BaseGenerator implements IGeneratorContext { public getStatementType(stmt: Statement | null): string { if (!stmt) return ""; - const stmtTyped = stmt as { type: string }; - const theType = stmtTyped.type; - return theType || ""; + return stmt.type || ""; } public hasClassGen(): boolean { @@ -1381,42 +1379,40 @@ export class LLVMGenerator extends BaseGenerator implements IGeneratorContext { if (!propValue) { llvmType = "double"; } else { - const propValueTyped = propValue as { type: string }; - const propValueType = propValueTyped.type; - if (propValueType === "string" || this.isStringExpression(propValue)) { + if (propValue.type === "string" || this.isStringExpression(propValue)) { llvmType = "i8*"; - } else if (propValueType === "array" || this.isStringArrayExpression(propValue)) { + } else if (propValue.type === "array" || this.isStringArrayExpression(propValue)) { llvmType = this.isStringArrayExpression(propValue) ? "%StringArray*" : "%Array*"; } else if (this.isArrayExpression(propValue)) { llvmType = "%Array*"; - } else if (propValueType === "map") { + } else if (propValue.type === "map") { llvmType = "%Map*"; - } else if (propValueType === "set") { + } else if (propValue.type === "set") { llvmType = "%Set*"; } else if ( - propValueType === "number" || - propValueType === "boolean" || - propValueType === "unary" + propValue.type === "number" || + propValue.type === "boolean" || + propValue.type === "unary" ) { llvmType = "double"; } else if ( - propValueType === "call" || - propValueType === "method_call" || - propValueType === "member_access" || - propValueType === "index_access" || - propValueType === "variable" || - propValueType === "template_literal" || - propValueType === "conditional" || - propValueType === "null" || - propValueType === "undefined" || - propValueType === "regex" || - propValueType === "new" || - propValueType === "object" + propValue.type === "call" || + propValue.type === "method_call" || + propValue.type === "member_access" || + propValue.type === "index_access" || + propValue.type === "variable" || + propValue.type === "template_literal" || + propValue.type === "conditional" || + propValue.type === "null" || + propValue.type === "undefined" || + propValue.type === "regex" || + propValue.type === "new" || + propValue.type === "object" ) { // Non-numeric expression types default to pointer — safer than double // since most runtime values (strings, objects, arrays) are pointers llvmType = "i8*"; - } else if (propValueType === "binary") { + } else if (propValue.type === "binary") { // Binary expressions could be arithmetic (double) or string concat (i8*). // Check if either side is a string to disambiguate. if (this.isStringExpression(propValue)) { @@ -1427,7 +1423,7 @@ export class LLVMGenerator extends BaseGenerator implements IGeneratorContext { } else { // Strict: unknown property expression type — emit warning instead of silent double this.emitWarning( - `object property '${prop.key}' has unrecognized expression type '${propValueType}', defaulting to i8*`, + `object property '${prop.key}' has unrecognized expression type '${propValue.type}', defaulting to i8*`, ); llvmType = "i8*"; } @@ -1984,8 +1980,7 @@ export class LLVMGenerator extends BaseGenerator implements IGeneratorContext { let kind: number = SymbolKind.Number; let defaultValue: string = "0.0"; - const stmtValueBase = stmt.value as { type: string }; - if (stmtValueBase.type === "new") { + if (stmt.value.type === "new") { const newNode = stmt.value as NewNode; if (newNode.className === "URL") { ir += `@${name} = global i8* null` + "\n"; @@ -2354,10 +2349,9 @@ export class LLVMGenerator extends BaseGenerator implements IGeneratorContext { ); continue; } - if (stmt.value && (stmt.value as { type: string }).type === "index_access") { + if (stmt.value && stmt.value.type === "index_access") { const idxNode = stmt.value as IndexAccessNode; - const idxObjBase = idxNode.object as { type: string }; - if (idxObjBase && idxObjBase.type === "variable") { + if (idxNode.object && idxNode.object.type === "variable") { const idxObjVar = idxNode.object as VariableNode; if (idxObjVar.name) { const arrSym = this.symbolTable.lookup(idxObjVar.name); @@ -3134,11 +3128,9 @@ export class LLVMGenerator extends BaseGenerator implements IGeneratorContext { while (true) { if (currentBin.op !== "+") return null; revPieces.push(currentBin.right); - const leftTyped = currentBin.left as { type: string }; - const leftType = leftTyped.type; - if (leftType === "binary") { + if (currentBin.left.type === "binary") { currentBin = currentBin.left as BinaryNode; - } else if (leftType === "variable") { + } else if (currentBin.left.type === "variable") { if ((currentBin.left as VariableNode).name !== varName) return null; const pieces: Expression[] = []; let ri = revPieces.length - 1; @@ -3314,8 +3306,7 @@ export class LLVMGenerator extends BaseGenerator implements IGeneratorContext { continue; } - const stmtValueBase = stmt.value as { type: string }; - if (stmtValueBase.type === "object" && this.currentFunctionTsReturnType) { + if (stmt.value.type === "object" && this.currentFunctionTsReturnType) { const inlineType = this.extractInlineInterfaceType(this.currentFunctionTsReturnType); if (inlineType) { this.currentDeclaredInterfaceType = inlineType; @@ -3488,7 +3479,6 @@ export class LLVMGenerator extends BaseGenerator implements IGeneratorContext { ); } } - const exprBase = expr as { type: string }; return this.exprGen.generate(expr, params); } @@ -3549,8 +3539,7 @@ export class LLVMGenerator extends BaseGenerator implements IGeneratorContext { } private getGenericMethodReturnError(expr: MethodCallNode, varName: string): string | null { - const objBase = expr.object as { type: string }; - if (objBase.type !== "variable") return null; + if (expr.object.type !== "variable") return null; const objName = (expr.object as VariableNode).name; const className = this.symbolTable.getConcreteClass(objName); if (!className || !this.ast || !this.ast.classes) return null; @@ -3727,8 +3716,8 @@ export class LLVMGenerator extends BaseGenerator implements IGeneratorContext { } let hasTry = false; for (let i = 0; i < this.ast.topLevelStatements.length; i++) { - const stmt = this.ast.topLevelStatements[i] as { type: string }; - if (stmt && stmt.type === "try") { + const stmt = this.ast.topLevelStatements[i]; + if (stmt && (stmt.type as string) === "try") { hasTry = true; break; } diff --git a/src/codegen/statements/control-flow.ts b/src/codegen/statements/control-flow.ts index 11043468..6589b44a 100644 --- a/src/codegen/statements/control-flow.ts +++ b/src/codegen/statements/control-flow.ts @@ -301,8 +301,7 @@ export class ControlFlowGenerator { // Generate init if present if (forStmt.init) { - const initBase = forStmt.init as { type: string }; - if (initBase.type === "variable_declaration") { + if (forStmt.init.type === "variable_declaration") { const initVarDecl = forStmt.init as { type: string; kind: string; @@ -319,7 +318,7 @@ export class ControlFlowGenerator { this.ctx.defineVariable(initVarDecl.name, allocaReg, "double", SymbolKind.Number, "local"); this.emit(`${allocaReg} = alloca double`); this.ctx.emitStore("double", dblValue, allocaReg); - } else if (initBase.type === "assignment") { + } else if (forStmt.init.type === "assignment") { const initAssign = forStmt.init as AssignmentStatement; let value = this.ctx.generateExpression(initAssign.value, params); const allocaReg = this.ctx.getVariableAlloca(initAssign.name); diff --git a/src/codegen/types/collections/array.ts b/src/codegen/types/collections/array.ts index d29a577f..099559ca 100644 --- a/src/codegen/types/collections/array.ts +++ b/src/codegen/types/collections/array.ts @@ -42,8 +42,7 @@ export class ArrayGenerator { const arrExpr = expr as ArrayNode; if (arrExpr.elements) { for (let i = 0; i < arrExpr.elements.length; i++) { - const el = arrExpr.elements[i] as { type: string }; - if (el.type === "spread_element" || el.type.indexOf("spread:") === 0) { + if (arrExpr.elements[i].type === "spread_element" || arrExpr.elements[i].type.indexOf("spread:") === 0) { return generateArrayLiteralWithSpread(this.ctx, arrExpr, params); } } diff --git a/src/codegen/types/objects/class.ts b/src/codegen/types/objects/class.ts index ff2e30c4..590a1ac9 100644 --- a/src/codegen/types/objects/class.ts +++ b/src/codegen/types/objects/class.ts @@ -1089,8 +1089,7 @@ export class ClassGenerator { for (let ai = 0; ai < loopLimit; ai++) { if (ai < args.length) { const arg = args[ai]; - const argTyped = arg as { type: string }; - if (argTyped.type === "arrow_function" && ai < paramTypes.length) { + if (arg.type === "arrow_function" && ai < paramTypes.length) { const paramTypeStr = paramTypes[ai]; if (paramTypeStr.startsWith("(")) { const colonIdx = paramTypeStr.indexOf(": "); @@ -1130,7 +1129,7 @@ export class ClassGenerator { argType = this.ctx.getVariableType(val)!; } else if (val.startsWith("@.str")) { argType = "i8*"; - } else if (argTyped.type === "variable") { + } else if (arg.type === "variable") { const varName = (arg as VariableNode).name; if (this.ctx.hasVariableType(`%${varName}`)) { argType = this.ctx.getVariableType(`%${varName}`)!; From f6c292f7e2978ae85d5ca36c441e75c35710810b Mon Sep 17 00:00:00 2001 From: cs01 Date: Sat, 7 Mar 2026 07:29:07 -0800 Subject: [PATCH 5/7] style: fix prettier formatting in type-resolver and array --- src/codegen/infrastructure/type-resolver/type-resolver.ts | 5 ++++- src/codegen/types/collections/array.ts | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/codegen/infrastructure/type-resolver/type-resolver.ts b/src/codegen/infrastructure/type-resolver/type-resolver.ts index a555afb2..b44ed8dd 100644 --- a/src/codegen/infrastructure/type-resolver/type-resolver.ts +++ b/src/codegen/infrastructure/type-resolver/type-resolver.ts @@ -1123,7 +1123,10 @@ export class TypeResolver { if (memberAccess.object.type === "variable") { const varName = (memberAccess.object as VariableNode).name; objectInfo = this.ctx.symbolTable.getObjectInfo(varName); - } else if (memberAccess.object.type === "member_access" || memberAccess.object.type === "this") { + } else if ( + memberAccess.object.type === "member_access" || + memberAccess.object.type === "this" + ) { const arrayType = this.resolveMemberAccessArrayType(memberAccess); if (arrayType) { return this.getInterfaceMetadata(arrayType); diff --git a/src/codegen/types/collections/array.ts b/src/codegen/types/collections/array.ts index 099559ca..a08897b4 100644 --- a/src/codegen/types/collections/array.ts +++ b/src/codegen/types/collections/array.ts @@ -42,7 +42,10 @@ export class ArrayGenerator { const arrExpr = expr as ArrayNode; if (arrExpr.elements) { for (let i = 0; i < arrExpr.elements.length; i++) { - if (arrExpr.elements[i].type === "spread_element" || arrExpr.elements[i].type.indexOf("spread:") === 0) { + if ( + arrExpr.elements[i].type === "spread_element" || + arrExpr.elements[i].type.indexOf("spread:") === 0 + ) { return generateArrayLiteralWithSpread(this.ctx, arrExpr, params); } } From 20fe2ce8bdf6e82400408025428bdf34b2b99690 Mon Sep 17 00:00:00 2001 From: cs01 Date: Sat, 7 Mar 2026 22:48:53 -0800 Subject: [PATCH 6/7] fix: restore intermediate casts for union-typed field accesses causing native compiler regressions --- .../infrastructure/variable-allocator.ts | 11 ++-- src/codegen/llvm-generator.ts | 53 ++++++++++--------- 2 files changed, 36 insertions(+), 28 deletions(-) diff --git a/src/codegen/infrastructure/variable-allocator.ts b/src/codegen/infrastructure/variable-allocator.ts index 97f80229..acdb58ca 100644 --- a/src/codegen/infrastructure/variable-allocator.ts +++ b/src/codegen/infrastructure/variable-allocator.ts @@ -271,7 +271,8 @@ export class VariableAllocator { private getGenericMethodReturnError(expr: Expression, varName: string): string | null { if (expr.type !== "method_call") return null; const methodExpr = expr as MethodCallNode; - if (methodExpr.object.type !== "variable") return null; + const methodObjTyped = methodExpr.object as { type: string }; + if (methodObjTyped.type !== "variable") return null; const objName = (methodExpr.object as VariableNode).name; const className = this.ctx.symbolTable.getConcreteClass(objName); if (!className) return null; @@ -705,8 +706,9 @@ export class VariableAllocator { } const stmtValue = stmt.value!; + const stmtValueBase = stmtValue as { type: string }; - if (stmtValue.type === "new") { + if (stmtValueBase.type === "new") { const newNode = stmtValue as NewNode; if (newNode.className === "URL") { this.allocateUrl(stmt, params); @@ -1824,7 +1826,8 @@ export class VariableAllocator { let setTypeInfoResult = this.parseSetType(stmt.declaredType); if (!setTypeInfoResult && stmt.value) { - if (stmt.value.type === "new") { + const valueBase = stmt.value as { type: string }; + if (valueBase.type === "new") { const newExpr = stmt.value as { type: string; className: string; @@ -1834,7 +1837,7 @@ export class VariableAllocator { if (newExpr.className === "Set" && newExpr.typeArgs && newExpr.typeArgs.length > 0) { setTypeInfoResult = { valueType: newExpr.typeArgs[0] }; } - } else if (stmt.value.type === "set") { + } else if (valueBase.type === "set") { const setExpr = stmt.value as { valueType?: string }; if (setExpr.valueType) { setTypeInfoResult = { valueType: setExpr.valueType }; diff --git a/src/codegen/llvm-generator.ts b/src/codegen/llvm-generator.ts index 0337515b..425cd7a9 100644 --- a/src/codegen/llvm-generator.ts +++ b/src/codegen/llvm-generator.ts @@ -1376,43 +1376,45 @@ export class LLVMGenerator extends BaseGenerator implements IGeneratorContext { let llvmType: string; const propValue = prop.value as Expression; + const propValueTyped = propValue as { type: string }; + const propValueType = propValueTyped.type; if (!propValue) { llvmType = "double"; } else { - if (propValue.type === "string" || this.isStringExpression(propValue)) { + if (propValueType === "string" || this.isStringExpression(propValue)) { llvmType = "i8*"; - } else if (propValue.type === "array" || this.isStringArrayExpression(propValue)) { + } else if (propValueType === "array" || this.isStringArrayExpression(propValue)) { llvmType = this.isStringArrayExpression(propValue) ? "%StringArray*" : "%Array*"; } else if (this.isArrayExpression(propValue)) { llvmType = "%Array*"; - } else if (propValue.type === "map") { + } else if (propValueType === "map") { llvmType = "%Map*"; - } else if (propValue.type === "set") { + } else if (propValueType === "set") { llvmType = "%Set*"; } else if ( - propValue.type === "number" || - propValue.type === "boolean" || - propValue.type === "unary" + propValueType === "number" || + propValueType === "boolean" || + propValueType === "unary" ) { llvmType = "double"; } else if ( - propValue.type === "call" || - propValue.type === "method_call" || - propValue.type === "member_access" || - propValue.type === "index_access" || - propValue.type === "variable" || - propValue.type === "template_literal" || - propValue.type === "conditional" || - propValue.type === "null" || - propValue.type === "undefined" || - propValue.type === "regex" || - propValue.type === "new" || - propValue.type === "object" + propValueType === "call" || + propValueType === "method_call" || + propValueType === "member_access" || + propValueType === "index_access" || + propValueType === "variable" || + propValueType === "template_literal" || + propValueType === "conditional" || + propValueType === "null" || + propValueType === "undefined" || + propValueType === "regex" || + propValueType === "new" || + propValueType === "object" ) { // Non-numeric expression types default to pointer — safer than double // since most runtime values (strings, objects, arrays) are pointers llvmType = "i8*"; - } else if (propValue.type === "binary") { + } else if (propValueType === "binary") { // Binary expressions could be arithmetic (double) or string concat (i8*). // Check if either side is a string to disambiguate. if (this.isStringExpression(propValue)) { @@ -1423,7 +1425,7 @@ export class LLVMGenerator extends BaseGenerator implements IGeneratorContext { } else { // Strict: unknown property expression type — emit warning instead of silent double this.emitWarning( - `object property '${prop.key}' has unrecognized expression type '${propValue.type}', defaulting to i8*`, + `object property '${prop.key}' has unrecognized expression type '${propValueType}', defaulting to i8*`, ); llvmType = "i8*"; } @@ -1980,7 +1982,8 @@ export class LLVMGenerator extends BaseGenerator implements IGeneratorContext { let kind: number = SymbolKind.Number; let defaultValue: string = "0.0"; - if (stmt.value.type === "new") { + const stmtValueBase = stmt.value as { type: string }; + if (stmtValueBase.type === "new") { const newNode = stmt.value as NewNode; if (newNode.className === "URL") { ir += `@${name} = global i8* null` + "\n"; @@ -2349,7 +2352,8 @@ export class LLVMGenerator extends BaseGenerator implements IGeneratorContext { ); continue; } - if (stmt.value && stmt.value.type === "index_access") { + const stmtValBase2 = stmt.value as { type: string }; + if (stmt.value && stmtValBase2.type === "index_access") { const idxNode = stmt.value as IndexAccessNode; if (idxNode.object && idxNode.object.type === "variable") { const idxObjVar = idxNode.object as VariableNode; @@ -3306,7 +3310,8 @@ export class LLVMGenerator extends BaseGenerator implements IGeneratorContext { continue; } - if (stmt.value.type === "object" && this.currentFunctionTsReturnType) { + const stmtValBase3 = stmt.value as { type: string }; + if (stmtValBase3.type === "object" && this.currentFunctionTsReturnType) { const inlineType = this.extractInlineInterfaceType(this.currentFunctionTsReturnType); if (inlineType) { this.currentDeclaredInterfaceType = inlineType; From afeda82c562b0142382e16cf692f316215a1c4ea Mon Sep 17 00:00:00 2001 From: cs01 Date: Sat, 7 Mar 2026 23:00:14 -0800 Subject: [PATCH 7/7] fix: restore as { type: string } casts for union-typed array/field accesses in native compiler --- src/codegen/expressions/calls.ts | 6 ++++-- src/codegen/expressions/templates.ts | 3 ++- src/codegen/infrastructure/closure-analyzer.ts | 6 ++++-- src/codegen/infrastructure/function-generator.ts | 6 +++--- src/codegen/statements/control-flow.ts | 5 +++-- src/codegen/types/collections/array.ts | 6 ++---- src/codegen/types/objects/class.ts | 5 +++-- 7 files changed, 21 insertions(+), 16 deletions(-) diff --git a/src/codegen/expressions/calls.ts b/src/codegen/expressions/calls.ts index f77b95ad..375018ec 100644 --- a/src/codegen/expressions/calls.ts +++ b/src/codegen/expressions/calls.ts @@ -136,13 +136,15 @@ export class CallExpressionGenerator { // Handle test() - built-in test runner (only when called with string + arrow/function callback) if (expr.name === "test" && expr.args.length >= 2) { - if (expr.args[1].type === "arrow_function" || expr.args[1].type === "variable") { + const testSecondArg = expr.args[1] as { type: string }; + if (testSecondArg.type === "arrow_function" || testSecondArg.type === "variable") { return this.generateTest(expr, params); } } if (expr.name === "describe" && expr.args.length >= 2) { - if (expr.args[1].type === "arrow_function" || expr.args[1].type === "variable") { + const descSecondArg = expr.args[1] as { type: string }; + if (descSecondArg.type === "arrow_function" || descSecondArg.type === "variable") { return this.generateDescribe(expr, params); } } diff --git a/src/codegen/expressions/templates.ts b/src/codegen/expressions/templates.ts index 662bf34c..ea1eec80 100644 --- a/src/codegen/expressions/templates.ts +++ b/src/codegen/expressions/templates.ts @@ -77,8 +77,9 @@ export class TemplateLiteralGenerator { partValue = this.ctx.stringGen.doCreateStringConstant(partAsObj.value || ""); } else { const exprPart = part as Expression; + const exprPartTyped = exprPart as { type: string }; const exprValue = this.ctx.generateExpression(exprPart, params); - if (exprPart.type === "boolean") { + if (exprPartTyped.type === "boolean") { partValue = this.booleanToString(exprValue); } else if ( this.ctx.isStringExpression(exprPart) || diff --git a/src/codegen/infrastructure/closure-analyzer.ts b/src/codegen/infrastructure/closure-analyzer.ts index 7abe0174..19416914 100644 --- a/src/codegen/infrastructure/closure-analyzer.ts +++ b/src/codegen/infrastructure/closure-analyzer.ts @@ -324,7 +324,8 @@ export class ClosureAnalyzer { if (s.init) this.walkStatement(s.init); if (s.condition) this.walkExpression(s.condition); if (s.update) { - if (s.update.type) { + const upd = s.update as { type: string }; + if (upd.type) { this.walkStatement(s.update as Statement); } else { this.walkExpression(s.update as Expression); @@ -421,7 +422,8 @@ export class ClosureAnalyzer { for (let _ppi = 0; _ppi < e.params.length; _ppi++) { this.declaredVars.add(e.params[_ppi]); } - if (e.body.type === "block") { + const bodyTyped = e.body as { type: string }; + if (bodyTyped.type === "block") { this.walkBlock(e.body as BlockStatement); } else { this.walkExpression(e.body as Expression); diff --git a/src/codegen/infrastructure/function-generator.ts b/src/codegen/infrastructure/function-generator.ts index f2f3e19d..c83e84f3 100644 --- a/src/codegen/infrastructure/function-generator.ts +++ b/src/codegen/infrastructure/function-generator.ts @@ -729,7 +729,7 @@ export class FunctionGenerator { if (!block) return false; if (!block.statements) return false; for (let i = 0; i < block.statements.length; i++) { - const stmt = block.statements[i]; + const stmt = block.statements[i] as { type: string }; if (!stmt) continue; if (stmt.type === "return") { return true; @@ -755,7 +755,7 @@ export class FunctionGenerator { if (!caseItem) continue; if (!caseItem.consequent) continue; for (let k = 0; k < caseItem.consequent.length; k++) { - const consequentStmt = caseItem.consequent[k]; + const consequentStmt = caseItem.consequent[k] as { type: string }; if (!consequentStmt) continue; if (consequentStmt.type === "return") return true; } @@ -773,7 +773,7 @@ export class FunctionGenerator { if (!block) return false; if (!block.statements) return false; for (let i = 0; i < block.statements.length; i++) { - const stmt = block.statements[i]; + const stmt = block.statements[i] as { type: string }; if (!stmt) continue; if (stmt.type === "try") return true; if (stmt.type === "if") { diff --git a/src/codegen/statements/control-flow.ts b/src/codegen/statements/control-flow.ts index 6589b44a..11043468 100644 --- a/src/codegen/statements/control-flow.ts +++ b/src/codegen/statements/control-flow.ts @@ -301,7 +301,8 @@ export class ControlFlowGenerator { // Generate init if present if (forStmt.init) { - if (forStmt.init.type === "variable_declaration") { + const initBase = forStmt.init as { type: string }; + if (initBase.type === "variable_declaration") { const initVarDecl = forStmt.init as { type: string; kind: string; @@ -318,7 +319,7 @@ export class ControlFlowGenerator { this.ctx.defineVariable(initVarDecl.name, allocaReg, "double", SymbolKind.Number, "local"); this.emit(`${allocaReg} = alloca double`); this.ctx.emitStore("double", dblValue, allocaReg); - } else if (forStmt.init.type === "assignment") { + } else if (initBase.type === "assignment") { const initAssign = forStmt.init as AssignmentStatement; let value = this.ctx.generateExpression(initAssign.value, params); const allocaReg = this.ctx.getVariableAlloca(initAssign.name); diff --git a/src/codegen/types/collections/array.ts b/src/codegen/types/collections/array.ts index a08897b4..d29a577f 100644 --- a/src/codegen/types/collections/array.ts +++ b/src/codegen/types/collections/array.ts @@ -42,10 +42,8 @@ export class ArrayGenerator { const arrExpr = expr as ArrayNode; if (arrExpr.elements) { for (let i = 0; i < arrExpr.elements.length; i++) { - if ( - arrExpr.elements[i].type === "spread_element" || - arrExpr.elements[i].type.indexOf("spread:") === 0 - ) { + const el = arrExpr.elements[i] as { type: string }; + if (el.type === "spread_element" || el.type.indexOf("spread:") === 0) { return generateArrayLiteralWithSpread(this.ctx, arrExpr, params); } } diff --git a/src/codegen/types/objects/class.ts b/src/codegen/types/objects/class.ts index 590a1ac9..ff2e30c4 100644 --- a/src/codegen/types/objects/class.ts +++ b/src/codegen/types/objects/class.ts @@ -1089,7 +1089,8 @@ export class ClassGenerator { for (let ai = 0; ai < loopLimit; ai++) { if (ai < args.length) { const arg = args[ai]; - if (arg.type === "arrow_function" && ai < paramTypes.length) { + const argTyped = arg as { type: string }; + if (argTyped.type === "arrow_function" && ai < paramTypes.length) { const paramTypeStr = paramTypes[ai]; if (paramTypeStr.startsWith("(")) { const colonIdx = paramTypeStr.indexOf(": "); @@ -1129,7 +1130,7 @@ export class ClassGenerator { argType = this.ctx.getVariableType(val)!; } else if (val.startsWith("@.str")) { argType = "i8*"; - } else if (arg.type === "variable") { + } else if (argTyped.type === "variable") { const varName = (arg as VariableNode).name; if (this.ctx.hasVariableType(`%${varName}`)) { argType = this.ctx.getVariableType(`%${varName}`)!;