-
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
Syntactically permit attributes on 'if' expressions #68658
Conversation
Fixes rust-lang#68618 Previously, attributes on 'if' expressions (e.g. `#[attr] if true {}`) were disallowed during parsing. This made it impossible for macros to perform any custom handling of such attributes (e.g. stripping them away), since a compilation error would be emitted before they ever had a chance to run. This PR permits attributes on 'if' expressions ('if-attrs' from here on) syntactically, i.e. during parsing. We instead deny if-attrs during AST validation, which occurs after all macro expansions have run. This is a conservative change which allows more code to be processed by macros. It does not commit us to *semantically* accepting if-attrs. For example, the following code is not allowed even with this PR: ```rust fn builtin_attr() { #[allow(warnings)] if true {} } fn custom_attr() { #[my_proc_macro_attr] if true {} } ``` However, the following code *is* accepted ```rust #[cfg(FALSE)] fn foo() { #[allow(warnings)] if true {} #[my_custom_attr] if true {} } #[my_custom_attr] fn use_within_body() { #[allow(warnings)] if true {} #[my_custom_attr] if true {} } ```
We may want to change this, but it would be a much larger change than this PR is making.
r? @varkor (rust_highfive has picked a reviewer for you, use r? to override) |
r? @Centril |
In the process of writing this, I discovered that the following syntax does not even parse: fn main() {
if true {
} #[attr] else {
}
} gives the error:
That is, we don't even recognize Personally, I think this should be accepted syntactically as well. However, I think that would probably need an FCP (if not an RFC), since it would require changing what the parser even recognizes, not syntax that we parse but explicitly reject. |
@@ -0,0 +1,9 @@ | |||
fn main() { | |||
#[allow(warnings)] if true {} //~ ERROR attributes are not yet allowed on `if` expressions |
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.
Can you test the interactions with from the RFC?:
#[cfg(not(foo))]
if cond1 {
} else #[cfg(not(bar))] if cond2 {
} else #[cfg(not(baz))] {
}
Would be good to also test things like:
fn bar() {}
fn foo() {
bar(#[cfg(F)] if true {}); // The `match` equivalent is allowed.
}
..as well as some procedural macro attribute being used like #[my_proc_attr] if true { ... }
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.
#[cfg(not(foo))]
if cond1 {
} else #[cfg(not(bar))] if cond2 {
} else #[cfg(not(baz))] {
}
doesn't parse - I have somthing similar in src/test/ui/parser/if-attrs/else-attrs.rs
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.
Right, hmm... the check-pass test does exercise the config behavior, but for extra robustness, could we add something like this?
// run-pass
fn main() {
#[cfg(FALSE)]
if true { panic!() }
}
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 confused - the whole point of this PR is that if-attrs are allowed syntactically, but denied semantically. Your example will error with "attributes are not yet allowed on if
expressions" both before and after this PR - it's just that the error is being emitted from a different place now.
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.
Ah right; I was thinking that cfg expansion might strip out the attribute. Can you add #[cfg(FALSE)]
and #[cfg_attr(FALSE, whatever)]
to this file to exercise that behavior for conditional compilation specifically?
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.
@Centril: Actually, you're right - that code does compile with this PR, though I think it probably shouldn't. Should I modify cfg-expansion to deny #[cfg]
attributes on if
expressions?
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.
Yeah, that does seem like the conservative choice, although I have no idea how complicated it would be. Remember to also cover cfg_attr
. But this is something that @petrochenkov should r+ :)
That Maybe |
Also, let's cc @rust-lang/lang in case there are objections to this. I personally think we could also semantically allow proc macro attributes here, as it doesn't feel that confusing to have the attributes apply to the exterior as opposed to the interior. We might also want to run this through FCP possibly, let's wait and see. :) |
That would be gated as a proc-macro expression attribute, right? |
I think so, but I would prefer for @petrochenkov to sign off on those changes as macros is not my expertise. |
These are evaluated prior to AST validation, so we need to check for them separately.
I'm not particularly happy with how this PR is denying |
Left some comments on Discord: https://discordapp.com/channels/442252698964721669/443151243398086667/672271149740326922 |
Reassigning to r? @petrochenkov for the config implementation since that's not my wheelhouse. |
☔ The latest upstream changes (presumably #68133) made this pull request unmergeable. Please resolve the merge conflicts. |
Perhaps it's time to just remove the restriction? It's pretty established now that macros and attributes work with expressions/statements and not arbitrary syntactic fragments, so the choice in favor of the alternative 2 from https://github.com/rust-lang/rfcs/blob/master/text/0016-more-attributes.md#if is unlikely. I can't say I like the "figure gating" this PR is trying to do. |
@Centril |
I would love to discuss it with the team. (I agree with your assessment re. removing restriction wholesale and not being a fan of the complex gating here.) |
Can somebody summarize precisely what changes are being proposed here? It seems like there was some iteration. |
@nikomatsakis: In its current form, this PR syntactically permits attributes on
@petrochenkov and @Centril suggested allowing attributes semantically as well. This would permit attributes on I do not want to consider attributes on other parts of the 'if-else' chain (e.g.
if true {} else {
#[attr]
if false {
} else {}
} That is, parse the attribute as applying to the entire remaining part of the if-else chain, not just the particular 'branch' being annotated. This is the most 'natural' way to parse it based on how 'if-else' chains are parsed. However, I think it's extremely counter-intuitive, and would be an enormous footgun when combined with
This approach would either require additional lookahead during parsing (to see if an if true {} #[weird_attr] /* note the lack of `else` */ Personally, I don't think having attributes available in more places makes the drawbacks worth it. Either approach would probably need RFC, given that it would be a much larger change than what this PR currently does. |
FWIW, if I had a use case for attributes on "else blocks", my first thought would be attaching those attributes to the "blocks" / pairs of curly braces, which bypasses most of the weirdness:
And if you really need your attribute to munch the But I agree that any variation of this deserves a separate RFC with some concrete use cases. |
We discussed this in today's language team meeting (2020-02-13). Those present, in particular myself and @joshtriplett, felt that of the options available (1. keep the status quo, 2. accept only syntactically, 3. accept semantically also), that the 3rd option would be the simplest, and least complex approach. Specifically, we would like to allow In reaching this conclusion, we did consult RFC 16 regarding attributes on
We also noted that while many users interpret code in a linear / textual fashion as opposed to as a tree-like structure, we still believe that the semantics of e.g. @Aaron1011 Can you please adjust the PR to remove the semantic restrictions and add relevant tests (including interactions with |
@Centril: I'm going to open a new PR, since almost all of this discussion in this PR doesn't apply once we allow if-attrs semantically. |
Closing in favor of #69201, which allows if-attrs both syntactically and semantically. |
Fixes #68618
Previously, attributes on 'if' expressions (e.g.
#[attr] if true {}
)were disallowed during parsing. This made it impossible for macros to
perform any custom handling of such attributes (e.g. stripping them
away), since a compilation error would be emitted before they ever had a
chance to run.
This PR permits attributes on 'if' expressions ('if-attrs' from here on)
syntactically, i.e. during parsing. We instead deny if-attrs
during AST validation, which occurs after all macro expansions have run.
This is a conservative change which allows more code to be processed by
macros. It does not commit us to semantically accepting if-attrs. For
example, the following code is not allowed even with this PR:
However, the following code is accepted