diff --git a/active/0000-more-attributes.md b/active/0000-more-attributes.md index 3c508a68e9ee4..00ede1c4426f9 100644 --- a/active/0000-more-attributes.md +++ b/active/0000-more-attributes.md @@ -4,15 +4,14 @@ # Summary -Allow attributes on more places inside functions, such as match arms -and statements. +Allow attributes on more places inside functions, such as statements, +blocks and (possibly) expressions. # Motivation One sometimes wishes to annotate things inside functions with, for -example, lint `#[allow]`s, conditional compilation `#[cfg]`s, branch -weight hints and even extra semantic (or otherwise) annotations for -external tools. +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 @@ -24,54 +23,9 @@ 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]`. A case study is -[sfackler's bindings to OpenSSL](https://github.com/sfackler/rust-openssl), -where many distributions remove SSLv2 support, and so that portion of -Rust bindings needs to be conditionally disabled. The obvious way to -support the various different SSL versions is an enum - -```rust -pub enum SslMethod { - #[cfg(sslv2)] - /// Only support the SSLv2 protocol - Sslv2, - /// Only support the SSLv3 protocol - Sslv3, - /// Only support the TLSv1 protocol - Tlsv1, - /// Support the SSLv2, SSLv3 and TLSv1 protocols - Sslv23, -} -``` - -However, all `match`s can only mention `Sslv2` when the `cfg` is -active, i.e. the following is invalid: - -```rust -fn name(method: SslMethod) -> &'static str { - match method { - Sslv2 => "SSLv2", - Sslv3 => "SSLv3", - _ => "..." - } -} -``` - -A valid method would be to have two definitions: `#[cfg(sslv2)] fn -name(...)` and `#[cfg(not(sslv2)] fn name(...)`. The former has the -`Sslv2` arm, the latter does not. Clearly, this explodes exponentially -for each additional `cfg`'d variant in an enum. - -Branch weights would allow the careful micro-optimiser to inform the -compiler that, for example, a certain match arm is rarely taken: - -```rust -match foo { - Common => {} - #[cold] - Rare => {} -} -``` +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 @@ -85,59 +39,77 @@ 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: +Normal attribute syntax on `let` statements and blocks. ```rust fn foo() { - #[attr] + #[attr1] let x = 1; - #[attr] - foo(); + #[attr2] + { + // code + } - #[attr] - match x { - #[attr] - Thing => {} + #[attr3] + unsafe { + // code } +} +``` + +## Extension to arbitrary expressions + +It would also be theoretically possible to extend this to support +arbitrary expressions (rather than just blocks, which are themselves +expressions). This would allow writing + +```rust +fn foo() { + #[attr4] foo(); - #[attr] - if foo { - } else { + #[attr5] if cond { + bar() + } else #[attr6] { + baz() } + + let x = #[attr7] 1; + + qux(3 + #[attr8] 2); + + foo(x, #[attr9] y, z); } ``` -# Alternatives +These last examples indicate a possible difficulty: what happens with +say `1 + #[cfg(foo)] 2`? This should be an error, i.e. `#[cfg]` is +only allowed on the "exterior" of expressions, that is, legal in all +examples above except `#[attr7]` and `#[attr8]`. `#[attr9]` is +questionable: if it were a `cfg`, then it could reasonably be +interpreted as meaning `foo(x, z)` or `foo(x, y, z)` conditional on +the `cfg`. -There aren't really any general alternatives; one could probably hack -around the conditional-enum-variants & matches with some macros and -helper functions to share as much code as possible; but in general -this won't work. +Allowing attributes there would also require considering +precedence. There are two sensible options, `#[...]` binds tighter +than everything else, i.e. `#[attr] 1 + 2` is `(#[attr] 1) + 2`, or it +is weaker, so `#[attr] 1 + 2` is `#[attr] (1 + 2)`. -The other instances could be approximated with macros and helper -functions, but to an even lesser degree (e.g. how would one annotate a +# 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). # Unresolved questions -- Should one be able to annotate the `else` branch(es) of an `if`? e.g. - - ```rust - if foo { - } #[attr] else if bar { - } #[attr] else { - } - ``` - - or maybe +- Are the complications of allowing attributes on arbitrary + expressions worth the benefits? - ```rust - if foo { - } else #[attr] if bar { - } else #[attr] { - } - ``` +- Which precedence should attributes have on arbitrary expressions?