|
| 1 | +- Start Date: 2014-03-20 |
| 2 | +- RFC PR #: (leave this empty) |
| 3 | +- Rust Issue #: (leave this empty) |
| 4 | + |
| 5 | +# Summary |
| 6 | + |
| 7 | +Allow attributes on more places inside functions, such as statements, |
| 8 | +blocks and expressions. |
| 9 | + |
| 10 | +# Motivation |
| 11 | + |
| 12 | +One sometimes wishes to annotate things inside functions with, for |
| 13 | +example, lint `#[allow]`s, conditional compilation `#[cfg]`s, and even |
| 14 | +extra semantic (or otherwise) annotations for external tools. |
| 15 | + |
| 16 | +For the lints, one can currently only activate lints at the level of |
| 17 | +the function which is possibly larger than one needs, and so may allow |
| 18 | +other "bad" things to sneak through accidentally. E.g. |
| 19 | + |
| 20 | +```rust |
| 21 | +#[allow(uppercase_variable)] |
| 22 | +let L = List::new(); // lowercase looks like one or capital i |
| 23 | +``` |
| 24 | + |
| 25 | +For the conditional compilation, the work-around is duplicating the |
| 26 | +whole containing function with a `#[cfg]`, or breaking the conditional |
| 27 | +code into a its own function. This does mean that any variables need |
| 28 | +to be explicitly passed as arguments. |
| 29 | + |
| 30 | +The sort of things one could do with other arbitrary annotations are |
| 31 | + |
| 32 | +```rust |
| 33 | +#[allowed_unsafe_actions(ffi)] |
| 34 | +#[audited="2014-04-22"] |
| 35 | +unsafe { ... } |
| 36 | +``` |
| 37 | + |
| 38 | +and then have an external tool that checks that that `unsafe` block's |
| 39 | +only unsafe actions are FFI, or a tool that lists blocks that have |
| 40 | +been changed since the last audit or haven't been audited ever. |
| 41 | + |
| 42 | +The minimum useful functionality would be supporting attributes on |
| 43 | +blocks and `let` statements, since these are flexible enough to allow |
| 44 | +for relatively precise attribute handling. |
| 45 | + |
| 46 | +# Detailed design |
| 47 | + |
| 48 | +Normal attribute syntax on `let` statements, blocks and expressions. |
| 49 | + |
| 50 | +```rust |
| 51 | +fn foo() { |
| 52 | + #[attr1] |
| 53 | + let x = 1; |
| 54 | + |
| 55 | + #[attr2] |
| 56 | + { |
| 57 | + // code |
| 58 | + } |
| 59 | + |
| 60 | + #[attr3] |
| 61 | + unsafe { |
| 62 | + // code |
| 63 | + } |
| 64 | + #[attr4] foo(); |
| 65 | + |
| 66 | + let x = #[attr5] 1; |
| 67 | + |
| 68 | + qux(3 + #[attr6] 2); |
| 69 | + |
| 70 | + foo(x, #[attr7] y, z); |
| 71 | +} |
| 72 | +``` |
| 73 | + |
| 74 | +Attributes bind tighter than any operator, that is `#[attr] x op y` is |
| 75 | +always parsed as `(#[attr] x) op y`. |
| 76 | + |
| 77 | +## `cfg` |
| 78 | + |
| 79 | +It is definitely an error to place a `#[cfg]` attribute on a |
| 80 | +non-statement expressions, that is, `attr1`--`attr4` can possibly be |
| 81 | +`#[cfg(foo)]`, but `attr5`--`attr7` cannot, since it makes little |
| 82 | +sense to strip code down to `let x = ;`. |
| 83 | + |
| 84 | +However, like `#ifdef` in C/C++, widespread use of `#[cfg]` may be an |
| 85 | +antipattern that makes code harder to read. This RFC is just adding |
| 86 | +the ability for attributes to be placed in specific places, it is not |
| 87 | +mandating that `#[cfg]` actually be stripped in those places (although |
| 88 | +it should be an error if it is ignored). |
| 89 | + |
| 90 | +## Inner attributes |
| 91 | + |
| 92 | +Inner attributes can be placed at the top of blocks (and other |
| 93 | +structure incorporating a block) and apply to that block. |
| 94 | + |
| 95 | +```rust |
| 96 | +{ |
| 97 | + #![attr11] |
| 98 | + |
| 99 | + foo() |
| 100 | +} |
| 101 | + |
| 102 | +match bar { |
| 103 | + #![attr12] |
| 104 | + |
| 105 | + _ => {} |
| 106 | +} |
| 107 | + |
| 108 | +// are the same as |
| 109 | + |
| 110 | +#[attr11] |
| 111 | +{ |
| 112 | + foo() |
| 113 | +} |
| 114 | + |
| 115 | +#[attr12] |
| 116 | +match bar { |
| 117 | + _ => {} |
| 118 | +} |
| 119 | +``` |
| 120 | + |
| 121 | +## `if` |
| 122 | + |
| 123 | +Attributes would be disallowed on `if` for now, because the |
| 124 | +interaction with `if`/`else` chains are funky, and can be simulated in |
| 125 | +other ways. |
| 126 | + |
| 127 | +```rust |
| 128 | +#[cfg(not(foo))] |
| 129 | +if cond1 { |
| 130 | +} else #[cfg(not(bar))] if cond2 { |
| 131 | +} else #[cfg(not(baz))] { |
| 132 | +} |
| 133 | +``` |
| 134 | + |
| 135 | +There is two possible interpretations of such a piece of code, |
| 136 | +depending on if one regards the attributes as attaching to the whole |
| 137 | +`if ... else` chain ("exterior") or just to the branch on which they |
| 138 | +are placed ("interior"). |
| 139 | + |
| 140 | +- `--cfg foo`: could be either removing the whole chain (exterior) or |
| 141 | + equivalent to `if cond2 {} else {}` (interior). |
| 142 | +- `--cfg bar`: could be either `if cond1 {}` (*e*) or `if cond1 {} |
| 143 | + else {}` (*i*) |
| 144 | +- `--cfg baz`: equivalent to `if cond1 {} else if cond2 {}` (no subtlety). |
| 145 | +- `--cfg foo --cfg bar`: could be removing the whole chain (*e*) or the two |
| 146 | + `if` branches (leaving only the `else` branch) (*i*). |
| 147 | + |
| 148 | +(This applies to any attribute that has some sense of scoping, not |
| 149 | +just `#[cfg]`, e.g. `#[allow]` and `#[warn]` for lints.) |
| 150 | + |
| 151 | +As such, to avoid confusion, attributes would not be supported on |
| 152 | +`if`. Alternatives include using blocks: |
| 153 | + |
| 154 | +```rust |
| 155 | +#[attr] if cond { ... } else ... |
| 156 | +// becomes, for an exterior attribute, |
| 157 | +#[attr] { |
| 158 | + if cond { ... } else ... |
| 159 | +} |
| 160 | +// and, for an interior attribute, |
| 161 | +if cond { |
| 162 | + #[attr] { ... } |
| 163 | +} else ... |
| 164 | +``` |
| 165 | + |
| 166 | +And, if the attributes are meant to be associated with the actual |
| 167 | +branching (e.g. a hypothetical `#[cold]` attribute that indicates a |
| 168 | +branch is unlikely), one can annotate `match` arms: |
| 169 | + |
| 170 | +```rust |
| 171 | +match cond { |
| 172 | + #[attr] true => { ... } |
| 173 | + #[attr] false => { ... } |
| 174 | +} |
| 175 | +``` |
| 176 | + |
| 177 | +# Drawbacks |
| 178 | + |
| 179 | +This starts mixing attributes with nearly arbitrary code, possibly |
| 180 | +dramatically restricting syntactic changes related to them, for |
| 181 | +example, there was some consideration for using `@` for attributes, |
| 182 | +this change may make this impossible (especially if `@` gets reused |
| 183 | +for something else, e.g. Python is |
| 184 | +[using it for matrix multiplication](http://legacy.python.org/dev/peps/pep-0465/)). It |
| 185 | +may also make it impossible to use `#` for other things. |
| 186 | + |
| 187 | +As stated above, allowing `#[cfg]`s everywhere can make code harder to |
| 188 | +reason about, but (also stated), this RFC is not for making such |
| 189 | +`#[cfg]`s be obeyed, it just opens the language syntax to possibly |
| 190 | +allow it. |
| 191 | + |
| 192 | +# Alternatives |
| 193 | + |
| 194 | +These instances could possibly be approximated with macros and helper |
| 195 | +functions, but to a low degree degree (e.g. how would one annotate a |
| 196 | +general `unsafe` block). |
| 197 | + |
| 198 | +Only allowing attributes on "statement expressions" that is, |
| 199 | +expressions at the top level of a block, this is slightly limiting; |
| 200 | +but we can expand to support other contexts backwards compatibly in |
| 201 | +the future. |
| 202 | + |
| 203 | +The `if`/`else` issue may be able to be resolved by introducing |
| 204 | +explicit "interior" and "exterior" attributes on `if`: by having |
| 205 | +`#[attr] if cond { ...` be an exterior attribute (applying to the |
| 206 | +whole `if`/`else` chain) and `if cond #[attr] { ... ` be an interior |
| 207 | +attribute (applying to only the current `if` branch). There is no |
| 208 | +difference between interior and exterior for an `else {` branch, and |
| 209 | +so `else #[attr] {` is sufficient. |
| 210 | + |
| 211 | + |
| 212 | +# Unresolved questions |
| 213 | + |
| 214 | +Are the complications of allowing attributes on arbitrary |
| 215 | +expressions worth the benefits? |
0 commit comments