Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

parse error in select!/select_biased! macro #2824

Closed
antonok-edm opened this issue Jan 28, 2024 · 2 comments · Fixed by #2832
Closed

parse error in select!/select_biased! macro #2824

antonok-edm opened this issue Jan 28, 2024 · 2 comments · Fixed by #2832
Labels
A-macro Area: macro related bug

Comments

@antonok-edm
Copy link

I noticed that in the select!/select_biased! macros, the unit type (()) only works for the pattern-side of a branch in the first position.

Consider the following minimal example:

use futures::{future::pending, select, FutureExt};

async fn foo() {
    select! {
        () = pending::<()>().fuse() => {}
        () = pending::<()>().fuse() => {}
    }
}

It produces the following error (note: line 6 is the second branch):

error: expected `,`
 --> src/lib.rs:6:37
  |
6 |         () = ready::<()>(()).fuse() => {}
  |                                     ^

This version compiles without issues:

use futures::{future::ready, select, FutureExt};

async fn foo() {
    select! {
        () = ready::<()>(()).fuse() => {}
        _ = ready::<()>(()).fuse() => {}
    }
}
@taiki-e
Copy link
Member

taiki-e commented Feb 25, 2024

I added the two dbg to the following code:

// `=> <expr>`
input.parse::<Token![=>]>()?;
let expr = input.parse::<Expr>()?;

 fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
 
             // `=> <expr>`
             input.parse::<Token![=>]>()?;
+            dbg!(&input);
             let expr = input.parse::<Expr>()?;
+            dbg!(&input);
 
             // Commas after the expression are only optional if it's a `Block`
             // or it is the last branch in the `match`.

And the output is:

[futures-macro/src/select.rs:62:13] &input = TokenStream [
    Group {
        delimiter: Brace,
        stream: TokenStream [],
        span: #0 bytes(10909..10911),
    },
    Group {
        delimiter: Parenthesis,
        stream: TokenStream [],
        span: #0 bytes(10924..10926),
    },
    Punct {
        ch: '=',
        spacing: Alone,
        span: #0 bytes(10927..10928),
    },
    Ident {
        ident: "pending",
        span: #0 bytes(10929..10936),
    },
    Punct {
        ch: ':',
        spacing: Joint,
        span: #0 bytes(10936..10937),
    },
    Punct {
        ch: ':',
        spacing: Joint,
        span: #0 bytes(10937..10938),
    },
    Punct {
        ch: '<',
        spacing: Alone,
        span: #0 bytes(10938..10939),
    },
    Group {
        delimiter: Parenthesis,
        stream: TokenStream [],
        span: #0 bytes(10939..10941),
    },
    Punct {
        ch: '>',
        spacing: Alone,
        span: #0 bytes(10941..10942),
    },
    Group {
        delimiter: Parenthesis,
        stream: TokenStream [],
        span: #0 bytes(10942..10944),
    },
    Punct {
        ch: '.',
        spacing: Alone,
        span: #0 bytes(10944..10945),
    },
    Ident {
        ident: "fuse",
        span: #0 bytes(10945..10949),
    },
    Group {
        delimiter: Parenthesis,
        stream: TokenStream [],
        span: #0 bytes(10949..10951),
    },
    Punct {
        ch: '=',
        spacing: Joint,
        span: #0 bytes(10952..10953),
    },
    Punct {
        ch: '>',
        spacing: Alone,
        span: #0 bytes(10953..10954),
    },
    Group {
        delimiter: Brace,
        stream: TokenStream [],
        span: #0 bytes(10955..10957),
    },
]
[futures-macro/src/select.rs:64:13] &input = TokenStream [
    Punct {
        ch: '=',
        spacing: Joint,
        span: #0 bytes(10952..10953),
    },
    Punct {
        ch: '>',
        spacing: Alone,
        span: #0 bytes(10953..10954),
    },
    Group {
        delimiter: Brace,
        stream: TokenStream [],
        span: #0 bytes(10955..10957),
    },
]
error: expected `,`
   --> futures/tests/async_await_macros.rs:402:41
    |
402 |             () = pending::<()>().fuse() => {}
    |                                         ^

It seems syn treats {} () = pending::<()>().fuse() as a single expr.
cc @dtolnay

@taiki-e taiki-e added bug A-macro Area: macro related labels Feb 25, 2024
@dtolnay
Copy link
Member

dtolnay commented Feb 25, 2024

{} () = pending::<()>().fuse() is a single expr.

macro_rules! expr {
    ($e:expr) => {};
}

expr!({} () = pending::<()>().fuse());

To parse this in a way that uses the expression termination rules of match arms, what you could do is: after parsing your =>, collect all the rest of the tokens into a TokenStream prefixed with _ =>, then parse syn::Arm from that, and keep just the body from that. Then repeat on whatever tokens are left.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-macro Area: macro related bug
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants