From 17297b5236450b2ea5fe61e6ab178841999c955c Mon Sep 17 00:00:00 2001 From: Sean Patrick Santos Date: Tue, 17 Feb 2015 02:15:27 -0700 Subject: [PATCH 1/5] Add one_sided_match_ranges RFC. --- text/0000-one-sided-match-ranges.md | 118 ++++++++++++++++++++++++++++ 1 file changed, 118 insertions(+) create mode 100644 text/0000-one-sided-match-ranges.md diff --git a/text/0000-one-sided-match-ranges.md b/text/0000-one-sided-match-ranges.md new file mode 100644 index 00000000000..df8af553689 --- /dev/null +++ b/text/0000-one-sided-match-ranges.md @@ -0,0 +1,118 @@ +- Feature Name: one_sided_match_ranges +- Start Date: 2015-02-15 +- RFC PR: (leave this empty) +- Rust Issue: (leave this empty) + +# Summary + +Allow inclusive ranges in match patterns that are bounded on only one side, +analogous to the `RangeTo` and `RangeFrom` exclusive ranges used in expressions. + +Also have the compiler check whether integer patterns exhaust all possible +values, so that providing an unreachable match arm for certain integer match +expressions is no longer necessary (or in fact allowed). + +# Motivation + +This clears up a minor wart that adds some confusion and perceived complexity to +the language. Allowed range patterns include `i..j`, `..j`, `i..`, and simply +`..`. However, match pattern ranges can only have the form `i...j`. + +Furthermore, even if the patterns in a match statement are provably exhaustive, +the reference compiler currently requires a catch-all `_` pattern, which in +such cases is actually unreachable. + +A more speculative use case relates to the future addition of dependent types +(or at least types that depend on integer constants, as `[T; N]` arrays already +do). For such types, match patterns could be useful for expressing bounds on +constant parameters in types, in a way that's limited enough to inform coherence +checks and allow for specialization of impls. + +# Detailed design + +Consider the following example: + +```rust +let error_code: u8 = do_something(); +match error_code { + 0 => Success, + 1 | 3 => Failure, + 2...255 => Error, + _ => unreachable!(), +} +``` + +Assuming that all referenced items have been defined, the above code is legal +today. + +Under this proposal, this code would need to be adjusted to the following, since +the compiler would verify that these possibilities exhaust the entire allowed +range of integers: + +```rust +let error_code: u8 = do_something(); +match error_code { + 0 => Success, + 1 | 3 => Failure, + 2...255 => Error, +} +``` + +Since `255` is the maximum representable value for `u8`, the following would be +equivalent: + +```rust +let error_code: u8 = do_something(); +match error_code { + 0 => Success, + 1 | 3 => Failure, + 2... => Error, +} +``` + +The types that are affected by the new check for exhaustiveness will be the +integer primitives and `char`. A set of patterns will be considered to +exhaustively cover `char` if all valid `char` values (that is, all Unicode +Scalar Values) are covered. + +Floating point values will benefit from the new syntax (e.g. `...1.0f32` will be +an allowed pattern), but will not be subject to the changes in exhaustiveness +checking. The main rationale for this inconsistency is that floating-point +numbers have much more complex, and in some cases even platform-dependent, +semantics. + +An prototype implementation of an algorithm that can perform most of the work +for the exhaustiveness check is +[here](https://github.com/quantheory/int_range_check). This is sufficient to +check whether a series of ranges cover all allowed values of an integer type. + +# Drawbacks + +Since more match arms can be proven unreachable, some code that currently +compiles may be broken. However, such match expressions are probably rare. + +Pattern syntax will become slightly more complex. + +# Alternatives + +## Do nothing + +The current behavior is only a mild nuisance right now, so keeping it is +definitely an option. + +## Keep backwards compatibility + +We could avoid breaking existing code by accepting match expressions with +unreachable arms in some cases. In the examples above, the match expression that +is allowed today would still be allowed, but the new expressions would also be +allowed. + +## Take only one of the two features + +The new syntax and the change to exhaustiveness checks are independent +features. They are included together mainly because the new syntax would make it +easier to write exhaustive matches without using wildcards. + +# Unresolved questions + +N/A From b6ccce9fb85b2ddcc0d0ee8baae45301428c17bc Mon Sep 17 00:00:00 2001 From: Sean Patrick Santos Date: Tue, 17 Feb 2015 02:34:26 -0700 Subject: [PATCH 2/5] Make one_sided_match_ranges design more explicit. --- text/0000-one-sided-match-ranges.md | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/text/0000-one-sided-match-ranges.md b/text/0000-one-sided-match-ranges.md index df8af553689..bea334e806c 100644 --- a/text/0000-one-sided-match-ranges.md +++ b/text/0000-one-sided-match-ranges.md @@ -30,6 +30,15 @@ checks and allow for specialization of impls. # Detailed design +Just as the match pattern `i...j` matches numbers between `i` and `j`, `i...` +will match all numbers greater than `i`, and `...j` will match all numbers +greater than `j`. This capability is extended to `char` as well, under the same +ordering used for match patterns now. + +Additionally, exhaustiveness checks will be performed for patterns that match +integers and `char`, as opposed to the current implementation, which requires +the last arm of such match patterns to be a blanket match. + Consider the following example: ```rust @@ -70,10 +79,10 @@ match error_code { } ``` -The types that are affected by the new check for exhaustiveness will be the -integer primitives and `char`. A set of patterns will be considered to -exhaustively cover `char` if all valid `char` values (that is, all Unicode -Scalar Values) are covered. +A set of patterns will be considered to exhaustively cover an integer type if +all possible values for that type are covered. A set of patterns will be +considered to exhaustively cover `char` if all valid `char` values (that is, all +Unicode Scalar Values) are covered. Floating point values will benefit from the new syntax (e.g. `...1.0f32` will be an allowed pattern), but will not be subject to the changes in exhaustiveness From 2edf5a3bd5598338a6a12eebded9b1225e0f1616 Mon Sep 17 00:00:00 2001 From: Sean Patrick Santos Date: Tue, 17 Feb 2015 23:55:28 -0700 Subject: [PATCH 3/5] Fix error in one_sided_match_ranges. --- text/0000-one-sided-match-ranges.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/text/0000-one-sided-match-ranges.md b/text/0000-one-sided-match-ranges.md index bea334e806c..19f3e7fdfa2 100644 --- a/text/0000-one-sided-match-ranges.md +++ b/text/0000-one-sided-match-ranges.md @@ -31,9 +31,9 @@ checks and allow for specialization of impls. # Detailed design Just as the match pattern `i...j` matches numbers between `i` and `j`, `i...` -will match all numbers greater than `i`, and `...j` will match all numbers -greater than `j`. This capability is extended to `char` as well, under the same -ordering used for match patterns now. +will match all numbers greater than `i`, and `...j` will match all numbers less +than `j`. This capability is extended to `char` as well, under the same ordering +used for match patterns now. Additionally, exhaustiveness checks will be performed for patterns that match integers and `char`, as opposed to the current implementation, which requires From 01d1dae95ad6374265c2eeebf21e63bbf21e47d0 Mon Sep 17 00:00:00 2001 From: Sean Patrick Santos Date: Wed, 18 Feb 2015 00:00:35 -0700 Subject: [PATCH 4/5] Fix one_sided_match_ranges again.. --- text/0000-one-sided-match-ranges.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/text/0000-one-sided-match-ranges.md b/text/0000-one-sided-match-ranges.md index 19f3e7fdfa2..8eb1b1e5611 100644 --- a/text/0000-one-sided-match-ranges.md +++ b/text/0000-one-sided-match-ranges.md @@ -30,10 +30,10 @@ checks and allow for specialization of impls. # Detailed design -Just as the match pattern `i...j` matches numbers between `i` and `j`, `i...` -will match all numbers greater than `i`, and `...j` will match all numbers less -than `j`. This capability is extended to `char` as well, under the same ordering -used for match patterns now. +Just as the match pattern `i...j` matches numbers between `i` and `j` +(inclusive), `i...` will match all numbers greater than or equal to `i`, and +`...j` will match all numbers less than or equal to `j`. This capability is +extended to `char` as well, under the same ordering used for match patterns now. Additionally, exhaustiveness checks will be performed for patterns that match integers and `char`, as opposed to the current implementation, which requires From 3ce0f703a5fc6d423ebe094ed4a85bcf66f7086c Mon Sep 17 00:00:00 2001 From: Sean Patrick Santos Date: Wed, 18 Feb 2015 00:19:46 -0700 Subject: [PATCH 5/5] Add missing pattern guards section. --- text/0000-one-sided-match-ranges.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/text/0000-one-sided-match-ranges.md b/text/0000-one-sided-match-ranges.md index 8eb1b1e5611..2804ffaf0ea 100644 --- a/text/0000-one-sided-match-ranges.md +++ b/text/0000-one-sided-match-ranges.md @@ -95,6 +95,12 @@ for the exhaustiveness check is [here](https://github.com/quantheory/int_range_check). This is sufficient to check whether a series of ranges cover all allowed values of an integer type. +Finally, note that pattern guards will interact with exhaustiveness checks in +the same way as they do now. Namely, the compiler will check to see if a match +arm with a pattern guard is reachable, but will otherwise disregard it during +exhaustiveness checks. This means that the pattern `2...` will not always be +equivalent to the pattern `x if x >= 2`. + # Drawbacks Since more match arms can be proven unreachable, some code that currently