Skip to content

Commit da5ba14

Browse files
committed
Auto merge of #45294 - petrochenkov:prioplus, r=nikomatsakis
syntax: Lower priority of `+` in `impl Trait`/`dyn Trait` Now you have to write `Fn() -> (impl A + B)` instead of `Fn() -> impl A + B`, this is consistent with priority of `+` in trait objects (`Fn() -> A + B` means `(Fn() -> A) + B`). To make this viable I changed the syntax to also permit `+` in return types in function declarations ``` fn f() -> dyn A + B { ... } // OK, don't have to write `-> (dyn A + B)` // This is acceptable, because `dyn A + B` here is an isolated type and // not part of a larger type with various operator priorities in play // like `dyn A + B` in `Fn() -> dyn A + B` despite syntax similarities. ``` but you still have to use `-> (dyn A + B)` in function types and function-like trait object types (see this PR's tests for examples). This can be a breaking change for code using `impl Trait` on nightly. The thing that is most likely to break is `&impl A + B`, it needs to be rewritten as `&(impl A + B)`. cc #34511 #44662 rust-lang/rfcs#438
2 parents 90eb44a + f57ea7c commit da5ba14

File tree

5 files changed

+154
-12
lines changed

5 files changed

+154
-12
lines changed

src/libsyntax/parse/parser.rs

+25-10
Original file line numberDiff line numberDiff line change
@@ -1364,7 +1364,7 @@ impl<'a> Parser<'a> {
13641364

13651365
self.expect_keyword(keywords::Fn)?;
13661366
let (inputs, variadic) = self.parse_fn_args(false, true)?;
1367-
let ret_ty = self.parse_ret_ty()?;
1367+
let ret_ty = self.parse_ret_ty(false)?;
13681368
let decl = P(FnDecl {
13691369
inputs,
13701370
output: ret_ty,
@@ -1503,9 +1503,9 @@ impl<'a> Parser<'a> {
15031503
}
15041504

15051505
/// Parse optional return type [ -> TY ] in function decl
1506-
pub fn parse_ret_ty(&mut self) -> PResult<'a, FunctionRetTy> {
1506+
fn parse_ret_ty(&mut self, allow_plus: bool) -> PResult<'a, FunctionRetTy> {
15071507
if self.eat(&token::RArrow) {
1508-
Ok(FunctionRetTy::Ty(self.parse_ty_no_plus()?))
1508+
Ok(FunctionRetTy::Ty(self.parse_ty_common(allow_plus, true)?))
15091509
} else {
15101510
Ok(FunctionRetTy::Default(self.span.with_hi(self.span.lo())))
15111511
}
@@ -1530,6 +1530,7 @@ impl<'a> Parser<'a> {
15301530
maybe_whole!(self, NtTy, |x| x);
15311531

15321532
let lo = self.span;
1533+
let mut impl_dyn_multi = false;
15331534
let node = if self.eat(&token::OpenDelim(token::Paren)) {
15341535
// `(TYPE)` is a parenthesized type.
15351536
// `(TYPE,)` is a tuple with a single field of type TYPE.
@@ -1616,13 +1617,17 @@ impl<'a> Parser<'a> {
16161617
self.parse_remaining_bounds(lifetime_defs, path, lo, parse_plus)?
16171618
}
16181619
} else if self.eat_keyword(keywords::Impl) {
1619-
// FIXME: figure out priority of `+` in `impl Trait1 + Trait2` (#34511).
1620-
TyKind::ImplTrait(self.parse_ty_param_bounds()?)
1620+
// Always parse bounds greedily for better error recovery.
1621+
let bounds = self.parse_ty_param_bounds()?;
1622+
impl_dyn_multi = bounds.len() > 1 || self.prev_token_kind == PrevTokenKind::Plus;
1623+
TyKind::ImplTrait(bounds)
16211624
} else if self.check_keyword(keywords::Dyn) &&
16221625
self.look_ahead(1, |t| t.can_begin_bound() && !can_continue_type_after_ident(t)) {
1623-
// FIXME: figure out priority of `+` in `dyn Trait1 + Trait2` (#34511).
16241626
self.bump(); // `dyn`
1625-
TyKind::TraitObject(self.parse_ty_param_bounds()?, TraitObjectSyntax::Dyn)
1627+
// Always parse bounds greedily for better error recovery.
1628+
let bounds = self.parse_ty_param_bounds()?;
1629+
impl_dyn_multi = bounds.len() > 1 || self.prev_token_kind == PrevTokenKind::Plus;
1630+
TyKind::TraitObject(bounds, TraitObjectSyntax::Dyn)
16261631
} else if self.check(&token::Question) ||
16271632
self.check_lifetime() && self.look_ahead(1, |t| t == &token::BinOp(token::Plus)) {
16281633
// Bound list (trait object type)
@@ -1658,6 +1663,7 @@ impl<'a> Parser<'a> {
16581663
let ty = Ty { node, span, id: ast::DUMMY_NODE_ID };
16591664

16601665
// Try to recover from use of `+` with incorrect priority.
1666+
self.maybe_report_ambiguous_plus(allow_plus, impl_dyn_multi, &ty);
16611667
self.maybe_recover_from_bad_type_plus(allow_plus, &ty)?;
16621668
let ty = self.maybe_recover_from_bad_qpath(ty, allow_qpath_recovery)?;
16631669

@@ -1675,6 +1681,15 @@ impl<'a> Parser<'a> {
16751681
Ok(TyKind::TraitObject(bounds, TraitObjectSyntax::None))
16761682
}
16771683

1684+
fn maybe_report_ambiguous_plus(&mut self, allow_plus: bool, impl_dyn_multi: bool, ty: &Ty) {
1685+
if !allow_plus && impl_dyn_multi {
1686+
let sum_with_parens = format!("({})", pprust::ty_to_string(&ty));
1687+
self.struct_span_err(ty.span, "ambiguous `+` in a type")
1688+
.span_suggestion(ty.span, "use parentheses to disambiguate", sum_with_parens)
1689+
.emit();
1690+
}
1691+
}
1692+
16781693
fn maybe_recover_from_bad_type_plus(&mut self, allow_plus: bool, ty: &Ty) -> PResult<'a, ()> {
16791694
// Do not add `+` to expected tokens.
16801695
if !allow_plus || self.token != token::BinOp(token::Plus) {
@@ -4896,7 +4911,7 @@ impl<'a> Parser<'a> {
48964911
pub fn parse_fn_decl(&mut self, allow_variadic: bool) -> PResult<'a, P<FnDecl>> {
48974912

48984913
let (args, variadic) = self.parse_fn_args(true, allow_variadic)?;
4899-
let ret_ty = self.parse_ret_ty()?;
4914+
let ret_ty = self.parse_ret_ty(true)?;
49004915

49014916
Ok(P(FnDecl {
49024917
inputs: args,
@@ -5037,7 +5052,7 @@ impl<'a> Parser<'a> {
50375052
self.expect(&token::CloseDelim(token::Paren))?;
50385053
Ok(P(FnDecl {
50395054
inputs: fn_inputs,
5040-
output: self.parse_ret_ty()?,
5055+
output: self.parse_ret_ty(true)?,
50415056
variadic: false
50425057
}))
50435058
}
@@ -5059,7 +5074,7 @@ impl<'a> Parser<'a> {
50595074
args
50605075
}
50615076
};
5062-
let output = self.parse_ret_ty()?;
5077+
let output = self.parse_ret_ty(true)?;
50635078

50645079
Ok(P(FnDecl {
50655080
inputs: inputs_captures,

src/test/parse-fail/closure-return-syntax.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,5 @@
1313

1414
fn main() {
1515
let x = || -> i32 22;
16-
//~^ ERROR expected one of `!`, `(`, `::`, `<`, or `{`, found `22`
16+
//~^ ERROR expected one of `!`, `(`, `+`, `::`, `<`, or `{`, found `22`
1717
}

src/test/parse-fail/issue-24780.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,6 @@
1515
// compile-flags: -Z parse-only
1616

1717
fn foo() -> Vec<usize>> {
18-
//~^ ERROR expected one of `!`, `::`, `where`, or `{`, found `>`
18+
//~^ ERROR expected one of `!`, `+`, `::`, `where`, or `{`, found `>`
1919
Vec::new()
2020
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// compile-flags: -Z parse-only -Z continue-parse-after-error
12+
13+
fn f() -> impl A + {} // OK
14+
fn f() -> impl A + B {} // OK
15+
fn f() -> dyn A + B {} // OK
16+
fn f() -> A + B {} // OK
17+
18+
impl S {
19+
fn f(self) -> impl A + { // OK
20+
let _ = |a, b| -> impl A + {}; // OK
21+
}
22+
fn f(self) -> impl A + B { // OK
23+
let _ = |a, b| -> impl A + B {}; // OK
24+
}
25+
fn f(self) -> dyn A + B { // OK
26+
let _ = |a, b| -> dyn A + B {}; // OK
27+
}
28+
fn f(self) -> A + B { // OK
29+
let _ = |a, b| -> A + B {}; // OK
30+
}
31+
}
32+
33+
type A = fn() -> impl A +;
34+
//~^ ERROR ambiguous `+` in a type
35+
type A = fn() -> impl A + B;
36+
//~^ ERROR ambiguous `+` in a type
37+
type A = fn() -> dyn A + B;
38+
//~^ ERROR ambiguous `+` in a type
39+
type A = fn() -> A + B;
40+
//~^ ERROR expected a path on the left-hand side of `+`, not `fn() -> A`
41+
42+
type A = Fn() -> impl A +;
43+
//~^ ERROR ambiguous `+` in a type
44+
type A = Fn() -> impl A + B;
45+
//~^ ERROR ambiguous `+` in a type
46+
type A = Fn() -> dyn A + B;
47+
//~^ ERROR ambiguous `+` in a type
48+
type A = Fn() -> A + B; // OK, interpreted as `(Fn() -> A) + B` for compatibility
49+
50+
type A = &impl A +;
51+
//~^ ERROR ambiguous `+` in a type
52+
type A = &impl A + B;
53+
//~^ ERROR ambiguous `+` in a type
54+
type A = &dyn A + B;
55+
//~^ ERROR ambiguous `+` in a type
56+
type A = &A + B;
57+
//~^ ERROR expected a path on the left-hand side of `+`, not `&A`
58+
59+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
error: ambiguous `+` in a type
2+
--> $DIR/impl-trait-plus-priority.rs:33:18
3+
|
4+
33 | type A = fn() -> impl A +;
5+
| ^^^^^^^^ help: use parentheses to disambiguate: `(impl A)`
6+
7+
error: ambiguous `+` in a type
8+
--> $DIR/impl-trait-plus-priority.rs:35:18
9+
|
10+
35 | type A = fn() -> impl A + B;
11+
| ^^^^^^^^^^ help: use parentheses to disambiguate: `(impl A + B)`
12+
13+
error: ambiguous `+` in a type
14+
--> $DIR/impl-trait-plus-priority.rs:37:18
15+
|
16+
37 | type A = fn() -> dyn A + B;
17+
| ^^^^^^^^^ help: use parentheses to disambiguate: `(dyn A + B)`
18+
19+
error[E0178]: expected a path on the left-hand side of `+`, not `fn() -> A`
20+
--> $DIR/impl-trait-plus-priority.rs:39:10
21+
|
22+
39 | type A = fn() -> A + B;
23+
| ^^^^^^^^^^^^^ perhaps you forgot parentheses?
24+
25+
error: ambiguous `+` in a type
26+
--> $DIR/impl-trait-plus-priority.rs:42:18
27+
|
28+
42 | type A = Fn() -> impl A +;
29+
| ^^^^^^^^ help: use parentheses to disambiguate: `(impl A)`
30+
31+
error: ambiguous `+` in a type
32+
--> $DIR/impl-trait-plus-priority.rs:44:18
33+
|
34+
44 | type A = Fn() -> impl A + B;
35+
| ^^^^^^^^^^ help: use parentheses to disambiguate: `(impl A + B)`
36+
37+
error: ambiguous `+` in a type
38+
--> $DIR/impl-trait-plus-priority.rs:46:18
39+
|
40+
46 | type A = Fn() -> dyn A + B;
41+
| ^^^^^^^^^ help: use parentheses to disambiguate: `(dyn A + B)`
42+
43+
error: ambiguous `+` in a type
44+
--> $DIR/impl-trait-plus-priority.rs:50:11
45+
|
46+
50 | type A = &impl A +;
47+
| ^^^^^^^^ help: use parentheses to disambiguate: `(impl A)`
48+
49+
error: ambiguous `+` in a type
50+
--> $DIR/impl-trait-plus-priority.rs:52:11
51+
|
52+
52 | type A = &impl A + B;
53+
| ^^^^^^^^^^ help: use parentheses to disambiguate: `(impl A + B)`
54+
55+
error: ambiguous `+` in a type
56+
--> $DIR/impl-trait-plus-priority.rs:54:11
57+
|
58+
54 | type A = &dyn A + B;
59+
| ^^^^^^^^^ help: use parentheses to disambiguate: `(dyn A + B)`
60+
61+
error[E0178]: expected a path on the left-hand side of `+`, not `&A`
62+
--> $DIR/impl-trait-plus-priority.rs:56:10
63+
|
64+
56 | type A = &A + B;
65+
| ^^^^^^ help: try adding parentheses: `&(A + B)`
66+
67+
error: aborting due to 11 previous errors
68+

0 commit comments

Comments
 (0)