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

Change precedence of + in type grammar #438

Merged
merged 4 commits into from
Nov 18, 2014
Merged
Changes from all commits
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
92 changes: 92 additions & 0 deletions text/0000-precedence-of-plus.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
- Start Date: (fill me in with today's date, YYYY-MM-DD)
- RFC PR: (leave this empty)
- Rust Issue: (leave this empty)

# Summary

Change the precedence of `+` (object bounds) in type grammar so that
it is similar to the precedence in the expression grammars.

# Motivation

Currently `+` in types has a much higher precedence than it does in expressions.
This means that for example one can write a type like the following:

```
&Object+Send
```

Whereas if that were an expression, parentheses would be required:

```rust
&(Object+Send)
````

Besides being confusing in its own right, this loose approach with
regard to precedence yields ambiguities with unboxed closure bounds:

```rust
fn foo<F>(f: F)
where F: FnOnce(&int) -> &Object + Send
Copy link
Member

Choose a reason for hiding this comment

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

So, under this scheme, a type written like this is (FnOnce(&int) -> &Object) + Send?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

On Tue, Nov 04, 2014 at 03:16:07AM -0800, Huon Wilson wrote:

So, under this scheme, a type written like this is (FnOnce(&int) -> &Object) + Send?

Yes, right. That might occur in a context like Box<FnOnce(&int) -> &Object + Send>

{ }
```

In this example, it is unclear whether `F` returns an object which is
`Send`, or whether `F` itself is `Send`.

# Detailed design

This RFC proposes that the precedence of `+` be made lower than unary
type operators. In addition, the grammar is segregated such that in
"open-ended" contexts (e.g., after `->`), parentheses are required to
use a `+`, whereas in others (e.g., inside `<>`), parentheses are not.
Here are some examples:

```rust
// Before After Note
// ~~~~~~ ~~~~~ ~~~~
&Object+Send &(Object+Send)
&'a Object+'a &'a (Object+'a)
Box<Object+Send> Box<Object+Send>
foo::<Object+Send,int>(...) foo::<Object+Send,int>(...)
Fn() -> Object+Send Fn() -> (Object+Send) // (*)
Fn() -> &Object+Send Fn() -> &(Object+Send)

// (*) Must yield a type error, as return type must be `Sized`.
```

More fully, the type grammar is as follows (EBNF notation):

TYPE = PATH
| '&' [LIFETIME] TYPE
| '&' [LIFETIME] 'mut' TYPE
| '*' 'const' TYPE
| '*' 'mut' TYPE
| ...
| '(' SUM ')'
SUM = TYPE { '+' TYPE }
PATH = IDS '<' SUM { ',' SUM } '>'
| IDS '(' SUM { ',' SUM } ')' '->' TYPE
IDS = ['::'] ID { '::' ID }

Where clauses would use the following grammar:

WHERE_CLAUSE = PATH { '+' PATH }

One property of this grammar is that the `TYPE` nonterminal does not
require a terminator as it has no "open-ended" expansions. `SUM`, in
contrast, can be extended any number of times via the `+` token. Hence
is why `SUM` must be enclosed in parens to make it into a `TYPE`.

# Drawbacks

Common types like `&'a Foo+'a` become slightly longer (`&'a (Foo+'a)`).

# Alternatives

We could live with the inconsistency between the type/expression
grammars and disambiguate where clauses in an ad-hoc way.

# Unresolved questions

None.