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:

&(Object+Send)

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

fn foo<F>(f: F)
    where F: 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:

// 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.