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

Macros by example 2.0 (macro!) #1584

Merged
merged 3 commits into from Jan 30, 2017

Conversation

@nrc
Member

nrc commented Apr 19, 2016

Macros by example 2.0. A replacement for macro_rules!. This is mostly a
placeholder RFC since many of the issues affecting the new macro system are
(or will be) addressed in other RFCs. This RFC may be expanded at a later date.

Rendered

Tracking issue

@nrc nrc self-assigned this Apr 19, 2016

@nrc nrc added the T-lang label Apr 19, 2016

@durka

This comment has been minimized.

Contributor

durka commented Apr 19, 2016

Hmm so we are going to have three macro systems? That is, legacy macro_rules!, macro!, and procedural macros. Though I assume that the first two should be implementable using procedural macros, right?

I am curious about the specific, substantive, backwards-incompatible improvements to Macro By Example that motivate this. Because there have been many useful, backwards-compatible improvements proposed that have been shoved to the side in anticipation of procedural macros.

mod a {
// Macro privacy (TBA)
pub macro! foo { ... }

This comment has been minimized.

@ticki

ticki Apr 19, 2016

Contributor

What is the point of the ! after the keyword? I see no reason for that. Perhaps more fitting would be a ! after foo.

This comment has been minimized.

@nrc

nrc Apr 19, 2016

Member

Consistency with macro_rules!. Dropping the ! is listed in the alternatives section.

This comment has been minimized.

@nagisa

nagisa Apr 19, 2016

Contributor

These already are incompatible systems and there’s no reason to have such weird consistency quirk IMO.

This comment has been minimized.

@ticki

ticki Apr 20, 2016

Contributor

Yeah, I don't get the point. There is no compatibility anyways.

This comment has been minimized.

@nixpulvis

nixpulvis Apr 20, 2016

See my comment in #1561 (comment)

This comment has been minimized.

@solson

solson Sep 17, 2016

Member

@ticki What effect could an import possibly have that the programmer cares about? Macros are only potentially surprisingly at the invocation site.

This comment has been minimized.

@joshtriplett

joshtriplett Sep 18, 2016

Member

@solson What if you have a macro named foo! and a function named foo, and you want to export them both for other modules to import? Those two names do not conflict; see https://is.gd/QCJ7Iv for an example. How would you disambiguate and allow importing those names independently? Including the ! on the import of the macro seems like it would produce the behavior people would expect.

This comment has been minimized.

@solson

solson Sep 18, 2016

Member

@joshtriplett Yeah, it might be necessary for name clashes. @eternaleye also brought up the fact that with procedural macros you would usually want to import the macro, but you might also want to import the fn that implements the macro, which has the same name, so you could call it as a subroutine of another procedural macro.

This comment has been minimized.

@eternaleye

eternaleye Sep 19, 2016

@solson: that latter case is especially meaningful because using it as a macro means the compiler needs it at build time, while using it as a function means the crate needs it at run time. As a result, importing both and relying on them being called in different ways does not suffice.

This comment has been minimized.

@Ericson2314

Ericson2314 Sep 26, 2016

Contributor

Yeah I was going to propose extern! for phase-incrementing imports. See rust-lang/rust#35900 (comment) in the procedural macros 1.1 tracking issue where I brought up the problem.

@nrc

This comment has been minimized.

Member

nrc commented Apr 19, 2016

Hmm so we are going to have three macro systems? That is, legacy macro_rules!, macro!, and procedural macros.

Well in the short term, four - old and new macros by example, and old and new procedural macros. In the long term, hopefully, both old versions will disappear.

Though I assume that the first two should be implementable using procedural macros, right?

I don't consider this a goal. While it would be nice (it's kind of cute from a language geek perspective), it doesn't bring any material advantages (making macros by example 'pluggable' is about the only real advantage, and I don't think that is a great one, others may differ). So, I'm not trying to avoid this, but if there are benefits we get by not doing it then, we'll take them.

specific, substantive, backwards-incompatible improvements to Macro By Example

  • naming and modularisation (i.e., naming macros like other items in Rust)
  • privacy for macros
  • being able to use relative paths in macros (path hygiene)
  • better hygiene (e.g., for items, type parameters, lifetime parameters, etc.)
  • potentially, some nicer syntax
@durka

This comment has been minimized.

Contributor

durka commented Apr 19, 2016

How about

  • working concat_idents!
  • more matchers (lifetime, visibility, moelarry)
  • a way to escape $
  • macro re-exportation (sounds like it'll be covered under modularisation)
  • one-or-zero repetition

How many macro systems do we need to go through before feature requests like that can be considered? I don't mean to sound sarcastic -- I'm excited, but a bit dismayed about continual punting.

@durka

This comment has been minimized.

Contributor

durka commented Apr 19, 2016

I actually meant "can we implement Macro By Example 2.0 using new procedural macros" as a serious suggestion, not a cute one. If procedural macros are powerful enough and ergonomic enough to do it, then we can iterate on Macro By Example features without arguing an RFC for each one.

@nrc

This comment has been minimized.

Member

nrc commented Apr 19, 2016

working concat_idents!

RFC coming up in the next few weeks

a way to escape $

part of the new syntax, should be coming soon

macro re-exportation (sounds like it'll be covered under modularisation)

export/re-export disappears, replaced by modularisation

more matchers (lifetime, visibility, moelarry)
one-or-zero repetition

backwards compatible, I'm not planning anything here, but I'd be happy to see RFCs building on macros 2.0.

In short, I see macros 2.0 as a prerequisite for this kind of stuff - we don't want to expend effort on them until we're sure about the foundations. I don't see macro reform as punting that stuff, more like bringing it closer.

@durka

This comment has been minimized.

Contributor

durka commented Apr 19, 2016

Awesome!

I'd be happy to see RFCs building on macros 2.0

That's what I wanted to hear :)

@kennytm

This comment has been minimized.

Member

kennytm commented Apr 30, 2016

cc #1266. I think having a way to generate a macro-private mod (i.e. creating a hygienic item) should be enough for most use cases of gensym.

Also, would this address #991 (use + or * as separators)?

@comex comex referenced this pull request Sep 5, 2016

Closed

Longer question marks RFC #1737

@nrc

This comment has been minimized.

Member

nrc commented Nov 11, 2016

@rfcbot fcp merge

This RFC proposes the concept of macros by example 2.0, without defining the details. Together with other RFCs, some parts (e.g., naming and modularisation) could be implemented today. Future RFCs will further specify other aspects of macros 2.0. It is somewhat akin to the proposed concept of motivation RFCs, although there is a small amount of implementation specified.

Feedback has been positive, with the only controversial point whether to use macro or macro! to declare macros. I have changed the RFC from the latter to the former.

An alternative to accepting this RFC now, might be to accept some implementation in the compiler without an accepted RFC and fill out this RFC into a more complete version.

@rfcbot

This comment has been minimized.

rfcbot commented Nov 11, 2016

Team member @nrc has proposed to merge this. The next step is review by the rest of the tagged teams:

No concerns currently listed.

Once these reviewers reach consensus, this will enter its final comment period. If you spot a major issue that hasn't been raised at any point in this process, please speak up!

See this document for info about what commands tagged team members can give me.

@withoutboats

This comment has been minimized.

Contributor

withoutboats commented Nov 11, 2016

Reviewed the RFC again and I like it.

I am still pretty strongly in favor of macro!. I hold the perhaps too optimistic perspective that some day macro!, format_args!, #[proc_macro_derive], and possibly some of the other "compiler built ins" can simply be libraries on top of the procedural macro API. I think the conceptual elegance of having a single metaprogramming layer with a bunch of tools written in it would make the language easier to "get" and more appealing to users, and I think holding out for that is worth the cost of having to write macro! instead of macro.

This isn't to say that we should have implementing the APIs to make this possible in our near horizon, just that we shouldn't foreclose on the possibility. Maybe the idea of ever achieving this is unrealistic though.

@joshtriplett

This comment has been minimized.

Member

joshtriplett commented Nov 11, 2016

I'd like to see macro! for similar reasons.

@solson

This comment has been minimized.

Member

solson commented Nov 11, 2016

I don't understand why the thing that defines macro items needs to look like a macro invocation. I'm pretty strongly in favor of plain macro. Today's macro_rules! presents a bit of a weird separate world from regular Rust syntax, and using a ! for the declaration is one way it differs from normal Rust items. Why not make macros more normal?

I find it even more troubling to use the ! when the RFC proposes to attach a privacy to the front. pub macro! foo(...) reads like a weird mix of treating macros like first class items but also not.

@eddyb

This comment has been minimized.

Member

eddyb commented Nov 11, 2016

Not sure where or even if it was mentioned, but pub macro foo = macro_rules! {...}; might work.

@withoutboats

This comment has been minimized.

Contributor

withoutboats commented Nov 11, 2016

I don't understand why the thing that defines macro items needs to look like a macro invocation.

So that it might be a macro invokation someday.

(I'm very interested in arguments that this wouldn't be possible!)

@solson

This comment has been minimized.

Member

solson commented Nov 11, 2016

I don't know about possible, it just doesn't seem desirable, unless it looked like eddyb's syntax. It really doesn't fit well with pub and being a proper item.

@eddyb

This comment has been minimized.

Member

eddyb commented Nov 11, 2016

@withoutboats What language primitive would it expand to? I'd be more interested in figuring out a method of declaration with a pluggable "body". Although purely token-based expansion might not cut it either way.

@withoutboats

This comment has been minimized.

Contributor

withoutboats commented Nov 11, 2016

@eddyb it would need to expand to a proc macro, I suppose.

@solson more generally I am in favor of someday having a proc macro form for things that look like items but YMMV on that idea.

@keeperofdakeys

This comment has been minimized.

keeperofdakeys commented Nov 11, 2016

From a user's perspective, I can't think of a consistent way of making macro! behave like other item definitions. So using the macro keyword seems like the best choice for now. If this is the wrong choice, I'm sure there will be plenty of chances to change this down the line.

@solson

This comment has been minimized.

Member

solson commented Nov 11, 2016

@withoutboats I'm not opposed to a more general macro syntax concept that works for item-like things. I just think the macro! idea being proposed right now doesn't fit into the existing syntax.

@nikomatsakis

This comment has been minimized.

Contributor

nikomatsakis commented Nov 11, 2016

@withoutboats

So that it might be a macro invokation someday.

(I'm very interested in arguments that this wouldn't be possible!)

The argument that I remember has to do with cross compilation: macros defined in other crates can be used, even if those crates are not compiled for your current target. I'm not sure if this could be easily "simulated" using plugins.

@nikomatsakis

This comment has been minimized.

Contributor

nikomatsakis commented Nov 11, 2016

So, I left my check on this RFC, but to be clear -- all we're basically giving agreement to here is the idea of a macro 2.0 system, right? All details (e.g., syntax, probably even macro vs macro!) remain for future RFCs?

@withoutboats

This comment has been minimized.

Contributor

withoutboats commented Nov 11, 2016

@solson When you say it doesn't fit into existing syntax, does that mean you wouldn't favor a macro extension which would allow you to define macros that look like e.g. pub actor! Mailer { }, pub class! AbstractManagerFactory { } etc?

@solson

This comment has been minimized.

Member

solson commented Nov 11, 2016

@withoutboats Like I said, I'm not opposed to a more general macro syntax concept that works for item-like things, so I would be fine with that. When I say it doesn't fit into existing syntax, I really mean existing syntax. :) It would fit fine if we introduced this new general thing.

(It would fit fine syntactically, at least - @eddyb's question about what it expands into is an important one.)

@withoutboats

This comment has been minimized.

Contributor

withoutboats commented Nov 11, 2016

@solson but if we don't include the bang now, we can never make it use that new syntax extension? It has to be built into the compiler forever.

@solson

This comment has been minimized.

Member

solson commented Nov 11, 2016

@withoutboats That seems to be the case. In the other direction, including it now forces us to introduce the new invocation syntax or it would forever be a strange wart. So if we want to use ! we have to decide if we want the new invocation syntax now rather than later.

You're saying "built into the compiler forever" like it's a bad thing. I don't see the downside of giving these macros a first-class syntax, even if they could eventually be implemented a different way.

There are a number of things in Rust that the compiler desugars into other things that for the most part could just be macros. Internally, a struct is a univariant enum, and we could write a pub struct! Foo { ... } which expands to a univariant enum. If we introduced a macro syntax for control-flow structures, then we could have if_let! instead of a compiler built-in desugaring.

A macro! could expand into a procedural macro; a macro could desugar into one.

Some things are important enough to have dedicated surface syntax, and I think the replacement for macro_rules! is one of them.

@Wyverald

This comment has been minimized.

Wyverald commented Jan 22, 2017

+1 for the name "declarative macros". "Macros by example" always sounded like the name of a book to me. "Declarative" also forms a nice contrast with "procedural", just like in programming language families where procedural languages are a type of imperative languages, which are in contrast with declarative languages.

@solson

This comment has been minimized.

Member

solson commented Jan 22, 2017

@withoutboats I would actually say declarative is a bit more specific, since procedural macros may also do syntax pattern matching using a library.

And as @eddyb implied, patterns are just half of a declarative macro - the other half is the template you expand to. But then, procedural macros can also use templates with the quote crate.

@steveklabnik

This comment has been minimized.

Member

steveklabnik commented Jan 22, 2017

@rfcbot

This comment has been minimized.

rfcbot commented Jan 23, 2017

The final comment period is now complete.

@nikomatsakis

This comment has been minimized.

Contributor

nikomatsakis commented Jan 27, 2017

I prefer the name "pattern macro" to "declarative macro".

@steveklabnik

This comment has been minimized.

Member

steveklabnik commented Jan 27, 2017

@nikomatsakis can you say more? What do you think about the declarative/procedural split? I mostly prefer declarative due to the symmetry, but could be swayed 😄

@Ixrec

This comment has been minimized.

Contributor

Ixrec commented Jan 27, 2017

+1 for "pattern macro". That's the only name I've heard for it which might give newcomers a somewhat accurate impression of what the feature actually is.

Although I also like the symmetry of "declarative" vs "procedural", the word "declarative" is so much vaguer than "pattern" that it doesn't mean much of anything without context.

@solson

This comment has been minimized.

Member

solson commented Jan 27, 2017

@nikomatsakis That seems potentially confusing with macros that expand to patterns.

i.e. one might call foo in match x { foo!(42) => ... } a pattern macro

@ZhangHanDong

This comment has been minimized.

ZhangHanDong commented Jan 30, 2017

+1 for "pattern macro"

nrc added some commits Apr 19, 2016

Macros by example 2.0 (macro!)
Macros by example 2.0. A replacement for `macro_rules!`. This is mostly a
placeholder RFC since many of the issues affecting the new macro system are
(or will be) addressed in other RFCs. This RFC may be expanded at a later date.
Update the RFC
Only major change is moving from `macro!` to `macro` to declare a macro.

@nrc nrc referenced this pull request Jan 30, 2017

Open

Tracking issue: declarative macros 2.0 #39412

9 of 19 tasks complete

@nrc nrc merged commit 7dcb737 into rust-lang:master Jan 30, 2017

@elahn

This comment has been minimized.

elahn commented Jan 31, 2017

"regular macro" - rule-governed, conforming to a pattern.

@nikomatsakis

This comment has been minimized.

Contributor

nikomatsakis commented Feb 1, 2017

@steveklabnik

@nikomatsakis can you say more? What do you think about the declarative/procedural split? I mostly prefer declarative due to the symmetry, but could be swayed 😄

The reason I prefer "pattern macro" is that we are applying a series of patterns, one by one, and testing for a match. "Declarative" macro is also ok, but I think that the sense in which "declarative" vs procedural is being used here is ultimately a kind of jargon, and the name just won't "stick" as easily as pattern.

I suppose there could be some confusion regarding a macro that expands to a pattern, but I feel like I have basically never categorized a macro by what it expands to in this way -- that is, I don't say "it's an expression macro". I say, "it's a macro that generates an expression". (Actually, I don't say that at all most of the time, since I never seem to find I have to clarify this.)

@steveklabnik

This comment has been minimized.

Member

steveklabnik commented Feb 1, 2017

Seems fine. I don't care super strongly about this, and I think that reasoning is good too.

@colin-kiegel

This comment has been minimized.

colin-kiegel commented Feb 1, 2017

+1 for pattern macro

I think pattern matching is an essential part of what you can do with them. And declarative doesn't really tell me anything. It feels a bit academic to call it declarative but is not does not help me understand what it does.

@solson

This comment has been minimized.

Member

solson commented Feb 1, 2017

Please avoid "+1" comments with no content. GitHub added the 👍 reaction so we could avoid getting emails about nothing. :)

@nrc nrc deleted the nrc:macros-2.0 branch Feb 2, 2017

@nrc

This comment has been minimized.

Member

nrc commented Feb 2, 2017

I merged this and forgot to leave a summary at the time.

Tracking issue: rust-lang/rust#39412

This RFC is primarily a statement of intent that we will add a new declarative macros system to Rust (macros 2.0). The only real detail is that we will use macro (c.f., macro_rules!) to declare such macros (while leaving the specific syntax for future work). There has been some discussion of that in the thread, in particular about whether to use macro or macro!. The key point in that debate is that whatever the underlying mechanism for macro expansion, one can think of a desugaring step from the macro syntax, into that mechanism.

The other contentious point seems what to call these things. I added some options to the RFC before merging. My preference is for 'declarative macro' and I merged the RFC with that (I don't think it is worth postponing merging just for this). My feeling is that for now we need a name that is useful for implementors since we have to implement the feature, and we don't need to describe it to users (well, not much, anyway) until it is implemented. The main part of the name for me is its opposition to procedural macros, i.e., these are macros defined using declarative syntax, rather than Rust code. I prefer 'declarative' to 'pattern' since there might be declarative macros which do not rely on pattern matching so centrally, and which might be a candidate for macros 2.0 (I don't have anything in mind here, just not shutting any doors). When it comes to describing macros to users, I personally think 'declarative' is fine, but we could also use 'pattern macro' informally - I don't think renaming features should require an RFC or anything.

@ZhangHanDong

This comment has been minimized.

ZhangHanDong commented Feb 2, 2017

Well, You're right. @nrc

bors added a commit to rust-lang/rust that referenced this pull request May 22, 2017

Auto merge of #40847 - jseyfried:decl_macro, r=nrc
Initial implementation of declarative macros 2.0

Implement declarative macros 2.0 (rust-lang/rfcs#1584) behind `#![feature(decl_macro)]`.
Differences from `macro_rules!` include:
 - new syntax: `macro m(..) { .. }` instead of `macro_rules! m { (..) => { .. } }`
 - declarative macros are items:
```rust
// crate A:
mod foo {
    m!(); // use before definition; declaration order is irrelevant
    pub macro m() {} // `pub`, `pub(super)`, etc. work
}
fn main() {
    foo::m!(); // named like other items
    { use foo::m as n; n!(); } // imported like other items
}
pub use foo::m; // re-exported like other items

// crate B:
extern crate A; // no need for `#[macro_use]`
A::foo::m!(); A::m!();
```
 - Racket-like hygiene for items, imports, methods, fields, type parameters, privacy, etc.
   - Intuitively, names in a macro definition are resolved in the macro definition's scope, not the scope in which the macro is used.
   - This [explaination](http://beautifulracket.com/explainer/hygiene.html) of hygiene for Racket applies here (except for the "Breaking Hygiene" section). I wrote a similar [explanation](https://github.com/jseyfried/rfcs/blob/hygiene/text/0000-hygiene.md) for Rust.
   - Generally speaking, if `fn f() { <body> }` resolves, `pub macro m() { <body> } ... m!()` also resolves, even if `m!()` is in a separate crate.
   - `::foo::bar` in a `macro` behaves like `$crate::foo::bar` in a `macro_rules!`, except it can access everything visible from the `macro` (thus more permissive).
   - See [`src/test/{run-pass, compile-fail}/hygiene`](afe7d89) for examples. Small example:
```rust
mod foo {
    fn f() { println!("hello world"); }
    pub macro m() { f(); }
}
fn main() { foo::m!(); }
```

Limitations:
 - This does not address planned changes to matchers (`expr`,`ty`, etc.), c.f. #26361.
 - Lints (including stability and deprecation) and `unsafe` are not hygienic.
   - adding hygiene here will be mostly or entirely backwards compatible
 - Nested macro definitions (a `macro` inside another `macro`) don't always work correctly when invoked from external crates.
   - pending improvements in how we encode macro definitions in crate metadata
 - There is no way to "escape" hygiene without using a procedural macro.

r? @nrc

bors added a commit to rust-lang/rust that referenced this pull request May 23, 2017

Auto merge of #40847 - jseyfried:decl_macro, r=nrc
Initial implementation of declarative macros 2.0

Implement declarative macros 2.0 (rust-lang/rfcs#1584) behind `#![feature(decl_macro)]`.
Differences from `macro_rules!` include:
 - new syntax: `macro m(..) { .. }` instead of `macro_rules! m { (..) => { .. } }`
 - declarative macros are items:
```rust
// crate A:
pub mod foo {
    m!(); // use before definition; declaration order is irrelevant
    pub macro m() {} // `pub`, `pub(super)`, etc. work
}
fn main() {
    foo::m!(); // named like other items
    { use foo::m as n; n!(); } // imported like other items
}
pub use foo::m; // re-exported like other items

// crate B:
extern crate A; // no need for `#[macro_use]`
A::foo::m!(); A::m!();
```
 - Racket-like hygiene for items, imports, methods, fields, type parameters, privacy, etc.
   - Intuitively, names in a macro definition are resolved in the macro definition's scope, not the scope in which the macro is used.
   - This [explaination](http://beautifulracket.com/explainer/hygiene.html) of hygiene for Racket applies here (except for the "Breaking Hygiene" section). I wrote a similar [explanation](https://github.com/jseyfried/rfcs/blob/hygiene/text/0000-hygiene.md) for Rust.
   - Generally speaking, if `fn f() { <body> }` resolves, `pub macro m() { <body> } ... m!()` also resolves, even if `m!()` is in a separate crate.
   - `::foo::bar` in a `macro` behaves like `$crate::foo::bar` in a `macro_rules!`, except it can access everything visible from the `macro` (thus more permissive).
   - See [`src/test/{run-pass, compile-fail}/hygiene`](afe7d89) for examples. Small example:
```rust
mod foo {
    fn f() { println!("hello world"); }
    pub macro m() { f(); }
}
fn main() { foo::m!(); }
```

Limitations:
 - This does not address planned changes to matchers (`expr`,`ty`, etc.), c.f. #26361.
 - Lints (including stability and deprecation) and `unsafe` are not hygienic.
   - adding hygiene here will be mostly or entirely backwards compatible
 - Nested macro definitions (a `macro` inside another `macro`) don't always work correctly when invoked from external crates.
   - pending improvements in how we encode macro definitions in crate metadata
 - There is no way to "escape" hygiene without using a procedural macro.

r? @nrc

bors added a commit to rust-lang/rust that referenced this pull request May 25, 2017

Auto merge of #40847 - jseyfried:decl_macro, r=nrc
Initial implementation of declarative macros 2.0

Implement declarative macros 2.0 (rust-lang/rfcs#1584) behind `#![feature(decl_macro)]`.
Differences from `macro_rules!` include:
 - new syntax: `macro m(..) { .. }` instead of `macro_rules! m { (..) => { .. } }`
 - declarative macros are items:
```rust
// crate A:
pub mod foo {
    m!(); // use before definition; declaration order is irrelevant
    pub macro m() {} // `pub`, `pub(super)`, etc. work
}
fn main() {
    foo::m!(); // named like other items
    { use foo::m as n; n!(); } // imported like other items
}
pub use foo::m; // re-exported like other items

// crate B:
extern crate A; // no need for `#[macro_use]`
A::foo::m!(); A::m!();
```
 - Racket-like hygiene for items, imports, methods, fields, type parameters, privacy, etc.
   - Intuitively, names in a macro definition are resolved in the macro definition's scope, not the scope in which the macro is used.
   - This [explaination](http://beautifulracket.com/explainer/hygiene.html) of hygiene for Racket applies here (except for the "Breaking Hygiene" section). I wrote a similar [explanation](https://github.com/jseyfried/rfcs/blob/hygiene/text/0000-hygiene.md) for Rust.
   - Generally speaking, if `fn f() { <body> }` resolves, `pub macro m() { <body> } ... m!()` also resolves, even if `m!()` is in a separate crate.
   - `::foo::bar` in a `macro` behaves like `$crate::foo::bar` in a `macro_rules!`, except it can access everything visible from the `macro` (thus more permissive).
   - See [`src/test/{run-pass, compile-fail}/hygiene`](afe7d89) for examples. Small example:
```rust
mod foo {
    fn f() { println!("hello world"); }
    pub macro m() { f(); }
}
fn main() { foo::m!(); }
```

Limitations:
 - This does not address planned changes to matchers (`expr`,`ty`, etc.), c.f. #26361.
 - Lints (including stability and deprecation) and `unsafe` are not hygienic.
   - adding hygiene here will be mostly or entirely backwards compatible
 - Nested macro definitions (a `macro` inside another `macro`) don't always work correctly when invoked from external crates.
   - pending improvements in how we encode macro definitions in crate metadata
 - There is no way to "escape" hygiene without using a procedural macro.

r? @nrc

bors added a commit to rust-lang/rust that referenced this pull request May 25, 2017

Auto merge of #40847 - jseyfried:decl_macro, r=nrc
Initial implementation of declarative macros 2.0

Implement declarative macros 2.0 (rust-lang/rfcs#1584) behind `#![feature(decl_macro)]`.
Differences from `macro_rules!` include:
 - new syntax: `macro m(..) { .. }` instead of `macro_rules! m { (..) => { .. } }`
 - declarative macros are items:
```rust
// crate A:
pub mod foo {
    m!(); // use before definition; declaration order is irrelevant
    pub macro m() {} // `pub`, `pub(super)`, etc. work
}
fn main() {
    foo::m!(); // named like other items
    { use foo::m as n; n!(); } // imported like other items
}
pub use foo::m; // re-exported like other items

// crate B:
extern crate A; // no need for `#[macro_use]`
A::foo::m!(); A::m!();
```
 - Racket-like hygiene for items, imports, methods, fields, type parameters, privacy, etc.
   - Intuitively, names in a macro definition are resolved in the macro definition's scope, not the scope in which the macro is used.
   - This [explaination](http://beautifulracket.com/explainer/hygiene.html) of hygiene for Racket applies here (except for the "Breaking Hygiene" section). I wrote a similar [explanation](https://github.com/jseyfried/rfcs/blob/hygiene/text/0000-hygiene.md) for Rust.
   - Generally speaking, if `fn f() { <body> }` resolves, `pub macro m() { <body> } ... m!()` also resolves, even if `m!()` is in a separate crate.
   - `::foo::bar` in a `macro` behaves like `$crate::foo::bar` in a `macro_rules!`, except it can access everything visible from the `macro` (thus more permissive).
   - See [`src/test/{run-pass, compile-fail}/hygiene`](afe7d89) for examples. Small example:
```rust
mod foo {
    fn f() { println!("hello world"); }
    pub macro m() { f(); }
}
fn main() { foo::m!(); }
```

Limitations:
 - This does not address planned changes to matchers (`expr`,`ty`, etc.), c.f. #26361.
 - Lints (including stability and deprecation) and `unsafe` are not hygienic.
   - adding hygiene here will be mostly or entirely backwards compatible
 - Nested macro definitions (a `macro` inside another `macro`) don't always work correctly when invoked from external crates.
   - pending improvements in how we encode macro definitions in crate metadata
 - There is no way to "escape" hygiene without using a procedural macro.

r? @nrc
@U007D

This comment has been minimized.

U007D commented Oct 26, 2017

Just a quick comment on an old thread to say I love love love the tone and civility of this thread. Special hat tip to @withoutboats for so eloquently expressing ideas and concerns even as they were partially formed--publicly sharing ideas before they are fully formed is both very difficult and courageous.

Huge respect and thanks to all--many people (myself included) are the beneficiaries of this discussion (and not just its technical content).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment