Skip to content

Commit 5dd2c57

Browse files
committed
Opportunistically split != to successfully parse never type
1 parent 5771665 commit 5dd2c57

File tree

5 files changed

+50
-18
lines changed

5 files changed

+50
-18
lines changed

compiler/rustc_parse/src/parser/mod.rs

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -774,24 +774,29 @@ impl<'a> Parser<'a> {
774774
}
775775
}
776776

777-
/// Eats `+` possibly breaking tokens like `+=` in process.
777+
/// Eats `+` possibly breaking tokens like `+=` in the process.
778778
fn eat_plus(&mut self) -> bool {
779779
self.break_and_eat(exp!(Plus))
780780
}
781781

782-
/// Eats `&` possibly breaking tokens like `&&` in process.
782+
/// Eats `!` possibly breaking tokens like `!=` in the process.
783+
fn eat_bang(&mut self) -> bool {
784+
self.break_and_eat(exp!(Bang))
785+
}
786+
787+
/// Eats `&` possibly breaking tokens like `&&` in the process.
783788
/// Signals an error if `&` is not eaten.
784789
fn expect_and(&mut self) -> PResult<'a, ()> {
785790
if self.break_and_eat(exp!(And)) { Ok(()) } else { self.unexpected() }
786791
}
787792

788-
/// Eats `|` possibly breaking tokens like `||` in process.
793+
/// Eats `|` possibly breaking tokens like `||` in the process.
789794
/// Signals an error if `|` was not eaten.
790795
fn expect_or(&mut self) -> PResult<'a, ()> {
791796
if self.break_and_eat(exp!(Or)) { Ok(()) } else { self.unexpected() }
792797
}
793798

794-
/// Eats `<` possibly breaking tokens like `<<` in process.
799+
/// Eats `<` possibly breaking tokens like `<<` in the process.
795800
fn eat_lt(&mut self) -> bool {
796801
let ate = self.break_and_eat(exp!(Lt));
797802
if ate {
@@ -802,13 +807,13 @@ impl<'a> Parser<'a> {
802807
ate
803808
}
804809

805-
/// Eats `<` possibly breaking tokens like `<<` in process.
810+
/// Eats `<` possibly breaking tokens like `<<` in the process.
806811
/// Signals an error if `<` was not eaten.
807812
fn expect_lt(&mut self) -> PResult<'a, ()> {
808813
if self.eat_lt() { Ok(()) } else { self.unexpected() }
809814
}
810815

811-
/// Eats `>` possibly breaking tokens like `>>` in process.
816+
/// Eats `>` possibly breaking tokens like `>>` in the process.
812817
/// Signals an error if `>` was not eaten.
813818
fn expect_gt(&mut self) -> PResult<'a, ()> {
814819
if self.break_and_eat(exp!(Gt)) {

compiler/rustc_parse/src/parser/pat.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -758,7 +758,9 @@ impl<'a> Parser<'a> {
758758
} else if let Some(form) = self.parse_range_end() {
759759
self.parse_pat_range_to(form)? // `..=X`, `...X`, or `..X`.
760760
} else if self.eat(exp!(Bang)) {
761-
// Parse `!`
761+
// Ideally we'd use `eat_bang` here to allow us to parse `!=>` as `! =>`. However,
762+
// `break_and_eat` doesn't "reglue" the split-off `=` with any following `>` (since
763+
// that would likely be super fragile and complex).
762764
self.psess.gated_spans.gate(sym::never_patterns, self.prev_token.span);
763765
PatKind::Never
764766
} else if self.eat_keyword(exp!(Underscore)) {

compiler/rustc_parse/src/parser/ty.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -280,12 +280,12 @@ impl<'a> Parser<'a> {
280280
return Ok(ty);
281281
}
282282

283-
let lo = self.token.span;
284283
let mut impl_dyn_multi = false;
284+
let mut lo = self.token.span;
285285
let kind = if self.check(exp!(OpenParen)) {
286286
self.parse_ty_tuple_or_parens(lo, allow_plus)?
287-
} else if self.eat(exp!(Bang)) {
288-
// Never type `!`
287+
} else if self.eat_bang() {
288+
lo = self.prev_token.span;
289289
TyKind::Never
290290
} else if self.eat(exp!(Star)) {
291291
self.parse_ty_ptr()?

tests/ui/parser/token-splitting.rs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// Check some places in which we want to split multi-character punctuation.
2+
//@ edition: 2015
3+
#![feature(never_type)] // only used inside `bang_eq_never_ty`!
4+
5+
//@ check-pass
6+
7+
fn plus_eq_bound() {
8+
// issue: <https://github.com/rust-lang/rust/issues/47856>
9+
struct W<T: Clone + = ()> { t: T }
10+
struct S<T: Clone += ()> { t: T }
11+
12+
// Bare & `dyn`-prefixed trait object types take different paths in the parser.
13+
// Therefore, test both branches.
14+
15+
#[cfg(false)] let _: Debug + = *(&() as &dyn Debug);
16+
#[cfg(false)] let _: Debug += *(&() as &dyn Debug);
17+
18+
#[cfg(false)] let _: dyn Debug + = *(&() as &dyn Debug);
19+
#[cfg(false)] let _: dyn Debug += *(&() as &dyn Debug);
20+
21+
#[cfg(false)] fn w() where Trait + = () {}
22+
#[cfg(false)] fn s() where Trait += () {}
23+
}
24+
25+
fn bang_eq_never_ty(x: !) {
26+
let _: ! = x;
27+
let _: != x;
28+
29+
#[cfg(false)] struct W<const X: ! = { loop {} }>;
30+
#[cfg(false)] struct S<const X: != { loop {} }>;
31+
}
32+
33+
fn main() {}

tests/ui/parser/trait-plusequal-splitting.rs

Lines changed: 0 additions & 8 deletions
This file was deleted.

0 commit comments

Comments
 (0)