Skip to content
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

RFC for attributes on statements and blocks. #16

Merged
merged 7 commits into from Jul 15, 2014

Conversation

Projects
None yet
@huonw
Copy link
Member

huonw commented Mar 20, 2014

Allow attributes on more places inside functions, such as statements and blocks.

```rust
fn name(method: SslMethod) -> &'static str {
match method {
Sslv2 => "SSLv2",

This comment has been minimized.

@sfackler

sfackler Mar 20, 2014

Member

Missing attribute?

This comment has been minimized.

@huonw

huonw Mar 20, 2014

Author Member

This is meant to be a bad example:

i.e. the following is invalid:

This comment has been minimized.

@sfackler

sfackler Mar 20, 2014

Member

Ah, I misread


# Unresolved questions

- Should one be able to annotate the `else` branch(es) of an `if`? e.g.

This comment has been minimized.

@cmr

cmr Mar 20, 2014

Member

Maybe, but I don't think it's a big concern.

This comment has been minimized.

@pnkfelix

pnkfelix Apr 8, 2014

Member

If we go down this road, I assume there would be a distinction between:

#[attr] if foo { ... } else { ... } // applies to whole statement, including both branches

and

if foo #[attr] { ... } else { ... } // only applies to the then-branch

I don't have a problem with this; if you cfg-disable a then-branch, you get a compilation error, presumably. (Though I do think it might motivate support for inner attributes.)

This comment has been minimized.

@cmr

cmr Apr 8, 2014

Member

I think this is an important detail. As I see it, #[attr] if ..., the attribute would apply to the expression, but if foo { #![attr] ... } would have it apply to the block. I do not want to allow if foo #[attr] { ... }, I find it confusing.

This comment has been minimized.

@pnkfelix

pnkfelix Apr 8, 2014

Member

I'm not inclined to disallow if foo #[attr] { ... } though I also would probably find inner-attributes more readable.

(I'd suggest we "just lint for it", but ... Does our lint infrastructure have enough info to try to catch the outer-attribute form here? Or are all the attributes boiled away by the time the lints run?)

This comment has been minimized.

@huonw

huonw Apr 8, 2014

Author Member

I was thinking that if ... { ... } was a single unit, i.e. the {} are part of the if not a separate block. Maybe having both separately would be OK (I hadn't thought about it at all).

This comment has been minimized.

@lilyball

lilyball Apr 8, 2014

Contributor

@huonw I have the same feeling. if expr { ... } feels like a single unit to me. If #[attr] were allowed on just the { ... } then I would expect if expr expr to be valid (e.g. if true println!("this works?");. But it's not valid.

Given that, I also find the suggestion of if expr { ... } else #[attr] if expr { ... } to be invalid. An else-statement is also a syntactical construct where the body is either an if-statement or a braced block. And if you really think this should be valid, then I question what happens if the #[cfg(foo)] evaluates to false? You then have a bare else with no associated code. I think that if you want an attribute on an else-if then you just need to nest, as in if expr { ... } else { #[attr] if expr { ... } }. Usage of this should be rare enough that this nesting is not a problem.

This comment has been minimized.

@lilyball

lilyball Apr 8, 2014

Contributor

Oh, and given all that I said above, I think the inner-attribute form doesn't make sense, as it appears as though it should affect the block, not the entire if-statement (especially if it occurs inside of an else-block). Similarly, putting the attribute in between the expression and the block makes no sense.

Disallowing these usages also resolves the question of how to deal with needing to select between several behaviors. if expr #[cfg(one)] { ... } #[cfg(two)] { ... } #[cfg(three)] { ... } looks pretty nonsensical, especially if two of the configs match (or if none match).

Additionally, if we did allow #[attr] on just the block, that would imply that we should allow it on all expressions, not just statements. I can see why that might be desired, but in actual practice it seems like it would be extremely confusing. Restricting it to blocks instead of arbitrary expressions would lessen the confusion, but would reintroduce confusion over why you can't put an attribute on the block of an if-statement (and allowing that brings up the problems I already mentioned).

Overall, I think the simplest thing to do is to just allow it on match arms and on statements. Not on blocks or expressions (unless those happen to be statements as well).

@cmr

This comment has been minimized.

Copy link
Member

cmr commented Mar 20, 2014

👍

@andrew-d

This comment has been minimized.

Copy link

andrew-d commented Mar 21, 2014

👍 - this would be nice to have 😄

@esummers

This comment has been minimized.

Copy link

esummers commented Mar 22, 2014

Looks unnatural to me if extended to if/else. My random thought is that maybe if/else should look more like a match:

if foo => { },
#[cfg(foo)] if bar > 10 => println!("Hello World."),
_ => println!("So Long, and Thanks for All the Fish.");
@huonw

This comment has been minimized.

Copy link
Member Author

huonw commented Mar 22, 2014

I've been thinking about it a little more, and I actually think my second proposed if/else extension (i.e. else #[attr] { ... } and else #[attr] if ...) is the most natural (other than completely changing the if syntax... which seems like a non-starter); it's natural because if/else look like

if ... {
     ...
} else X

where X can be a block { ... } or another if ... { ... chain, and these two things get attributes from this RFC already, so (theoretically) there won't even need to be a special case for parsing those attributes: they come automatically out of the new grammar.

@esummers

This comment has been minimized.

Copy link

esummers commented Mar 22, 2014

I think that looks the best too. else is basically a four character wide comma.

@andrew-d

This comment has been minimized.

Copy link

andrew-d commented Mar 25, 2014

So, what's the process to get a consensus on this? Having this merged would be super useful for me right now.

@alexcrichton

This comment has been minimized.

Copy link
Member

alexcrichton commented Mar 25, 2014

@andrew-d, the process is outlined in RFC #1, the relevant part being:

Eventually, somebody on the core team will either accept the RFC by merging the pull request and assigning the RFC a number, at which point the RFC is 'active', or reject it by closing the pull request.

Discussions of RFCs are predominately occurring during weekly meetings right now, but this is not a hard requirement.

@nikomatsakis

This comment has been minimized.

Copy link
Contributor

nikomatsakis commented Mar 26, 2014

I'm generally in favor. I'm vaguely concerned about parser ambiguities but it should probably be ok. (Oh, my kingdom for a proper grammar.)

@chris-morgan

This comment has been minimized.

Copy link
Member

chris-morgan commented Mar 26, 2014

@nikomatsakis I don't see any ambiguities: attributes and only attributes start with #. This is still LL(1).

@nikomatsakis

This comment has been minimized.

Copy link
Contributor

nikomatsakis commented Mar 26, 2014

@chris-morgan I generally agree, I've just learned to be cautious when it comes to annotations that precede blocks etc.

@flaper87

This comment has been minimized.

Copy link

flaper87 commented Mar 26, 2014

I'm in favor 👍 🍰

@lilyball

This comment has been minimized.

Copy link
Contributor

lilyball commented Apr 1, 2014

👍

@pnkfelix

This comment has been minimized.

Copy link
Member

pnkfelix commented Apr 8, 2014

I don't think its explicitly spelled out in the RFC, so I will ask: Are only outer-attributes allowed to be used in this manner? Or are inner-attributes allowed as well?

Supporting inner-attributes could help improve the appearance of e.g. the if/else examples, depending on what one's opinion is of the examples that were presented.

So, If the intent is that inner-attributes should also be allowed, then we would need to resolve questions of what their scope is (I'm guessing it would be the most tightly enclosing block), and it would probably be good to require all inner-attributes to occur at the start of the block, before any items/declarations/outer-attributes etc).

But I would be fine if inner-attributes are not supported, or are not supported in the first iteration of this feature.

} else {
}
}
```

This comment has been minimized.

@brendanzab

brendanzab Apr 9, 2014

Member

Could this include blocks?

#[attr] {
    // ...
}

{
    #![attr]
    // ...
}
@alexcrichton

This comment has been minimized.

Copy link
Member

alexcrichton commented Apr 16, 2014

There was a good amount of discussion in today's meeting about this RFC.

The general opinion is that it seems a little odd to limit attributes to just statements. One big reason is that many things which look like statements are actually expressions, such as:

fn foo() {
    if some_condition() { println!("test") } // this is an expression, not a statement
}

There are some concerns about how far-reaching it would be to modify the grammar to allow attributes on all expressions (which is the ideal ending point for something like this).

In the meantime, how would you feel about paring down the RFC to just attributes on match arms? That seems have a very real and pressing use case, and it pretty much works as expected because it can appear anywhere a match can appear.

@huonw

This comment has been minimized.

Copy link
Member Author

huonw commented Apr 16, 2014

Should I cut it down here? Or open a new RFC, since it will be a rather large simplification (maybe this RFC can be co-opted for the more general statements/expressions attributes).

@alexcrichton

This comment has been minimized.

Copy link
Member

alexcrichton commented Apr 16, 2014

I think either way is ok. It sounds like statements/expressions will want to be a separate RFC, so feel free to pick either one to be this one (whichever is easiest)

@huonw

This comment has been minimized.

Copy link
Member Author

huonw commented Apr 17, 2014

I opened #49 since pretty much none of the discussion here is about match arms. (I'll update this to be statement/expression specific at some point.)

@huonw

This comment has been minimized.

Copy link
Member Author

huonw commented May 17, 2014

Updated.

@huonw huonw changed the title RFC for attributes on match arms and statements. RFC for attributes on statements and blocks. May 17, 2014

@anasazi

This comment has been minimized.

Copy link

anasazi commented Jun 3, 2014

+1 Attributes on all the things!

@cmr

This comment has been minimized.

Copy link
Member

cmr commented Jun 3, 2014

A possibly better way to implement this generically would be to add an
sidetable containing all NodeId => Vec, and then we don't need
to pollute the entire AST with 4 words everywhere for the attributes it can
have. This does mean giving more things Ids I think. Possibly not.

On Tue, Jun 3, 2014 at 2:09 PM, Eric Reed notifications@github.com wrote:

+1 Attributes on all the things!


Reply to this email directly or view it on GitHub
#16 (comment).

http://octayn.net/

@pnkfelix

This comment has been minimized.

Copy link
Member

pnkfelix commented Jun 5, 2014

Some notes from discussion during triage meeting:

  1. The scope of the RFC needs to be precisely defined. The detailed description section includes a fuzzy optional extension to expressions. Trim it to exactly what we'll support.
  2. There are some questions that need to be resolved.
    • From one POV it seem safe to restrict the scope of the rfc to just blocks and statements, but every expression can be turned into a statement, so its not clear whether that actually restricts the scope of the RFC all that much.
    • Also it would be good to know whether inner attributes are including the scope of this RFC, and if so, what do they apply to (just blocks, presumably?)

@nikomatsakis you had some additional thoughts that seemed a little implementation oriented in terms of what it would be like to parse code that has an arbitrary number of attributes followed by some statment-or-expression, feel free to to to clarify my points above.

Anyway, @huonw , we invite you to revise the RFC and then we will try to address it formally at a team meeting.

@kmcallister

This comment has been minimized.

Copy link
Contributor

kmcallister commented Jun 6, 2014

This would be really useful, especially with lint plugins. I'm interested in implementing this if the RFC is accepted.

@sfackler

This comment has been minimized.

Copy link
Member

sfackler commented Jun 6, 2014

As a point of reference, Java 8 significantly expanded the parts of the grammar that can be tagged with annotations: http://java.dzone.com/articles/java-8-type-annotations

@huonw

This comment has been minimized.

Copy link
Member Author

huonw commented Jun 27, 2014

Updated to include expressions more definitely. There are some annoying interactions with if detailed in the last section.


This can be addressed by having `#[attr] if cond { ...` be an exterior
attribute (applying to the whole `if`/`else` chain) and `if cond
#[attr] { ... ` be an interior attribute (applying to only the current

This comment has been minimized.

@glaebhoerl

glaebhoerl Jun 27, 2014

Contributor

Markdown is interpreting this as a heading :-)

This comment has been minimized.

@huonw

huonw Jun 27, 2014

Author Member

Haha, whoops. Fixed.

@tbu-

This comment has been minimized.

Copy link
Contributor

tbu- commented Jul 4, 2014

Not related to attributes in general, but allowing #[cfg]s everywhere makes it harder to reason about the code, like in C/C++ when you have preprocessor logic all over a function instead of different functions where the platform specific behavior is isolated.

That's why I think it might be a bit dangerous to allow this as this might make people move away from separate functions for #[cfg]-dependant behavior.

huonw added some commits Jul 8, 2014

Feedback from @pnkfelix's review:
- explicitly disallowing attributes on `if`/`else` due to weirdness
- provide alternatives for this
- add a drawbacks section discussing syntactic lock-down and the
  antipattern of `#[cfg]`
- clarify that this RFC is just for parsing attributes, not requiring that
  `#[cfg]` stripping be added in all these new places.
general `unsafe` block).

Only allowing attributes on "statement expressions" that is,
expressions at the top level of a block,

This comment has been minimized.

@pnkfelix

pnkfelix Jul 15, 2014

Member

@huonw oops, missed this earlier: I assume you wanted to finish this sentence with a period, or some expository text explaining why narrowing the scope of the change could be good, or more importantly, why it is not good, i.e. reiterating why it is important to support annotating (most) expressions.

Finish my
sentences.
@brson

This comment has been minimized.

Copy link
Contributor

brson commented Jul 15, 2014

@slimsag

This comment has been minimized.

Copy link

slimsag commented Jun 7, 2015

shouldn't this be removed from the active RFC list if it's been accepted/merged?

@sfackler

This comment has been minimized.

Copy link
Member

sfackler commented Jun 7, 2015

The active list contains RFCs that have been accepted but not implemented.

@Kimundi

This comment has been minimized.

Copy link
Member

Kimundi commented Nov 8, 2015

I'm currently in the process of implementing this, and in accordance with the current RFC text attributes will be initially prohibited on if expressions.

However, thinking about it I wonder if the following scheme could be used:

#[covers_remaining_three] if expr #[covers_block] {
    // ...
} else #[covers_remaining_two] if expr #[covers_block] {
    // ...
} else #[covers_block] {
    // ...
}

The idea being that an attribute before an if token applies to the whole, chained if expression, while a attribute before the block only applies to that block expression.

Of course, the "disadvantage" here is that for consistency you'd want to support those block-only attributes on anything taking a block, like the loop expressions. And then there would be more than one location per block-taking expression where a attribute might apply, which while not ambiguous could become potentially confusing.

@huonw

This comment has been minimized.

Copy link
Member Author

huonw commented Nov 10, 2015

@Kimundi, that is in fact one of the alternatives. I think nailing down this aspect of the behaviour is probably worth an amendment RFC.

bors added a commit to rust-lang/rust that referenced this pull request Dec 3, 2015

Auto merge of #29850 - Kimundi:attributes_that_make_a_statement, r=pn…
…kfelix

See rust-lang/rfcs#16 and #15701

- Added syntax support for attributes on expressions and all syntax nodes in statement position.
- Extended `#[cfg]` folder to allow removal of statements, and
of expressions in optional positions like expression lists and trailing
block expressions.
- Extended lint checker to recognize lint levels on expressions and
locals.
- As per RFC, attributes are not yet accepted on `if` expressions.

Examples:
  ```rust
let x = y;
{
        ...
}
assert_eq!((1, #[cfg(unset)] 2, 3), (1, 3));

let FOO = 0;
```

Implementation wise, there are a few rough corners and open questions:
- The parser work ended up a bit ugly.
- The pretty printer change was based mostly on guessing.
- Similar to the `if` case, there are some places in the grammar where a new `Expr` node starts,
  but where it seemed weird to accept attributes and hence the parser doesn't. This includes:
  - const expressions in patterns
  - in the middle of an postfix operator chain (that is, after `.`, before indexing, before calls)
  - on range expressions, since `#[attr] x .. y` parses as  `(#[attr] x) .. y`, which is inconsistent with
    `#[attr] .. y` which would parse as `#[attr] (.. y)`
- Attributes are added as additional `Option<Box<Vec<Attribute>>>` fields in expressions and locals.
- Memory impact has not been measured yet.
- A cfg-away trailing expression in a block does not currently promote the previous `StmtExpr` in a block to a new trailing expr. That is to say, this won't work:
```rust
let x = {
    #[cfg(foo)]
    Foo { data: x }
    #[cfg(not(foo))]
    Foo { data: y }
};
```
- One-element tuples can have their inner expression removed to become Unit, but just Parenthesis can't. Eg, `(#[cfg(unset)] x,) == ()` but `(#[cfg(unset)] x) == error`. This seemed reasonable to me since tuples and unit are type constructors, but could probably be argued either way.
- Attributes on macro nodes are currently unconditionally dropped during macro expansion, which seemed fine since macro disappear at that point?
- Attributes on `ast::ExprParens` will be prepend-ed to the inner expression in the hir folder.
- The work on pretty printer tests for this did trigger, but not fix errors regarding macros:
  - expression `foo![]` prints as `foo!()`
  - expression `foo!{}` prints as `foo!()`
  - statement `foo![];` prints as `foo!();`
  - statement `foo!{};` prints as `foo!();`
  - statement `foo!{}` triggers a `None` unwrap ICE.

bors added a commit to rust-lang/rust that referenced this pull request Dec 4, 2015

Auto merge of #29850 - Kimundi:attributes_that_make_a_statement, r=pn…
…kfelix

See rust-lang/rfcs#16 and #15701

- Added syntax support for attributes on expressions and all syntax nodes in statement position.
- Extended `#[cfg]` folder to allow removal of statements, and
of expressions in optional positions like expression lists and trailing
block expressions.
- Extended lint checker to recognize lint levels on expressions and
locals.
- As per RFC, attributes are not yet accepted on `if` expressions.

Examples:
  ```rust
let x = y;
{
        ...
}
assert_eq!((1, #[cfg(unset)] 2, 3), (1, 3));

let FOO = 0;
```

Implementation wise, there are a few rough corners and open questions:
- The parser work ended up a bit ugly.
- The pretty printer change was based mostly on guessing.
- Similar to the `if` case, there are some places in the grammar where a new `Expr` node starts,
  but where it seemed weird to accept attributes and hence the parser doesn't. This includes:
  - const expressions in patterns
  - in the middle of an postfix operator chain (that is, after `.`, before indexing, before calls)
  - on range expressions, since `#[attr] x .. y` parses as  `(#[attr] x) .. y`, which is inconsistent with
    `#[attr] .. y` which would parse as `#[attr] (.. y)`
- Attributes are added as additional `Option<Box<Vec<Attribute>>>` fields in expressions and locals.
- Memory impact has not been measured yet.
- A cfg-away trailing expression in a block does not currently promote the previous `StmtExpr` in a block to a new trailing expr. That is to say, this won't work:
```rust
let x = {
    #[cfg(foo)]
    Foo { data: x }
    #[cfg(not(foo))]
    Foo { data: y }
};
```
- One-element tuples can have their inner expression removed to become Unit, but just Parenthesis can't. Eg, `(#[cfg(unset)] x,) == ()` but `(#[cfg(unset)] x) == error`. This seemed reasonable to me since tuples and unit are type constructors, but could probably be argued either way.
- Attributes on macro nodes are currently unconditionally dropped during macro expansion, which seemed fine since macro disappear at that point?
- Attributes on `ast::ExprParens` will be prepend-ed to the inner expression in the hir folder.
- The work on pretty printer tests for this did trigger, but not fix errors regarding macros:
  - expression `foo![]` prints as `foo!()`
  - expression `foo!{}` prints as `foo!()`
  - statement `foo![];` prints as `foo!();`
  - statement `foo!{};` prints as `foo!();`
  - statement `foo!{}` triggers a `None` unwrap ICE.

@chriskrycho chriskrycho referenced this pull request Dec 30, 2016

Closed

Document all features in the reference #38643

0 of 17 tasks complete

withoutboats pushed a commit to withoutboats/rfcs that referenced this pull request Jan 15, 2017

@chriskrycho chriskrycho referenced this pull request Mar 11, 2017

Closed

Document all features #9

18 of 48 tasks complete

cramertj added a commit to cramertj/rfcs that referenced this pull request Jan 23, 2019

Merge pull request rust-lang#16 from cramertj/less-waker
Remove LocalWaker and simplify the RawWakerVTable
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.