|
| 1 | +- Feature Name: attributes_with_literals |
| 2 | +- Start Date: 2016-03-28 |
| 3 | +- RFC PR: (leave this empty) |
| 4 | +- Rust Issue: (leave this empty) |
| 5 | + |
| 6 | +# Summary |
| 7 | +[summary]: #summary |
| 8 | + |
| 9 | +This RFC proposes accepting literals in attributes by defining the grammar of attributes as: |
| 10 | + |
| 11 | +```ebnf |
| 12 | +attr : '#' '!'? '[' meta_item ']' ; |
| 13 | +
|
| 14 | +meta_item : IDENT ( '=' LIT | '(' meta_item_inner? ')' )? ; |
| 15 | +
|
| 16 | +meta_item_inner : (meta_item | LIT) (',' meta_item_inner)? ; |
| 17 | +``` |
| 18 | + |
| 19 | +Note that `LIT` is a valid Rust literal and `IDENT` is a valid Rust identifier. The following |
| 20 | +attributes, among others, would be accepted by this grammar: |
| 21 | + |
| 22 | +```rust |
| 23 | +#[attr] |
| 24 | +#[attr(true)] |
| 25 | +#[attr(ident)] |
| 26 | +#[attr(ident, 100, true, "true", ident = 100, ident = "hello", ident(100))] |
| 27 | +#[attr(100)] |
| 28 | +#[attr(enabled = true)] |
| 29 | +#[enabled(true)] |
| 30 | +#[attr("hello")] |
| 31 | +#[repr(C, align = 4)] |
| 32 | +#[repr(C, align(4))] |
| 33 | +``` |
| 34 | + |
| 35 | +# Motivation |
| 36 | +[motivation]: #motivation |
| 37 | + |
| 38 | +At present, literals are only accepted as the value of a key-value pair in attributes. What's more, |
| 39 | +only _string_ literals are accepted. This means that literals can only appear in forms of |
| 40 | +`#[attr(name = "value")]` or `#[attr = "value"]`. |
| 41 | + |
| 42 | +This forces non-string literal values to be awkwardly stringified. For example, while it is clear |
| 43 | +that something like alignment should be an integer value, the following are disallowed: |
| 44 | +`#[align(4)]`, `#[align = 4]`. Instead, we must use something akin to `#[align = "4"]`. Even |
| 45 | +`#[align("4")]` and `#[name("name")]` are disallowed, forcing key-value pairs or identifiers to be |
| 46 | +used instead: `#[align(size = "4")]` or `#[name(name)]`. |
| 47 | + |
| 48 | +In short, the current design forces users to use values of a single type, and thus occasionally the |
| 49 | +_wrong_ type, in attributes. |
| 50 | + |
| 51 | +### Cleaner Attributes |
| 52 | + |
| 53 | +Implementation of this RFC can clean up the following attributes in the standard library: |
| 54 | + |
| 55 | +* `#![recursion_limit = "64"]` **=>** `#![recursion_limit = 64]` or `#![recursion_limit(64)]` |
| 56 | +* `#[cfg(all(unix, target_pointer_width = "32"))]` **=>** `#[cfg(all(unix, target_pointer_width = 32))]` |
| 57 | + |
| 58 | +If `align` were to be added as an attribute, the following are now valid options for its syntax: |
| 59 | + |
| 60 | +* `#[repr(align(4))]` |
| 61 | +* `#[repr(align = 4)]` |
| 62 | +* `#[align = 4]` |
| 63 | +* `#[align(4)]` |
| 64 | + |
| 65 | +### Syntax Extensions |
| 66 | + |
| 67 | +As syntax extensions mature and become more widely used, being able to use literals in a variety of |
| 68 | +positions becomes more important. |
| 69 | + |
| 70 | +# Detailed design |
| 71 | +[design]: #detailed-design |
| 72 | + |
| 73 | +To clarify, _literals_ are: |
| 74 | + |
| 75 | + * **Strings:** `"foo"`, `r##"foo"##` |
| 76 | + * **Byte Strings:** `b"foo"` |
| 77 | + * **Byte Characters:** `b'f'` |
| 78 | + * **Characters:** `'a'` |
| 79 | + * **Integers:** `1`, `1{i,u}{8,16,32,64,size}` |
| 80 | + * **Floats:** `1.0`, `1.0f{32,64}` |
| 81 | + * **Booleans:** `true`, `false` |
| 82 | + |
| 83 | +They are defined in the [manual] and by implementation in the [AST]. |
| 84 | + |
| 85 | + [manual]: https://doc.rust-lang.org/reference.html#literals |
| 86 | + [AST]: http://manishearth.github.io/rust-internals-docs/syntax/ast/enum.LitKind.html |
| 87 | + |
| 88 | +Implementation of this RFC requires the following changes: |
| 89 | + |
| 90 | +1. The `MetaItemKind` structure would need to allow literals as top-level entities: |
| 91 | + |
| 92 | + ```rust |
| 93 | + pub enum MetaItemKind { |
| 94 | + Word(InternedString), |
| 95 | + List(InternedString, Vec<P<MetaItem>>), |
| 96 | + NameValue(InternedString, Lit), |
| 97 | + Literal(Lit), |
| 98 | + } |
| 99 | + ``` |
| 100 | + |
| 101 | +2. `libsyntax` (`libsyntax/parse/attr.rs`) would need to be modified to allow literals as values in |
| 102 | + k/v pairs and as top-level entities of a list. |
| 103 | + |
| 104 | +3. Crate metadata encoding/decoding would need to encode and decode literals in attributes. |
| 105 | + |
| 106 | +# Drawbacks |
| 107 | +[drawbacks]: #drawbacks |
| 108 | + |
| 109 | +This RFC requires a change to the AST and is likely to break syntax extensions using attributes in |
| 110 | +the wild. |
| 111 | + |
| 112 | +# Alternatives |
| 113 | +[alternatives]: #alternatives |
| 114 | + |
| 115 | +### Token trees |
| 116 | + |
| 117 | +An alternative is to allow any tokens inside of an attribute. That is, the grammar could be: |
| 118 | + |
| 119 | +```ebnf |
| 120 | +attr : '#' '!'? '[' TOKEN+ ']' ; |
| 121 | +``` |
| 122 | + |
| 123 | +where `TOKEN` is any valid Rust token. The drawback to this approach is that attributes lose any |
| 124 | +sense of structure. This results in more difficult and verbose attribute parsing, although this |
| 125 | +could be ameliorated through libraries. Further, this would require almost all of the existing |
| 126 | +attribute parsing code to change. |
| 127 | + |
| 128 | +The advantage, of course, is that it allows any syntax and is rather future proof. It is also more |
| 129 | +inline with `macro!`s. |
| 130 | + |
| 131 | +### Allow only unsuffixed literals |
| 132 | + |
| 133 | +This RFC proposes allowing _any_ valid Rust literals in attributes. Instead, the use of literals |
| 134 | +could be restricted to only those that are unsuffixed. That is, only the following literals could be |
| 135 | +allowed: |
| 136 | + |
| 137 | + * **Strings:** `"foo"` |
| 138 | + * **Characters:** `'a'` |
| 139 | + * **Integers:** `1` |
| 140 | + * **Floats:** `1.0` |
| 141 | + * **Booleans:** `true`, `false` |
| 142 | + |
| 143 | +This cleans up the appearance of attributes will still increasing flexibility. |
| 144 | + |
| 145 | +### Allow literals only as values in k/v pairs |
| 146 | + |
| 147 | +Instead of allowing literals in top-level positions, i.e. `#[attr(4)]`, only allow them as values in |
| 148 | +key value pairs: `#[attr = 4]` or `#[attr(ident = 4)]`. This has the nice advantage that it was the |
| 149 | +initial idea for attributes, and so the AST types already reflect this. As such, no changes would |
| 150 | +have to be made to existing code. The drawback, of course, is the lack of flexibility. `#[repr(C, |
| 151 | +align(4))]` would no longer be valid. |
| 152 | + |
| 153 | +### Do nothing |
| 154 | + |
| 155 | +Of course, the current design could be kept. Although it seems that the initial intention was for a |
| 156 | +form of literals to be allowed. Unfortunately, this idea was [scrapped due to release pressure] and |
| 157 | +never revisited. Even [the reference] alludes to allowing all literals as values in k/v pairs. |
| 158 | + |
| 159 | + [scrapped due to release pressure]: https://github.com/rust-lang/rust/issues/623 |
| 160 | + [the reference]: https://doc.rust-lang.org/reference.html#attributes |
| 161 | + |
| 162 | +# Unresolved questions |
| 163 | +[unresolved]: #unresolved-questions |
| 164 | + |
| 165 | +None that I can think of. |
0 commit comments