Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
187 changes: 187 additions & 0 deletions text/0000-macro-naming.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
- Feature Name: N/A (part of other unstable features)
- Start Date: 2016-02-11
- RFC PR: (leave this empty)
- Rust Issue: (leave this empty)

# Summary
[summary]: #summary

Naming and modularisation for macros.

This RFC proposes making macros a first-class citizen in the Rust module system.
Both macros by example (`macro_rules` macros) and procedural macros (aka syntax
extensions) would use the same naming and modularisation scheme as other items
in Rust.

For procedural macros, this RFC could be implemented immediately or as part of a
larger effort to reform procedural macros. For macros by example, this would be
part of a macros 2.0 feature, the rest of which will be described in a separate
RFC. This RFC depends on the changes to name resolution described in
[RFC 1560](https://github.com/rust-lang/rfcs/pull/1560).

# Motivation
[motivation]: #motivation

Currently, procedural macros are not modularised at all (beyond the crate
level). Macros by example have a [custom modularisation
scheme](https://github.com/rust-lang/rfcs/blob/master/text/0453-macro-reform.md)
which involves modules to some extent, but relies on source ordering and
attributes which are not used for other items. Macros cannot be imported or
named using the usual syntax. It is confusing that macros use their own system
for modularisation. It would be far nicer if they were a more regular feature of
Rust in this respect.


# Detailed design
[design]: #detailed-design

## Defining macros

This RFC does not propose changes to macro definitions. It is envisaged that
definitions of procedural macros will change, see [this blog post](http://ncameron.org/blog/macro-plans-syntax/)
for some rough ideas. I'm assuming that procedural macros will be defined in
some function-like way and that these functions will be defined in modules in
their own crate (to start with).

Ordering of macro definitions in the source text will no longer be significant.
A macro may be used before it is defined, as long as it can be named. That is,
macros follow the same rules regarding ordering as other items. E.g., this will
work:

```
foo!();

macro! foo { ... }
```

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this is a good idea, because it won't play nice with macro-defining macros. For example, the following sort of thing should (and currently does) work:

  macro_rules! macro_defining { 
    ($name:ident) => { macro_rules! $name { () => (println!("Yay!");) } }
  }

  macro_defining!( say_yay );

  say_yay!();

...but it only works if the macros are expanded in the order they are written, and there's no reasonable static analysis that can tell us that fact.

(Note, I'm using a hypothetical `macro!` defintion which I will define in a future
RFC. The reader can assume it works much like `macro_rules!`, but with the new
naming scheme).

Macro expansion order is also not defined by source order. E.g., in `foo!(); bar!();`,
`bar` may be expanded before `foo`. Ordering is only guaranteed as far as it is
necessary. E.g., if `bar` is only defined by expanding `foo`, then `foo` must be
expanded before `bar`.

## Function-like macro uses

A function-like macro use (c.f., attribute-like macro use) is a macro use which
uses `foo!(...)` or `foo! ident (...)` syntax (where `()` may also be `[]` or `{}`).

Macros may be named by using a `::`-separated path. Naming follows the same
rules as other items in Rust.

If a macro `baz` (by example or procedural) is defined in a module `bar` which
is nested in `foo`, then it may be used anywhere in the crate using an
absolute path: `::foo::bar::baz!(...)`. It can be used via relative paths in the
usual way, e.g., inside `foo` as `bar::baz!()`.

Macros declared inside a function body can only be used inside that function
body.

For procedural macros, the path must point to the function defining the macro.

The grammar for macros is changed, anywhere we currently parser `name "!"`, we
now parse `path "!"`. I don't think this introduces any issues.

Name lookup follows the same name resolution rules as other items. See [RFC
1560](https://github.com/rust-lang/rfcs/pull/1560) for details on how name
resolution could be adapted to support this.

## Attribute-like macro uses

Attribute macros may also be named using a `::`-separated path. Other than
appearing in an attribute, these also follow the usual Rust naming rules.

E.g., `#[::foo::bar::baz(...)]` and `#[bar::baz(...)]` are uses of absolute and
relative paths, respectively.


## Importing macros

Importing macros is done using `use` in the same way as other items. An `!` is
not necessary in an import item. Macros are imported into their own namespace
and do not shadow or overlap items with the same name in the type or value
namespaces.

E.g., `use foo::bar::baz;` imports the macro `baz` from the module `::foo::bar`.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isn't that problematic, because it also imports module/function baz from module ::foo::bar?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have the same issue today with baz importing both a type and value if they exist in the specified module. I don't think that causes problems, and it makes any other solution here less desirable.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not use the syntax, use foo::bar::baz!;?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I would prefer paths to use bangs in the identifiers for macros. Eg. use approx::{relative_eq!, assert_ulps_eq!};

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rustdoc also displays macro names with !s (e.g., std::println!). Obviously this could be changed, but I think it’s sort of become an informal standard to write macro names with the bang, even now when macro names are just identifiers and not paths.

Also, types and constants have (unenforced, but still generally followed) capitalisation schemes which help distinguish between them in imports, while macros are named in the same manner as functions (if you don’t count the !).

Copy link

@eternaleye eternaleye Apr 22, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you misunderstand me.

extern crate has_macro;
use has_macro::useful! as invokable!;
use has_macro::useful as callable;

#[cfg(macro)]
fn my_macro(...) -> ... {
    let x = callable(...);
    invokable!(x)
}

This should link has_macro to the compiler when compiling my crate, and to my crate when compiling the user's source, where they invoke my macro.

Copy link
Member

@eddyb eddyb Apr 22, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Those imports also need #[cfg(macro)]. Also, I expect macro functions are not be present in the function namespace in the current crate, because it's not in the same crate, really, it's a separate plugin crate.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That'd ba a shame, because it'd be quite useful IMO.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think the #[cfg(macro)] stuff has settled - I believe in its current form it can't even work.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't believe that there's any need for #[cfg(macro)]s to be usable as functions at all. I don't believe that the equivalent feature is present in Racket, probably because functions that spit out syntax aren't useful except where syntax is expected. So expanding to code that invokes procedural macros will probably happen all the time (even recursive invocation of the macro currently being defined!), but there's probably little benefit to calling them as if they were functions.

Macro imports may be used in import lists (with other macro imports and with
non-macro imports).

Where a glob import (`use ...::*;`) imports names from a module including macro
definitions, the names of those macros are also imported. E.g., `use
foo::bar::*;` would import `baz` along with any other items in `foo::bar`.

Where macros are defined in a separate crate, these are imported in the same way
as other items by an `extern crate` item.

No `#[macro_use]` or `#[macro_export]` annotations are required.


## Shadowing

Macro names follow the same shadowing rules as other names. For example, an
explicitly declared macro would shadow a glob-imported macro with the same name.
Note that since macros are in a different namespace from types and values, a
macro cannot shadow a type or value or vice versa.


# Drawbacks
[drawbacks]: #drawbacks

If the new macro system is not well adopted by users, we could be left with two
very different schemes for naming macros depending on whether a macro is defined
by example or procedurally. That would be inconsistent and annoying. However, I
hope we can make the new macro system appealing enough and close enough to the
existing system that migration is both desirable and easy.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could simply lint on #[macro_use] and #[macro_export]. This would never show up in dependencies, but only in the crate that's currently compiled



# Alternatives
[alternatives]: #alternatives

We could adopt the proposed scheme for procedural macros only and keep the
existing scheme for macros by example.

We could adapt the current macros by example scheme to procedural macros.

We could require the `!` in macro imports to distinguish them from other names.
I don't think this is necessary or helpful.

We could continue to require `macro_export` annotations on top of this scheme.
However, I prefer moving to a scheme using the same privacy system as the rest
of Rust, see below.


# Unresolved questions
[unresolved]: #unresolved-questions

## Privacy for macros

I would like that macros follow the same rules for privacy as other Rust items,
i.e., they are private by default and may be marked as `pub` to make them
public. This is not as straightforward as it sounds as it requires parsing `pub
macro! foo` as a macro definition, etc. I leave this for a separate RFC.

## Scoped attributes

It would be nice for tools to use scoped attributes as well as procedural
macros, e.g., `#[rustfmt::skip]` or `#[rust::new_attribute]`. I believe this
should be straightforward syntactically, but there are open questions around
when attributes are ignored or seen by tools and the compiler. Again, I leave it
for a future RFC.

## Inline procedural macros

Some day, I hope that procedural macros may be defined in the same crate in
which they are used. I leave the details of this for later, however, I don't
think this affects the design of naming - it should all Just Work.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you provide some hints on how you plan to implement this?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Procedural macros and any items marked with #[cfg(macro)] would be split off and compiled into one crate, everything else compiled into another. The first crate would be linked in to the compiler while it compiles the second (or equivalently, called using IPC).

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So macros will only be able to call functions marked with #[cfg(macro)]?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That seems strange, #[cfg]-gating can only remove items with #[cfg(P)] where the cfg predicate P doesn't hold.


## Applying to existing macros

This RFC is framed in terms of a new macro system. There are various ways that
some parts of it could be applied to existing macros (`macro_rules!`) to
backwards compatibly make existing macros usable under the new naming system.

I want to leave this question unanswered for now. Until we get some experience
implementing this feature it is unclear how much this is possible. Once we know
that we can try to decide how much of that is also desirable.