Treat exported consts as variables; fix const functions across modules - #313
Merged
Conversation
…not the function Follows the design agreed for #311: an exported module-level const is a variable, like an exported let, instead of being exported as a function. This replaces the DllExport / declaration-mode handling from the previous commit. - mlirGen(VariableDeclarationList): an exported, module-level, non-import const becomes Let. Its global stays, __decls prints `let`, and importers read the value out of it. - adjustGlobalVariableType: a global other modules reach (export, import, external) holds a function as a hybrid function, as a local let does. The library inferred a plain function while importers read the __decls type back as hybrid, so -shared loaded a larger value than was stored: `export let f = () => n * 3; f(2)` printed 0. - isGlobalConstLambda erases a const's global only when its initializer is a function named after the const. `const alias = namedFunction` and a module-level `const g = function* () {}` (a generator wrapper is named otherwise) lost their global and then failed with "can't resolve name". Tests: export_const_functions.ts adds an exported alias, an exported let arrow and a non-exported generator const. Static, -shared and -jit -shared pass; suite 2,713 of 2,713. Not covered: an exported generator (`export function* g()` or `export const g = function* ...`) is still wrong under -shared - its __decls return type prints the boxed generator object as a value tuple. Fails the same way on main. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
getTypeAndInit is a template over the declaration type, so
`initializer.as<FunctionLikeDeclarationBase>()` is a dependent member
template call. GCC rejects it ("expected primary-expression before '>'
token"); MSVC accepted it. Spelled `initializer.template as<...>()`.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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
Fixes #311. A module-level
constholding a function can now be used from another module. The rule: an exported module-levelconstis a variable, like an exportedlet, and importers read the function out of it.Why it broke
The library compiled
export const f = (n) => ...into a function namedf: the arrow function took the const's name, andisGlobalConstLambdaerased the global. Importers still looked for a variablef:undefined symbol: f-shared-jit -shared0xC0000005: the importer loaded a "pointer" from the function's code bytesexport const g = function () {...}had nogsymbol at all.Fix
Exported const → let (
mlirGen(VariableDeclarationList)): an exported, module-level, non-importconstbecomesLet.__declsprintslet f : ....Function-typed globals other modules reach are hybrid functions (
adjustGlobalVariableType), as a localletalready was.__declstype(p0: number) => numberreads back as a hybrid function.-sharedthe arguments landed in the wrong place. Before this,export let f = (n) => n * 3; f(2)printed0under-shared; it now prints6.isGlobalConstLambdaonly erases a const that is the function: the initializer is a function named after the const. Otherwise the global is kept, which fixes these cases in the module itself:const alias = namedFunctionconst g = function* () {}, whose generator wrapper has another nameBoth failed with
can't resolve namebefore.Function expressions take the const's name like arrow functions. The receiver name is the short name, so a namespaced one no longer prints
NS.finsidenamespace NS { }.Tests
import_const_functions.ts/export_const_functions.tscover:constlocalletarrowThey're registered as static multi-file,
-shared, and-jit -shared.Also checked in all three modes:
asyncarrow constletvalues under-shared: a number literal, a computed number, a pointer to a named function, and an arrowNot covered
An exported generator is still wrong under
-shared, whetherexport function* g()orexport const g = function* .... Its__declsreturn type prints the boxed generator object as a value tuple (() => {.step:s32, next(): ...}). It fails the same way onmainand is filed separately.Test plan
const-localstests (Fix const and destructuring locals in modules imported as source #312) still passctest, Windows release: 2,713 of 2,713.as<T>()in thegetTypeAndInittemplate needed.template, fixed in f6d7a99)🤖 Generated with Claude Code