-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
fed1f9f
RFC for attributes on match arms and statements.
huonw 7520d05
Revise after splitting #8 for match arm attributes (now landed).
huonw 6148795
Revisions to include expressions more definitely.
huonw 403d222
An unfortunately placed # broke the formatting.
huonw a037604
Reword a point for clarity.
huonw 0130066
Feedback from @pnkfelix's review:
huonw 12e924b
Finish my
huonw File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,215 @@ | ||
- Start Date: 2014-03-20 | ||
- RFC PR #: (leave this empty) | ||
- Rust Issue #: (leave this empty) | ||
|
||
# Summary | ||
|
||
Allow attributes on more places inside functions, such as statements, | ||
blocks and expressions. | ||
|
||
# Motivation | ||
|
||
One sometimes wishes to annotate things inside functions with, for | ||
example, lint `#[allow]`s, conditional compilation `#[cfg]`s, and even | ||
extra semantic (or otherwise) annotations for external tools. | ||
|
||
For the lints, one can currently only activate lints at the level of | ||
the function which is possibly larger than one needs, and so may allow | ||
other "bad" things to sneak through accidentally. E.g. | ||
|
||
```rust | ||
#[allow(uppercase_variable)] | ||
let L = List::new(); // lowercase looks like one or capital i | ||
``` | ||
|
||
For the conditional compilation, the work-around is duplicating the | ||
whole containing function with a `#[cfg]`, or breaking the conditional | ||
code into a its own function. This does mean that any variables need | ||
to be explicitly passed as arguments. | ||
|
||
The sort of things one could do with other arbitrary annotations are | ||
|
||
```rust | ||
#[allowed_unsafe_actions(ffi)] | ||
#[audited="2014-04-22"] | ||
unsafe { ... } | ||
``` | ||
|
||
and then have an external tool that checks that that `unsafe` block's | ||
only unsafe actions are FFI, or a tool that lists blocks that have | ||
been changed since the last audit or haven't been audited ever. | ||
|
||
The minimum useful functionality would be supporting attributes on | ||
blocks and `let` statements, since these are flexible enough to allow | ||
for relatively precise attribute handling. | ||
|
||
# Detailed design | ||
|
||
Normal attribute syntax on `let` statements, blocks and expressions. | ||
|
||
```rust | ||
fn foo() { | ||
#[attr1] | ||
let x = 1; | ||
|
||
#[attr2] | ||
{ | ||
// code | ||
} | ||
|
||
#[attr3] | ||
unsafe { | ||
// code | ||
} | ||
#[attr4] foo(); | ||
|
||
let x = #[attr5] 1; | ||
|
||
qux(3 + #[attr6] 2); | ||
|
||
foo(x, #[attr7] y, z); | ||
} | ||
``` | ||
|
||
Attributes bind tighter than any operator, that is `#[attr] x op y` is | ||
always parsed as `(#[attr] x) op y`. | ||
|
||
## `cfg` | ||
|
||
It is definitely an error to place a `#[cfg]` attribute on a | ||
non-statement expressions, that is, `attr1`--`attr4` can possibly be | ||
`#[cfg(foo)]`, but `attr5`--`attr7` cannot, since it makes little | ||
sense to strip code down to `let x = ;`. | ||
|
||
However, like `#ifdef` in C/C++, widespread use of `#[cfg]` may be an | ||
antipattern that makes code harder to read. This RFC is just adding | ||
the ability for attributes to be placed in specific places, it is not | ||
mandating that `#[cfg]` actually be stripped in those places (although | ||
it should be an error if it is ignored). | ||
|
||
## Inner attributes | ||
|
||
Inner attributes can be placed at the top of blocks (and other | ||
structure incorporating a block) and apply to that block. | ||
|
||
```rust | ||
{ | ||
#![attr11] | ||
|
||
foo() | ||
} | ||
|
||
match bar { | ||
#![attr12] | ||
|
||
_ => {} | ||
} | ||
|
||
// are the same as | ||
|
||
#[attr11] | ||
{ | ||
foo() | ||
} | ||
|
||
#[attr12] | ||
match bar { | ||
_ => {} | ||
} | ||
``` | ||
|
||
## `if` | ||
|
||
Attributes would be disallowed on `if` for now, because the | ||
interaction with `if`/`else` chains are funky, and can be simulated in | ||
other ways. | ||
|
||
```rust | ||
#[cfg(not(foo))] | ||
if cond1 { | ||
} else #[cfg(not(bar))] if cond2 { | ||
} else #[cfg(not(baz))] { | ||
} | ||
``` | ||
|
||
There is two possible interpretations of such a piece of code, | ||
depending on if one regards the attributes as attaching to the whole | ||
`if ... else` chain ("exterior") or just to the branch on which they | ||
are placed ("interior"). | ||
|
||
- `--cfg foo`: could be either removing the whole chain (exterior) or | ||
equivalent to `if cond2 {} else {}` (interior). | ||
- `--cfg bar`: could be either `if cond1 {}` (*e*) or `if cond1 {} | ||
else {}` (*i*) | ||
- `--cfg baz`: equivalent to `if cond1 {} else if cond2 {}` (no subtlety). | ||
- `--cfg foo --cfg bar`: could be removing the whole chain (*e*) or the two | ||
`if` branches (leaving only the `else` branch) (*i*). | ||
|
||
(This applies to any attribute that has some sense of scoping, not | ||
just `#[cfg]`, e.g. `#[allow]` and `#[warn]` for lints.) | ||
|
||
As such, to avoid confusion, attributes would not be supported on | ||
`if`. Alternatives include using blocks: | ||
|
||
```rust | ||
#[attr] if cond { ... } else ... | ||
// becomes, for an exterior attribute, | ||
#[attr] { | ||
if cond { ... } else ... | ||
} | ||
// and, for an interior attribute, | ||
if cond { | ||
#[attr] { ... } | ||
} else ... | ||
``` | ||
|
||
And, if the attributes are meant to be associated with the actual | ||
branching (e.g. a hypothetical `#[cold]` attribute that indicates a | ||
branch is unlikely), one can annotate `match` arms: | ||
|
||
```rust | ||
match cond { | ||
#[attr] true => { ... } | ||
#[attr] false => { ... } | ||
} | ||
``` | ||
|
||
# Drawbacks | ||
|
||
This starts mixing attributes with nearly arbitrary code, possibly | ||
dramatically restricting syntactic changes related to them, for | ||
example, there was some consideration for using `@` for attributes, | ||
this change may make this impossible (especially if `@` gets reused | ||
for something else, e.g. Python is | ||
[using it for matrix multiplication](http://legacy.python.org/dev/peps/pep-0465/)). It | ||
may also make it impossible to use `#` for other things. | ||
|
||
As stated above, allowing `#[cfg]`s everywhere can make code harder to | ||
reason about, but (also stated), this RFC is not for making such | ||
`#[cfg]`s be obeyed, it just opens the language syntax to possibly | ||
allow it. | ||
|
||
# Alternatives | ||
|
||
These instances could possibly be approximated with macros and helper | ||
functions, but to a low degree degree (e.g. how would one annotate a | ||
general `unsafe` block). | ||
|
||
Only allowing attributes on "statement expressions" that is, | ||
expressions at the top level of a block, this is slightly limiting; | ||
but we can expand to support other contexts backwards compatibly in | ||
the future. | ||
|
||
The `if`/`else` issue may be able to be resolved by introducing | ||
explicit "interior" and "exterior" attributes on `if`: 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 `if` branch). There is no | ||
difference between interior and exterior for an `else {` branch, and | ||
so `else #[attr] {` is sufficient. | ||
|
||
|
||
# Unresolved questions | ||
|
||
Are the complications of allowing attributes on arbitrary | ||
expressions worth the benefits? |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
Could this include blocks?