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

$crate in a macro pattern behaves oddly #99037

Open
CAD97 opened this issue Jul 8, 2022 · 1 comment
Open

$crate in a macro pattern behaves oddly #99037

CAD97 opened this issue Jul 8, 2022 · 1 comment
Labels
A-macros Area: All kinds of macros (custom derive, macro_rules!, proc macros, ..) C-bug Category: This is a bug. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Comments

@CAD97
Copy link
Contributor

CAD97 commented Jul 8, 2022

It is my opinion that this should not have been allowed in the first place, and should be forward-compat deprecated for eventual removal.

I tried this code:

macro_rules! m {
    ($crate) => {};
}

I expected to see this happen:

A compiler error. Likely,

error: missing fragment specifier
 --> src/lib.rs:2:6
  |
2 |     ($crate) => {};
  |      ^^^^^^
  |
  = note: `#[deny(missing_fragment_specifier)]` on by default
  = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
  = note: for more information, [see issue #40107 <https://github.com/rust-lang/rust/issues/40107>](https://github.com/rust-lang/rust/issues/40107)

Instead, this happened:

The macro compiles without any errors. Calling such a macro is difficult:

m!($crate); //~ error: no rules expected the token `$`

macro_rules! call {
    () => { m!($crate); };
}

call!(); // this works

The following illustrates what is going on here:

// lib.rs
#[macro_export]
macro_rules! lib_macro {
    ($crate) => {};
}

#[macro_export]
macro_rules! try_call {
    ($m:path) => {
        $m!($crate);
    };
}

// main.rs
use lib::*;

macro_rules! main_macro {
    ($crate) => {};
}

macro_rules! tt_macro {
    ($krate:tt) => {};
}

macro_rules! ident_macro {
    ($krate:ident) => {};
}

try_call!(lib_macro);
try_call!(main_macro);
try_call!(tt_macro);
try_call!(ident_macro);

fn main() {}

All of these calls work. What is happening is that $crate even in macro patterns is getting glued into a single identifier token. Thus, in the macro pattern, it can only be fulfilled by another glued $crate token, which is only possible do in the expansion of a macro.

I think it is better to forbid this usage, as it is strongly inconsistent with the behavior of other keywords in macro binders (they work like any other identifier and are currently not reserved in this position), and $crate is taught as

Within a macro imported from a crate named foo, the special macro variable $crate will expand to ::foo. [old 1.5 edition of The Book]

Hygiene is also the reason that we need the $crate metavariable when our macro needs access to other items in the defining crate. What this special metavariable does is that it expands to an absolute path to the defining crate. [The Little Book of Rust Macros]

While both of these are subtly wrong ($crate can be observed to "expand" into a single identifier), they agree that $crate is semantically a "reserved binder" which expands to the crate that the containing macro_rules! is defined in.

The current behavior of $crate in macro pattern position is as a compound token (only producible with macros) is incompatible with this understanding of $crate, which is otherwise (mostly) correct. The behavior of $crate in pattern position should by this definition be to

  • without a fragment specifier, error indicating that a fragment specifier is missing (and perhaps also/instead)
  • with a fragment specifier, error indicating that $crate is a reserved keyword name that cannot be used as a custom binder.

Meta

rustc --version --verbose:

rustc 1.64.0-nightly (27eb6d701 2022-07-04)
binary: rustc
commit-hash: 27eb6d7018e397cf98d51c205e3576951d766323
commit-date: 2022-07-04
host: x86_64-pc-windows-msvc
release: 1.64.0-nightly
LLVM version: 14.0.6
@CAD97 CAD97 added the C-bug Category: This is a bug. label Jul 8, 2022
@CAD97
Copy link
Contributor Author

CAD97 commented Jul 8, 2022

Related: #99035 is another issue resulting from $crate's treatment as a glued token rather than a reserved metevariable/binder.

@fmease fmease added A-macros Area: All kinds of macros (custom derive, macro_rules!, proc macros, ..) T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. and removed needs-triage-legacy labels Jan 25, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-macros Area: All kinds of macros (custom derive, macro_rules!, proc macros, ..) C-bug Category: This is a bug. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants