Fix const and destructuring locals in modules imported as source - #312
Merged
Merged
Conversation
…nto fix-import-const-declaration-mode
This was referenced Sep 13, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
import './lib'fell over wheneverlib.tsread a localconstin any function. It also failed on array or object destructuring in a function body, withlettoo.When no shared library sits beside the importer,
lib.tsis included as source in declaration mode. Its functions become declarations, and their bodies are walked only to infer types. In that walk, every variable's initializer was evaluated for its type alone, locals included, so a local had no value:const s = n + 1; return s;error: can't resolve name: sconst [a, b] = [n, n + 1];(orlet)error: failed statementconst { x, y } = { x: n, y: 5 };(orlet)A
constbinds its name directly to its value, and a destructuring pattern reads its elements from the value, so both need one.letsurvived because it gets storage.Fix
mlirGen(VariableDeclaration)inMLIRGenVariables.cpp: the type-only initializer now applies only to module-level variables (declarationMode && !genContext.funcOp). Locals get real values. The bodies are a dummy run whose IR is discarded, so nothing extra reaches the output.A first attempt gave the
conststorage instead, and was rejected. The name then reads as the storage reference, so a function without a return annotation inferredref<number>in the importer andnumberin the library. The class-method call then crashed at run time (0xC0000409). The tests include an inferred-return function for that reason.Tests
import_const_locals.ts/export_const_locals.tscover:constifblock, a class method, and a namespace functionThey're registered three ways:
test-compile-export-import-const-locals: static multi-file. This is the mode that includes the library as source and exercises the fix.test-compile-shared-export-import-const-localsandtest-jit-shared-export-import-const-locals: these read the DLL's declarations instead, and stay as regular coverage.The ownership verifier also compiles
import_const_locals.tsalone, which includes the library as source.Per-feature pairs checked before and after the fix, in all three modes: everything above fails before and passes after.
Not covered: an exported arrow function (
export const f = (n) => {...}) fails across modules even without aconstinside. That's a separate bug, filed as #311, and left out of the test pair.Test plan
ctest, Windows release, before mergingmain: 2,707 of 2,707ctest, Windows release, after mergingmain(with One garbage collector per process: linkage choice, import checks, Linux, default-library tests #310): 2,710 of 2,71000try_finally_returnfailure, "reference to block defined in another region", which also hit One garbage collector per process: linkage choice, import checks, Linux, default-library tests #310)🤖 Generated with Claude Code