I am building a DSL (domain specific language) that targets WASM directly. I need some direction on the module system within the language itself. I am struggling to find a solution to compile each module and combine them into a final root component.
My basic approach so far has been to compile each source file of the DSL to it's own core wasm module. From there I see three paths forward.
Convert each core module to a component and use straightforward composition. Aka dynamic linking to construct the root component.
Link each core module in a shared everything linking approach and then make a new component from this linked module.
Write custom code to merge core modules making assumption unique to my DSL. This is vague and not entirely sure where I'd start.
I have tried the first two and ran into challenges that made me think I am going about this incorrectly.
For the component composition strategy this means I need a WIT for each source file of the DSL. I can derive this but found it difficult as it seems I need to generate wit text programmatically and then immediately parse it. Additionally my DSL has generics and monomorphization so depending the program there will be unique wit definitions. Given that I believe my language features are amenable to link directly in a shared everything mode this approach seems backwards.
However for the dynamic linking approach I ran into issues about the GC proposal not being supported. I use basic structs etc from the GC proposal. Is there anything fundamental about GC features that means a shared everything linking strategy will not work? Or is it simply not implementing yet? I am using the Rust wit-component crate.
Anyone have some general advice on how to think about this challenge? I am new to many of these concepts so any good reading would also be appreciated.
Ordinarily/usually, components are seen as a trust boundary, and the copying semantics across the boundary means that they might not be the best fit for "individual module in source language": my expectation as a developer with most languages is that I can freely refactor code into modules and submodules and move things around with ~zero impact on the cost function. So personally I'd rule out the component-per-source-module approach unless you have some fairly unique requirements
I suspect that you'll want to address the "separate compilation" question in general first -- i.e., decide what processing can happen per module and what needs to happen when you see the whole program. That can have some surprising subtleties, especially around things like generics/monomorphization, or inlining, or ...
Then once you know this, you'll have a better idea whether you want "just a Wasm linker, but one that understands GC" (that seems like a thing that should exist and probably the gaps just haven't been filled in yet?) or "LTO step that does more custom stuff"
clang uses a special .o format for wasm that is, internally, a wasm module with custom sections describing relocations, and clang's wasm-ld links those into a single module
Thanks, I agree and to be clear there will be a separation between source files and modules but left those details for simplicity
and chris is probably about to explain this but theres WIP for creating a canonical abi for GC proposal types. until then, you cant use gc types at component boundaries, you'd have to serialize into a memory and then use memory for canonical abi
That as well! (fitzgen has been leading that -- won't tag him but feel free to if questions)
Is there anything written up yet that I can read?
that probably falls under "tag him if questions" -- I'm not aware of any public writeups yet
https://github.com/WebAssembly/component-model/issues/525
if this is all in one trust domain, though, I'd gently nudge away from that and toward shared-everything linking with GC -- to your question
Is there anything fundamental about GC features that means a shared everything linking strategy will not work? Or is it simply not implementing yet?
it should be possible to merge GC type sections the same way wasm-ld merges data, functions, and other entities -- likely it's "just" a matter of extending it, nothing fundamental.
Ok excellent, yeah it's all one trust domain and using shared everything seems simpler in my case. I'll give it a try. Thanks for the advice and context
Last updated: Dec 06 2025 at 05:03 UTC