Macro_rules! 1.N feature: $mod

Like how $crate was added to allow macro_rules! macros to refer to their defining crate, provide $mod to allow macro_rules! macros to refer to their defining module.

(I've deliberately used $mod and not $self, despite non-macro paths being ::crate::* and ::self::*, to avoid conflicting with potential use of $self for method-position macros.)

This allows macro_rules! macros to integrate into the module/privacy system a little bit better again. (While we wait for full-hygiene macros 2.0, which use def-site name lookup by default.)

Initially, $mod doesn't seem like it gives macro_rules! any new powers, since the macro could just use $crate::path::to::me instead. But $mod unlocks new functionality for two specific cases:

  • Macro expanded macros don't know ahead of time what module they're going to be defined in, so don't know the path to the module. You could pass the definition module in, but either you bind it as $:path and it's basically completely useless since you can't refer to an item in the module from the bound path[1], or you pass it in as $($path_head:ident)? $(::$path_segment)* and can't use the path when repeating any other macro binder repetition.
  • $mod also allows privacy to potentially be based on the defining module instead of use site.

Backwards and future compatibility

$mod is currently allowed as a macro binder, as is every other keyword (yes, including $crate... kinda[2]). In order to make this a compatible change:

  • < editionNext, warn when $mod is used as a macro binder. If $mod is not a macro binder, it can be used as a keyword.
  • >= editionNext, make using $mod as a macro binder a hard error. (Consider doing this for all other keywords as well.)

As an alternative, $crate::self should never work as a path in the current compiler and could be used.


  1. $:path matches a full path-to-item, including turbofish, so is a basically useless matcher in practice. Basically the only use is if you want to refer to a name in at least two of the type/item/macro namespaces from the same binder. Basically :path is only useful for capturing and using a tuple struct path like a tuple struct, since it's both in the type and item namespaces, and can be generic. ↩︎

  2. What was the rollout story for $crate? Currently, it doesn't introduce a binder, and seems to match the unwritable crate reference token. I have no idea what the cross crate behavior is. ↩︎

2 Likes

I think a warning is fine, letting existing binders "shadow" the special use.

Tangentially related is $crate in a macro pattern behaves oddly · Issue #99037 · rust-lang/rust · GitHub... I did a bit more work determining how $crate behaves. Ideally, I think $mod and $crate should behave the same, but matching $crate's current behavior is pretty clearly nondesirable, and instead $crate should be adjusted towards being a simple specially-known default-provided metavariable, and $mod should behave the same way.

Whatever the resolution to `$$crate` unexpectedly fails to defer which crate `$crate` refers to · Issue #99035 · rust-lang/rust · GitHub would also apply to $mod. Handling it as an eagerly glued compound token is what leads to the odd behaviors.

I agree that just shadowing the builtin metavariable names (and warning when they're shadowed) is a reasonable way to handle new builtin metavariables.