Macro path uses novel syntax

I think this is an oddity, that this compiles:

macro_rules! foo {
    ($x:path) => { $x() }
}

fn main() {
    let mut v = foo!(Vec<i32>::new);
    v.push(1);
}

playground link

It is odd because Vec<i32>::new is not valid syntax anywhere else. It’s either <Vec<i32>>::new (<Type>::method) or Vec::<i32>::new (path-like).

There are 3 kinds of path styles:

  • Module, generic parameters disallowed i.e. Result::Err. These styles of paths are used in:
    • Attributes #[path::attribute]
    • Macro invocations path::macro!()
    • Module reference (pub(restricted) and use)
  • Type, double-colons prohibited before generic parameters i.e. Result<T, U>::Err. These styles of paths are used in:
    • Type and trait references
    • Macros 1.0 :path
  • Expression, double-colons required before generic parameters i.e. Result::<T,U>::Err
    • Expressions
    • Patterns

(In a qualified path <P as Trait>::Q, if the whole path uses style X, the inner path P is also parsed using style X.)

The oddity is $x:path is parsed where a type is expected (i.e. Vec<i32>::new is parsed like an inner type called new inside the struct Vec<i32>). After a path is parsed the style information is thrown out, so it can be used like an expression $x(). This causes the strange syntax mismatch here.

I’m not sure if this is something Macros 2.0 (#![feature(decl_macro)]) is going to improve or worsen here, as I heard that $x there will eventually just mean the token stream Vec<i32>::new without any special meaning, so it will become a syntax error.

2 Likes

I think this can be “fixed” in the same way as https://github.com/rust-lang/rust/issues/41740.

I.e. instead of three “styles” of paths described by @kennytm, there will be two styles of paths - “usual” paths with optional :: before <...> usable everywhere except for expressions, and “sorry but we need a disambiguator” paths with mandatory :: before <...> used in expressions.
Generic arguments in import/visibility/etc paths will be rejected by a semantic check and not by parser (this is already necessary, see https://github.com/rust-lang/rust/blob/master/src/test/compile-fail/import-ty-params.rs).

This way you will be able to pass any kinds of paths to macros and choose the variant that looks better / more natural, e.g. Vec::<i32>::new for methods (even if Vec<i32>::new will still be accepted).

1 Like

I've found an interesting example while implementing this:

#[attr_name(attr_value)]

is parsed like a path segment with generic argument, similar to Fn(ArgType)!
So, it seems like an equivalent of PathStyle::Mod is still needed.

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.