Procedural macro dependent on generic type

I have written my own procedural macro My_Proc_Macro so I can do things like:

#[derive(My_Proc_Macro)
pub struct/enum Ident {
...
}

Now, consider a generic case:

#[derive(My_Proc_Macro)
pub struct/enum Ident<T> {
...
}

In the case of T: My_Porc_Macro, I want My_Proc_Macro to generate code for Ident<T>

In the case T does not satisfy My_Proc_Macro, I want My_Proc_Macro to no-op on Ident<T>

How do I do this in a procedural macro ?

Aside: I believe this is possible given the behaviour of Clone on types T depending on whether T: Clone

As posed, this isn't possible. The only thing a procedural macro knows about T is that it is a type parameter called T, as part of the syntax tree of the item (in this case, the struct or enum Ident) it's processing. The macro implementation isn't provided with any information about the rest of the program, such as where Ident is used or what types populate T.

However, if you take macro processing out of the picture for a second, I think what you want may be possible in ordinary Rust. And, if it is, then you can generate that Rust from within your macro processor.

Consider the following:

pub struct Ident<T> {
  some_field: T,
}

impl<T> My_Proc_Macro for Ident<T>
where T: My_Proc_Macro {
  // ...
}

This will provide an implementation of the My_Proc_Macro trait for Ident<T> corresponding to every type T that, itself, also implements My_Proc_Macro, and for no other types. At first glance, this seems to be to be what you described (edit: and as @quinedot says, this is how Clone and other deriveable types are implemented); is there a way in which it's not?

2 Likes

(I probably won't be able to answer your actual question, but)

What do you mean by "satisfy My_Proc_Macro?

Pre-submit edit: Oh, reading the above reply (posted while I was writing) maybe "My_Proc_Macro" is (also) a trait, ok.

That's simply just something along the lines of

impl<X, Y, Z> Clone for Struct<X, Y, Z>
where
    X: Clone,
    Y: Clone,
    Z: Clone,

(and not macro magic).

2 Likes

@derspiny @quinedot

  1. I think I have this solved now.

  2. I made a mistake in my original post. Instead of writing My_Proc_Macro, I would have written My_Trait, which has two parts: a Rust Trait and a Procedural macro -- much like Clone it self -- both a Rust Trait AND a procedural macro.

  3. The solution is that my procedural macro, instead of generating

impl <...> My_Trait for Foo<A, B, C> { ... }

it needs to generate

impl <A: My_Trait + ..., B: My_Trait + ..., C: My_Trait + ...> My_Trait for Foo<A, B, C> {
...
}

then the generated code is used when all generic arguments satisfy My_Trait and no-ops when anyone of them fails to satisfy the My_Trait

  1. Sorry for the confusion, my use of My_Proc_Macro instead of My_Trait made it hard to infer it was two things (proc macro & trait) sharing a single name (like Clone).
1 Like

Yeah I don't know if it was much of a mistake really, the phrasing of "satisfy [my macro]" just threw me off initially :sweat_smile:. Should have taken 2 seconds to think about what #[derive(...)] meant beyond the name within.

1 Like