7

I'm trying to match the trait bounds for generic types:

macro_rules! test {
    (
        where $(
            $bounded_type:ident: $( $bound:tt )++,
        )+
    ) => {
        // Dummy expansion for test:
        struct Foo<T, U>
        where $(
            $bounded_type : $( $bound )++,
        )+
        {
            t: T,
            u: U
        }
    }
}

test! {
    where
        T: PartialEq + Clone,
        U: PartialEq,
}

fn main() {}

Unfortunately, if I understand well, the only way to match a trait is a tt fragment, but this fragment can match almost anything, so whatever I do, I get an error:

error: local ambiguity: multiple parsing options: built-in NTs tt ('bound') or 1 other option.

How can I match this piece of code?

Note that I do not need something very elegant (I do not need it for plublic users) but of course, the more elegant, the better.

Peter Hall
  • 53,120
  • 14
  • 139
  • 204
Boiethios
  • 38,438
  • 19
  • 134
  • 183
  • I had a lot of problems both matching and emitting type bounds. It was a while ago, but I think I ended up just doing something else instead... – Peter Hall Jul 29 '18 at 11:20
  • @PeterHall The Rust team should do something about that. IMO, the macro system should permit to easily match and emit the language syntax. – Boiethios Jul 29 '18 at 11:48

3 Answers3

4

Your best bet is to read the source code for the parse-generics-shim crate; it's a bit old, but should hopefully still work. This is way too convoluted to explain in a Stack Overflow question, since it would basically involve copy+pasting the source of that crate into the answer.

The easier approach is to just not parse actual Rust syntax, and use something the macro parser can handle, like wrapping the constraints in a group (like { ... }).

DK.
  • 55,277
  • 5
  • 189
  • 162
  • `parse_where_shim!` calls `parse_where!` in this file https://github.com/DanielKeep/rust-parse-generics/blob/master/parse-generics-shim/src/parse_where_shim.rs, but I cannot find the definition of the latter. – Boiethios Jul 29 '18 at 20:47
  • @Boiethios You'll note that the next macro is *also* called `parse_where_shim!` and has the exact inverse conditional compilation condition. `parse_where!` was a proposed addition to the compiler that was rejected; hence the alternate implementation that doesn't use it. – DK. Jul 31 '18 at 00:07
2

I was able to get this to match by splitting the first bound from the rest.

macro_rules! test {
    (
        where $(
            $bounded_type:ident: $bound:tt $(+ $others:tt )*,
        )+
    ) => {
        // Dummy expansion for test:
        struct Foo<T, U>
        where $(
            $bounded_type : $bound $(+ $others)*,
        )+

        {
            t: T,
            u: U
        }
    }
}

However, this isn't going to work if the traits have parameters.

Peter Hall
  • 53,120
  • 14
  • 139
  • 204
  • 1
    Unfortunately, I do have generic trait bounds :( But your idea is good ! – Boiethios Jul 29 '18 at 12:08
  • I initially expected `tt` to match things like `Into` here actually. I think you really want to use `ty`, but there are too many restrictions about what can follow it - e.g. `+` cannot. – Peter Hall Jul 29 '18 at 13:40
  • I tried with `ty` with coma separator and semicolon termination, but when I try to expand the macro, I'm getting this error: `expected `where`, or `{` after struct name` – Boiethios Jul 29 '18 at 14:08
1

Since around 2016 with the closing of this issue, you could use the path macro type to match TypePaths.

For example, from my own code:

($name:ident<$($l:lifetime, )*$($x:ident : $xt:path),+>($s:ident$(, $a:ident: $t:ty)*) -> $ret:ty  => $body:block) => {
}
norcalli
  • 1,215
  • 2
  • 11
  • 22
  • I'm confused by this answer and [the documentation on `TypePath`s](https://doc.rust-lang.org/reference/paths.html#paths-in-types). Going by the name, I would expect a `TypePath` to be the `foo::bar` in `foo::bar::MyType`. `$type:ty` will already match types in general, including the `foo::bar` part, so I'm not sure what the difference is. – Joseph Garvin Sep 08 '21 at 23:58
  • Also does this handle bounds with more than one constraint, e.g. `T: Clone + Eq`? – Joseph Garvin Sep 09 '21 at 00:02