Skip to content

Commit f57ea7c

Browse files
committed
Make + in impl/dyn Trait non-associative
1 parent d79f7cd commit f57ea7c

File tree

3 files changed

+106
-10
lines changed

3 files changed

+106
-10
lines changed

src/libsyntax/parse/parser.rs

+19-3
Original file line numberDiff line numberDiff line change
@@ -1528,6 +1528,7 @@ impl<'a> Parser<'a> {
15281528
maybe_whole!(self, NtTy, |x| x);
15291529

15301530
let lo = self.span;
1531+
let mut impl_dyn_multi = false;
15311532
let node = if self.eat(&token::OpenDelim(token::Paren)) {
15321533
// `(TYPE)` is a parenthesized type.
15331534
// `(TYPE,)` is a tuple with a single field of type TYPE.
@@ -1614,12 +1615,17 @@ impl<'a> Parser<'a> {
16141615
self.parse_remaining_bounds(lifetime_defs, path, lo, parse_plus)?
16151616
}
16161617
} else if self.eat_keyword(keywords::Impl) {
1617-
TyKind::ImplTrait(self.parse_ty_param_bounds_common(allow_plus)?)
1618+
// Always parse bounds greedily for better error recovery.
1619+
let bounds = self.parse_ty_param_bounds()?;
1620+
impl_dyn_multi = bounds.len() > 1 || self.prev_token_kind == PrevTokenKind::Plus;
1621+
TyKind::ImplTrait(bounds)
16181622
} else if self.check_keyword(keywords::Dyn) &&
16191623
self.look_ahead(1, |t| t.can_begin_bound() && !can_continue_type_after_ident(t)) {
16201624
self.bump(); // `dyn`
1621-
TyKind::TraitObject(self.parse_ty_param_bounds_common(allow_plus)?,
1622-
TraitObjectSyntax::Dyn)
1625+
// Always parse bounds greedily for better error recovery.
1626+
let bounds = self.parse_ty_param_bounds()?;
1627+
impl_dyn_multi = bounds.len() > 1 || self.prev_token_kind == PrevTokenKind::Plus;
1628+
TyKind::TraitObject(bounds, TraitObjectSyntax::Dyn)
16231629
} else if self.check(&token::Question) ||
16241630
self.check_lifetime() && self.look_ahead(1, |t| t == &token::BinOp(token::Plus)) {
16251631
// Bound list (trait object type)
@@ -1655,6 +1661,7 @@ impl<'a> Parser<'a> {
16551661
let ty = Ty { node, span, id: ast::DUMMY_NODE_ID };
16561662

16571663
// Try to recover from use of `+` with incorrect priority.
1664+
self.maybe_report_ambiguous_plus(allow_plus, impl_dyn_multi, &ty);
16581665
self.maybe_recover_from_bad_type_plus(allow_plus, &ty)?;
16591666
let ty = self.maybe_recover_from_bad_qpath(ty, allow_qpath_recovery)?;
16601667

@@ -1672,6 +1679,15 @@ impl<'a> Parser<'a> {
16721679
Ok(TyKind::TraitObject(bounds, TraitObjectSyntax::None))
16731680
}
16741681

1682+
fn maybe_report_ambiguous_plus(&mut self, allow_plus: bool, impl_dyn_multi: bool, ty: &Ty) {
1683+
if !allow_plus && impl_dyn_multi {
1684+
let sum_with_parens = format!("({})", pprust::ty_to_string(&ty));
1685+
self.struct_span_err(ty.span, "ambiguous `+` in a type")
1686+
.span_suggestion(ty.span, "use parentheses to disambiguate", sum_with_parens)
1687+
.emit();
1688+
}
1689+
}
1690+
16751691
fn maybe_recover_from_bad_type_plus(&mut self, allow_plus: bool, ty: &Ty) -> PResult<'a, ()> {
16761692
// Do not add `+` to expected tokens.
16771693
if !allow_plus || self.token != token::BinOp(token::Plus) {

src/test/parse-fail/impl-trait-plus-priority.rs renamed to src/test/ui/impl-trait/impl-trait-plus-priority.rs

+19-7
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,15 @@
1010

1111
// compile-flags: -Z parse-only -Z continue-parse-after-error
1212

13+
fn f() -> impl A + {} // OK
1314
fn f() -> impl A + B {} // OK
1415
fn f() -> dyn A + B {} // OK
1516
fn f() -> A + B {} // OK
1617

1718
impl S {
19+
fn f(self) -> impl A + { // OK
20+
let _ = |a, b| -> impl A + {}; // OK
21+
}
1822
fn f(self) -> impl A + B { // OK
1923
let _ = |a, b| -> impl A + B {}; // OK
2024
}
@@ -26,21 +30,29 @@ impl S {
2630
}
2731
}
2832

33+
type A = fn() -> impl A +;
34+
//~^ ERROR ambiguous `+` in a type
2935
type A = fn() -> impl A + B;
30-
//~^ ERROR expected a path on the left-hand side of `+`, not `fn() -> impl A`
36+
//~^ ERROR ambiguous `+` in a type
3137
type A = fn() -> dyn A + B;
32-
//~^ ERROR expected a path on the left-hand side of `+`, not `fn() -> dyn A`
38+
//~^ ERROR ambiguous `+` in a type
3339
type A = fn() -> A + B;
3440
//~^ ERROR expected a path on the left-hand side of `+`, not `fn() -> A`
3541

36-
type A = Fn() -> impl A + B; // OK, interpreted as `(Fn() -> impl A) + B`
37-
type A = Fn() -> dyn A + B; // OK, interpreted as `(Fn() -> dyn A) + B`
38-
type A = Fn() -> A + B; // OK, interpreted as `(Fn() -> A) + B`
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
3949

50+
type A = &impl A +;
51+
//~^ ERROR ambiguous `+` in a type
4052
type A = &impl A + B;
41-
//~^ ERROR expected a path on the left-hand side of `+`, not `&impl A`
53+
//~^ ERROR ambiguous `+` in a type
4254
type A = &dyn A + B;
43-
//~^ ERROR expected a path on the left-hand side of `+`, not `&dyn A`
55+
//~^ ERROR ambiguous `+` in a type
4456
type A = &A + B;
4557
//~^ ERROR expected a path on the left-hand side of `+`, not `&A`
4658

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)