Skip to content

Commit

Permalink
Typed modules (#1739)
Browse files Browse the repository at this point in the history
* Initial prototype of typed modules

* Using "placeholder class" for representing module types

* Enhance module imports

* Fix support for untyped modules

* Fix support for multiple typed module imports

* Remove redundant log statements

* Update type of loadedModulesTypes

* Handle built-in functions as special cases

* Add typed modules tests

* Load types only when variant is Typed

* Revert temporary fixes

* Fix bug such that untyped module should not be type-checked

* Changed code according to code review
  • Loading branch information
tohlh authored Mar 2, 2025
1 parent a50f7dc commit 3c5afad
Show file tree
Hide file tree
Showing 7 changed files with 472 additions and 20 deletions.
3 changes: 2 additions & 1 deletion src/createContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,8 @@ const createNativeStorage = (): NativeStorage => ({
gpu: new Map(Object.entries(gpu_lib)),
maxExecTime: JSSLANG_PROPERTIES.maxExecTime,
evaller: null,
loadedModules: {}
loadedModules: {},
loadedModuleTypes: {}
})

export const createEmptyContext = <T>(
Expand Down
23 changes: 23 additions & 0 deletions src/errors/typeErrors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -735,3 +735,26 @@ export class DuplicateTypeAliasError implements SourceError {
return this.explain()
}
}

export class NameNotFoundInModuleError implements SourceError {
public type = ErrorType.TYPE
public severity = ErrorSeverity.ERROR

constructor(
public node: tsEs.ImportDeclaration,
public moduleName: string,
public name: string
) {}

get location() {
return this.node.loc ?? UNKNOWN_LOCATION
}

public explain() {
return `Module '${this.moduleName}' has no exported member '${this.name}'.`
}

public elaborate() {
return this.explain()
}
}
16 changes: 14 additions & 2 deletions src/modules/loader/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,25 @@ export default async function loadSourceModules(
return [moduleName, bundle] as [string, ModuleFunctions]
})
)

const loadedObj = Object.fromEntries(loadedModules)
context.nativeStorage.loadedModules = loadedObj

return loadedObj
}

export async function loadSourceModuleTypes(sourceModulesToImport: Set<string>, context: Context) {
const loadedModules = await Promise.all(
[...sourceModulesToImport].map(async moduleName => {
await initModuleContextAsync(moduleName, context, false)
const bundle = await loadModuleBundleAsync(moduleName, context)
return [moduleName, bundle] as [string, ModuleFunctions]
})
)
const loadedObj = Object.fromEntries(loadedModules)
sourceModulesToImport.forEach(module => {
context.nativeStorage.loadedModuleTypes[module] = loadedObj[module].type_map
})
}

export {
setModulesStaticURL,
MODULES_STATIC_URL,
Expand Down
30 changes: 28 additions & 2 deletions src/modules/preprocessor/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import type es from 'estree'
// import * as TypedES from '../../typeChecker/tsESTree'

import type { Context, IOptions } from '../..'
import type { RecursivePartial } from '../../types'
import loadSourceModules from '../loader'
import { RecursivePartial, Variant } from '../../types'
import loadSourceModules, { loadSourceModuleTypes } from '../loader'
import type { FileGetter } from '../moduleTypes'
import analyzeImportsAndExports from './analyzer'
import parseProgramsAndConstructImportGraph from './linker'
Expand Down Expand Up @@ -45,6 +45,20 @@ const preprocessFileImports = async (
options: RecursivePartial<IOptions> = {},
bundler: Bundler = defaultBundler
): Promise<PreprocessResult> => {
if (context.variant === Variant.TYPED) {
// Load typed source modules into context first to ensure that the type checker has access to all types.
// TODO: This is a temporary solution, and we should consider a better way to handle this.
try {
await loadSourceModuleTypes(new Set<string>(['rune', 'curve']), context)
} catch (error) {
context.errors.push(error)
return {
ok: false,
verboseErrors: false
}
}
}

// Parse all files into ASTs and build the import graph.
const linkerResult = await parseProgramsAndConstructImportGraph(
files,
Expand All @@ -62,6 +76,18 @@ const preprocessFileImports = async (

try {
await loadSourceModules(sourceModulesToImport, context, options.importOptions?.loadTabs ?? true)
// Run type checking on the programs after loading the source modules and their types.
const linkerResult = await parseProgramsAndConstructImportGraph(
files,
entrypointFilePath,
context,
options?.importOptions,
!!options?.shouldAddFileName
)
// Return 'undefined' if there are errors while parsing.
if (!linkerResult.ok) {
return linkerResult
}

analyzeImportsAndExports(
programs,
Expand Down
Loading

0 comments on commit 3c5afad

Please sign in to comment.