-
Notifications
You must be signed in to change notification settings - Fork 12.7k
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
Expand items before their derives #48465
Conversation
(rust_highfive has picked a reviewer for you, use r? to override) |
r? @nrc cc @keeperofdakeys @dtolnay @sgrif @alexcrichton @jseyfried (users participating on original PR) |
@nrc Build passed, will squash after review |
cc #38356 |
☔ The latest upstream changes (presumably #48449) made this pull request unmergeable. Please resolve the merge conflicts. |
31c2fae
to
bf39cb5
Compare
@petrochenkov perhaps you'd like to look at this one too? |
Started reviewing, will finish tomorrow. |
☔ The latest upstream changes (presumably #48586) made this pull request unmergeable. Please resolve the merge conflicts. |
228c8cb
to
f33551c
Compare
let item = item.map_attrs(|mut attrs| { | ||
attrs.retain(|a| a.path != "structural_match" && a.path != "rustc_copy_clone_marker"); | ||
attrs | ||
}); |
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.
@abonander
Do you know why these attributes are filtered away here?
They are normally produced by builtin derives, but I'm not sure how they affect custom derives and why they need to be filtered away before applying a custom derive.
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.
@jseyfried added this in the original PR. Naive guess, it's avoiding exposing implementation details to the custom derive? Because these would be retained in the actual item since custom derives don't re-emit the item they're applied to.
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.
custom derives don't re-emit the item they're applied to
Aha, this must be the key observation. Everything looks reasonable then.
fn collect_invocations(&mut self, expansion: Expansion, derives: &[Mark]) | ||
-> (Expansion, Vec<Invocation>) { | ||
let result = { | ||
fn collect_invocations(&mut 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.
@abonander
Did you figure out what happens here and above in fn expand
?
I can't understand it just by reading the code, but it usually becomes more clear after actively doing some refactoring or modifications.
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.
I'm not sure what you mean by "figure out what happens here". It seems to do what it says on the tin; it accumulates bang/attribute macro invocations and then expand
expands them. It has to be done in a loop because macro expansions can produce more invocations.
src/libsyntax/ext/expand.rs
Outdated
monotonic: bool, // c.f. `cx.monotonic_expander()` | ||
} | ||
|
||
/* |
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.
Nit: comment style, //
please.
struct Visitor<'a> { | ||
struct Visitor<'a, 'b: 'a> { | ||
cx: &'a ExtCtxt<'b>, | ||
span: Span, |
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.
The original PR removed these fields and fn visit_mac
below.
Are they needed again for some reason?
src/librustc_resolve/macros.rs
Outdated
for &(derive, _) in derives { | ||
unresolved.insert(derive); | ||
self.invocations.insert(derive, invocation); | ||
} |
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.
Hmm, this commit also reverts some changes done in the original PR.
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 and the other nonsensical changes are probably due to a naive rebase where I just applied all nonconflicting changes from both sides. I'll have to go through and fix these or redo the rebase.
Ok, r=me once Travis is green and all the rebase issues are cleaned up (ideally by doing a "less naive" rebase). EDIT: Also squashing non-@jseyfried commits would be nice. |
@petrochenkov Since I don't completely understand everything that this code is doing, I'm slightly concerned about derives that declare custom attributes: it seems that this should break it since we would always end up looking at the attributes first and throw an error since they don't resolve to anything. However, the following tests using custom attributes are passing:
Do you think these are enough of a smoke test that we don't need a separate test to ensure this hasn't broken custom attributes for derives? @jseyfried could you weigh in on this since it's mostly your code? |
Just to give an update, crater run will start in ~4 days. Sorry :( This PR got pushed down because a more time-critical one cropped up. |
I'm not sure what is this about exactly. |
I thought attributes declared by derives are meant to be applied by the user and consumed by the derive, i.e. removed after the derive completes. This is how Serde uses them anyway: https://serde.rs/attributes.html This PR, at least nominally, expands derives last on an item, processing other macros first: https://github.com/rust-lang/rust/pull/48465/files#diff-54079e90040377e52f969c857e3f558aR21 If we expand the item itself first before its derives, the expander would look at these custom attributes and find that they don't resolve to anything and throw an error. However, the tests I linked are passing which suggests this has already been accounted for. |
I suspect that the expander classifies the attributes first (into builtin, unknown, whitelisted, macro-attributes of various kinds of legacy-ness) and expands only those that are actually macros. |
The IIRC this works because errors regarding macro resolution only become a hard error once everything has expanded. This change doesn't make derives happen last, it makes bang macros happen first (so called partial expansions). |
Mmm, good assessment. I've only recently started getting back into macro expansion stuff so a lot of these changes went over my head. |
Crater run started |
This came up again today when discussing procedural macros (e.g., Macros 1.2). I'm still pretty hesitant to make changes here -- it's not clear that expanding before derive is what we want to do. |
(Re)reviewed, LGTM. Were there any other questions about the code not answered by #48465 (comment) or #48465 (comment)? (thanks @keeperofdakeys!) |
@nikomatsakis Has @nrc changed his mind? The main motivation for this was that we already "expand" |
Yes. Sorry, I did not at all understand what is going on here. I don't think we can land this because changing expansion order is a breaking change, but also because the more complex the expansion order, the harder it is to understand what is going on. The fact that The trouble I think is that one can create examples which benefit from either expansion order. Given that, I think we must go for the most predictable and consistent behaviour, which I believe is the current behaviour. Apologies that I didn't get to this conclusion earlier. |
@nrc What about making the expansion order opt-in? We could apply some derives pre-expansion and some derives post-expansion. |
@nrc Ok. To be clear,
I doubt this is breaking in practice, but yeah back-compat could complicate things...
I think it's worth noting though that just about every major derive macro today needs
Can you think of examples of derives that don't want their input fully expanded? These seem like a far smaller class, especially if we assume that attribute macros will eventually be allowed on struct fields.
I think most predictable/consistent would be either everything is expanded first, or the macro sees the original, raw, unmodified I like @abonander's idea of allowing the macro author to request either unexpanded or fully expanded input. If derive back-compat is an issue, not requesting either could default to today's behavior. |
Something like
Or do we want to provide it as a general mechanism in |
Just providing |
I meant for that to be the way to opt-in to input expansion, by programmatically asking for it: #[proc_macro_derive(MyTrait)]
pub fn my_trait_derive(input: TokenStream) -> TokenStream {
let expanded = proc_macro::expand(input);
// ...
} |
So, for proper proc macros my preferred way to opt-in is to let macros eagerly expand macros in their body by adding
Where |
Ok,
Interesting, ideally I would have hoped "inert attributes" would be used for that: |
Hmm, this is an excellent question. I'm not really sure - I don't see why from a design perspective, however, from an implementation point of view messing with the order of 'expansion' of cfgs c.f. macros might be hard.
iiuc, the problem there is that writing a lot of code inside an attribute is not so nice to look at. |
Nah, it'd be pretty straightforward -- quite a bit simpler than this PR.
Yeah... I think with the right formatting it'd still be nicer, but if that's what's being done in the wild then so be it. |
Hi @petrochenkov (crater requester/PR reviewer)! Crater results are at: http://cargobomb-reports.s3.amazonaws.com/pr-48465/index.html. 'Blacklisted' crates (spurious failures etc) can be found here. If you see any spurious failures not on the list, please make a PR against that file. (interested observers: Crater is a tool for testing the impact of changes on the crates.io ecosystem. You can find out more at the repo if you're curious) |
We've got some significant breakages in derive crates:
Several of these derives were expecting |
@abonander |
Thing with RFCs is that they're primarily for proposing solutions and I don't think we've gotten that far. I think exposing input expansion programmatically (in |
Yeah, a pre-RFC on IRLO would probably be even better. |
Created a topic on IRLO: https://internals.rust-lang.org/t/pre-rfc-input-expansion-for-proc-macros-derives-primarily/7210 |
continuation of #41029
closes #47358