Procmacros are shared objects containing arbitrary code which run in rustc's address space. This has two problems:
-
There's no guarantee that a procmacro is deterministic. Many perform completely deterministic transformations, such as serde. However there's nothing which prevents a procmacro from accessing arbitrary files or talking to network services. This makes them non-deterministic from a build system point of view, because they have dependencies which aren't visible to or taken into account by the build system. And there's no general way two distinguish deterministic from non-deterministic.
-
Worse, a procmacro could be actually malicious, and set out to do bad things. It could make unrelated changes to the filesystem, or even manipulate rustc's internal state. I don't think this is a present threat, but it could definitely happen - similar things have happened in other language ecosystems.
(Yes, these both apply to build scripts, but that's a conversation for another day.)
These two points came up in various forms in various discussions over the course of RustConf. Various solutions were suggested: supplying procmacros with a stubbed out libstd, prohibit unsafe
in procmacros, hoping for the best.
But then during @linclark's closing keynote it struck me: we could compile procmacros to WebAssembly, and run them in rustc in that form. Procmacros have a very narrow API, so it would be easy to serialize between the rustc and wasm environments. Within wasm their code would have no access to external state, and so would be forced to be completely deterministic. Even if malicious, they couldn't do anything other than attack rustc through the very narrow API. And procmacros aren't very performance sensitive - their effect on compile time is mostly from what they generate rather than while they're generating it.
When talking with @alexcrichton and @dtolnay, they both mentioned another benefit: we could put prebuilt procmacros on crates.io and distribute them directly. Apparently procmacro build time is a considerable pain point, which this would solve in one sweep.
I've made an initial attempt on the cargo side with https://github.com/rust-lang/cargo/pull/7297. I'm going to start exploring rustc next to see what needs to happen there. I'm excited because its my first real change to cargo, the most substantial change to rustc, and also the first time I've done anything with either procmacro internals or web assembly.
The main question in my mind is how many procmacros can actually run in this environment? Non-deterministic ones are explicitly excluded, and I'm pretty sure staples like serde
will be fine. But are any procmacros using threads or rayon
? In principle that would be fine, but as I understand it, wasm doesn't yet support threads. Are there any other things I'm overlooking? Are there any procmacros where wasm performance would be an issue?
(I want to get to a prototype stage soon to answer these questions directly, but I thought I'd ask.)