Skip to content

Commit 5682c27

Browse files
committed
Introduce '$self' for hygienic macro items
1 parent 68e17ac commit 5682c27

File tree

1 file changed

+245
-0
lines changed

1 file changed

+245
-0
lines changed

text/0000-self-macro-metavar.md

Lines changed: 245 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,245 @@
1+
- Feature Name: `self_macro_metavar`
2+
- Start Date: 2020-08-03
3+
- RFC PR: [rust-lang/rfcs#0000](https://github.com/rust-lang/rfcs/pull/0000)
4+
- Rust Issue: [rust-lang/rust#0000](https://github.com/rust-lang/rust/issues/0000)
5+
6+
# Summary
7+
[summary]: #summary
8+
9+
Introduce the `$self` macro metavariable, a companion to `$crate`, that allows
10+
macros hygienic access to items.
11+
12+
# Motivation
13+
[motivation]: #motivation
14+
15+
It is presently impossible to define macros with identifiers that resolve at the
16+
macro's definition site upon expansion. This shortcoming is well-acknowledged
17+
and well-known, and, while [declarative macros 2.0] aimed to resolve this issue,
18+
its implementation and subsequent stabilization sit in limbo.
19+
20+
As an example of a macro that's presently impossible to write, consider the
21+
following, where `PRIVATE` is expected to resolve to `submod::PRIVATE`
22+
regardless of where `m` is expanded:
23+
24+
```rust
25+
mod submod {
26+
static PRIVATE: &'static str = "PRIVATE_SUBMOD";
27+
28+
#[macro_export]
29+
macro_rules! m {
30+
() => (println!("{}", PRIVATE))
31+
}
32+
33+
pub use m;
34+
}
35+
36+
pub fn main() {
37+
submod::m!(); // error[E0425]: cannot find value `PRIVATE` in this scope
38+
}
39+
```
40+
41+
As illustrated, the call to the `m!()` errors as "`PRIVATE` is not in-scope".
42+
Specifically, the call to `m!()` expands to `println!("{}, PRIVATE);`, where
43+
`PRIVATE` resolves as if it were an `item` identifier. This implies that the
44+
following _does_ compile, printing `Hi!` when run, perhaps unexpectedly:
45+
46+
```rust
47+
fn main() {
48+
submod::m!();
49+
static PRIVATE: &'static str = "Hi!";
50+
}
51+
```
52+
53+
Today, no combination of `macro_rules!()` or `proc_macro` invocations embedded
54+
within allows for declaring an `m` that expands such that `PRIVATE` in the
55+
expansion resolves to `submod::PRIVATE`. Even the following example, which
56+
mimics what is possible with identifiers today, fails:
57+
58+
```rust
59+
mod submod {
60+
static PRIVATE: &'static str = "PRIVATE_SUBMOD";
61+
62+
macro_rules! make_local {
63+
($local:expr) => (
64+
#[macro_export]
65+
macro_rules! m {
66+
() => (println!("{}", $local))
67+
}
68+
69+
pub use m;
70+
)
71+
}
72+
73+
make_local!(PRIVATE);
74+
}
75+
76+
pub fn main() {
77+
submod::m!(); // error[E0425]: cannot find value `PRIVATE` in this scope
78+
}
79+
```
80+
81+
`$self` resolves this deficiency. With `$self`, `m` could be declared as:
82+
83+
```rust
84+
mod submod {
85+
static PRIVATE: &'static str = "PRIVATE_SUBMOD";
86+
87+
#[macro_export]
88+
macro_rules! m {
89+
() => (println!("{}", $self::PRIVATE))
90+
}
91+
}
92+
93+
pub fn main() {
94+
submod::m!(); // `PRIVATE` unconditionally resolves to `submod::PRIVATE`
95+
}
96+
```
97+
98+
On expansion of `m`, `PRIVATE` unambiguously and unconditionally resolves as if
99+
it were at the definition site, that is, to `submod::PRIVATE`.
100+
101+
[declarative macros 2.0]: https://github.com/rust-lang/rust/issues/39412
102+
103+
# Guide-level explanation
104+
[guide-level-explanation]: #guide-level-explanation
105+
106+
The `$self` macro metavariable, like the `$crate` metavariable, can be used to
107+
modify the hygeine of identifiers in a macro. `$self` works a lot like the
108+
`self` in module paths: when used at the start of a path in a macro, the
109+
succeeding path will be resolved as if it were in the module where the macro is
110+
defined, regardless of where the macro is expanded. Different from `self` in
111+
module paths, however, `$self` _also_ captures the visibility of the module path
112+
at the definition site: the succeeding path will be visible in the expansion if
113+
it is visible at the macro's definition site.
114+
115+
Said differently, `$self` _captures_ the module scope at the macro definition
116+
site and applies it to the succeeding path upon expansion. As an example,
117+
consider the definition of the macro `submod::m!`:
118+
119+
```rust
120+
mod submod {
121+
static PRIVATE: &'static str = "PRIVATE_SUBMOD";
122+
123+
#[macro_export]
124+
macro_rules! m {
125+
() => (println!("{}", $self::PRIVATE))
126+
}
127+
}
128+
129+
pub fn main() {
130+
submod::m!(); // `PRIVATE` unconditionally resolves to `submod::PRIVATE`
131+
}
132+
```
133+
134+
Without `$self`, it would not be possible to reference `submod::PRIVATE` outside
135+
of `submod`. Observe, too, that unlike `$crate`, `$self` _does_ have an effect
136+
on visibility: while `submod::PRIVATE` in `main` would _not_ resolve, the
137+
expansion including `$self::PRIVATE` does!
138+
139+
# Reference-level explanation
140+
[reference-level-explanation]: #reference-level-explanation
141+
142+
At its core, `$self` is `$crate` at the module-level as opposed to the crate
143+
level. Macro metavariable naming collisions are handled in the same way as with
144+
`$crate`. In particular, a declaration of `$self` in a macro shadows the `$self`
145+
described here. The following works as expected, and importantly, as it does
146+
today:
147+
148+
```rust
149+
macro_rules! m {
150+
($self:ident) => (println!("{}", $self))
151+
}
152+
```
153+
154+
Additionally, like `$crate`, a non-user-declared `$self` _must_ be followed by
155+
`::`.
156+
157+
Notably different is that while `$crate` can be implemented as a purely
158+
syntactic transformation, substituting `$crate` for the name of the crate in
159+
which the macro is defined, `$self` must apply the full resolution context of
160+
the macro's definition site to the succeeding path. When calling a macro using
161+
`$self` cross-crate, this requires cross-crate hygiene. Thankfully, this was
162+
recently added to the compiler in https://github.com/rust-lang/rust/pull/72121.
163+
164+
Thus, `$self` can be simply and without further caveats by specified as: for
165+
every path in the expansion that begins with `$self`, the resolution context of
166+
the path is set to resolution context of the `Span::source()` of `$self`.
167+
168+
In addition to the examples in the introductory text, consider the following:
169+
170+
```rust
171+
mod a {
172+
static PRIVATE: &'static str = "B";
173+
174+
#[macro_export]
175+
macro_rules! m1 {
176+
($($var:tt)*) => (println!("{}, {}", $self::PRIVATE, $($var)*))
177+
}
178+
}
179+
180+
mod b {
181+
static PRIVATE: &'static str = "A";
182+
183+
#[macro_export]
184+
macro_rules! m2 {
185+
() => (m1!($self::PRIVATE))
186+
}
187+
}
188+
189+
pub fn main() {
190+
m2!();
191+
}
192+
```
193+
194+
The resulting program prints `B, A`.
195+
196+
# Drawbacks
197+
[drawbacks]: #drawbacks
198+
199+
As always, introducing new language-level features can add the cognitive
200+
overhead. However, `$self`'s similarity to `$crate` means that it doesn't
201+
introduce an entirely new concept. What's more, it is orthogonal to all existing
202+
language features, which means users find one solution to the problem it
203+
resolves.
204+
205+
`$self` as described here is backwards-compatible: there are no compatibility
206+
hazards.
207+
208+
# Rationale and alternatives
209+
[rationale-and-alternatives]: #rationale-and-alternatives
210+
211+
1. Wait for Macros 2.0
212+
213+
Self-explanatory. Unfortunately, the implementation and stabilization of
214+
macros 2.0 is in limbo.
215+
216+
2. Propagate Resolution Context for Items, Too
217+
218+
The second `submod` example in the introductory text could be made to work.
219+
Unfortunately, this has the major drawback that it breaks existing code. That
220+
is, it is not backwards-compatible. Furthermore, it requires two expansions
221+
to achieve the same net-effect that this proposal allows in one.
222+
223+
3. Use some other syntax, like `#PRIVATE`, to capture hygiene
224+
225+
Instead of `$self::PRIVATE`, `#PRIVATE` could yield the same effect. This
226+
introduces brand new syntax with no existing analogy, however, and so would
227+
be harder to teach.
228+
229+
# Prior art
230+
[prior-art]: #prior-art
231+
232+
I am not aware of an existing `$self`-like mechanism in other languages. Rust's
233+
own `$crate` is the inspiration for this feature. Other issues, notably going
234+
back to https://github.com/rust-lang/rust/issues/22462, have also considered the
235+
deficiency resolved by this proposal.
236+
237+
# Unresolved questions
238+
[unresolved-questions]: #unresolved-questions
239+
240+
None.
241+
242+
# Future possibilities
243+
[future-possibilities]: #future-possibilities
244+
245+
None. Macros 2.0 continues to be the eventual goal.

0 commit comments

Comments
 (0)