Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RFC for attributes on statements and blocks. #16

Merged
merged 7 commits into from
Jul 15, 2014
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Revise after splitting #8 for match arm attributes (now landed).
  • Loading branch information
huonw committed May 17, 2014
commit 7520d05955f334ac2a22e098c4cf8e06a68ca367
146 changes: 59 additions & 87 deletions active/0000-more-attributes.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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

Expand All @@ -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);
}
```
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could this include blocks?

#[attr] {
    // ...
}

{
    #![attr]
    // ...
}


# 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?