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

Matching function signature is nearly impossible in declarative macros (mbe) #73174

Open
WaffleLapkin opened this issue Jun 9, 2020 · 0 comments
Labels
A-macros Area: All kinds of macros (custom derive, macro_rules!, proc macros, ..) C-feature-request Category: A feature request, i.e: not implemented / a PR. T-lang Relevant to the language team, which will review and decide on the PR/issue.

Comments

@WaffleLapkin
Copy link
Member

I'm trying a write a macro that needs a function signature. I want smt like this to work:

macros! {
    pub fn foo(&self, arg: Arg) -> Ret;
    // inside the macro I have access to visibility, function qualifiers, arguments, 
}

However, there are 2 things that are nearly impossible to parse...

Qualifiers

According to rust reference rust function can have

  • either const or async
  • unsafe
  • extern / extern "Abi"

It's possible to use $( unsafe )? (and same for other qualifiers), however, you then can't expand it back (because in this repetition there are no $vars).

It's also not possible to match them with tts because :vis doesn't allow tt after it.

My workaround is to use [ $( $qual:tt )* ], but it's just a workaround and it requires changes to macro input. (you need to write pub [unsafe] fn ...)

Trait bounds and where clause

there are several problems with bounds...

1. there is no way to match a trait (path::Trait<'life, Type>)

This one is more tricky. There are no such fragments as trait or bound, so we'll need to work around this somehow. (:ty doesn't work because it assumes that a trait is a trait object and it can't be used in a bound)

I'd like this to work: $trait:path $( < $( $lifes:lifetime ),* $(,)? $( $generics:ty ),* $(,)? > )?, however, there are 2 problems with it:

  1. :path doesn't allow < after it. (workaround: use $tr_head:ident $( :: $tr_tail:ident )* instead of :path)
  2. rustc can't choose between parsing 'life as a lifetime or as a type (???) (it somehow thinks that 'life can be a part of trait object)

I thought that 2. can be worked around by using smt like $life_or_ty_head:tt $( :: $ty_tail:ident )* $( < $( $lifes:lifetime ),* $(,)? $( $generics:ty ),* $(,)? > )? (:tt will match both 'life and head part of head::Ty...), but here is a catch: to express a type you need generics with lifetimes over again :)

2. it's not possible to use +, * and ? as a separator in repetition

You need this to match a bound: : Trait + 'a + Trait2 + 'b ...

Related issue (opened in 2014! >_<): #18700

3. you can't express 'either PatternA or PatternB' with mbe

This is needed because in where clause you can alternate lifetimes and types:

fn test<'t, 'a, 'b, 'c, A, B>() 
where
    A: Trait,
    't: 'a + 'b,
    B: Trait,
    't: 'c,
{}

(playground)

Possible solutions

One a may think of is to add missing fragments & separator escaping, e.g.:

  • :trait (matches the same things as :ty but assumes that it's trait)
  • :bound (matches bound, e.g.: Trait + 'life + Trait<'life, i32, T>)
  • :qual (matches one of const, async, unsafe, extern "Abi" and extern)
  • $( ... )$+* (matches 0 or more ... separated by +, see RFC: macro escape char rfcs#944)

This would solve most of the mentioned problems (except 'life/Gen ordering), but I have no idea what problems all of that can bring, is it hard to implement, is this breaking change, etc. I would like to hear from somebody who is more competent in this theme...

The lifetime/generics ordering problem can be solved with 'or patterns' (ADT/enums in macros? 🤔) but this needs a syntax decision and there are a lot of open questions (I think).

I can imagine smt like ${ A( $a:ident ... ) | B( ... $b:ident ) } & ${ A => { a($a) }; B => { b($b) }; } (first is a pattern and second is a expansion thing) that will expand t ... to a(t) and ... t to b(t)......

@crlf0710 crlf0710 added A-macros Area: All kinds of macros (custom derive, macro_rules!, proc macros, ..) C-feature-request Category: A feature request, i.e: not implemented / a PR. T-lang Relevant to the language team, which will review and decide on the PR/issue. labels Jun 11, 2020
sharksforarms added a commit to sharksforarms/variyak that referenced this issue Apr 6, 2021
note: can't use `:path` because it cannot be followed by `(`

Ideally there would be a better way to do this...
rust-lang/rust#73174
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-macros Area: All kinds of macros (custom derive, macro_rules!, proc macros, ..) C-feature-request Category: A feature request, i.e: not implemented / a PR. T-lang Relevant to the language team, which will review and decide on the PR/issue.
Projects
None yet
Development

No branches or pull requests

2 participants