-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
Introduce '$self' macro metavar for hygienic macro items #2968
base: master
Are you sure you want to change the base?
Conversation
5682c27
to
a77075f
Compare
At first glance, I was thinking |
a77075f
to
4b8cbce
Compare
I don't think this misunderstanding is a concern: |
@SergioBenitez Rustc dev guide book says: To make the proposed RFC work, it seems to me that it will break the current order of name resolution. |
Postfix macros do not exist yet, but it has been proposed in #2442 and it propose to use
While not completely incompatible, they still propose a conflicting purpose for the idiomatic use of
Another thing, while this rfc state that
would it not make sense to "update" the meaning of |
This comment has been minimized.
This comment has been minimized.
One thing I've realized that this (as well as macro 2.0) has an interesting implication for IDE support. One of the core data structures of an IDE is map which maps each crate to a set of items this crate contains. In rust-analyzer, this is the heaviest data structure. One obvious optimization would be to include only publicly-visible API in this map for dependencies. This would cut down the size significantly directly. Moreover, it should help with incremental compilation --- you know that, if the public interface of the crate hasn't changed, you don't have to re-compile it's reverse dependencies. Note that neither of the two optimizations are implemented yet, but they seems both plausible and relatively important in terms of impact. From this point of view, the current setup where you have to mark items used by macros across crates beneficial. A plausible solution here is to require marking of macro-accessible cross-crate public items: mod submod {
pub(macro) static PRIVATE: &'static str = "PRIVATE_SUBMOD";
#[macro_export]
macro_rules! m {
() => (println!("{}", $self::PRIVATE))
}
pub use m;
} Note that there's no problems withing a single crate, as there you need unabridged map anyhow.
To clarify, this is in terms of |
Can a |
|
||
Thus, `$self` can be simply and without further caveats by specified as: for | ||
every path in the expansion that begins with `$self`, the resolution context of | ||
the path is set to resolution context of the `Span::source()` of `$self`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of Span::source()
, I think it would make more sense to talk about Span::def_site()
. That is, using a $self
path (conceptually) first substitutes in the path to the macro's parent module, and then behaves as if a proc-macro had produced the tokens with Span::resolved_at(Span::def_site())
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unfortunately, I don't think Span::def_site()
is quite right either. For example, in the expansion chain #[attr] -> macro_rules foo!() { $self }
, $self
should resolve wherever #[attr]
was called (or equivalently, where the tokens macro_rules foo!() { $self }
expanded to), not where #[attr]
was defined.
I believe the precise way to phrase this is:
Thus,
$self
can simply and without further caveats by specified as: for
every path in the expansion that begins with$self
, the resolution context of
the path is set to resolution context of the firstmacro_rules!
definition in which
the$self
tokens appeared.
Then proceed to show, as examples, 1) nested macro_rules!
, where the outer scope is used, and 2) a proc-macro
that expands to a macro_rules!
, where the call-site of the proc-macro
is used.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry for being unclear - I meant that the $self
path should be resolved as if it was produced by a proc-macro with Span::def_site()
, instead of a macro_rules!
macro (after the parent module path has been substituted).
What should the behavior of the following code be? struct Bar;
macro_rules! outer {
() => {
#[macro_export]
macro_rules! inner {
() => { struct Foo($self::Bar); }
}
}
}
mod other_mod {
outer!();
inner!();
} For consistency with |
I don't think the two proposals conflict at all!
I like the idea. I think we should keep this particular RFC focused on
Besides semantic issues with
Agreed. This is exactly the intention:
It can be produced by struct Bar;
#[proc_macro]
fn outer(_: TokenStream) -> TokenStream {
quote! {
#[macro_export]
macro_rules! inner {
() => { struct Foo($self::Bar); }
}
}
}
mod other_mod {
outer!();
inner!();
} But the following would: #[proc_macro]
fn outer(_: TokenStream) -> TokenStream {
quote! {
#[macro_export]
macro_rules! inner {
() => { struct Foo($self::Bar); }
}
}
}
mod other_mod {
struct Bar;
outer!();
inner!();
} @Aaron1011 If this make sense, I can update the RFC text accordingly. |
That makes sense to me - the |
We discussed this briefly in triage this week. We are uncertain about allowing access to private members visible to def-site right now; it feels like a bigger addition to macro capabilities than we'd want to make under the 2021 roadmap. However, we are more comfortable with a more limited form of We also discussed whether this behavior would also make self to apply to the |
Can you tell me more about where uncertainty is arising? Access to def-site hygiene is my primary objective with this RFC, and so I'd be rather disappointed to see it go by the wayside. The RFC as proposed is fully backwards compatible, so waiting for 2021 seems unnecessary. What's more, this RFC merely pushes forward already accepted ideas from To be concrete, there are at least two major usability issues in Rocket that this RFC would resolve. I'll explain the most prominent one now, rwf2/Rocket#1120. In short, to support Rust stable, where we used to generate a
I agree, and I'd be happy to amend the RFC to give |
Would that become the default behavior of |
@SergioBenitez I didn't find the mentioned discussions/RFCs after some moderately thorough googling. Could you provide the links for people like me who haven't been following the development closely? I've found #1584 ("placeholder" RFC for declarative macros) and https://github.com/jseyfried/rfcs/blob/hygiene/text/0000-hygiene.md (hygiene RFC text which, to my knowledge, was never submitted as an RFC, and didn't go through the RFC process). |
We have issues with optimizations like this in macros 2.0, which allow cross-crate access to private items. |
Assuming
|
There's a tiny bit of backward incompatibility here that affects any possible new macro_rules! takes_two_tokens {
($dollar:tt $self:tt) => {}
}
macro_rules! check {
() => {
takes_two_tokens!{$self} // Currently OK, but will break
takes_two_tokens!{$crate} // Currently not OK, "processed" $crate is a single identifier token
}
}
check!();
fn main() {} |
Would you say that if this RFC is approved and implemented, it will be released with the 2021 edition because of that backward incompatibility? Also, I would vote for |
Putting in my vote for |
|
We discussed this in today's backlog bonanza, but our conclusion was roughly the same as what @withoutboats already wrote some time back:
In short, tackling and exposing hygiene feels like a bigger project and one that we don't currently have enough expertise to tackle (and this is basically a hygiene system). Right now @petrochenkov definitely understands that system best, the main problem is that we haven't made much progress in spreading understanding of the current system or documenting how it works. As @petrochenkov noted:
and this feels like a decent chunk of work to vet and manage that we'd have to schedule carefully. There remains some interest in |
Following up, we'd love to see this RFC partitioned into two chunks:
|
In addition to the backwards compatibility issues that @petrochenkov raised, there's an additional compatibility issue that I didn't see discussed in this thread: today, you cannot write a macro parameter named macro_rules! mac {
($super:expr) => { $super + $super }
}
fn main() {
let x = mac!(2 * 3);
println!("{}", x);
} If we gave
As far as I can tell, macro_rules! mac {
($crate:expr) => { $crate + $crate }
}
fn main() {
let x = mac!(2 * 3);
println!("{}", x);
} |
Following up on this: it turns out that quite a few crates currently use One alternative we're looking at for extending macro syntax would be something like |
Maybe while we're at it, we could add a way of escaping macro_rules! m {
($v:ident) => {
assert_eq!(
stringify!($$v), // or some other escaping syntax, maybe `$dollar`
"$v",
);
};
} |
Would |
This RFC introduces the
$self
macro metavariable, a companion to$crate
, that allows macros hygienic access to items. With$self
, a macrom
could be declared as:On expansion of
m
,PRIVATE
unconditionally resolves as if it were at the definition site, that is, tosubmod::PRIVATE
.Rendered