Compile time type check in macro, is it possible?

I have a little problem getting a macro to work. I take a repetition of type parameters, and I generate several match statements over all those types. In one of them there is a function call with isn’t valid code for all of the types. It’s conditional on a trait associated type. So it won’t compile if I generate match arms for all types. I try to do it conditionally, something like this, but it won’t compile, and I’m afraid that even if condition would compile, it would still be invalid, because the condition is runtime and the method call doesn’t compile…

Any ways to do this? Taking two separate lists in macro’s isn’t possible either if I understand well, on top of being a degradation of the API.

I also tried implementing send_service_gen twice, depending on the trait bound, but that’s not allowed either it seems…

If the code would be hand written instead of macro generated, the obvious right thing to do is to only include the match statements for types that have the correct associated type on their trait impl,…

match x
	$( _ if x == <$service>::sid() =>
		if TypeId::of::< <$service as Message>::Result >() == TypeId::of<()>()
			Self::send_service_gen::<$service>( msg, receiver )

			panic!( "Can only call send on messages which have Result=()." );

	}, )*

	_ => panic!( "got unknown service: {:?}", x ) ,

Macros can take multiple lists but you have to separate them, for example:
($($list1: ident)*; $($list2: ident)*)
and you call it with example![List1Type1 List1Type2; List2Type1]
Without more informations, I can’t help you more than that, can you show the error and the types involved?

@leudz Thanks for the hint, I was just experimenting with multiple lists on the playground to see if I can get it working that way.

The error is:

48 | | < (  ) > (  ) { Self :: send_service_gen :: < $ service > ( msg , receiver ) }
   | |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected struct `common::ResponseA`, found ()

Basically send_service_gen has the following trait bound:

where S: Service + Message<Result=()>

So it will only be valid to call this with types that implement Message with a tuple as the Result associated type.

The error happens because one of the types passed to the macro has an impl for Message like:

impl Message for ServiceA { type Result = ResponseA; }

I could post the entire code on github if you are motivated, but this macro expands to 200 lines of code…

To summarize:

  • I have a list of services coming into the macro
  • I need to generate some statements for all of them, like impl a trait on all of them
  • In one method I have to generate a match that matches all of them and calls a method generic over the $service type, much like in the sample code above.
  • and then the sample above: In one method I have to generate a match, but it will only be valid code on a subset of types, based on the associated type of a trait they all implement.

For now I imagine I need two lists I can refer to. The two lists will need to be distinct, since impl a trait on all of them should not lead to a double impl.
Fortunately the one is a subset of the other, so I can do some things for both lists, and the problematic code only for one list.

It’s a tad bit unfortunate for API ergonomics, but at least it will work.

You’ll have to decide, you can use 2 lists, it will complicate the macro but you’ll have the same code you’d have without macro. Or you could stick with a single list and check the type at runtime.

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