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

struct constructors are some kind of weird type/path hybrid #41740

Closed
durka opened this Issue May 4, 2017 · 1 comment

Comments

Projects
None yet
3 participants
@durka
Copy link
Contributor

durka commented May 4, 2017

Is this a known issue?

Consider the following setup:

mod foo {
    pub struct Bar<T>(pub T);
}

You can construct a Bar:

let _ = foo::Bar::<i32>(42);

Now I want to try it with a macro.

macro_rules! construct {
    ($strukt:path, $val:expr) => {
        $strukt($val)
    }
}

The obvious thing doesn't parse at all:

let _ = construct!(foo::Bar::<i32>, 42); // ERROR
//                         ^ unexpected token `::`

We need to remove the second ::, which makes things inconsistent with the macro-less syntax:

let _ = construct!(foo::Bar<i32>, 42); // ok

Why this inconsistency? It seems like a struct constructor isn't really a path. But that's the only macro fragment that can be used in that position (besides ident and tt).

@petrochenkov

This comment has been minimized.

Copy link
Contributor

petrochenkov commented May 4, 2017

This issue is not related to struct constructors in general, it's related to what path matcher accepts in macros.
Minimal example:

macro_rules! m {
    ($p: path) => {}
}

fn main() {
    m!(a<b>); // OK
    m!(a::<b>); // FAIL
}

There are two flavors of paths (syntactically) - with disambiguating "turbofish" (a::<b>) and without it (a<b>). :: is required in expressions for disambiguation, but in other contexts it's not necessary and should probably just be optional, but is currently prohibited instead (I don't know why, maybe there's a history behind it, maybe not). The issue is that paths passed to macros through path matchers are considered "non-expression" paths.
As I said previously turbofish being rejected in non-expression paths is an artificial restriction and this issue gives a good motivation for removing it.

@petrochenkov petrochenkov self-assigned this May 4, 2017

bors added a commit that referenced this issue Aug 21, 2017

Auto merge of #43540 - petrochenkov:pathrelax, r=nikomatsakis
syntax: Relax path grammar

TLDR: Accept the disambiguator `::` in "type" paths (`Type::<Args>`), accept the disambiguator `::` before parenthesized generic arguments (`Fn::(Args)`).

The "turbofish" disambiguator `::<>` in expression paths is a necessary evil required for path parsing to be both simple and to give reasonable results.
Since paths in expressions usually refer to values (but not necessarily, e.g. `Struct::<u8> { field: 0 }` is disambiguated, but refers to a type), people often consider `::<>` to be inherent to *values*, and not *expressions* and want to write disambiguated paths for values even in contexts where disambiguation is not strictly necessary, for example when a path is passed to a macro `m!(Vec::<i32>::new)`.
The problem is that currently, if the disambiguator is not *required*, then it's *prohibited*. This results in confusion - see #41740, https://internals.rust-lang.org/t/macro-path-uses-novel-syntax/5561.

This PR makes the disambiguator *optional* instead of prohibited in contexts where it's not strictly required, so people can pass paths to macros in whatever form they consider natural (e.g. disambiguated form for value paths).
This PR also accepts the disambiguator in paths with parenthesized arguments (`Fn::(Args)`) for consistency and to simplify testing of stuff like #41856 (comment).

Closes #41740

cc @rust-lang/lang
r? @nikomatsakis

@bors bors closed this in #43540 Aug 22, 2017

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.