Skip to content

Wasm proc macro support #1017

Description

@Mark-Simulacrum

Proposal

Summary

This proposes adding support for proc macros in wasm code natively in rustc. The current goals, though with somewhat indeterminate timeline, are:

  • Replace -Zdual-proc-macros (cc Tracking issue for dual-proc-macros rust#64671). We pass this during compilation today to compile proc macros for both the host and target platforms, and then at runtime will load the host code (dlopen, for execution) and the target metadata. This was added to Cargo (Add an unstable option to build proc macros for both the host and the target cargo#6547) and rustc (Create a derive macro for HashStable and allow proc macros in rustc rust#58013) to support rustc's usage of proc macros. Once we support wasm-based macros, they can be loaded and executed on any target, so we stop needing to compile them twice and can remove this implementation.
    • This does require that wasm execution support is present on all targets rustc itself wants to support compiling to.
  • Enable sound(er) caching of proc macros. Today rust-analyzer assumes that macro outputs don't change (never re-running them) and rustc has a flag to do the same (-Zcache-proc-macros). Since we'll now tightly control what the macro has access to, it should be much easier to enable incremental caching by "default". To start this would be limited to users who are willing to opt-in somehow -- some macros intentionally need external access to file system or network paths, and we'd need users to have a wasm toolchain on hand -- but for simple cases this should be a win.
  • Improve macro determinism. Today it's easy for authors of macros to depending on platform randomness (e.g., via hashmap iteration order), which results in non-deterministic builds. This hurts reproducible builds and causes decreases in cache hit rates in build systems reliant on identical outputs (e.g., Buck) as a result. Wasm makes it much easier to control what randomness (or other non-deterministic inputs) macros have access to because we act as a ~kernel for the running program.

Non-goals:

  • Make any security guarantees. rustc continues to assume trusted input; there's no attempt to make guarantees around sandboxing or making it otherwise safe to compile untrusted code.
  • Design interface for opt-in/out / stability guarantees on what interfaces are accessible.

Implementation plan

We are aiming to use wasmtime for execution (though may start on wasmi if we run into issues). The maintainers are happy to work with us on any features / gaps that we need from our early discussion at the 2026 all hands (rust-lang/all-hands-2026#73).

WASI is the intended target family. This avoids a bespoke macro target for basic functionality to work (e.g., println!), and in general should allow more code to 'just work' if/when we want to allow it (e.g., network or filesystem access) without necessarily needing special case interfaces (e.g., like proc_macro::tracked). This also lets us dogfood the component model to help make sure that users have a good experience on wasi targets.

We have two draft PRs that share some common bits up right now (wasip1 on wasmi and wasip2 on wasmtime). @bjorn3 and @Mark-Simulacrum have discussed and we're aligned on landing the common/shared code first -- compiletest support, rustc flags, crate loading changes. Once that lands we'll figure out whether to go ahead with wasmtime or wasmi depending on what blockers we hit (or don't hit) with wasmtime. As of writing for this MCP it looks like wasmtime should support all targets in its interpreter mode, but we'll validate this. We're also looking at whether we can reduce the duplication of ABI surface area for macros. In general though we're expecting that edits to the proc macro implementation (e.g. adding new host-exposed ABI) shouldn't really need to know about wasm support to implement functionality regardless of approach. If we run into concerns we may land some of the implementation behind #[cfg] to allow iterating without maintaining an out of tree patch set, but we're hoping to avoid this.

What are we approving in this MCP?

The above should be mostly considered background. At a high level this is just approving landing unstable support for wasm proc macros. This was technically almost approved in the past (#475 and with concerns addressed here in #876), but since those were a while back we're cutting a new MCP before we land any work into the compiler tree.

Mentors or Reviewers

@bjorn3 / @Mark-Simulacrum will be jointly reviewing/implementing the work.

Process

The main points of the Major Change Process are as follows:

  • File an issue describing the proposal.
  • A compiler team member who is knowledgeable in the area can second by writing @rustbot second or kickoff a team FCP with @rfcbot fcp $RESOLUTION.
  • Once an MCP is seconded, the Final Comment Period begins.
    • Final Comment Period lasts for 10 days after all outstanding concerns are solved.
    • Outstanding concerns will block the Final Comment Period from finishing. Once all concerns are resolved, the 10 day countdown is restarted.
    • If no concerns are raised after 10 days since the resolution of the last outstanding concern, the MCP is considered approved.

You can read more about Major Change Proposals on forge.

Metadata

Metadata

Assignees

No one assigned

    Labels

    T-compilerAdd this label so rfcbot knows to poll the compiler teamfinal-comment-periodThe FCP has started, most (if not all) team members are in agreementmajor-changeA proposal to make a major change to rustc

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions