Matching function signature is nearly impossible in declarative macros (mbe) #73174
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.
I'm trying a write a macro that needs a function signature. I want smt like this to work:
However, there are 2 things that are nearly impossible to parse...
Qualifiers
According to rust reference rust function can have
const
orasync
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$var
s).It's also not possible to match them with
tt
s because:vis
doesn't allowtt
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 writepub [unsafe] fn ...
)Trait bounds and
where
clausethere 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::path
doesn't allow<
after it. (workaround: use$tr_head:ident $( :: $tr_tail:ident )*
instead of:path
)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
andhead
part ofhead::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 repetitionYou 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:
(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 ofconst
,async
,unsafe
,extern "Abi"
andextern
)$( ... )$+*
(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 expandt ...
toa(t)
and... t
tob(t)
......The text was updated successfully, but these errors were encountered: