-
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
Changes from 6 commits
fed1f9f
7520d05
6148795
403d222
a037604
0130066
12e924b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,213 @@ | ||
- 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, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @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. |
||
|
||
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? |
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?