about summary refs log tree commit diff
path: root/src/libsyntax
diff options
context:
space:
mode:
authorYuki Okushi <huyuumi.dev@gmail.com>2020-01-11 04:50:46 +0900
committerGitHub <noreply@github.com>2020-01-11 04:50:46 +0900
commit793b1be6df51a259477880a45a78fb5b42f1c9b4 (patch)
treed29b8a4b57c383e12742c71fa115759e47595e4e /src/libsyntax
parent2dbcf0841a81fbada9a2b3af253b62b30a8318cc (diff)
parentd5598aa7a07b324789576585f4f035c93993fea4 (diff)
downloadrust-793b1be6df51a259477880a45a78fb5b42f1c9b4.tar.gz
rust-793b1be6df51a259477880a45a78fb5b42f1c9b4.zip
Rollup merge of #67258 - Centril:open-ended-ranges, r=oli-obk
Introduce `X..`, `..X`, and `..=X` range patterns

Tracking issue: https://github.com/rust-lang/rust/issues/67264
Feature gate: `#![feature(half_open_range_patterns)]`

---------------------------

In this PR, we introduce range-from (`X..`), range-to (`..X`), and range-to-inclusive (`..=X`) patterns.
These correspond to the `RangeFrom`, `RangeTo`, and `RangeToInclusive` expression forms introduced with the same syntaxes. The correspondence is both syntactic and semantic (in the sense that e.g. a `X..` pattern matching on a scrutinee `s` holds exactly when `(X..).contains(&s)` holds).

---------------------------

Noteworthy:

- The compiler complexity added with this PR is around 10 lines (discounting new tests, which account for the large PR size).

- `...X` is accepted syntactically with the same meaning as `..=X`. This is done primarily to simplify and unify the implementation & spec. If-and-when we decide to make `X...Y` a hard error on a new edition, we can do the same for `...X` patterns as well.

- `X...` and `X..=` is rejected syntactically just like it is for the expression equivalents. We should perhaps make these into semantic restrictions (cc @petrochenkov).

- In HAIR, these half-open ranges are represented by inserting the max/min values for the approprate types. That is, `X..` where `X: u8` would become `X..=u8::MAX` in HAIR (note the `..=` since `RangeFrom` includes the end).

- Exhaustive integer / char matching does not (yet) allow for e.g. exhaustive matching on `0usize..` or `..5usize | 5..` (same idea for `isize`). This would be a substantially more invasive change, and could be added in some other PR.

- The issues with slice pattern syntax has been resolved as we decided to use `..` to mean a "rest-pattern" and `[xs @ ..]` to bind the rest to a name in a slice pattern.

- Like with https://github.com/rust-lang/rust/pull/35712, which provided `X..Y` range patterns, this is not yet backed up by an RFC. I'm providing this experimental implementation now to have something concrete to discuss. I would be happy to provide an RFC for this PR as well as for #35712 to finalize and confirm the ideas with the larger community.

Closes https://github.com/rust-lang/rfcs/issues/947.

---------------------------

r? @varkor cc @matthewjasper @oli-obk

I would recommend reviewing this (in particular HAIR-lowering and pattern parsing changes) with whitespace changes ignored.
Diffstat (limited to 'src/libsyntax')
-rw-r--r--src/libsyntax/ast.rs2
-rw-r--r--src/libsyntax/feature_gate/check.rs1
-rw-r--r--src/libsyntax/mut_visit.rs4
-rw-r--r--src/libsyntax/print/pprust.rs10
-rw-r--r--src/libsyntax/visit.rs4
5 files changed, 13 insertions, 8 deletions
diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs
index 1d3bb7d8768..33acba8eba0 100644
--- a/src/libsyntax/ast.rs
+++ b/src/libsyntax/ast.rs
@@ -649,7 +649,7 @@ pub enum PatKind {
     Lit(P<Expr>),
 
     /// A range pattern (e.g., `1...2`, `1..=2` or `1..2`).
-    Range(P<Expr>, P<Expr>, Spanned<RangeEnd>),
+    Range(Option<P<Expr>>, Option<P<Expr>>, Spanned<RangeEnd>),
 
     /// A slice pattern `[a, b, c]`.
     Slice(Vec<P<Pat>>),
diff --git a/src/libsyntax/feature_gate/check.rs b/src/libsyntax/feature_gate/check.rs
index 52eb20d320f..5e431905172 100644
--- a/src/libsyntax/feature_gate/check.rs
+++ b/src/libsyntax/feature_gate/check.rs
@@ -911,6 +911,7 @@ pub fn check_crate(
     gate_all!(raw_ref_op, "raw address of syntax is experimental");
     gate_all!(const_trait_bound_opt_out, "`?const` on trait bounds is experimental");
     gate_all!(const_trait_impl, "const trait impls are experimental");
+    gate_all!(half_open_range_patterns, "half-open range patterns are unstable");
 
     // All uses of `gate_all!` below this point were added in #65742,
     // and subsequently disabled (with the non-early gating readded).
diff --git a/src/libsyntax/mut_visit.rs b/src/libsyntax/mut_visit.rs
index 264ba25cede..58d4e46111b 100644
--- a/src/libsyntax/mut_visit.rs
+++ b/src/libsyntax/mut_visit.rs
@@ -1075,8 +1075,8 @@ pub fn noop_visit_pat<T: MutVisitor>(pat: &mut P<Pat>, vis: &mut T) {
         PatKind::Box(inner) => vis.visit_pat(inner),
         PatKind::Ref(inner, _mutbl) => vis.visit_pat(inner),
         PatKind::Range(e1, e2, Spanned { span: _, node: _ }) => {
-            vis.visit_expr(e1);
-            vis.visit_expr(e2);
+            visit_opt(e1, |e| vis.visit_expr(e));
+            visit_opt(e2, |e| vis.visit_expr(e));
             vis.visit_span(span);
         }
         PatKind::Tuple(elems) | PatKind::Slice(elems) | PatKind::Or(elems) => {
diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs
index dd9976510dc..11c8cb8ef75 100644
--- a/src/libsyntax/print/pprust.rs
+++ b/src/libsyntax/print/pprust.rs
@@ -2329,14 +2329,18 @@ impl<'a> State<'a> {
             }
             PatKind::Lit(ref e) => self.print_expr(&**e),
             PatKind::Range(ref begin, ref end, Spanned { node: ref end_kind, .. }) => {
-                self.print_expr(begin);
-                self.s.space();
+                if let Some(e) = begin {
+                    self.print_expr(e);
+                    self.s.space();
+                }
                 match *end_kind {
                     RangeEnd::Included(RangeSyntax::DotDotDot) => self.s.word("..."),
                     RangeEnd::Included(RangeSyntax::DotDotEq) => self.s.word("..="),
                     RangeEnd::Excluded => self.s.word(".."),
                 }
-                self.print_expr(end);
+                if let Some(e) = end {
+                    self.print_expr(e);
+                }
             }
             PatKind::Slice(ref elts) => {
                 self.s.word("[");
diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs
index ebb49abebb0..3c2ebacbc4e 100644
--- a/src/libsyntax/visit.rs
+++ b/src/libsyntax/visit.rs
@@ -492,8 +492,8 @@ pub fn walk_pat<'a, V: Visitor<'a>>(visitor: &mut V, pattern: &'a Pat) {
         }
         PatKind::Lit(ref expression) => visitor.visit_expr(expression),
         PatKind::Range(ref lower_bound, ref upper_bound, _) => {
-            visitor.visit_expr(lower_bound);
-            visitor.visit_expr(upper_bound);
+            walk_list!(visitor, visit_expr, lower_bound);
+            walk_list!(visitor, visit_expr, upper_bound);
         }
         PatKind::Wild | PatKind::Rest => {}
         PatKind::Tuple(ref elems) | PatKind::Slice(ref elems) | PatKind::Or(ref elems) => {