Currently rustc processes proc macros during compilation. This has a couple of disadvanteges:
- Code builders must run macro code on their machine, this may have security hasserts
- Macro needs dedicated dependencies that need to be built as well. This takes compilation time.
- It is difficult to inspect, what code is generated by macros.
- Tools like rust-analysers have a hard time to deal with macros and often rely on hacky solutions
In order to overcome these issues, I would propose a human readable macro cache and two compilation modes (syntax only a suggestion):
rustc <crateroot.rs> --emit=macros --macro-cache-dir=<path_to_cache_dir>
In this mode macros are expanded, each expantion is cached into a suitible named file in the specified cache dir.
rustc <crateroot.rs> --macro-cache-dir=<path_to_cache_dir>
This works like the normal build mode, however proc_macro code will never be invocted, but instead the compiler tries to read the expansions from the specified cache dir. If this fails the compilation will abort.
Each cache files should contain three sections, seperated by "§§":
- the first section should contains some metadata as a json, about the macro invocation described.
- the second section contains the full macro expression that is being replaced.
- the third section contains the expanded macro code.
For example, the wstr macro, a macro that is used to generate utf16 literals such an expansion file could look like this ( would be substituted by suitable hash values):
{"rust_macro_cache_version": 1, macro_kind: "function", "macro_item": "wstr_<hash>::wstr", macro_hash: <hash>, "expand_crate" : "foocrate", "expand_module": "foocrate::foo::bar", "expand_linenum": 32, "macro-frag-spec": "expr", "targets" : "*", hash: <hash>}
§§
wstr!("Hello 🦀")
§§
[0x0048u16, 0x0065u16, 0x006Cu16, 0x006Cu16, 0x006Fu16, 0xD83Eu16, 0xDD80u16].as_slice()
macro_kind would be "function", "attribute" or "derive". "macro-frag-spec" would be one of the macro fragment specifiers (similar to those listed for declarative macros), that matches what kind of code fragment is expected in the macro expansion context, e.g. "expr", "stmt" or "item". targets could be used to allow for the macro cache to be rejected on certain targets. In order to record macro hygiene identifier can carry an additional suffice (like "§1" "§2") or something indicating which name scope an identifier should live in (macro name scope or expansion scope).
Using § as a special symbol is just a suggestion. I picked it, because it is very commonly available in fonts (due to being in latin-1) but not really usefull in handwritten code due to not being present on all keybords.