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

Weirdness with macros trying to redefine each other #45732

Closed
durka opened this issue Nov 3, 2017 · 2 comments
Closed

Weirdness with macros trying to redefine each other #45732

durka opened this issue Nov 3, 2017 · 2 comments

Comments

@durka
Copy link
Contributor

durka commented Nov 3, 2017

On IRC we were trying to make a macro that turns itself into a no-op after the first call. I think it's not actually possible, but I was confused by the compiler's behavior.

A macro that tries to redefine itself (or another already existing macro) is allowed to do so once, but fails with an error the second time.

macro_rules! once {
    () => {
        macro_rules! once { () => {} }
    }
}

once!(); // OK
once!(); // ERROR
error: `once` is already in scope
 --> src/main.rs:3:9
  |
3 |         macro_rules! once { () => {} }
  |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
...
7 | once!();
  | -------- in this macro invocation
  |
  = note: macro-expanded `macro_rules!`s may not shadow existing macros (see RFC 1560)

The error points to the first invocation. However, there is no error if I remove the second invocation!

Also, why can't I do this? :) The error refers us to RFC 1560 which doesn't talk about macros.

cc @jseyfried

@jseyfried
Copy link
Contributor

jseyfried commented Nov 3, 2017

@durka

It used to always be possible to macros to "redefine" each other; macro invocations expended "before" the redefine would resolve to the old definition and macros invocation expanded "after" the redefine would resolve to the new definition.

However, with macros 2.0 naming / RFC 1560, macro expansion no longer has an "order" -- we now resolve and expand all we can, update the module graph and make progress resolving use imports (which might import more macros), resolve and expand again, etc. until everything is resolved and expanded or we hit fix point and error.

To be able to make progress coherently resolving names in the fix point algorithm, we wanted to forbid one macro_rules! definition from shadowing another. However, enforcing this would have broken too much code in practice, so we instead only forbid shadowing if the shadowing definition is used since this is just as useful for coherently resolving names yet didn't cause breakage in practice. There is discussion of the trade-offs between making progress resolving names and allowing shadowing in rust-lang/rfcs#1560.

In the above example, if there is a single invocation of once!(), it resolves to the original macro_rules! and expands to create a shadowing macro_rules! that is never used. Thus there is no error. The second once!() resolves to the shadowing macro_rules! produced by the first once!, so you see the shadowing error.

Note that with macros 2.0, it is an error even with just one once!:

#![feature(decl_macro)]

pub macro once($mac:ident) {
    macro $mac() {}
    // If we just put `macro once() {}` here like as in the original example, it wouldn't be usable outside the macro definition due to hygiene and wouldn't be an error.
}

once!(once); // error

cc @nrc

@durka
Copy link
Contributor Author

durka commented Nov 4, 2017

@jseyfried OK sounds like this is fully by design, thanks for the explanation!

@durka durka closed this as completed Nov 4, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants