|
| 1 | +- Start Date: 2014-08-09 |
| 2 | +- RFC PR: |
| 3 | +- Rust Issue: |
| 4 | + |
| 5 | +# Summary |
| 6 | + |
| 7 | +The `#[cfg(...)]` attribute provides a mechanism for conditional compilation of |
| 8 | +items in a Rust crate. This RFC proposes to change the syntax of `#[cfg]` to |
| 9 | +make more sense as well as enable expansion of the conditional compilation |
| 10 | +system to attributes while maintaining a single syntax. |
| 11 | + |
| 12 | +# Motivation |
| 13 | + |
| 14 | +In the current implementation, `#[cfg(...)]` takes a comma separated list of |
| 15 | +`key`, `key = "value"`, `not(key)`, or `not(key = "value")`. An individual |
| 16 | +`#[cfg(...)]` attribute "matches" if *all* of the contained cfg patterns match |
| 17 | +the compilation environment, and an item preserved if it *either* has no |
| 18 | +`#[cfg(...)]` attributes or *any* of the `#[cfg(...)]` attributes present |
| 19 | +match. |
| 20 | + |
| 21 | +This is problematic for several reasons: |
| 22 | + |
| 23 | +* It is excessively verbose in certain situations. For example, implementing |
| 24 | + the equivalent of `(a AND (b OR c OR d))` requires three separate |
| 25 | + attributes and `a` to be duplicated in each. |
| 26 | +* It differs from all other attributes in that all `#[cfg(...)]` attributes on |
| 27 | + an item must be processed together instead of in isolation. This change |
| 28 | + will move `#[cfg(...)]` closer to implementation as a normal syntax |
| 29 | + extension. |
| 30 | + |
| 31 | +# Detailed design |
| 32 | + |
| 33 | +The `<p>` inside of `#[cfg(<p>)]` will be called a *cfg pattern* and have a |
| 34 | +simple recursive syntax: |
| 35 | + |
| 36 | +* `key` is a cfg pattern and will match if `key` is present in the |
| 37 | + compilation environment. |
| 38 | +* `key = "value"` is a cfg pattern and will match if a mapping from `key` |
| 39 | + to `value` is present in the compilation environment. At present, key-value |
| 40 | + pairs only exist for compiler defined keys such as `target_os` and |
| 41 | + `endian`. |
| 42 | +* `not(<p>)` is a cfg pattern if `<p>` is and matches if `<p>` does not match. |
| 43 | +* `all(<p>, ...)` is a cfg pattern if all of the comma-separated `<p>`s are cfg |
| 44 | + patterns and all of them match. |
| 45 | +* `any(<p>, ...)` is a cfg pattern if all of the comma-separated `<p>`s are cfg |
| 46 | + patterns and any of them match. |
| 47 | + |
| 48 | +If an item is tagged with `#[cfg(<p>)]`, that item will be stripped from the |
| 49 | +AST if the cfg pattern `<p>` does not match. |
| 50 | + |
| 51 | +One implementation hazard is that the semantics of |
| 52 | +```rust |
| 53 | +#[cfg(a)] |
| 54 | +#[cfg(b)] |
| 55 | +fn foo() {} |
| 56 | +``` |
| 57 | +will change from "include `foo` if *either of* `a` and `b` are present in the |
| 58 | +compilation environment" to "include `foo` if *both of* `a` and `b` are present |
| 59 | +in the compilation environment". To ease the transition, the old semantics of |
| 60 | +multiple `#[cfg(...)]` attributes will be maintained as a special case, with a |
| 61 | +warning. After some reasonable period of time, the special case will be |
| 62 | +removed. |
| 63 | + |
| 64 | +In addition, `#[cfg(a, b, c)]` will be accepted with a warning and be |
| 65 | +equivalent to `#[cfg(all(a, b, c))]`. Again, after some reasonable period of |
| 66 | +time, this behavior will be removed as well. |
| 67 | + |
| 68 | +The `cfg!()` syntax extension will be modified to accept cfg patterns as well. |
| 69 | +A `#[cfg_attr(<p>, <attr>)]` syntax extension will be added |
| 70 | +([PR 16230](https://github.com/rust-lang/rust/pull/16230)) which will expand to |
| 71 | +`#[<attr>]` if the cfg pattern `<p>` matches. The test harness's |
| 72 | +`#[ignore]` attribute will have its built-in cfg filtering |
| 73 | +functionality stripped in favor of `#[cfg_attr(<p>, ignore)]`. |
| 74 | + |
| 75 | +# Drawbacks |
| 76 | + |
| 77 | +While the implementation of this change in the compiler will be |
| 78 | +straightforward, the effects on downstream code will be significant, especially |
| 79 | +in the standard library. |
| 80 | + |
| 81 | +# Alternatives |
| 82 | + |
| 83 | +`all` and `any` could be renamed to `and` and `or`, though I feel that the |
| 84 | +proposed names read better with the function-like syntax and are consistent |
| 85 | +with `Iterator::all` and `Iterator::any`. |
| 86 | + |
| 87 | +Issue [#2119](https://github.com/rust-lang/rust/issues/2119) proposed the |
| 88 | +addition of `||` and `&&` operators and parantheses to the attribute syntax |
| 89 | +to result in something like `#[cfg(a || (b && c)]`. I don't favor this proposal |
| 90 | +since it would result in a major change to the attribute syntax for relatively |
| 91 | +little readability gain. |
| 92 | + |
| 93 | +# Unresolved questions |
| 94 | + |
| 95 | +How long should multiple `#[cfg(...)]` attributes on a single item be |
| 96 | +forbidden? It should probably be at least until after 0.12 releases. |
| 97 | + |
| 98 | +Should we permanently keep the behavior of treating `#[cfg(a, b)]` as |
| 99 | +`#[cfg(all(a, b))]`? It is the common case, and adding this interpretation |
| 100 | +can reduce the noise level a bit. On the other hand, it may be a bit confusing |
| 101 | +to read as it's not immediately clear if it will be processed as `and(..)` or |
| 102 | +`all(..)`. |
0 commit comments