diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs index 8ceff303774b5..8b5b41fc777ea 100644 --- a/src/librustc/mir/mod.rs +++ b/src/librustc/mir/mod.rs @@ -2228,7 +2228,7 @@ pub fn fmt_const_val(fmt: &mut W, const_val: &ty::Const) -> fmt::Resul } } -pub fn print_miri_value(value: Value, ty: Ty, f: &mut W) -> fmt::Result { +pub fn print_miri_value<'tcx, W: Write>(value: Value, ty: Ty<'tcx>, f: &mut W) -> fmt::Result { use ty::TypeVariants::*; // print some primitives if let Value::Scalar(ScalarMaybeUndef::Scalar(Scalar::Bits { bits, .. })) = value { diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index 5d45955771185..81f7d439feb25 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -8,6 +8,164 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +/// This file includes the logic for exhaustiveness and usefulness checking for +/// pattern-matching. Specifically, given a list of patterns for a type, we can +/// tell whether: +/// (a) the patterns cover every possible constructor for the type [exhaustiveness] +/// (b) each pattern is necessary [usefulness] +/// +/// The algorithm implemented here is a modified version of the one described in: +/// http://moscova.inria.fr/~maranget/papers/warn/index.html +/// However, to save future implementors from reading the original paper, I'm going +/// to summarise the algorithm here to hopefully save time and be a little clearer +/// (without being so rigorous). +/// +/// The core of the algorithm revolves about a "usefulness" check. In particular, we +/// are trying to compute a predicate `U(P, p_{m + 1})` where `P` is a list of patterns +/// of length `m` for a compound (product) type with `n` components (we refer to this as +/// a matrix). `U(P, p_{m + 1})` represents whether, given an existing list of patterns +/// `p_1 ..= p_m`, adding a new pattern will be "useful" (that is, cover previously- +/// uncovered values of the type). +/// +/// If we have this predicate, then we can easily compute both exhaustiveness of an +/// entire set of patterns and the individual usefulness of each one. +/// (a) the set of patterns is exhaustive iff `U(P, _)` is false (i.e. adding a wildcard +/// match doesn't increase the number of values we're matching) +/// (b) a pattern `p_i` is not useful if `U(P[0..=(i-1), p_i)` is false (i.e. adding a +/// pattern to those that have come before it doesn't increase the number of values +/// we're matching). +/// +/// For example, say we have the following: +/// ``` +/// // x: (Option, Result<()>) +/// match x { +/// (Some(true), _) => {} +/// (None, Err(())) => {} +/// (None, Err(_)) => {} +/// } +/// ``` +/// Here, the matrix `P` is 3 x 2 (rows x columns). +/// [ +/// [Some(true), _], +/// [None, Err(())], +/// [None, Err(_)], +/// ] +/// We can tell it's not exhaustive, because `U(P, _)` is true (we're not covering +/// `[Some(false), _]`, for instance). In addition, row 3 is not useful, because +/// all the values it covers are already covered by row 2. +/// +/// To compute `U`, we must have two other concepts. +/// 1. `S(c, P)` is a "specialised matrix", where `c` is a constructor (like `Some` or +/// `None`). You can think of it as filtering `P` to just the rows whose *first* pattern +/// can cover `c` (and expanding OR-patterns into distinct patterns), and then expanding +/// the constructor into all of its components. +/// The specialisation of a row vector is computed by `specialize`. +/// +/// It is computed as follows. For each row `p_i` of P, we have four cases: +/// 1.1. `p_(i,1) = c(r_1, .., r_a)`. Then `S(c, P)` has a corresponding row: +/// r_1, .., r_a, p_(i,2), .., p_(i,n) +/// 1.2. `p_(i,1) = c'(r_1, .., r_a')` where `c ≠ c'`. Then `S(c, P)` has no +/// corresponding row. +/// 1.3. `p_(i,1) = _`. Then `S(c, P)` has a corresponding row: +/// _, .., _, p_(i,2), .., p_(i,n) +/// 1.4. `p_(i,1) = r_1 | r_2`. Then `S(c, P)` has corresponding rows inlined from: +/// S(c, (r_1, p_(i,2), .., p_(i,n))) +/// S(c, (r_2, p_(i,2), .., p_(i,n))) +/// +/// 2. `D(P)` is a "default matrix". This is used when we know there are missing +/// constructor cases, but there might be existing wildcard patterns, so to check the +/// usefulness of the matrix, we have to check all its *other* components. +/// The default matrix is computed inline in `is_useful`. +/// +/// It is computed as follows. For each row `p_i` of P, we have three cases: +/// 1.1. `p_(i,1) = c(r_1, .., r_a)`. Then `D(P)` has no corresponding row. +/// 1.2. `p_(i,1) = _`. Then `D(P)` has a corresponding row: +/// p_(i,2), .., p_(i,n) +/// 1.3. `p_(i,1) = r_1 | r_2`. Then `D(P)` has corresponding rows inlined from: +/// D((r_1, p_(i,2), .., p_(i,n))) +/// D((r_2, p_(i,2), .., p_(i,n))) +/// +/// Note that the OR-patterns are not always used directly in Rust, but are used to derive +/// the exhaustive integer matching rules, so they're written here for posterity. +/// +/// The algorithm for computing `U` +/// ------------------------------- +/// The algorithm is inductive (on the number of columns: i.e. components of tuple patterns). +/// That means we're going to check the components from left-to-right, so the algorithm +/// operates principally on the first component of the matrix and new pattern `p_{m + 1}`. +/// This algorithm is realised in the `is_useful` function. +/// +/// Base case. (`n = 0`, i.e. an empty tuple pattern) +/// - If `P` already contains an empty pattern (i.e. if the number of patterns `m > 0`), +/// then `U(P, p_{m + 1})` is false. +/// - Otherwise, `P` must be empty, so `U(P, p_{m + 1})` is true. +/// +/// Inductive step. (`n > 0`, i.e. whether there's at least one column +/// [which may then be expanded into further columns later]) +/// We're going to match on the new pattern, `p_{m + 1}`. +/// - If `p_{m + 1} == c(r_1, .., r_a)`, then we have a constructor pattern. +/// Thus, the usefulness of `p_{m + 1}` can be reduced to whether it is useful when +/// we ignore all the patterns in `P` that involve other constructors. This is where +/// `S(c, P)` comes in: +/// `U(P, p_{m + 1}) := U(S(c, P), S(c, p_{m + 1}))` +/// This special case is handled in `is_useful_specialized`. +/// - If `p_{m + 1} == _`, then we have two more cases: +/// + All the constructors of the first component of the type exist within +/// all the rows (after having expanded OR-patterns). In this case: +/// `U(P, p_{m + 1}) := ∨(k ϵ constructors) U(S(k, P), S(k, p_{m + 1}))` +/// I.e. the pattern `p_{m + 1}` is only useful when all the constructors are +/// present *if* its later components are useful for the respective constructors +/// covered by `p_{m + 1}` (usually a single constructor, but all in the case of `_`). +/// + Some constructors are not present in the existing rows (after having expanded +/// OR-patterns). However, there might be wildcard patterns (`_`) present. Thus, we +/// are only really concerned with the other patterns leading with wildcards. This is +/// where `D` comes in: +/// `U(P, p_{m + 1}) := U(D(P), p_({m + 1},2), .., p_({m + 1},n))` +/// - If `p_{m + 1} == r_1 | r_2`, then the usefulness depends on each separately: +/// `U(P, p_{m + 1}) := U(P, (r_1, p_({m + 1},2), .., p_({m + 1},n))) +/// || U(P, (r_2, p_({m + 1},2), .., p_({m + 1},n)))` +/// +/// Modifications to the algorithm +/// ------------------------------ +/// The algorithm in the paper doesn't cover some of the special cases that arise in Rust, for +/// example uninhabited types and variable-length slice patterns. These are drawn attention to +/// throughout the code below. I'll make a quick note here about how exhaustive integer matching +/// is accounted for, though. +/// +/// Exhaustive integer matching +/// --------------------------- +/// An integer type can be thought of as a (huge) sum type: 1 | 2 | 3 | ... +/// So to support exhaustive integer matching, we can make use of the logic in the paper for +/// OR-patterns. However, we obviously can't just treat ranges x..=y as individual sums, because +/// they are likely gigantic. So we instead treat ranges as constructors of the integers. This means +/// that we have a constructor *of* constructors (the integers themselves). We then need to work +/// through all the inductive step rules above, deriving how the ranges would be treated as +/// OR-patterns, and making sure that they're treated in the same way even when they're ranges. +/// There are really only four special cases here: +/// - When we match on a constructor that's actually a range, we have to treat it as if we would +/// an OR-pattern. +/// + It turns out that we can simply extend the case for single-value patterns in +/// `specialize` to either be *equal* to a value constructor, or *contained within* a range +/// constructor. +/// + When the pattern itself is a range, you just want to tell whether any of the values in +/// the pattern range coincide with values in the constructor range, which is precisely +/// intersection. +/// Since when encountering a range pattern for a value constructor, we also use inclusion, it +/// means that whenever the constructor is a value/range and the pattern is also a value/range, +/// we can simply use intersection to test usefulness. +/// - When we're testing for usefulness of a pattern and the pattern's first component is a +/// wildcard. +/// + If all the constructors appear in the matrix, we have a slight complication. By default, +/// the behaviour (i.e. a disjunction over specialised matrices for each constructor) is +/// invalid, because we want a disjunction over every *integer* in each range, not just a +/// disjunction over every range. This is a bit more tricky to deal with: essentially we need +/// to form equivalence classes of subranges of the constructor range for which the behaviour +/// of the matrix `P` and new pattern `p_{m + 1}` are the same. This is described in more +/// detail in `split_grouped_constructors`. +/// + If some constructors are missing from the matrix, it turns out we don't need to do +/// anything special (because we know none of the integers are actually wildcards: i.e. we +/// can't span wildcards using ranges). + use self::Constructor::*; use self::Usefulness::*; use self::WitnessPreference::*; @@ -21,18 +179,22 @@ use super::{PatternFoldable, PatternFolder, compare_const_vals}; use rustc::hir::def_id::DefId; use rustc::hir::RangeEnd; use rustc::ty::{self, Ty, TyCtxt, TypeFoldable}; +use rustc::ty::layout::{Integer, IntegerExt}; use rustc::mir::Field; use rustc::mir::interpret::ConstValue; use rustc::util::common::ErrorReported; +use syntax::attr::{SignedInt, UnsignedInt}; use syntax_pos::{Span, DUMMY_SP}; use arena::TypedArena; -use std::cmp::{self, Ordering}; +use std::cmp::{self, Ordering, min, max}; use std::fmt; use std::iter::{FromIterator, IntoIterator}; +use std::ops::RangeInclusive; +use std::u128; pub fn expand_pattern<'a, 'tcx>(cx: &MatchCheckCtxt<'a, 'tcx>, pat: Pattern<'tcx>) -> &'a Pattern<'tcx> @@ -138,7 +300,6 @@ impl<'a, 'tcx> FromIterator>> for Matrix<'a, 'tcx> { } } -//NOTE: appears to be the only place other then InferCtxt to contain a ParamEnv pub struct MatchCheckCtxt<'a, 'tcx: 'a> { pub tcx: TyCtxt<'a, 'tcx, 'tcx>, /// The module in which the match occurs. This is necessary for @@ -165,7 +326,7 @@ impl<'a, 'tcx> MatchCheckCtxt<'a, 'tcx> { tcx, module, pattern_arena: &pattern_arena, - byte_array_map: FxHashMap(), + byte_array_map: FxHashMap::default(), }) } @@ -273,7 +434,7 @@ impl<'tcx> Constructor<'tcx> { } } -#[derive(Clone)] +#[derive(Clone, Debug)] pub enum Usefulness<'tcx> { Useful, UsefulWithWitness(Vec>), @@ -289,7 +450,7 @@ impl<'tcx> Usefulness<'tcx> { } } -#[derive(Copy, Clone)] +#[derive(Copy, Clone, Debug)] pub enum WitnessPreference { ConstructWitness, LeaveOutWitness @@ -301,8 +462,39 @@ struct PatternContext<'tcx> { max_slice_length: u64, } -/// A stack of patterns in reverse order of construction -#[derive(Clone)] +/// A witness of non-exhaustiveness for error reporting, represented +/// as a list of patterns (in reverse order of construction) with +/// wildcards inside to represent elements that can take any inhabitant +/// of the type as a value. +/// +/// A witness against a list of patterns should have the same types +/// and length as the pattern matched against. Because Rust `match` +/// is always against a single pattern, at the end the witness will +/// have length 1, but in the middle of the algorithm, it can contain +/// multiple patterns. +/// +/// For example, if we are constructing a witness for the match against +/// ``` +/// struct Pair(Option<(u32, u32)>, bool); +/// +/// match (p: Pair) { +/// Pair(None, _) => {} +/// Pair(_, false) => {} +/// } +/// ``` +/// +/// We'll perform the following steps: +/// 1. Start with an empty witness +/// `Witness(vec![])` +/// 2. Push a witness `Some(_)` against the `None` +/// `Witness(vec![Some(_)])` +/// 3. Push a witness `true` against the `false` +/// `Witness(vec![Some(_), true])` +/// 4. Apply the `Pair` constructor to the witnesses +/// `Witness(vec![Pair(Some(_), true)])` +/// +/// The final `Pair(Some(_), true)` is then the resulting witness. +#[derive(Clone, Debug)] pub struct Witness<'tcx>(Vec>); impl<'tcx> Witness<'tcx> { @@ -353,7 +545,7 @@ impl<'tcx> Witness<'tcx> { let arity = constructor_arity(cx, ctor, ty); let pat = { let len = self.0.len() as u64; - let mut pats = self.0.drain((len-arity) as usize..).rev(); + let mut pats = self.0.drain((len - arity) as usize..).rev(); match ty.sty { ty::TyAdt(..) | @@ -396,6 +588,7 @@ impl<'tcx> Witness<'tcx> { _ => { match *ctor { ConstantValue(value) => PatternKind::Constant { value }, + ConstantRange(lo, hi, end) => PatternKind::Range { lo, hi, end }, _ => PatternKind::Wild, } } @@ -417,10 +610,6 @@ impl<'tcx> Witness<'tcx> { /// but is instead bounded by the maximum fixed length of slice patterns in /// the column of patterns being analyzed. /// -/// This intentionally does not list ConstantValue specializations for -/// non-booleans, because we currently assume that there is always a -/// "non-standard constant" that matches. See issue #12483. -/// /// We make sure to omit constructors that are statically impossible. eg for /// Option we do not include Some(_) in the returned list of constructors. fn all_constructors<'a, 'tcx: 'a>(cx: &mut MatchCheckCtxt<'a, 'tcx>, @@ -428,7 +617,8 @@ fn all_constructors<'a, 'tcx: 'a>(cx: &mut MatchCheckCtxt<'a, 'tcx>, -> Vec> { debug!("all_constructors({:?})", pcx.ty); - match pcx.ty.sty { + let exhaustive_integer_patterns = cx.tcx.features().exhaustive_integer_patterns; + let ctors = match pcx.ty.sty { ty::TyBool => { [true, false].iter().map(|&b| { ConstantValue(ty::Const::from_bool(cx.tcx, b)) @@ -457,6 +647,36 @@ fn all_constructors<'a, 'tcx: 'a>(cx: &mut MatchCheckCtxt<'a, 'tcx>, .map(|v| Variant(v.did)) .collect() } + ty::TyChar if exhaustive_integer_patterns => { + let endpoint = |c: char| { + let ty = ty::ParamEnv::empty().and(cx.tcx.types.char); + ty::Const::from_bits(cx.tcx, c as u128, ty) + }; + vec![ + // The valid Unicode Scalar Value ranges. + ConstantRange(endpoint('\u{0000}'), endpoint('\u{D7FF}'), RangeEnd::Included), + ConstantRange(endpoint('\u{E000}'), endpoint('\u{10FFFF}'), RangeEnd::Included), + ] + } + ty::TyInt(ity) if exhaustive_integer_patterns => { + // FIXME(49937): refactor these bit manipulations into interpret. + let bits = Integer::from_attr(cx.tcx, SignedInt(ity)).size().bits() as u128; + let min = 1u128 << (bits - 1); + let max = (1u128 << (bits - 1)) - 1; + let ty = ty::ParamEnv::empty().and(pcx.ty); + vec![ConstantRange(ty::Const::from_bits(cx.tcx, min as u128, ty), + ty::Const::from_bits(cx.tcx, max as u128, ty), + RangeEnd::Included)] + } + ty::TyUint(uty) if exhaustive_integer_patterns => { + // FIXME(49937): refactor these bit manipulations into interpret. + let bits = Integer::from_attr(cx.tcx, UnsignedInt(uty)).size().bits() as u128; + let max = !0u128 >> (128 - bits); + let ty = ty::ParamEnv::empty().and(pcx.ty); + vec![ConstantRange(ty::Const::from_bits(cx.tcx, 0, ty), + ty::Const::from_bits(cx.tcx, max, ty), + RangeEnd::Included)] + } _ => { if cx.is_uninhabited(pcx.ty) { vec![] @@ -464,7 +684,8 @@ fn all_constructors<'a, 'tcx: 'a>(cx: &mut MatchCheckCtxt<'a, 'tcx>, vec![Single] } } - } + }; + ctors } fn max_slice_length<'p, 'a: 'p, 'tcx: 'a, I>( @@ -497,7 +718,7 @@ fn max_slice_length<'p, 'a: 'p, 'tcx: 'a, I>( // `[true, ..]` // `[.., false]` // Then any slice of length ≥1 that matches one of these two - // patterns can be be trivially turned to a slice of any + // patterns can be trivially turned to a slice of any // other length ≥1 that matches them and vice-versa - for // but the slice from length 2 `[false, true]` that matches neither // of these patterns can't be turned to a slice from length 1 that @@ -569,6 +790,189 @@ fn max_slice_length<'p, 'a: 'p, 'tcx: 'a, I>( cmp::max(max_fixed_len + 1, max_prefix_len + max_suffix_len) } +/// An inclusive interval, used for precise integer exhaustiveness checking. +/// `IntRange`s always store a contiguous range. This means that values are +/// encoded such that `0` encodes the minimum value for the integer, +/// regardless of the signedness. +/// For example, the pattern `-128...127i8` is encoded as `0..=255`. +/// This makes comparisons and arithmetic on interval endpoints much more +/// straightforward. See `signed_bias` for details. +/// +/// `IntRange` is never used to encode an empty range or a "range" that wraps +/// around the (offset) space: i.e. `range.lo <= range.hi`. +#[derive(Clone)] +struct IntRange<'tcx> { + pub range: RangeInclusive, + pub ty: Ty<'tcx>, +} + +impl<'tcx> IntRange<'tcx> { + fn from_ctor(tcx: TyCtxt<'_, 'tcx, 'tcx>, + ctor: &Constructor<'tcx>) + -> Option> { + match ctor { + ConstantRange(lo, hi, end) => { + assert_eq!(lo.ty, hi.ty); + let ty = lo.ty; + let env_ty = ty::ParamEnv::empty().and(ty); + if let Some(lo) = lo.assert_bits(tcx, env_ty) { + if let Some(hi) = hi.assert_bits(tcx, env_ty) { + // Perform a shift if the underlying types are signed, + // which makes the interval arithmetic simpler. + let bias = IntRange::signed_bias(tcx, ty); + let (lo, hi) = (lo ^ bias, hi ^ bias); + // Make sure the interval is well-formed. + return if lo > hi || lo == hi && *end == RangeEnd::Excluded { + None + } else { + let offset = (*end == RangeEnd::Excluded) as u128; + Some(IntRange { range: lo..=(hi - offset), ty }) + }; + } + } + None + } + ConstantValue(val) => { + let ty = val.ty; + if let Some(val) = val.assert_bits(tcx, ty::ParamEnv::empty().and(ty)) { + let bias = IntRange::signed_bias(tcx, ty); + let val = val ^ bias; + Some(IntRange { range: val..=val, ty }) + } else { + None + } + } + Single | Variant(_) | Slice(_) => { + None + } + } + } + + fn from_pat(tcx: TyCtxt<'_, 'tcx, 'tcx>, + pat: &Pattern<'tcx>) + -> Option> { + Self::from_ctor(tcx, &match pat.kind { + box PatternKind::Constant { value } => ConstantValue(value), + box PatternKind::Range { lo, hi, end } => ConstantRange(lo, hi, end), + _ => return None, + }) + } + + // The return value of `signed_bias` should be XORed with an endpoint to encode/decode it. + fn signed_bias(tcx: TyCtxt<'_, 'tcx, 'tcx>, ty: Ty<'tcx>) -> u128 { + match ty.sty { + ty::TyInt(ity) => { + let bits = Integer::from_attr(tcx, SignedInt(ity)).size().bits() as u128; + 1u128 << (bits - 1) + } + _ => 0 + } + } + + /// Convert a `RangeInclusive` to a `ConstantValue` or inclusive `ConstantRange`. + fn range_to_ctor( + tcx: TyCtxt<'_, 'tcx, 'tcx>, + ty: Ty<'tcx>, + r: RangeInclusive, + ) -> Constructor<'tcx> { + let bias = IntRange::signed_bias(tcx, ty); + let ty = ty::ParamEnv::empty().and(ty); + let (lo, hi) = r.into_inner(); + if lo == hi { + ConstantValue(ty::Const::from_bits(tcx, lo ^ bias, ty)) + } else { + ConstantRange(ty::Const::from_bits(tcx, lo ^ bias, ty), + ty::Const::from_bits(tcx, hi ^ bias, ty), + RangeEnd::Included) + } + } + + /// Return a collection of ranges that spans the values covered by `ranges`, subtracted + /// by the values covered by `self`: i.e. `ranges \ self` (in set notation). + fn subtract_from(self, + tcx: TyCtxt<'_, 'tcx, 'tcx>, + ranges: Vec>) + -> Vec> { + let ranges = ranges.into_iter().filter_map(|r| { + IntRange::from_ctor(tcx, &r).map(|i| i.range) + }); + let mut remaining_ranges = vec![]; + let ty = self.ty; + let (lo, hi) = self.range.into_inner(); + for subrange in ranges { + let (subrange_lo, subrange_hi) = subrange.into_inner(); + if lo > subrange_hi || subrange_lo > hi { + // The pattern doesn't intersect with the subrange at all, + // so the subrange remains untouched. + remaining_ranges.push(Self::range_to_ctor(tcx, ty, subrange_lo..=subrange_hi)); + } else { + if lo > subrange_lo { + // The pattern intersects an upper section of the + // subrange, so a lower section will remain. + remaining_ranges.push(Self::range_to_ctor(tcx, ty, subrange_lo..=(lo - 1))); + } + if hi < subrange_hi { + // The pattern intersects a lower section of the + // subrange, so an upper section will remain. + remaining_ranges.push(Self::range_to_ctor(tcx, ty, (hi + 1)..=subrange_hi)); + } + } + } + remaining_ranges + } + + fn intersection(&self, other: &Self) -> Option { + let ty = self.ty; + let (lo, hi) = (*self.range.start(), *self.range.end()); + let (other_lo, other_hi) = (*other.range.start(), *other.range.end()); + if lo <= other_hi && other_lo <= hi { + Some(IntRange { range: max(lo, other_lo)..=min(hi, other_hi), ty }) + } else { + None + } + } +} + +// Return a set of constructors equivalent to `all_ctors \ used_ctors`. +fn compute_missing_ctors<'a, 'tcx: 'a>( + tcx: TyCtxt<'a, 'tcx, 'tcx>, + all_ctors: &Vec>, + used_ctors: &Vec>, +) -> Vec> { + let mut missing_ctors = vec![]; + + for req_ctor in all_ctors { + let mut refined_ctors = vec![req_ctor.clone()]; + for used_ctor in used_ctors { + if used_ctor == req_ctor { + // If a constructor appears in a `match` arm, we can + // eliminate it straight away. + refined_ctors = vec![] + } else if tcx.features().exhaustive_integer_patterns { + if let Some(interval) = IntRange::from_ctor(tcx, used_ctor) { + // Refine the required constructors for the type by subtracting + // the range defined by the current constructor pattern. + refined_ctors = interval.subtract_from(tcx, refined_ctors); + } + } + + // If the constructor patterns that have been considered so far + // already cover the entire range of values, then we the + // constructor is not missing, and we can move on to the next one. + if refined_ctors.is_empty() { + break; + } + } + // If a constructor has not been matched, then it is missing. + // We add `refined_ctors` instead of `req_ctor`, because then we can + // provide more detailed error information about precisely which + // ranges have been omitted. + missing_ctors.extend(refined_ctors); + } + + missing_ctors +} + /// Algorithm from http://moscova.inria.fr/~maranget/papers/warn/index.html /// The algorithm from the paper has been modified to correctly handle empty /// types. The changes are: @@ -637,8 +1041,7 @@ pub fn is_useful<'p, 'a: 'p, 'tcx: 'a>(cx: &mut MatchCheckCtxt<'a, 'tcx>, // FIXME: this might lead to "unstable" behavior with macro hygiene // introducing uninhabited patterns for inaccessible fields. We // need to figure out how to model that. - ty: rows.iter().map(|r| r[0].ty).find(|ty| !ty.references_error()) - .unwrap_or(v[0].ty), + ty: rows.iter().map(|r| r[0].ty).find(|ty| !ty.references_error()).unwrap_or(v[0].ty), max_slice_length: max_slice_length(cx, rows.iter().map(|r| r[0]).chain(Some(v[0]))) }; @@ -646,7 +1049,7 @@ pub fn is_useful<'p, 'a: 'p, 'tcx: 'a>(cx: &mut MatchCheckCtxt<'a, 'tcx>, if let Some(constructors) = pat_constructors(cx, v[0], pcx) { debug!("is_useful - expanding constructors: {:#?}", constructors); - constructors.into_iter().map(|c| + split_grouped_constructors(cx.tcx, constructors, matrix, pcx.ty).into_iter().map(|c| is_useful_specialized(cx, matrix, v, c.clone(), pcx.ty, witness) ).find(|result| result.is_useful()).unwrap_or(NotUseful) } else { @@ -656,11 +1059,10 @@ pub fn is_useful<'p, 'a: 'p, 'tcx: 'a>(cx: &mut MatchCheckCtxt<'a, 'tcx>, pat_constructors(cx, row[0], pcx).unwrap_or(vec![]) }).collect(); debug!("used_ctors = {:#?}", used_ctors); + // `all_ctors` are all the constructors for the given type, which + // should all be represented (or caught with the wild pattern `_`). let all_ctors = all_constructors(cx, pcx); debug!("all_ctors = {:#?}", all_ctors); - let missing_ctors: Vec = all_ctors.iter().filter(|c| { - !used_ctors.contains(*c) - }).cloned().collect(); // `missing_ctors` is the set of constructors from the same type as the // first column of `matrix` that are matched only by wildcard patterns @@ -681,10 +1083,12 @@ pub fn is_useful<'p, 'a: 'p, 'tcx: 'a>(cx: &mut MatchCheckCtxt<'a, 'tcx>, // feature flag is not present, so this is only // needed for that case. - let is_privately_empty = - all_ctors.is_empty() && !cx.is_uninhabited(pcx.ty); - let is_declared_nonexhaustive = - cx.is_non_exhaustive_enum(pcx.ty) && !cx.is_local(pcx.ty); + // Find those constructors that are not matched by any non-wildcard patterns in the + // current column. + let missing_ctors = compute_missing_ctors(cx.tcx, &all_ctors, &used_ctors); + + let is_privately_empty = all_ctors.is_empty() && !cx.is_uninhabited(pcx.ty); + let is_declared_nonexhaustive = cx.is_non_exhaustive_enum(pcx.ty) && !cx.is_local(pcx.ty); debug!("missing_ctors={:#?} is_privately_empty={:#?} is_declared_nonexhaustive={:#?}", missing_ctors, is_privately_empty, is_declared_nonexhaustive); @@ -693,7 +1097,7 @@ pub fn is_useful<'p, 'a: 'p, 'tcx: 'a>(cx: &mut MatchCheckCtxt<'a, 'tcx>, let is_non_exhaustive = is_privately_empty || is_declared_nonexhaustive; if missing_ctors.is_empty() && !is_non_exhaustive { - all_ctors.into_iter().map(|c| { + split_grouped_constructors(cx.tcx, all_ctors, matrix, pcx.ty).into_iter().map(|c| { is_useful_specialized(cx, matrix, v, c.clone(), pcx.ty, witness) }).find(|result| result.is_useful()).unwrap_or(NotUseful) } else { @@ -753,7 +1157,7 @@ pub fn is_useful<'p, 'a: 'p, 'tcx: 'a>(cx: &mut MatchCheckCtxt<'a, 'tcx>, // `used_ctors` is empty. let new_witnesses = if is_non_exhaustive || used_ctors.is_empty() { // All constructors are unused. Add wild patterns - // rather than each individual constructor + // rather than each individual constructor. pats.into_iter().map(|mut witness| { witness.0.push(Pattern { ty: pcx.ty, @@ -765,6 +1169,10 @@ pub fn is_useful<'p, 'a: 'p, 'tcx: 'a>(cx: &mut MatchCheckCtxt<'a, 'tcx>, } else { pats.into_iter().flat_map(|witness| { missing_ctors.iter().map(move |ctor| { + // Extends the witness with a "wild" version of this + // constructor, that matches everything that can be built with + // it. For example, if `ctor` is a `Constructor::Variant` for + // `Option::Some`, this pushes the witness for `Some(_)`. witness.clone().push_wild_constructor(cx, ctor, pcx.ty) }) }).collect() @@ -777,14 +1185,16 @@ pub fn is_useful<'p, 'a: 'p, 'tcx: 'a>(cx: &mut MatchCheckCtxt<'a, 'tcx>, } } +/// A shorthand for the `U(S(c, P), S(c, q))` operation from the paper. I.e. `is_useful` applied +/// to the specialised version of both the pattern matrix `P` and the new pattern `q`. fn is_useful_specialized<'p, 'a:'p, 'tcx: 'a>( cx: &mut MatchCheckCtxt<'a, 'tcx>, &Matrix(ref m): &Matrix<'p, 'tcx>, v: &[&'p Pattern<'tcx>], ctor: Constructor<'tcx>, lty: Ty<'tcx>, - witness: WitnessPreference) -> Usefulness<'tcx> -{ + witness: WitnessPreference, +) -> Usefulness<'tcx> { debug!("is_useful_specialized({:#?}, {:#?}, {:?})", v, ctor, lty); let sub_pat_tys = constructor_sub_pattern_tys(cx, &ctor, lty); let wild_patterns_owned: Vec<_> = sub_pat_tys.iter().map(|ty| { @@ -806,7 +1216,7 @@ fn is_useful_specialized<'p, 'a:'p, 'tcx: 'a>( .collect() ), result => result - }, + } None => NotUseful } } @@ -818,23 +1228,20 @@ fn is_useful_specialized<'p, 'a:'p, 'tcx: 'a>( /// Slice patterns, however, can match slices of different lengths. For instance, /// `[a, b, ..tail]` can match a slice of length 2, 3, 4 and so on. /// -/// Returns None in case of a catch-all, which can't be specialized. +/// Returns `None` in case of a catch-all, which can't be specialized. fn pat_constructors<'tcx>(cx: &mut MatchCheckCtxt, pat: &Pattern<'tcx>, pcx: PatternContext) -> Option>> { match *pat.kind { - PatternKind::Binding { .. } | PatternKind::Wild => - None, - PatternKind::Leaf { .. } | PatternKind::Deref { .. } => - Some(vec![Single]), - PatternKind::Variant { adt_def, variant_index, .. } => - Some(vec![Variant(adt_def.variants[variant_index].did)]), - PatternKind::Constant { value } => - Some(vec![ConstantValue(value)]), - PatternKind::Range { lo, hi, end } => - Some(vec![ConstantRange(lo, hi, end)]), + PatternKind::Binding { .. } | PatternKind::Wild => None, + PatternKind::Leaf { .. } | PatternKind::Deref { .. } => Some(vec![Single]), + PatternKind::Variant { adt_def, variant_index, .. } => { + Some(vec![Variant(adt_def.variants[variant_index].did)]) + } + PatternKind::Constant { value } => Some(vec![ConstantValue(value)]), + PatternKind::Range { lo, hi, end } => Some(vec![ConstantRange(lo, hi, end)]), PatternKind::Array { .. } => match pcx.ty.sty { ty::TyArray(_, length) => Some(vec![ Slice(length.unwrap_usize(cx.tcx)) @@ -970,13 +1377,167 @@ fn slice_pat_covered_by_constructor<'tcx>( Ok(true) } +// Whether to evaluate a constructor using exhaustive integer matching. This is true if the +// constructor is a range or constant with an integer type. +fn should_treat_range_exhaustively(tcx: TyCtxt<'_, 'tcx, 'tcx>, ctor: &Constructor<'tcx>) -> bool { + if tcx.features().exhaustive_integer_patterns { + if let ConstantValue(value) | ConstantRange(value, _, _) = ctor { + if let ty::TyChar | ty::TyInt(_) | ty::TyUint(_) = value.ty.sty { + return true; + } + } + } + false +} + +/// For exhaustive integer matching, some constructors are grouped within other constructors +/// (namely integer typed values are grouped within ranges). However, when specialising these +/// constructors, we want to be specialising for the underlying constructors (the integers), not +/// the groups (the ranges). Thus we need to split the groups up. Splitting them up naïvely would +/// mean creating a separate constructor for every single value in the range, which is clearly +/// impractical. However, observe that for some ranges of integers, the specialisation will be +/// identical across all values in that range (i.e. there are equivalence classes of ranges of +/// constructors based on their `is_useful_specialised` outcome). These classes are grouped by +/// the patterns that apply to them (in the matrix `P`). We can split the range whenever the +/// patterns that apply to that range (specifically: the patterns that *intersect* with that range) +/// change. +/// Our solution, therefore, is to split the range constructor into subranges at every single point +/// the group of intersecting patterns changes (using the method described below). +/// And voilà! We're testing precisely those ranges that we need to, without any exhaustive matching +/// on actual integers. The nice thing about this is that the number of subranges is linear in the +/// number of rows in the matrix (i.e. the number of cases in the `match` statement), so we don't +/// need to be worried about matching over gargantuan ranges. +/// +/// Essentially, given the first column of a matrix representing ranges, looking like the following: +/// +/// |------| |----------| |-------| || +/// |-------| |-------| |----| || +/// |---------| +/// +/// We split the ranges up into equivalence classes so the ranges are no longer overlapping: +/// +/// |--|--|||-||||--||---|||-------| |-|||| || +/// +/// The logic for determining how to split the ranges is fairly straightforward: we calculate +/// boundaries for each interval range, sort them, then create constructors for each new interval +/// between every pair of boundary points. (This essentially sums up to performing the intuitive +/// merging operation depicted above.) +fn split_grouped_constructors<'p, 'a: 'p, 'tcx: 'a>( + tcx: TyCtxt<'a, 'tcx, 'tcx>, + ctors: Vec>, + &Matrix(ref m): &Matrix<'p, 'tcx>, + ty: Ty<'tcx>, +) -> Vec> { + let mut split_ctors = Vec::with_capacity(ctors.len()); + + for ctor in ctors.into_iter() { + match ctor { + // For now, only ranges may denote groups of "subconstructors", so we only need to + // special-case constant ranges. + ConstantRange(..) if should_treat_range_exhaustively(tcx, &ctor) => { + // We only care about finding all the subranges within the range of the constructor + // range. Anything else is irrelevant, because it is guaranteed to result in + // `NotUseful`, which is the default case anyway, and can be ignored. + let ctor_range = IntRange::from_ctor(tcx, &ctor).unwrap(); + + /// Represents a border between 2 integers. Because the intervals spanning borders + /// must be able to cover every integer, we need to be able to represent + /// 2^128 + 1 such borders. + #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] + enum Border { + JustBefore(u128), + AfterMax, + } + + // A function for extracting the borders of an integer interval. + fn range_borders(r: IntRange<'_>) -> impl Iterator { + let (lo, hi) = r.range.into_inner(); + let from = Border::JustBefore(lo); + let to = match hi.checked_add(1) { + Some(m) => Border::JustBefore(m), + None => Border::AfterMax, + }; + vec![from, to].into_iter() + } + + // `borders` is the set of borders between equivalence classes: each equivalence + // class lies between 2 borders. + let row_borders = m.iter() + .flat_map(|row| IntRange::from_pat(tcx, row[0])) + .flat_map(|range| ctor_range.intersection(&range)) + .flat_map(|range| range_borders(range)); + let ctor_borders = range_borders(ctor_range.clone()); + let mut borders: Vec<_> = row_borders.chain(ctor_borders).collect(); + borders.sort_unstable(); + + // We're going to iterate through every pair of borders, making sure that each + // represents an interval of nonnegative length, and convert each such interval + // into a constructor. + for IntRange { range, .. } in borders.windows(2).filter_map(|window| { + match (window[0], window[1]) { + (Border::JustBefore(n), Border::JustBefore(m)) => { + if n < m { + Some(IntRange { range: n..=(m - 1), ty }) + } else { + None + } + } + (Border::JustBefore(n), Border::AfterMax) => { + Some(IntRange { range: n..=u128::MAX, ty }) + } + (Border::AfterMax, _) => None, + } + }) { + split_ctors.push(IntRange::range_to_ctor(tcx, ty, range)); + } + } + // Any other constructor can be used unchanged. + _ => split_ctors.push(ctor), + } + } + + split_ctors +} + +/// Check whether there exists any shared value in either `ctor` or `pat` by intersecting them. +fn constructor_intersects_pattern<'p, 'a: 'p, 'tcx: 'a>( + tcx: TyCtxt<'a, 'tcx, 'tcx>, + ctor: &Constructor<'tcx>, + pat: &'p Pattern<'tcx>, +) -> Option>> { + if should_treat_range_exhaustively(tcx, ctor) { + match (IntRange::from_ctor(tcx, ctor), IntRange::from_pat(tcx, pat)) { + (Some(ctor), Some(pat)) => { + ctor.intersection(&pat).map(|_| { + let (pat_lo, pat_hi) = pat.range.into_inner(); + let (ctor_lo, ctor_hi) = ctor.range.into_inner(); + assert!(pat_lo <= ctor_lo && ctor_hi <= pat_hi); + vec![] + }) + } + _ => None, + } + } else { + // Fallback for non-ranges and ranges that involve floating-point numbers, which are not + // conveniently handled by `IntRange`. For these cases, the constructor may not be a range + // so intersection actually devolves into being covered by the pattern. + match constructor_covered_by_range(tcx, ctor, pat) { + Ok(true) => Some(vec![]), + Ok(false) | Err(ErrorReported) => None, + } + } +} + fn constructor_covered_by_range<'a, 'tcx>( tcx: TyCtxt<'a, 'tcx, 'tcx>, ctor: &Constructor<'tcx>, - from: &'tcx ty::Const<'tcx>, to: &'tcx ty::Const<'tcx>, - end: RangeEnd, - ty: Ty<'tcx>, + pat: &Pattern<'tcx>, ) -> Result { + let (from, to, end, ty) = match pat.kind { + box PatternKind::Constant { value } => (value, value, RangeEnd::Included, value.ty), + box PatternKind::Range { lo, hi, end } => (lo, hi, end, lo.ty), + _ => bug!("`constructor_covered_by_range` called with {:?}", pat), + }; trace!("constructor_covered_by_range {:#?}, {:#?}, {:#?}, {}", ctor, from, to, ty); let cmp_from = |c_from| compare_const_vals(tcx, c_from, from, ty::ParamEnv::empty().and(ty)) .map(|res| res != Ordering::Less); @@ -1040,15 +1601,14 @@ fn specialize<'p, 'a: 'p, 'tcx: 'a>( cx: &mut MatchCheckCtxt<'a, 'tcx>, r: &[&'p Pattern<'tcx>], constructor: &Constructor<'tcx>, - wild_patterns: &[&'p Pattern<'tcx>]) - -> Option>> -{ + wild_patterns: &[&'p Pattern<'tcx>], +) -> Option>> { let pat = &r[0]; let head: Option> = match *pat.kind { PatternKind::Binding { .. } | PatternKind::Wild => { Some(wild_patterns.to_owned()) - }, + } PatternKind::Variant { adt_def, variant_index, ref subpatterns, .. } => { let ref variant = adt_def.variants[variant_index]; @@ -1062,6 +1622,7 @@ fn specialize<'p, 'a: 'p, 'tcx: 'a>( PatternKind::Leaf { ref subpatterns } => { Some(patterns_for_variant(subpatterns, wild_patterns)) } + PatternKind::Deref { ref subpattern } => { Some(vec![subpattern]) } @@ -1090,30 +1651,21 @@ fn specialize<'p, 'a: 'p, 'tcx: 'a>( span_bug!(pat.span, "unexpected const-val {:?} with ctor {:?}", value, constructor) } - }, + } _ => { - match constructor_covered_by_range( - cx.tcx, - constructor, value, value, RangeEnd::Included, - value.ty, - ) { - Ok(true) => Some(vec![]), - Ok(false) => None, - Err(ErrorReported) => None, - } + // If the constructor is a: + // Single value: add a row if the constructor equals the pattern. + // Range: add a row if the constructor contains the pattern. + constructor_intersects_pattern(cx.tcx, constructor, pat) } } } - PatternKind::Range { lo, hi, ref end } => { - match constructor_covered_by_range( - cx.tcx, - constructor, lo, hi, end.clone(), lo.ty, - ) { - Ok(true) => Some(vec![]), - Ok(false) => None, - Err(ErrorReported) => None, - } + PatternKind::Range { .. } => { + // If the constructor is a: + // Single value: add a row if the pattern contains the constructor. + // Range: add a row if the constructor intersects the pattern. + constructor_intersects_pattern(cx.tcx, constructor, pat) } PatternKind::Array { ref prefix, ref slice, ref suffix } | @@ -1123,14 +1675,12 @@ fn specialize<'p, 'a: 'p, 'tcx: 'a>( let pat_len = prefix.len() + suffix.len(); if let Some(slice_count) = wild_patterns.len().checked_sub(pat_len) { if slice_count == 0 || slice.is_some() { - Some( - prefix.iter().chain( - wild_patterns.iter().map(|p| *p) - .skip(prefix.len()) - .take(slice_count) - .chain( - suffix.iter() - )).collect()) + Some(prefix.iter().chain( + wild_patterns.iter().map(|p| *p) + .skip(prefix.len()) + .take(slice_count) + .chain(suffix.iter()) + ).collect()) } else { None } diff --git a/src/librustc_mir/hair/pattern/check_match.rs b/src/librustc_mir/hair/pattern/check_match.rs index 35f9dcee99f82..7f6298fd17173 100644 --- a/src/librustc_mir/hair/pattern/check_match.rs +++ b/src/librustc_mir/hair/pattern/check_match.rs @@ -272,7 +272,7 @@ impl<'a, 'tcx> MatchVisitor<'a, 'tcx> { self.tables); let pattern = patcx.lower_pattern(pat); let pattern_ty = pattern.ty; - let pats : Matrix = vec![vec![ + let pats: Matrix = vec![vec![ expand_pattern(cx, pattern) ]].into_iter().collect(); @@ -391,7 +391,7 @@ fn check_arms<'a, 'tcx>(cx: &mut MatchCheckCtxt<'a, 'tcx>, printed_if_let_err = true; } } - }, + } hir::MatchSource::WhileLetDesugar => { // check which arm we're on. diff --git a/src/librustc_mir/hair/pattern/mod.rs b/src/librustc_mir/hair/pattern/mod.rs index d614131c52683..7ec9373130419 100644 --- a/src/librustc_mir/hair/pattern/mod.rs +++ b/src/librustc_mir/hair/pattern/mod.rs @@ -233,7 +233,7 @@ impl<'tcx> fmt::Display for Pattern<'tcx> { PatternKind::Range { lo, hi, end } => { fmt_const_val(f, lo)?; match end { - RangeEnd::Included => write!(f, "...")?, + RangeEnd::Included => write!(f, "..=")?, RangeEnd::Excluded => write!(f, "..")?, } fmt_const_val(f, hi) @@ -368,9 +368,14 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> { "lower range bound must be less than upper", ); PatternKind::Wild - }, - (RangeEnd::Included, None) | - (RangeEnd::Included, Some(Ordering::Greater)) => { + } + (RangeEnd::Included, Some(Ordering::Equal)) => { + PatternKind::Constant { value: lo } + } + (RangeEnd::Included, Some(Ordering::Less)) => { + PatternKind::Range { lo, hi, end } + } + (RangeEnd::Included, _) => { let mut err = struct_span_err!( self.tcx.sess, lo_expr.span, @@ -390,8 +395,7 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> { } err.emit(); PatternKind::Wild - }, - (RangeEnd::Included, Some(_)) => PatternKind::Range { lo, hi, end }, + } } } _ => PatternKind::Wild diff --git a/src/librustc_mir/lib.rs b/src/librustc_mir/lib.rs index bda80ff562c75..d7bb666a80567 100644 --- a/src/librustc_mir/lib.rs +++ b/src/librustc_mir/lib.rs @@ -36,6 +36,7 @@ Rust MIR: a lowered representation of Rust. Also: an experiment! #![feature(unicode_internals)] #![feature(step_trait)] #![feature(slice_concat_ext)] +#![feature(if_while_or_patterns)] #![recursion_limit="256"] diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index f837bead6a085..a6908619d73d3 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -487,6 +487,9 @@ declare_features! ( // 'a: { break 'a; } (active, label_break_value, "1.28.0", Some(48594), None), + // Integer match exhaustiveness checking + (active, exhaustive_integer_patterns, "1.30.0", Some(50907), None), + // #[panic_implementation] (active, panic_implementation, "1.28.0", Some(44489), None), diff --git a/src/test/ui/exhaustive_integer_patterns.rs b/src/test/ui/exhaustive_integer_patterns.rs new file mode 100644 index 0000000000000..7825aaa291286 --- /dev/null +++ b/src/test/ui/exhaustive_integer_patterns.rs @@ -0,0 +1,173 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(exhaustive_integer_patterns)] +#![feature(exclusive_range_pattern)] +#![deny(unreachable_patterns)] + +use std::{char, usize, u8, u16, u32, u64, u128, isize, i8, i16, i32, i64, i128}; + +fn main() { + let x: u8 = 0; + + // A single range covering the entire domain. + match x { + 0 ..= 255 => {} // ok + } + + // A combination of ranges and values. + // These are currently allowed to be overlapping. + match x { + 0 ..= 32 => {} + 33 => {} + 34 .. 128 => {} + 100 ..= 200 => {} + 200 => {} //~ ERROR unreachable pattern + 201 ..= 255 => {} + } + + // An incomplete set of values. + match x { //~ ERROR non-exhaustive patterns + 0 .. 128 => {} + } + + // A more incomplete set of values. + match x { //~ ERROR non-exhaustive patterns + 0 ..= 10 => {} + 20 ..= 30 => {} + 35 => {} + 70 .. 255 => {} + } + + let x: i8 = 0; + match x { //~ ERROR non-exhaustive patterns + -7 => {} + -5..=120 => {} + -2..=20 => {} //~ ERROR unreachable pattern + 125 => {} + } + + // Let's test other types too! + let c: char = '\u{0}'; + match c { + '\u{0}' ..= char::MAX => {} // ok + } + + // We can actually get away with just covering the + // following two ranges, which correspond to all + // valid Unicode Scalar Values. + match c { + '\u{0000}' ..= '\u{D7FF}' => {} + '\u{E000}' ..= '\u{10_FFFF}' => {} + } + + match 0usize { + 0 ..= usize::MAX => {} // ok + } + + match 0u16 { + 0 ..= u16::MAX => {} // ok + } + + match 0u32 { + 0 ..= u32::MAX => {} // ok + } + + match 0u64 { + 0 ..= u64::MAX => {} // ok + } + + match 0u128 { + 0 ..= u128::MAX => {} // ok + } + + match 0isize { + isize::MIN ..= isize::MAX => {} // ok + } + + match 0i8 { + -128 ..= 127 => {} // ok + } + + match 0i8 { //~ ERROR non-exhaustive patterns + -127 ..= 127 => {} + } + + match 0i16 { + i16::MIN ..= i16::MAX => {} // ok + } + + match 0i16 { //~ ERROR non-exhaustive patterns + i16::MIN ..= -1 => {} + 1 ..= i16::MAX => {} + } + + match 0i32 { + i32::MIN ..= i32::MAX => {} // ok + } + + match 0i64 { + i64::MIN ..= i64::MAX => {} // ok + } + + match 0i128 { + i128::MIN ..= i128::MAX => {} // ok + } + + // Make sure that guards don't factor into the exhaustiveness checks. + match 0u8 { //~ ERROR non-exhaustive patterns + 0 .. 128 => {} + 128 ..= 255 if true => {} + } + + match 0u8 { + 0 .. 128 => {} + 128 ..= 255 if false => {} + 128 ..= 255 => {} // ok, because previous arm was guarded + } + + // Now things start getting a bit more interesting. Testing products! + match (0u8, Some(())) { //~ ERROR non-exhaustive patterns + (1, _) => {} + (_, None) => {} + } + + match (0u8, true) { //~ ERROR non-exhaustive patterns + (0 ..= 125, false) => {} + (128 ..= 255, false) => {} + (0 ..= 255, true) => {} + } + + match (0u8, true) { // ok + (0 ..= 125, false) => {} + (128 ..= 255, false) => {} + (0 ..= 255, true) => {} + (125 .. 128, false) => {} + } + + match 0u8 { // ok + 0 .. 2 => {} + 1 ..= 2 => {} + _ => {} + } + + const LIM: u128 = u128::MAX - 1; + match 0u128 { //~ ERROR non-exhaustive patterns + 0 ..= LIM => {} + } + + match 0u128 { //~ ERROR non-exhaustive patterns + 0 ..= 4 => {} + } + + match 0u128 { //~ ERROR non-exhaustive patterns + 4 ..= u128::MAX => {} + } +} diff --git a/src/test/ui/exhaustive_integer_patterns.stderr b/src/test/ui/exhaustive_integer_patterns.stderr new file mode 100644 index 0000000000000..44b05a12aebd2 --- /dev/null +++ b/src/test/ui/exhaustive_integer_patterns.stderr @@ -0,0 +1,87 @@ +error: unreachable pattern + --> $DIR/exhaustive_integer_patterns.rs:32:9 + | +LL | 200 => {} //~ ERROR unreachable pattern + | ^^^ + | +note: lint level defined here + --> $DIR/exhaustive_integer_patterns.rs:13:9 + | +LL | #![deny(unreachable_patterns)] + | ^^^^^^^^^^^^^^^^^^^^ + +error[E0004]: non-exhaustive patterns: `128u8..=255u8` not covered + --> $DIR/exhaustive_integer_patterns.rs:37:11 + | +LL | match x { //~ ERROR non-exhaustive patterns + | ^ pattern `128u8..=255u8` not covered + +error[E0004]: non-exhaustive patterns: `11u8..=19u8`, `31u8..=34u8`, `36u8..=69u8` and 1 more not covered + --> $DIR/exhaustive_integer_patterns.rs:42:11 + | +LL | match x { //~ ERROR non-exhaustive patterns + | ^ patterns `11u8..=19u8`, `31u8..=34u8`, `36u8..=69u8` and 1 more not covered + +error: unreachable pattern + --> $DIR/exhaustive_integer_patterns.rs:53:9 + | +LL | -2..=20 => {} //~ ERROR unreachable pattern + | ^^^^^^^ + +error[E0004]: non-exhaustive patterns: `-128i8..=-8i8`, `-6i8`, `121i8..=124i8` and 1 more not covered + --> $DIR/exhaustive_integer_patterns.rs:50:11 + | +LL | match x { //~ ERROR non-exhaustive patterns + | ^ patterns `-128i8..=-8i8`, `-6i8`, `121i8..=124i8` and 1 more not covered + +error[E0004]: non-exhaustive patterns: `-128i8` not covered + --> $DIR/exhaustive_integer_patterns.rs:99:11 + | +LL | match 0i8 { //~ ERROR non-exhaustive patterns + | ^^^ pattern `-128i8` not covered + +error[E0004]: non-exhaustive patterns: `0i16` not covered + --> $DIR/exhaustive_integer_patterns.rs:107:11 + | +LL | match 0i16 { //~ ERROR non-exhaustive patterns + | ^^^^ pattern `0i16` not covered + +error[E0004]: non-exhaustive patterns: `128u8..=255u8` not covered + --> $DIR/exhaustive_integer_patterns.rs:125:11 + | +LL | match 0u8 { //~ ERROR non-exhaustive patterns + | ^^^ pattern `128u8..=255u8` not covered + +error[E0004]: non-exhaustive patterns: `(0u8, Some(_))` and `(2u8..=255u8, Some(_))` not covered + --> $DIR/exhaustive_integer_patterns.rs:137:11 + | +LL | match (0u8, Some(())) { //~ ERROR non-exhaustive patterns + | ^^^^^^^^^^^^^^^ patterns `(0u8, Some(_))` and `(2u8..=255u8, Some(_))` not covered + +error[E0004]: non-exhaustive patterns: `(126u8..=127u8, false)` not covered + --> $DIR/exhaustive_integer_patterns.rs:142:11 + | +LL | match (0u8, true) { //~ ERROR non-exhaustive patterns + | ^^^^^^^^^^^ pattern `(126u8..=127u8, false)` not covered + +error[E0004]: non-exhaustive patterns: `340282366920938463463374607431768211455u128` not covered + --> $DIR/exhaustive_integer_patterns.rs:162:11 + | +LL | match 0u128 { //~ ERROR non-exhaustive patterns + | ^^^^^ pattern `340282366920938463463374607431768211455u128` not covered + +error[E0004]: non-exhaustive patterns: `5u128..=340282366920938463463374607431768211455u128` not covered + --> $DIR/exhaustive_integer_patterns.rs:166:11 + | +LL | match 0u128 { //~ ERROR non-exhaustive patterns + | ^^^^^ pattern `5u128..=340282366920938463463374607431768211455u128` not covered + +error[E0004]: non-exhaustive patterns: `0u128..=3u128` not covered + --> $DIR/exhaustive_integer_patterns.rs:170:11 + | +LL | match 0u128 { //~ ERROR non-exhaustive patterns + | ^^^^^ pattern `0u128..=3u128` not covered + +error: aborting due to 13 previous errors + +For more information about this error, try `rustc --explain E0004`. diff --git a/src/test/ui/feature-gate-exhaustive_integer_patterns.rs b/src/test/ui/feature-gate-exhaustive_integer_patterns.rs new file mode 100644 index 0000000000000..3aa1522945548 --- /dev/null +++ b/src/test/ui/feature-gate-exhaustive_integer_patterns.rs @@ -0,0 +1,16 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +fn main() { + let x: u8 = 0; + match x { //~ ERROR non-exhaustive patterns: `_` not covered + 0 ..= 255 => {} + } +} diff --git a/src/test/ui/feature-gate-exhaustive_integer_patterns.stderr b/src/test/ui/feature-gate-exhaustive_integer_patterns.stderr new file mode 100644 index 0000000000000..63d98f6b5eb64 --- /dev/null +++ b/src/test/ui/feature-gate-exhaustive_integer_patterns.stderr @@ -0,0 +1,9 @@ +error[E0004]: non-exhaustive patterns: `_` not covered + --> $DIR/feature-gate-exhaustive_integer_patterns.rs:13:11 + | +LL | match x { //~ ERROR non-exhaustive patterns: `_` not covered + | ^ pattern `_` not covered + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0004`.