Skip to content

Commit db98817

Browse files
committed
Merge remote-tracking branch 'huonw/attributes'
2 parents 5185160 + 12e924b commit db98817

File tree

1 file changed

+215
-0
lines changed

1 file changed

+215
-0
lines changed

active/0000-more-attributes.md

Lines changed: 215 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,215 @@
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

Comments
 (0)