-
Notifications
You must be signed in to change notification settings - Fork 12.8k
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
Ambiguity between custom derive attributes and proc macro attributes #52226
Comments
Some immediate mitigations that look like they should be in place are:
@alercah and @petrochenkov, can y'all comment to whether this fixes the issue at hand or not? This is the same as I mentioned here but @alercah I wanted to clarify that the importing case is hopefully handled by the second bullet. In general I also think the most pressing part here is future-proofing ourselves. We don't necessarily need a fully fleshed out and fully rationalized system from day 1, we mostly just need to buy us as much runway as we can. |
Now that I've done the hard work, I'm pretty sure conflicts will be resolved if you add:
But I worry that this isn't enough future-proofing, because the implicit import behaviour will be stabilized if we go ahead as-is. As I understand it, right now, if you import |
Ah, wait, I think that since that's only for imports, |
@alercah I'm gonna respond to your other comment over here to try to keep discussion centralized, copied below in case anyone needs it! So the main point of the inert attributes is to provide a way for If we imagined a world where On the other side, derive macros can't (currently) edit the token stream of the struct they are deriving for, so they cannot remove their residue. Therefore, we can't have these inert attributes always be an error. Based on testing, the current resolution system is this, modulo some bugs:
This is a bit of a mess. If we imagine ourselves trying to reconcile this against future plans like scoped attribute names, we run into issues because inert attribute processing in step 3 is unable to accommodate paths: the derive macro has no way of knowing if Regular attribute macros run into the same problem, if you try to coordinate between two of them (again, suppose you want to mark fields). Currently, attribute macros can fake inert attributes by removing them from fields as they are encountered, but they would run into the same issue of trying to identify other attributes by way of scoping. Additionally, the lack of explicit declarations for inert attributes is problematic. At the risk of relitigating #35900 and #37614, I'm going to consider what I would do if I was designing from scratch:
The first one actually seems like a fairly conservative extension to the The third is a bigger change, and would break existing users of The fourth is just a slight semantic reordering to let the In general, it strikes me as weird that
(Or, alternatively, we decide it should be part of 2018 but will be late, so we require 2018 code to use It's true yeah that the macros 1.1 custom derive inert attributes aren't necessarily going to mesh perfectly with the module system, but the fact of the matter is that macros 1.1 is stable in Rust and we don't currently plan to change/deprecate any portions of its design. While it's true that weird cases can come up they should also be weighed against how common they're expected to be to provide a signal as to how urgent it'd be to resolve them. In that sense we don't really have an opportunity to design the system from scratch, and it's basically already stable that I think it's possible to implement the extra restriction you mentioned but it affects stable code so we'd need to be very careful about making such a change. Additionally it doesn't actually impact macros 1.2 stabilization (it only affects stable code already) so it may mean we don't necessarily need to tackle this for the 2018 edition! |
Yes, sorry, I have a habit of writing "here's what I would do designing from scratch" as a way to sort through my thoughts, rather than actually proposing a ground-up rewrite. I think that the proposed changes to macros 1.1 are pretty straightforward, enough to maybe sneak along an epoch boundary to unify everything, but not urgent. I realized I didn't really think through how this all interacts with declarative macros, and I'm now worried that will also cause futureproofing issues. If we're going to consider putting those into the macro namespace at some point, then we have to prepare for that rather than having pseudo-namespaced identifiers. Part of the arguments I read today about the stabilization of |
You can, however, shadow built-in derives, even with non-derive macros. |
Ah, but you can't shadow them with other derives. That one's an error. |
Bumping to RC2 milestone |
@petrochenkov hey I was curious to check in on this issue, I believe the current state is that whitelisted attributes are not macro-expanded, even if there's a macro in scope, right? |
@alexcrichton macro serde() {}
#[derive(Serialize)]
#[serde] // ERROR macro `serde` may not be used in attributes
struct S;
fn main() {} This causes regressions like #53583 and #53898. My intention so far is to bump priority of derive helpers so they shadow other macros instead (#53583 (comment)). |
Ok cool, sounds good to me! |
Status update: future proofed in #54086. The model is largely the same as previously, the helper attribute (e.g. Future proofing: If derive helper attribute is ambiguous with any other macro name in scope (taking sub-namespacing into account), then ambiguity error is reported. Compatibility: to avoid regressions, helper attributes are succesfully resolved even if they are written before the derive
, this is not good - the attribute is not in scope yet at that point if we want to pursue straightforward left-to-right expansion model. Future plans:
I think we can close this generic issue now and perhaps create new more focused issues for "future plans" (if those even need tracking). |
expand/resolve: Turn `#[derive]` into a regular macro attribute This PR turns `#[derive]` into a regular attribute macro declared in libcore and defined in `rustc_builtin_macros`, like it was previously done with other "active" attributes in rust-lang#62086, rust-lang#62735 and other PRs. This PR is also a continuation of rust-lang#65252, rust-lang#69870 and other PRs linked from them, which layed the ground for converting `#[derive]` specifically. `#[derive]` still asks `rustc_resolve` to resolve paths inside `derive(...)`, and `rustc_expand` gets those resolution results through some backdoor (which I'll try to address later), but otherwise `#[derive]` is treated as any other macro attributes, which simplifies the resolution-expansion infra pretty significantly. The change has several observable effects on language and library. Some of the language changes are **feature-gated** by [`feature(macro_attributes_in_derive_output)`](rust-lang#81119). #### Library - `derive` is now available through standard library as `{core,std}::prelude::v1::derive`. #### Language - `derive` now goes through name resolution, so it can now be renamed - `use derive as my_derive; #[my_derive(Debug)] struct S;`. - `derive` now goes through name resolution, so this resolution can fail in corner cases. Crater found one such regression, where import `use foo as derive` goes into a cycle with `#[derive(Something)]`. - **[feature-gated]** `#[derive]` is now expanded as any other attributes in left-to-right order. This allows to remove the restriction on other macro attributes following `#[derive]` (rust-lang/reference#566). The following macro attributes become a part of the derive's input (this is not a change, non-macro attributes following `#[derive]` were treated in the same way previously). - `#[derive]` is now expanded as any other attributes in left-to-right order. This means two derive attributes `#[derive(Foo)] #[derive(Bar)]` are now expanded separately rather than together. It doesn't generally make difference, except for esoteric cases. For example `#[derive(Foo)]` can now produce an import bringing `Bar` into scope, but previously both `Foo` and `Bar` were required to be resolved before expanding any of them. - **[feature-gated]** `#[derive()]` (with empty list in parentheses) actually becomes useful. For historical reasons `#[derive]` *fully configures* its input, eagerly evaluating `cfg` everywhere in its target, for example on fields. Expansion infra doesn't do that for other attributes, but now when macro attributes attributes are allowed to be written after `#[derive]`, it means that derive can *fully configure* items for them. ```rust #[derive()] #[my_attr] struct S { #[cfg(FALSE)] // this field in removed by `#[derive()]` and not observed by `#[my_attr]` field: u8 } ``` - `#[derive]` on some non-item targets is now prohibited. This was accidentally allowed as noop in the past, but was warned about since early 2018 (rust-lang#50092), despite that crater found a few such cases in unmaintained crates. - Derive helper attributes used before their introduction are now reported with a deprecation lint. This change is long overdue (since macro modularization, rust-lang#52226 (comment)), but it was hard to do without fixing expansion order for derives. The deprecation is tracked by rust-lang#79202. ```rust #[trait_helper] // warning: derive helper attribute is used before it is introduced #[derive(Trait)] struct S {} ``` Crater analysis: rust-lang#79078 (comment)
Discovered by @alercah here
it's not clear what to do with this crate right now:
along with:
cc @petrochenkov
The text was updated successfully, but these errors were encountered: