Skip to content

Stabilize "RangeFrom" patterns in 1.55 #83918

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

Merged
merged 6 commits into from
Jul 11, 2021
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
5 changes: 4 additions & 1 deletion compiler/rustc_ast/src/ast.rs
Original file line number Diff line number Diff line change
@@ -675,7 +675,9 @@ pub enum BindingMode {

#[derive(Clone, Encodable, Decodable, Debug)]
pub enum RangeEnd {
/// `..=` or `...`
Included(RangeSyntax),
/// `..`
Excluded,
}

@@ -687,6 +689,7 @@ pub enum RangeSyntax {
DotDotEq,
}

/// All the different flavors of pattern that Rust recognizes.
#[derive(Clone, Encodable, Decodable, Debug)]
pub enum PatKind {
/// Represents a wildcard pattern (`_`).
@@ -727,7 +730,7 @@ pub enum PatKind {
/// A literal.
Lit(P<Expr>),

/// A range pattern (e.g., `1...2`, `1..=2` or `1..2`).
/// A range pattern (e.g., `1...2`, `1..2`, `1..`, `..2`, `1..=2`, `..=2`).
Range(Option<P<Expr>>, Option<P<Expr>>, Spanned<RangeEnd>),

/// A slice pattern `[a, b, c]`.
18 changes: 17 additions & 1 deletion compiler/rustc_ast_passes/src/feature_gate.rs
Original file line number Diff line number Diff line change
@@ -540,6 +540,22 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {

fn visit_pat(&mut self, pattern: &'a ast::Pat) {
match &pattern.kind {
PatKind::Slice(pats) => {
for pat in pats {
let inner_pat = match &pat.kind {
PatKind::Ident(.., Some(pat)) => pat,
_ => pat,
};
if let PatKind::Range(Some(_), None, Spanned { .. }) = inner_pat.kind {
gate_feature_post!(
&self,
half_open_range_patterns,
pat.span,
"`X..` patterns in slices are experimental"
);
}
}
}
PatKind::Box(..) => {
gate_feature_post!(
&self,
@@ -548,7 +564,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
"box pattern syntax is experimental"
);
}
PatKind::Range(_, _, Spanned { node: RangeEnd::Excluded, .. }) => {
PatKind::Range(_, Some(_), Spanned { node: RangeEnd::Excluded, .. }) => {
gate_feature_post!(
&self,
exclusive_range_pattern,
2 changes: 0 additions & 2 deletions compiler/rustc_parse/src/parser/pat.rs
Original file line number Diff line number Diff line change
@@ -715,7 +715,6 @@ impl<'a> Parser<'a> {
} else if self.eat(&token::DotDotEq) {
RangeEnd::Included(RangeSyntax::DotDotEq)
} else if self.eat(&token::DotDot) {
self.sess.gated_spans.gate(sym::exclusive_range_pattern, self.prev_token.span);
RangeEnd::Excluded
} else {
return None;
@@ -735,7 +734,6 @@ impl<'a> Parser<'a> {
Some(self.parse_pat_range_end()?)
} else {
// Parsing e.g. `X..`.
self.sess.gated_spans.gate(sym::half_open_range_patterns, begin.span.to(re.span));
if let RangeEnd::Included(_) = re.node {
// FIXME(Centril): Consider semantic errors instead in `ast_validation`.
// Possibly also do this for `X..=` in *expression* contexts.
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# `exclusive_range_pattern`

The tracking issue for this feature is: [#37854].


[#67264]: https://github.com/rust-lang/rust/issues/67264
[#37854]: https://github.com/rust-lang/rust/issues/37854
-----

The `exclusive_range_pattern` feature allows non-inclusive range
patterns (`0..10`) to be used in appropriate pattern matching
contexts. It also can be combined with `#![feature(half_open_range_patterns]`
to be able to use RangeTo patterns (`..10`).

It also enabled RangeFrom patterns but that has since been
stabilized.

```rust
#![feature(exclusive_range_pattern)]
let x = 5;
match x {
0..10 => println!("single digit"),
10 => println!("ten isn't part of the above range"),
_ => println!("nor is everything else.")
}
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# `half_open_range_patterns`

The tracking issue for this feature is: [#67264]
It is part of the `#![exclusive_range_pattern]` feature,
tracked at [#37854].

[#67264]: https://github.com/rust-lang/rust/issues/67264
[#37854]: https://github.com/rust-lang/rust/issues/37854
-----

The `half_open_range_patterns` feature allows RangeTo patterns
(`..10`) to be used in appropriate pattern matching contexts.
This requires also enabling the `exclusive_range_pattern` feature.

It also enabled RangeFrom patterns but that has since been
stabilized.

```rust
#![feature(half_open_range_patterns)]
#![feature(exclusive_range_pattern)]
let x = 5;
match x {
..0 => println!("negative!"), // "RangeTo" pattern. Unstable.
0 => println!("zero!"),
1.. => println!("positive!"), // "RangeFrom" pattern. Stable.
}
```
Original file line number Diff line number Diff line change
@@ -11,12 +11,8 @@ fn foo() {
//~| ERROR range-to patterns with `...` are not allowed
if let ..5 = 0 {}
//~^ ERROR half-open range patterns are unstable
if let 5.. = 0 {}
//~^ ERROR half-open range patterns are unstable
if let 5..= = 0 {}
//~^ ERROR half-open range patterns are unstable
//~| ERROR inclusive range with no end
//~^ ERROR inclusive range with no end
if let 5... = 0 {}
//~^ ERROR half-open range patterns are unstable
//~| ERROR inclusive range with no end
//~^ ERROR inclusive range with no end
}
Original file line number Diff line number Diff line change
@@ -5,15 +5,15 @@ LL | if let ...5 = 0 {}
| ^^^ help: use `..=` instead

error[E0586]: inclusive range with no end
--> $DIR/feature-gate-half-open-range-patterns.rs:16:13
--> $DIR/feature-gate-half-open-range-patterns.rs:14:13
|
LL | if let 5..= = 0 {}
| ^^^ help: use `..` instead
|
= note: inclusive ranges must be bounded at the end (`..=b` or `a..=b`)

error[E0586]: inclusive range with no end
--> $DIR/feature-gate-half-open-range-patterns.rs:19:13
--> $DIR/feature-gate-half-open-range-patterns.rs:16:13
|
LL | if let 5... = 0 {}
| ^^^ help: use `..` instead
@@ -47,34 +47,7 @@ LL | if let ..5 = 0 {}
= note: see issue #67264 <https://github.com/rust-lang/rust/issues/67264> for more information
= help: add `#![feature(half_open_range_patterns)]` to the crate attributes to enable

error[E0658]: half-open range patterns are unstable
--> $DIR/feature-gate-half-open-range-patterns.rs:14:12
|
LL | if let 5.. = 0 {}
| ^^^
|
= note: see issue #67264 <https://github.com/rust-lang/rust/issues/67264> for more information
= help: add `#![feature(half_open_range_patterns)]` to the crate attributes to enable

error[E0658]: half-open range patterns are unstable
--> $DIR/feature-gate-half-open-range-patterns.rs:16:12
|
LL | if let 5..= = 0 {}
| ^^^^
|
= note: see issue #67264 <https://github.com/rust-lang/rust/issues/67264> for more information
= help: add `#![feature(half_open_range_patterns)]` to the crate attributes to enable

error[E0658]: half-open range patterns are unstable
--> $DIR/feature-gate-half-open-range-patterns.rs:19:12
|
LL | if let 5... = 0 {}
| ^^^^
|
= note: see issue #67264 <https://github.com/rust-lang/rust/issues/67264> for more information
= help: add `#![feature(half_open_range_patterns)]` to the crate attributes to enable

error: aborting due to 9 previous errors
error: aborting due to 6 previous errors

Some errors have detailed explanations: E0586, E0658.
For more information about an error, try `rustc --explain E0586`.
32 changes: 32 additions & 0 deletions src/test/ui/half-open-range-patterns/range_pat_interactions0.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// run-pass
#![allow(incomplete_features)]
#![feature(exclusive_range_pattern)]
#![feature(half_open_range_patterns)]
#![feature(inline_const)]

fn main() {
let mut if_lettable = vec![];
let mut first_or = vec![];
let mut or_two = vec![];
let mut range_from = vec![];
let mut bottom = vec![];

for x in -9 + 1..=(9 - 2) {
if let -1..=0 | 2..3 | 4 = x {
if_lettable.push(x)
}
match x {
1 | -3..0 => first_or.push(x),
y @ (0..5 | 6) => or_two.push(y),
y @ 0..const { 5 + 1 } => assert_eq!(y, 5),
y @ -5.. => range_from.push(y),
y @ ..-7 => assert_eq!(y, -8),
y => bottom.push(y),
}
}
assert_eq!(if_lettable, [-1, 0, 2, 4]);
assert_eq!(first_or, [-3, -2, -1, 1]);
assert_eq!(or_two, [0, 2, 3, 4, 6]);
assert_eq!(range_from, [-5, -4, 7]);
assert_eq!(bottom, [-7, -6]);
}
29 changes: 29 additions & 0 deletions src/test/ui/half-open-range-patterns/range_pat_interactions1.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
fn main() {
let mut if_lettable = Vec::<i32>::new();
let mut first_or = Vec::<i32>::new();
let mut or_two = Vec::<i32>::new();
let mut range_from = Vec::<i32>::new();
let mut bottom = Vec::<i32>::new();
let mut errors_only = Vec::<i32>::new();

for x in -9 + 1..=(9 - 2) {
if let n @ 2..3|4 = x {
//~^ error: variable `n` is not bound in all patterns
//~| exclusive range pattern syntax is experimental
errors_only.push(x);
} else if let 2..3 | 4 = x {
//~^ exclusive range pattern syntax is experimental
if_lettable.push(x);
}
match x as i32 {
0..5+1 => errors_only.push(x),
//~^ error: expected one of `=>`, `if`, or `|`, found `+`
1 | -3..0 => first_or.push(x),
y @ (0..5 | 6) => or_two.push(y),
y @ 0..const { 5 + 1 } => assert_eq!(y, 5),
y @ -5.. => range_from.push(y),
y @ ..-7 => assert_eq!(y, -8),
y => bottom.push(y),
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
error: expected one of `=>`, `if`, or `|`, found `+`
--> $DIR/range_pat_interactions1.rs:19:17
|
LL | 0..5+1 => errors_only.push(x),
| ^ expected one of `=>`, `if`, or `|`

error[E0408]: variable `n` is not bound in all patterns
--> $DIR/range_pat_interactions1.rs:10:25
|
LL | if let n @ 2..3|4 = x {
| - ^ pattern doesn't bind `n`
| |
| variable not in all patterns

error[E0658]: exclusive range pattern syntax is experimental
--> $DIR/range_pat_interactions1.rs:10:20
|
LL | if let n @ 2..3|4 = x {
| ^^^^
|
= note: see issue #37854 <https://github.com/rust-lang/rust/issues/37854> for more information
= help: add `#![feature(exclusive_range_pattern)]` to the crate attributes to enable

error[E0658]: exclusive range pattern syntax is experimental
--> $DIR/range_pat_interactions1.rs:14:23
|
LL | } else if let 2..3 | 4 = x {
| ^^^^
|
= note: see issue #37854 <https://github.com/rust-lang/rust/issues/37854> for more information
= help: add `#![feature(exclusive_range_pattern)]` to the crate attributes to enable

error: aborting due to 4 previous errors

Some errors have detailed explanations: E0408, E0658.
For more information about an error, try `rustc --explain E0408`.
21 changes: 21 additions & 0 deletions src/test/ui/half-open-range-patterns/range_pat_interactions2.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
fn main() {
let mut first_or = Vec::<i32>::new();
let mut or_two = Vec::<i32>::new();
let mut range_from = Vec::<i32>::new();
let mut bottom = Vec::<i32>::new();
let mut errors_only = Vec::<i32>::new();

for x in -9 + 1..=(9 - 2) {
match x as i32 {
0..=(5+1) => errors_only.push(x),
//~^ error: inclusive range with no end
//~| error: expected one of `=>`, `if`, or `|`, found `(`
1 | -3..0 => first_or.push(x),
y @ (0..5 | 6) => or_two.push(y),
y @ 0..const { 5 + 1 } => assert_eq!(y, 5),
y @ -5.. => range_from.push(y),
y @ ..-7 => assert_eq!(y, -8),
y => bottom.push(y),
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
error[E0586]: inclusive range with no end
--> $DIR/range_pat_interactions2.rs:10:14
|
LL | 0..=(5+1) => errors_only.push(x),
| ^^^ help: use `..` instead
|
= note: inclusive ranges must be bounded at the end (`..=b` or `a..=b`)

error: expected one of `=>`, `if`, or `|`, found `(`
--> $DIR/range_pat_interactions2.rs:10:17
|
LL | 0..=(5+1) => errors_only.push(x),
| ^ expected one of `=>`, `if`, or `|`

error: aborting due to 2 previous errors

For more information about this error, try `rustc --explain E0586`.
24 changes: 24 additions & 0 deletions src/test/ui/half-open-range-patterns/range_pat_interactions3.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
fn main() {
let mut first_or = Vec::<i32>::new();
let mut or_two = Vec::<i32>::new();
let mut range_from = Vec::<i32>::new();
let mut bottom = Vec::<i32>::new();

for x in -9 + 1..=(9 - 2) {
match x as i32 {
8.. => bottom.push(x),
1 | -3..0 => first_or.push(x),
//~^ exclusive range pattern syntax is experimental
y @ (0..5 | 6) => or_two.push(y),
//~^ exclusive range pattern syntax is experimental
y @ 0..const { 5 + 1 } => assert_eq!(y, 5),
//~^ inline-const is experimental
//~| exclusive range pattern syntax is experimental
y @ -5.. => range_from.push(y),
y @ ..-7 => assert_eq!(y, -8),
//~^ half-open range patterns are unstable
//~| exclusive range pattern syntax is experimental
y => bottom.push(y),
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
error[E0658]: half-open range patterns are unstable
--> $DIR/range_pat_interactions3.rs:18:17
|
LL | y @ ..-7 => assert_eq!(y, -8),
| ^^^^
|
= note: see issue #67264 <https://github.com/rust-lang/rust/issues/67264> for more information
= help: add `#![feature(half_open_range_patterns)]` to the crate attributes to enable

error[E0658]: inline-const is experimental
--> $DIR/range_pat_interactions3.rs:14:20
|
LL | y @ 0..const { 5 + 1 } => assert_eq!(y, 5),
| ^^^^^
|
= note: see issue #76001 <https://github.com/rust-lang/rust/issues/76001> for more information
= help: add `#![feature(inline_const)]` to the crate attributes to enable

error[E0658]: exclusive range pattern syntax is experimental
--> $DIR/range_pat_interactions3.rs:10:17
|
LL | 1 | -3..0 => first_or.push(x),
| ^^^^^
|
= note: see issue #37854 <https://github.com/rust-lang/rust/issues/37854> for more information
= help: add `#![feature(exclusive_range_pattern)]` to the crate attributes to enable

error[E0658]: exclusive range pattern syntax is experimental
--> $DIR/range_pat_interactions3.rs:12:18
|
LL | y @ (0..5 | 6) => or_two.push(y),
| ^^^^
|
= note: see issue #37854 <https://github.com/rust-lang/rust/issues/37854> for more information
= help: add `#![feature(exclusive_range_pattern)]` to the crate attributes to enable

error[E0658]: exclusive range pattern syntax is experimental
--> $DIR/range_pat_interactions3.rs:14:17
|
LL | y @ 0..const { 5 + 1 } => assert_eq!(y, 5),
| ^^^^^^^^^^^^^^^^^^
|
= note: see issue #37854 <https://github.com/rust-lang/rust/issues/37854> for more information
= help: add `#![feature(exclusive_range_pattern)]` to the crate attributes to enable

error[E0658]: exclusive range pattern syntax is experimental
--> $DIR/range_pat_interactions3.rs:18:17
|
LL | y @ ..-7 => assert_eq!(y, -8),
| ^^^^
|
= note: see issue #37854 <https://github.com/rust-lang/rust/issues/37854> for more information
= help: add `#![feature(exclusive_range_pattern)]` to the crate attributes to enable

error: aborting due to 6 previous errors

For more information about this error, try `rustc --explain E0658`.
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#![feature(half_open_range_patterns)]
#![feature(exclusive_range_pattern)]

fn main() {
let xs = [13, 1, 5, 2, 3, 1, 21, 8];
let [a, b, c, rest @ ..] = xs;
// Consider the following example:
assert!(a == 13 && b == 1 && c == 5 && rest.len() == 5);

// What if we wanted to pull this apart without individually binding a, b, and c?
let [first_three @ ..3, rest @ 2..] = xs;
//~^ pattern requires 2 elements but array has 8
// This is somewhat unintuitive and makes slice patterns exceedingly verbose.
// We want to stabilize half-open RangeFrom (`X..`) patterns
// but without banning us from using them for a more efficient slice pattern syntax.
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
error[E0527]: pattern requires 2 elements but array has 8
--> $DIR/slice_pattern_syntax_problem0.rs:11:9
|
LL | let [first_three @ ..3, rest @ 2..] = xs;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected 8 elements

error: aborting due to previous error

For more information about this error, try `rustc --explain E0527`.
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// Instead of allowing the previous case, maintain the feature gate for slice patterns for now.
fn main() {
let xs = [13, 1, 5, 2, 3, 1, 21, 8];
let [a @ 3.., b @ ..3, c @ 4..6, ..] = xs;
//~^ `X..` patterns in slices are experimental
//~| half-open range patterns are unstable
//~| exclusive range pattern syntax is experimental
//~| exclusive range pattern syntax is experimental
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
error[E0658]: half-open range patterns are unstable
--> $DIR/slice_pattern_syntax_problem1.rs:4:23
|
LL | let [a @ 3.., b @ ..3, c @ 4..6, ..] = xs;
| ^^^
|
= note: see issue #67264 <https://github.com/rust-lang/rust/issues/67264> for more information
= help: add `#![feature(half_open_range_patterns)]` to the crate attributes to enable

error[E0658]: `X..` patterns in slices are experimental
--> $DIR/slice_pattern_syntax_problem1.rs:4:10
|
LL | let [a @ 3.., b @ ..3, c @ 4..6, ..] = xs;
| ^^^^^^^
|
= note: see issue #67264 <https://github.com/rust-lang/rust/issues/67264> for more information
= help: add `#![feature(half_open_range_patterns)]` to the crate attributes to enable

error[E0658]: exclusive range pattern syntax is experimental
--> $DIR/slice_pattern_syntax_problem1.rs:4:23
|
LL | let [a @ 3.., b @ ..3, c @ 4..6, ..] = xs;
| ^^^
|
= note: see issue #37854 <https://github.com/rust-lang/rust/issues/37854> for more information
= help: add `#![feature(exclusive_range_pattern)]` to the crate attributes to enable

error[E0658]: exclusive range pattern syntax is experimental
--> $DIR/slice_pattern_syntax_problem1.rs:4:32
|
LL | let [a @ 3.., b @ ..3, c @ 4..6, ..] = xs;
| ^^^^
|
= note: see issue #37854 <https://github.com/rust-lang/rust/issues/37854> for more information
= help: add `#![feature(exclusive_range_pattern)]` to the crate attributes to enable

error: aborting due to 4 previous errors

For more information about this error, try `rustc --explain E0658`.
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// run-pass

fn main() {
let xs = [13, 1, 5, 2, 3, 1, 21, 8];
if let [3..=14, ..] = xs {
/* this variant must pass for now, unfortunately.
* This test is included here to help inform a future plan for these.
*/
};
}