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
Revisions to include expressions more definitely.
`if` is annoying.
  • Loading branch information
huonw committed Jun 27, 2014
commit 61487959677810a0761ab663f17d804d2351f470
117 changes: 86 additions & 31 deletions active/0000-more-attributes.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
# Summary

Allow attributes on more places inside functions, such as statements,
blocks and (possibly) expressions.
blocks and expressions.

# Motivation

Expand Down Expand Up @@ -45,7 +45,7 @@ for relatively precise attribute handling.

# Detailed design

Normal attribute syntax on `let` statements and blocks.
Normal attribute syntax on `let` statements, blocks and expressions.

```rust
fn foo() {
Expand All @@ -61,55 +61,110 @@ fn foo() {
unsafe {
// code
}
#[attr4] foo();

#[attr5]
if cond {
bar()
} else #[attr6] if cond {
baz()
} else #[attr7] {

};

let x = #[attr8] 1;

qux(3 + #[attr9] 2);

foo(x, #[attr10] 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]
    // ...
}


## Extension to arbitrary expressions
## `cfg`

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
It is an error to place a `#[cfg]` attribute on a non-statement
expressions, including `if`s/blocks inside an `if`/`else` chain, that
is, `attr1`--`attr7` can legally be `#[cfg(foo)]`, but
`attr8`--`attr10` cannot, since it makes little sense to strip code
down to `let x = ;`.

Attributes bind tighter than any operator, that is `#[attr] x op y` is
always parsed as `(#[attr] x) op y`.

## Inner attributes

Inner attributes can be placed at the top of blocks (and other
structure incorporating a block) and apply to that block.

```rust
fn foo() {
#[attr4] foo();
{
#![attr11]

#[attr5] if cond {
bar()
} else #[attr6] {
baz()
}
foo()
}

let x = #[attr7] 1;
match bar {
#![attr12]

qux(3 + #[attr8] 2);
_ => {}
}

foo(x, #[attr9] y, z);
if cond {
#![attr13]
}

// are the same as

#[attr11]
{
foo()
}

#[attr12]
match bar {
_ => {}
}
```

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`.
#[attr13]
if cond {
}
```

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)`.

# 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).

Only allowing attributes on "statement expressions" that is,
expressions at the top level of a block,
Copy link
Member

Choose a reason for hiding this comment

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

@huonw oops, missed this earlier: I assume you wanted to finish this sentence with a period, or some expository text explaining why narrowing the scope of the change could be good, or more importantly, why it is not good, i.e. reiterating why it is important to support annotating (most) expressions.


# Unresolved questions

- Are the complications of allowing attributes on arbitrary
expressions worth the benefits?
Are the complications of allowing attributes on arbitrary
expressions worth the benefits?

The interaction with `if`/`else` chains are somewhat subtle, and it
may be worth introducing "interior" and "exterior" attributes on `if`, or
just disallowing them entirely.

```rust
#[cfg(not(foo))]
if cond1 {
} else #[cfg(not(bar))] if cond2 {
} else #[cfg(not(baz))] {
}
```

- Which precedence should attributes have on arbitrary expressions?
- `--cfg foo`: could be either removing the whole chain ("exterior") or
equivalent to `if cond2 {} else {}` ("interior").
- `--cfg bar`: could be either `if cond1 {}` or `if cond1 {} else {}`
- `--cfg baz`: equivalent to `if cond1 {} else if cond2 {}` (no subtlety).
- `--cfg foo --cfg bar`: could be removing the whole chain or just
the `else` branch (i.e. both `if` branches removed).

This can be addressed by having `#[attr] if cond { ...` be an exterior
attribute (applying to the whole `if`/`else` chain) and `if cond
#[attr] { ... ` be an interior attribute (applying to only the current
Copy link
Contributor

Choose a reason for hiding this comment

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

Markdown is interpreting this as a heading :-)

Copy link
Member Author

Choose a reason for hiding this comment

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

Haha, whoops. Fixed.

`if` branch).