Skip to content

An exported variable beside top-level code is exported (#448) - #501

Merged
ASDAlexander77 merged 1 commit into
mainfrom
fix-export-const-toplevel
Oct 4, 2026
Merged

ASDAlexander77 merged 1 commit into
mainfrom
fix-export-const-toplevel

Conversation

@ASDAlexander77

Copy link
Copy Markdown
Owner

With -shared, the importer of export const k = 5; from a module that also had a top-level statement couldn't resolve k ("can't resolve name: k"), under every memory model.

A root with top-level code generates its own variable statements inside its entry function (generateGlobalEntryCode), where they stay module globals. But the export check treated any enclosing function as making a variable local (isExport = !genContext.funcOp && ...). So k never reached the library's __decls, and the library ended up with no __decls at all.

The context generateGlobalEntryCode gives the root's statements now records the entry function (rootStatementsFuncOp). A variable declared directly in that function counts as module level for the export check. A function nested inside those statements has a funcOp of its own, so --export=all still leaves function locals alone.

The importer also warned 'let' does not have initializer at the import, with or without top-level code. The library declares k to its importers as @dllimport let k : s32;, and the variable is defined in the library. A declaration with @dllimport no longer gets the warning, just as a declare declaration doesn't.

New tests import-source/import_export_const.ts + export_const_module.ts run compiled, -shared (the failing case on main), JIT with -shared, and JIT. Both -shared runs also fail if the warning appears.

Gate Result
Windows full suite (Release) 3834/3834
Linux (WSL) full suite 3819/3819
DefaultLib build + its own tests.ps1 -Model, release + debug, compile + JIT gc 159/159; rc, none 158 passed + weakref_basic skipped (gc only); all "All tests passed"
own corpus no flips, no first-error changes (460/618)

Closes #448

🤖 Generated with Claude Code

With -shared, the importer of `export const k = 5;` from a module that
also had a top-level statement could not resolve k ("can't resolve
name: k"), under every memory model. A root with top-level code
generates its own variable statements in its entry function
(generateGlobalEntryCode), where they stay module globals, but the
export check took any enclosing function to mean a local
(`isExport = !genContext.funcOp && ...`): k was never added to the
library's __decls, which then had none.

The context generateGlobalEntryCode gives the root's statements now
carries the entry function (rootStatementsFuncOp), and a variable
declared directly in it is at module level for the export check. A
function nested in them has a funcOp of its own, so --export=all still
leaves function locals alone.

The importer also warned "'let' does not have initializer" at the
import, with or without top-level code: the library declares k as
`@dllimport let k : s32;`, defined in the library. A declaration with
@dllimport no longer gets the warning, as a `declare` one does not.

import-source/import_export_const.ts and export_const_module.ts:
compiled, -shared (which failed), JIT with -shared, JIT; the -shared
runs fail on the warning as well.

Closes #448

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@ASDAlexander77
ASDAlexander77 merged commit a2a3258 into main Oct 4, 2026
2 checks passed
@ASDAlexander77
ASDAlexander77 deleted the fix-export-const-toplevel branch October 4, 2026 23:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

-shared: an exported const from a module with a top-level statement can't be resolved by the importer

1 participant