-
Notifications
You must be signed in to change notification settings - Fork 12.9k
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
Simplify the macro hygiene algorithm #34570
Conversation
e683cf8
to
33afddf
Compare
@@ -46,7 +45,7 @@ pub struct SyntaxContext(pub u32); | |||
/// An identifier contains a Name (index into the interner | |||
/// table) and a SyntaxContext to track renaming and | |||
/// macro expansion per Flatt et al., "Macros That Work Together" | |||
#[derive(Clone, Copy, Eq)] | |||
#[derive(Clone, Copy, PartialEq, Eq, Hash)] |
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.
So, operator ==
and hash for Ident
did unhygienic comparisons and now they do hygienic comparisons.
I'm not sure what unintended consequences it may have.
I remember derived implementations of PartialEq
were used in couple of places for unhygienic comparisons of larger parts of AST including identifiers as their parts (maybe even expressions?), now the meaning of those comparisons changed.
Have you looked at where ==
for Ident
s was previously used and how the meaning of that code changed?
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.
We used to panic!
when using ==
to compare two identifiers with different syntax contexts, so if we didn't panic!
before then the behavior won't change.
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.
This probably doesn't matter much because the comparisons that changed the meaning could only result in panic before, but still worth checking and maybe fixing later, it's probably not a good idea to compare AST pieces with derived PartialEq
anyway.
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.
Agreed -- I'll look into it but I don't think it blocks this PR.
Could you add a test with >1 levels of
|
This is awesome. The last autumn I tried to figure out how this combination of expand and mtwt works in detail but gave up and now it turns out that most of that complexity exists to support one mis-feature! |
@petrochenkov The tests that I had written for this PR landed in #34374 -- I think you'll like them :) |
Hi, I've been looking at this and I had some questions about it, I wanted to do some experimentation, but for some reason I couldn't get this branch to build. I think it is unrelated to the patch here and some weirdness thanks to LLVM (maybe due to an LLVM upgrade). Anyway, thanks for the PR and sorry for the delay in review, but I'm on it just as soon as I fix my build problem. |
@nrc no problem, I've also been having trouble building rustc recently. |
I built this and verified that it works on unborrow, my personal hygiene stress test :) |
cc @jbclements just in case |
sigh... @nrc , any progress on sets of scopes? Honestly, the whole approach of doing macro expansion after into ASTs seems fundamentally awkward, if not completely broken. |
☔ The latest upstream changes (presumably #34365) made this pull request unmergeable. Please resolve the merge conflicts. |
@@ -171,15 +104,24 @@ pub fn outer_mark(ctxt: SyntaxContext) -> Mrk { | |||
}) | |||
} | |||
|
|||
/// If `ident` is macro expanded, return the source ident from the macro definition |
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.
What does "source ident" mean for a procedural macro?
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.
Currently, this function returns None
for idents expanded from procedural macros.
Ideally, I think it should return the unmarked ident along with a "null" macro definition mark (since procedural macros are not expanded into existence).
Rebased |
@bors: r+ TBH, I'm still pretty surprised that this works, but I think I've convinced myself that it does (at least for macro_rules) - everything I threw at it worked as expected and any theoretical issue I could think of would not actually be an issue because of some aspect of Rust. I do worry that there might be problems with procedural macros, but I couldn't come up with anything, so lets see if something materlialises. |
📌 Commit 56c4ddf has been approved by |
Simplify the macro hygiene algorithm This PR removes renaming from the hygiene algorithm and treats differently marked identifiers as unequal. This change makes the scope of identifiers in `macro_rules!` items empty. That is, identifiers in `macro_rules!` definitions do not inherit any semantics from the `macro_rules!`'s scope. Since `macro_rules!` macros are items, the scope of their identifiers "should" be the same as that of other items; in particular, the scope should contain only items. Since all items are unhygienic today, this would mean the scope should be empty. However, the scope of an identifier in a `macro_rules!` statement today is the scope that the identifier would have if it replaced the `macro_rules!` (excluding anything unhygienic, i.e. locals only). To continue to support this, this PR tracks the scope of each `macro_rules!` and uses it in `resolve` to ensure that an identifier expanded from a `macro_rules!` gets a chance to resolve to the locals in the `macro_rules!`'s scope. This PR is a pure refactoring. After this PR, - `syntax::ext::expand` is much simpler. - We can expand macros in any order without causing problems for hygiene (needed for macro modularization). - We can deprecate or remove today's `macro_rules!` scope easily. - Expansion performance improves by 25%, post-expansion memory usage decreases by ~5%. - Expanding a block is no longer quadratic in the number of `let` statements (fixes #10607). r? @nrc
💔 Test failed - auto-win-msvc-64-cargotest |
@bors: retry @jseyfried failure looks like it might be genuine |
@bors r- |
@nrc Fixed and added a regression test. |
@bors r=nrc |
📌 Commit c1a6ff2 has been approved by |
⌛ Testing commit c1a6ff2 with merge 1d99dec... |
💔 Test failed - auto-win-gnu-64-opt |
@bors retry |
Simplify the macro hygiene algorithm This PR removes renaming from the hygiene algorithm and treats differently marked identifiers as unequal. This change makes the scope of identifiers in `macro_rules!` items empty. That is, identifiers in `macro_rules!` definitions do not inherit any semantics from the `macro_rules!`'s scope. Since `macro_rules!` macros are items, the scope of their identifiers "should" be the same as that of other items; in particular, the scope should contain only items. Since all items are unhygienic today, this would mean the scope should be empty. However, the scope of an identifier in a `macro_rules!` statement today is the scope that the identifier would have if it replaced the `macro_rules!` (excluding anything unhygienic, i.e. locals only). To continue to support this, this PR tracks the scope of each `macro_rules!` and uses it in `resolve` to ensure that an identifier expanded from a `macro_rules!` gets a chance to resolve to the locals in the `macro_rules!`'s scope. This PR is a pure refactoring. After this PR, - `syntax::ext::expand` is much simpler. - We can expand macros in any order without causing problems for hygiene (needed for macro modularization). - We can deprecate or remove today's `macro_rules!` scope easily. - Expansion performance improves by 25%, post-expansion memory usage decreases by ~5%. - Expanding a block is no longer quadratic in the number of `let` statements (fixes #10607). r? @nrc
This PR removes renaming from the hygiene algorithm and treats differently marked identifiers as unequal.
This change makes the scope of identifiers in
macro_rules!
items empty. That is, identifiers inmacro_rules!
definitions do not inherit any semantics from themacro_rules!
's scope.Since
macro_rules!
macros are items, the scope of their identifiers "should" be the same as that of other items; in particular, the scope should contain only items. Since all items are unhygienic today, this would mean the scope should be empty.However, the scope of an identifier in a
macro_rules!
statement today is the scope that the identifier would have if it replaced themacro_rules!
(excluding anything unhygienic, i.e. locals only).To continue to support this, this PR tracks the scope of each
macro_rules!
and uses it inresolve
to ensure that an identifier expanded from amacro_rules!
gets a chance to resolve to the locals in themacro_rules!
's scope.This PR is a pure refactoring. After this PR,
syntax::ext::expand
is much simpler.macro_rules!
scope easily.let
statements (fixes Macro expansion is quadratic in the number oflet
statements. #10607).r? @nrc