Proc_macro for types?


#1

Hi,

Setup

I’m trying to write a procedural macro that produces a type rather than an expression, but so far I’ve been unable to get that to work and was wondering if that’s something that’s currently supported and I’m just doing the wrong thing?

For example, given the following fairly simple setup (as instructed in the readme):

/// The implementation in, say, a macros_impl crate 
#[proc_macro_hack]
pub fn the_type(input: TokenStream) -> TokenStream {
    let expr = parse_macro_input!(input as Expr);
    let ast = quote! { #expr };
    //    println!("ast: [{}]", ast);
    TokenStream::from(ast)
}

/// Declaration in another, say, macros, crate
extern crate macros_impl;

#[proc_macro_hack]
pub use macros_impl::the_type;

Errors

When I try to use it via something like

let _: the_type!(isize) = 3;

I get the following error:

error: expected type, found `{`
  --> macros/src/lib.rs:15:1
   |
15 | #[proc_macro_hack]
   | ^
...
37 |         let _: the_type!(isize) = 3;
   |                ---------------- in this macro invocation

It looks like there’s some unexpected wrapping with curly braces happening that prevents the usage of the proc macro as a type, at least in the way that I have it now.

Other attempts/workarounds

I’ve also tried not using proc_macro_hack and using just proc_macro but it got a different error that seems to indicate I’m doing something that isn’t yet supported

error[E0658]: procedural macros cannot be expanded to types (see issue #38356)

But also saw https://github.com/dtolnay/quote/issues/44, which gave me some hope that maybe it is supported after all?

I have had success building said type, but using it in an expression, however, like generating something like works;

quote! {
  let t: #type_type!(isize) = 3;
  t
}

Asks

I guess I have 2 questions:

  1. Is using a proc_macro to build a type something that is currently supported?
    • If not, is there a workaround?
  2. If it is not supported and there is no workaround, are there plans to support it eventually?

Thanks so much in advance :slight_smile:


#2

Macros in general cannot be put in the type context. A work around is to make your macro put the type in a alias then use that alias.

i.e.

quote! {
    type SomeType = #the_type!(isize);
    
    let t: SomeType = 3;
}

#3

avoiding this is ( one of the reasons ) why rust needs first class types ( ala zig )


#4

I don’t think we need first class types when the workaround is only 1 extra line of code. Also I think this is a problem with macros that is being worked on.


#5

A work around is to make your macro put the type in a alias then use that alias.

Yep, I know this works (see workaround section in my post for something similar), and I use it for something else.

Also I think this is a problem with macros that is being worked on.

Do you happen to know where this is tracked? As I said, https://github.com/rust-lang/rust/issues/38356 looks to be where the error points me to, but I’m not seeing anything in that thread that tells me where to look for this, if indeed it is being worked on.

I guess the other part of this that makes me think it should (eventually?) work is that “old” type macros do exist and have been working for the better part of at least a year https://github.com/rust-lang/rust/issues/38356


#6

I don’t remember where it is being tracked, if I find it I will update this with a link.


#7

Yeah, about that…

macro_rules! ty {
    () => { i32 };
}

fn main() {
    let x: ty!() = 42i32;
    println!("{:?}", x);
}

(On playpen)


#8

Huh, i wasn’t aware of that, cool