Is it possible to procedurally create a proc macro?

This question made me wonder if it is possible to write a proc-macro that creates a proc-macro. Imagine the following pseudocode:

set_common_derive!(serde::Serialize, serde::Deserialize, Default, Debug, PartialEq);
// This should create the `proc-macro-attribute` `common_derives`

#[common_derives]
struct X { ... }

If my understandings are correct, this should not be possible because when the set_common_derive macro is used, the compiler already loaded all the possible plugins from proc-macro crates. Moreover, the generated code should be placed in a proc-macro crate.

Because procedural codegen is not enough! We want to generate procedural codegen code! At the end of the abstraction there is a beautiful empty executable, everything is done at compile time! :sweat_smile:

1 Like

Exactly.


It may be possible to do this with some "hackery", though, if the first call is in build.rs, which would generate an ephemeral proc-macro crate (e.g. in target/generated-proc-macros/...); and then the attribute would be usable in the main Rust code, provided the Cargo.toml had something like

[dependencies]
generated-proc-macros = { path = "target/generated-proc-macros" }
2 Likes

That question had me asking myself the same thing!

I know it's possible to create a macro_rules! macro from inside another macro_rules! macro (example), but I can imagine there might be times where you need to "abstract" over a function_style!() or #[attribute] proc macro.

In the meantime, would proc macros generating macro_rules! macros which generate Rust code be enough codegen to satisfy you?

2 Likes

I think that for my insane curiosity, that would be enough :grin:.

Talking seriously, for now the only limits I find with real problems is simply related to proc-macro-hygene. Rust macros are so powerful that I am more than happy for now -- I'll search for some convoluted recursive codegen approach in the future, maybe :wink:

I can think of one way to implement a #[common_derives] feature.

In the crate root (or some other "well known" location):

set_common_derive!(serde::Serialize, serde::Deserialize, Default, Debug, PartialEq);

// expands to

macro_rules! __apply_common_derives {
  ($( $tokens:tt )*) => {
    #[derive(serde::Serialize, serde::Deserialize, Default, Debug, PartialEq)]
    $( $tokens )*
  }
}

Then adding the #[common_derives] attribute to a type would expand to something like this:

$crate::__apply_common_derives! {
  struct X { ... }
}

My __apply_common_derives!() macro might need some tweaking because I'm not sure if you can prepend attributes to an arbitrary tt, but it should be possible on stable rust.


That said, the idea is pretty much a hack to solve something which isn't really a problem :sweat_smile:

5 Likes

Needless to say: :heart::heart::heart:

1 Like

The answer to the root question is "sort of". It's very possible to use a procedural macro from within a procedural macro crate.

But yes, it's not possible to, from within a regular crate, call a macro that causes a procedural macro to be defined.

(Functionlike proc-macro also isn't currently allowed to emit a macro_rules def, but derive proc-macro are, so a functionlike that wants to emit a macro_rules must shim through a derive.)

There's plenty of loopy codegen techniques available today, I think keeping the loading of proc-macros "sane" is a good idea.

2 Likes

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.