diff options
Diffstat (limited to 'crates/hir_ty/src/diagnostics/pattern/deconstruct_pat.rs')
-rw-r--r-- | crates/hir_ty/src/diagnostics/pattern/deconstruct_pat.rs | 145 |
1 files changed, 87 insertions, 58 deletions
diff --git a/crates/hir_ty/src/diagnostics/pattern/deconstruct_pat.rs b/crates/hir_ty/src/diagnostics/pattern/deconstruct_pat.rs index 1c86ed59b..9fa82a952 100644 --- a/crates/hir_ty/src/diagnostics/pattern/deconstruct_pat.rs +++ b/crates/hir_ty/src/diagnostics/pattern/deconstruct_pat.rs | |||
@@ -1,14 +1,54 @@ | |||
1 | //! [`super::usefulness`] explains most of what is happening in this file. As explained there, | ||
2 | //! values and patterns are made from constructors applied to fields. This file defines a | ||
3 | //! `Constructor` enum, a `Fields` struct, and various operations to manipulate them and convert | ||
4 | //! them from/to patterns. | ||
5 | //! | ||
6 | //! There's one idea that is not detailed in [`super::usefulness`] because the details are not | ||
7 | //! needed there: _constructor splitting_. | ||
8 | //! | ||
9 | //! # Constructor splitting | ||
10 | //! | ||
11 | //! The idea is as follows: given a constructor `c` and a matrix, we want to specialize in turn | ||
12 | //! with all the value constructors that are covered by `c`, and compute usefulness for each. | ||
13 | //! Instead of listing all those constructors (which is intractable), we group those value | ||
14 | //! constructors together as much as possible. Example: | ||
15 | //! | ||
16 | //! ``` | ||
17 | //! match (0, false) { | ||
18 | //! (0 ..=100, true) => {} // `p_1` | ||
19 | //! (50..=150, false) => {} // `p_2` | ||
20 | //! (0 ..=200, _) => {} // `q` | ||
21 | //! } | ||
22 | //! ``` | ||
23 | //! | ||
24 | //! The naive approach would try all numbers in the range `0..=200`. But we can be a lot more | ||
25 | //! clever: `0` and `1` for example will match the exact same rows, and return equivalent | ||
26 | //! witnesses. In fact all of `0..50` would. We can thus restrict our exploration to 4 | ||
27 | //! constructors: `0..50`, `50..=100`, `101..=150` and `151..=200`. That is enough and infinitely | ||
28 | //! more tractable. | ||
29 | //! | ||
30 | //! We capture this idea in a function `split(p_1 ... p_n, c)` which returns a list of constructors | ||
31 | //! `c'` covered by `c`. Given such a `c'`, we require that all value ctors `c''` covered by `c'` | ||
32 | //! return an equivalent set of witnesses after specializing and computing usefulness. | ||
33 | //! In the example above, witnesses for specializing by `c''` covered by `0..50` will only differ | ||
34 | //! in their first element. | ||
35 | //! | ||
36 | //! We usually also ask that the `c'` together cover all of the original `c`. However we allow | ||
37 | //! skipping some constructors as long as it doesn't change whether the resulting list of witnesses | ||
38 | //! is empty of not. We use this in the wildcard `_` case. | ||
39 | //! | ||
40 | //! Splitting is implemented in the [`Constructor::split`] function. We don't do splitting for | ||
41 | //! or-patterns; instead we just try the alternatives one-by-one. For details on splitting | ||
42 | //! wildcards, see [`SplitWildcard`]; for integer ranges, see [`SplitIntRange`]; for slices, see | ||
43 | //! [`SplitVarLenSlice`]. | ||
44 | |||
1 | use std::{ | 45 | use std::{ |
2 | cmp::{max, min}, | 46 | cmp::{max, min}, |
3 | iter::once, | 47 | iter::once, |
4 | ops::RangeInclusive, | 48 | ops::RangeInclusive, |
5 | }; | 49 | }; |
6 | 50 | ||
7 | use hir_def::{ | 51 | use hir_def::{EnumVariantId, HasModule, LocalFieldId, VariantId}; |
8 | expr::{Expr, Literal, RecordFieldPat}, | ||
9 | type_ref::Mutability, | ||
10 | AttrDefId, EnumVariantId, HasModule, LocalFieldId, VariantId, | ||
11 | }; | ||
12 | use smallvec::{smallvec, SmallVec}; | 52 | use smallvec::{smallvec, SmallVec}; |
13 | 53 | ||
14 | use crate::{AdtId, Interner, Scalar, Ty, TyExt, TyKind}; | 54 | use crate::{AdtId, Interner, Scalar, Ty, TyExt, TyKind}; |
@@ -20,9 +60,21 @@ use super::{ | |||
20 | 60 | ||
21 | use self::Constructor::*; | 61 | use self::Constructor::*; |
22 | 62 | ||
63 | /// [Constructor] uses this in umimplemented variants. | ||
64 | /// It allows porting match expressions from upstream algorithm without losing semantics. | ||
23 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] | 65 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] |
24 | pub(super) enum ToDo {} | 66 | pub(super) enum Void {} |
25 | 67 | ||
68 | /// An inclusive interval, used for precise integer exhaustiveness checking. | ||
69 | /// `IntRange`s always store a contiguous range. This means that values are | ||
70 | /// encoded such that `0` encodes the minimum value for the integer, | ||
71 | /// regardless of the signedness. | ||
72 | /// For example, the pattern `-128..=127i8` is encoded as `0..=255`. | ||
73 | /// This makes comparisons and arithmetic on interval endpoints much more | ||
74 | /// straightforward. See `signed_bias` for details. | ||
75 | /// | ||
76 | /// `IntRange` is never used to encode an empty range or a "range" that wraps | ||
77 | /// around the (offset) space: i.e., `range.lo <= range.hi`. | ||
26 | #[derive(Clone, Debug, PartialEq, Eq)] | 78 | #[derive(Clone, Debug, PartialEq, Eq)] |
27 | pub(super) struct IntRange { | 79 | pub(super) struct IntRange { |
28 | range: RangeInclusive<u128>, | 80 | range: RangeInclusive<u128>, |
@@ -55,11 +107,11 @@ impl IntRange { | |||
55 | } | 107 | } |
56 | 108 | ||
57 | #[inline] | 109 | #[inline] |
58 | fn from_range(cx: &MatchCheckCtx<'_>, lo: u128, hi: u128, scalar_ty: Scalar) -> IntRange { | 110 | fn from_range(lo: u128, hi: u128, scalar_ty: Scalar) -> IntRange { |
59 | if let Scalar::Bool = scalar_ty { | 111 | if let Scalar::Bool = scalar_ty { |
60 | IntRange { range: lo..=hi } | 112 | IntRange { range: lo..=hi } |
61 | } else { | 113 | } else { |
62 | todo!() | 114 | unimplemented!() |
63 | } | 115 | } |
64 | } | 116 | } |
65 | 117 | ||
@@ -188,13 +240,13 @@ impl SplitIntRange { | |||
188 | /// A constructor for array and slice patterns. | 240 | /// A constructor for array and slice patterns. |
189 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] | 241 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] |
190 | pub(super) struct Slice { | 242 | pub(super) struct Slice { |
191 | todo: ToDo, | 243 | _unimplemented: Void, |
192 | } | 244 | } |
193 | 245 | ||
194 | impl Slice { | 246 | impl Slice { |
195 | /// See `Constructor::is_covered_by` | 247 | /// See `Constructor::is_covered_by` |
196 | fn is_covered_by(self, other: Self) -> bool { | 248 | fn is_covered_by(self, _other: Self) -> bool { |
197 | todo!() | 249 | unimplemented!() // never called as Slice contains Void |
198 | } | 250 | } |
199 | } | 251 | } |
200 | 252 | ||
@@ -205,6 +257,7 @@ impl Slice { | |||
205 | /// `specialize_constructor` returns the list of fields corresponding to a pattern, given a | 257 | /// `specialize_constructor` returns the list of fields corresponding to a pattern, given a |
206 | /// constructor. `Constructor::apply` reconstructs the pattern from a pair of `Constructor` and | 258 | /// constructor. `Constructor::apply` reconstructs the pattern from a pair of `Constructor` and |
207 | /// `Fields`. | 259 | /// `Fields`. |
260 | #[allow(dead_code)] | ||
208 | #[derive(Clone, Debug, PartialEq)] | 261 | #[derive(Clone, Debug, PartialEq)] |
209 | pub(super) enum Constructor { | 262 | pub(super) enum Constructor { |
210 | /// The constructor for patterns that have a single constructor, like tuples, struct patterns | 263 | /// The constructor for patterns that have a single constructor, like tuples, struct patterns |
@@ -215,9 +268,9 @@ pub(super) enum Constructor { | |||
215 | /// Ranges of integer literal values (`2`, `2..=5` or `2..5`). | 268 | /// Ranges of integer literal values (`2`, `2..=5` or `2..5`). |
216 | IntRange(IntRange), | 269 | IntRange(IntRange), |
217 | /// Ranges of floating-point literal values (`2.0..=5.2`). | 270 | /// Ranges of floating-point literal values (`2.0..=5.2`). |
218 | FloatRange(ToDo), | 271 | FloatRange(Void), |
219 | /// String literals. Strings are not quite the same as `&[u8]` so we treat them separately. | 272 | /// String literals. Strings are not quite the same as `&[u8]` so we treat them separately. |
220 | Str(ToDo), | 273 | Str(Void), |
221 | /// Array and slice patterns. | 274 | /// Array and slice patterns. |
222 | Slice(Slice), | 275 | Slice(Slice), |
223 | /// Constants that must not be matched structurally. They are treated as black | 276 | /// Constants that must not be matched structurally. They are treated as black |
@@ -253,7 +306,7 @@ impl Constructor { | |||
253 | } | 306 | } |
254 | } | 307 | } |
255 | 308 | ||
256 | fn variant_id_for_adt(&self, adt: hir_def::AdtId, cx: &MatchCheckCtx<'_>) -> VariantId { | 309 | fn variant_id_for_adt(&self, adt: hir_def::AdtId) -> VariantId { |
257 | match *self { | 310 | match *self { |
258 | Variant(id) => id.into(), | 311 | Variant(id) => id.into(), |
259 | Single => { | 312 | Single => { |
@@ -270,7 +323,6 @@ impl Constructor { | |||
270 | 323 | ||
271 | /// Determines the constructor that the given pattern can be specialized to. | 324 | /// Determines the constructor that the given pattern can be specialized to. |
272 | pub(super) fn from_pat(cx: &MatchCheckCtx<'_>, pat: PatId) -> Self { | 325 | pub(super) fn from_pat(cx: &MatchCheckCtx<'_>, pat: PatId) -> Self { |
273 | let ty = cx.type_of(pat); | ||
274 | match cx.pattern_arena.borrow()[pat].kind.as_ref() { | 326 | match cx.pattern_arena.borrow()[pat].kind.as_ref() { |
275 | PatKind::Binding { .. } | PatKind::Wild => Wildcard, | 327 | PatKind::Binding { .. } | PatKind::Wild => Wildcard, |
276 | PatKind::Leaf { .. } | PatKind::Deref { .. } => Single, | 328 | PatKind::Leaf { .. } | PatKind::Deref { .. } => Single, |
@@ -312,7 +364,7 @@ impl Constructor { | |||
312 | split_range.split(int_ranges.cloned()); | 364 | split_range.split(int_ranges.cloned()); |
313 | split_range.iter().map(IntRange).collect() | 365 | split_range.iter().map(IntRange).collect() |
314 | } | 366 | } |
315 | Slice(_) => todo!("Constructor::split Slice"), | 367 | Slice(_) => unimplemented!(), |
316 | // Any other constructor can be used unchanged. | 368 | // Any other constructor can be used unchanged. |
317 | _ => smallvec![self.clone()], | 369 | _ => smallvec![self.clone()], |
318 | } | 370 | } |
@@ -323,7 +375,7 @@ impl Constructor { | |||
323 | /// this checks for inclusion. | 375 | /// this checks for inclusion. |
324 | // We inline because this has a single call site in `Matrix::specialize_constructor`. | 376 | // We inline because this has a single call site in `Matrix::specialize_constructor`. |
325 | #[inline] | 377 | #[inline] |
326 | pub(super) fn is_covered_by(&self, pcx: PatCtxt<'_>, other: &Self) -> bool { | 378 | pub(super) fn is_covered_by(&self, _pcx: PatCtxt<'_>, other: &Self) -> bool { |
327 | // This must be kept in sync with `is_covered_by_any`. | 379 | // This must be kept in sync with `is_covered_by_any`. |
328 | match (self, other) { | 380 | match (self, other) { |
329 | // Wildcards cover anything | 381 | // Wildcards cover anything |
@@ -336,10 +388,10 @@ impl Constructor { | |||
336 | 388 | ||
337 | (IntRange(self_range), IntRange(other_range)) => self_range.is_covered_by(other_range), | 389 | (IntRange(self_range), IntRange(other_range)) => self_range.is_covered_by(other_range), |
338 | (FloatRange(..), FloatRange(..)) => { | 390 | (FloatRange(..), FloatRange(..)) => { |
339 | todo!() | 391 | unimplemented!() |
340 | } | 392 | } |
341 | (Str(self_val), Str(other_val)) => { | 393 | (Str(..), Str(..)) => { |
342 | todo!() | 394 | unimplemented!() |
343 | } | 395 | } |
344 | (Slice(self_slice), Slice(other_slice)) => self_slice.is_covered_by(*other_slice), | 396 | (Slice(self_slice), Slice(other_slice)) => self_slice.is_covered_by(*other_slice), |
345 | 397 | ||
@@ -358,7 +410,7 @@ impl Constructor { | |||
358 | /// Faster version of `is_covered_by` when applied to many constructors. `used_ctors` is | 410 | /// Faster version of `is_covered_by` when applied to many constructors. `used_ctors` is |
359 | /// assumed to be built from `matrix.head_ctors()` with wildcards filtered out, and `self` is | 411 | /// assumed to be built from `matrix.head_ctors()` with wildcards filtered out, and `self` is |
360 | /// assumed to have been split from a wildcard. | 412 | /// assumed to have been split from a wildcard. |
361 | fn is_covered_by_any(&self, pcx: PatCtxt<'_>, used_ctors: &[Constructor]) -> bool { | 413 | fn is_covered_by_any(&self, _pcx: PatCtxt<'_>, used_ctors: &[Constructor]) -> bool { |
362 | if used_ctors.is_empty() { | 414 | if used_ctors.is_empty() { |
363 | return false; | 415 | return false; |
364 | } | 416 | } |
@@ -411,9 +463,10 @@ pub(super) struct SplitWildcard { | |||
411 | impl SplitWildcard { | 463 | impl SplitWildcard { |
412 | pub(super) fn new(pcx: PatCtxt<'_>) -> Self { | 464 | pub(super) fn new(pcx: PatCtxt<'_>) -> Self { |
413 | let cx = pcx.cx; | 465 | let cx = pcx.cx; |
414 | let make_range = | 466 | let make_range = |start, end, scalar| IntRange(IntRange::from_range(start, end, scalar)); |
415 | |start, end, scalar| IntRange(IntRange::from_range(cx, start, end, scalar)); | 467 | |
416 | // FIXME(iDawer) using NonExhaustive ctor for unhandled types | 468 | // Unhandled types are treated as non-exhaustive. Being explicit here instead of falling |
469 | // to catchall arm to ease further implementation. | ||
417 | let unhandled = || smallvec![NonExhaustive]; | 470 | let unhandled = || smallvec![NonExhaustive]; |
418 | 471 | ||
419 | // This determines the set of all possible constructors for the type `pcx.ty`. For numbers, | 472 | // This determines the set of all possible constructors for the type `pcx.ty`. For numbers, |
@@ -426,7 +479,7 @@ impl SplitWildcard { | |||
426 | // `cx.is_uninhabited()`). | 479 | // `cx.is_uninhabited()`). |
427 | let all_ctors = match pcx.ty.kind(&Interner) { | 480 | let all_ctors = match pcx.ty.kind(&Interner) { |
428 | TyKind::Scalar(Scalar::Bool) => smallvec![make_range(0, 1, Scalar::Bool)], | 481 | TyKind::Scalar(Scalar::Bool) => smallvec![make_range(0, 1, Scalar::Bool)], |
429 | // TyKind::Array(..) if ... => todo!(), | 482 | // TyKind::Array(..) if ... => unhandled(), |
430 | TyKind::Array(..) | TyKind::Slice(..) => unhandled(), | 483 | TyKind::Array(..) | TyKind::Slice(..) => unhandled(), |
431 | &TyKind::Adt(AdtId(hir_def::AdtId::EnumId(enum_id)), ref _substs) => { | 484 | &TyKind::Adt(AdtId(hir_def::AdtId::EnumId(enum_id)), ref _substs) => { |
432 | let enum_data = cx.db.enum_data(enum_id); | 485 | let enum_data = cx.db.enum_data(enum_id); |
@@ -556,26 +609,6 @@ impl SplitWildcard { | |||
556 | } | 609 | } |
557 | } | 610 | } |
558 | 611 | ||
559 | #[test] | ||
560 | fn it_works2() {} | ||
561 | |||
562 | /// Some fields need to be explicitly hidden away in certain cases; see the comment above the | ||
563 | /// `Fields` struct. This struct represents such a potentially-hidden field. | ||
564 | #[derive(Debug, Copy, Clone)] | ||
565 | pub(super) enum FilteredField { | ||
566 | Kept(PatId), | ||
567 | Hidden, | ||
568 | } | ||
569 | |||
570 | impl FilteredField { | ||
571 | fn kept(self) -> Option<PatId> { | ||
572 | match self { | ||
573 | FilteredField::Kept(p) => Some(p), | ||
574 | FilteredField::Hidden => None, | ||
575 | } | ||
576 | } | ||
577 | } | ||
578 | |||
579 | /// A value can be decomposed into a constructor applied to some fields. This struct represents | 612 | /// A value can be decomposed into a constructor applied to some fields. This struct represents |
580 | /// those fields, generalized to allow patterns in each field. See also `Constructor`. | 613 | /// those fields, generalized to allow patterns in each field. See also `Constructor`. |
581 | /// This is constructed from a constructor using [`Fields::wildcards()`]. | 614 | /// This is constructed from a constructor using [`Fields::wildcards()`]. |
@@ -623,14 +656,13 @@ impl Fields { | |||
623 | } | 656 | } |
624 | TyKind::Ref(.., rty) => Fields::from_single_pattern(wildcard_from_ty(rty)), | 657 | TyKind::Ref(.., rty) => Fields::from_single_pattern(wildcard_from_ty(rty)), |
625 | TyKind::Adt(AdtId(adt), substs) => { | 658 | TyKind::Adt(AdtId(adt), substs) => { |
626 | let adt_is_box = false; // TODO(iDawer): handle box patterns | 659 | let adt_is_box = false; // TODO(iDawer): implement this |
627 | if adt_is_box { | 660 | if adt_is_box { |
628 | // Use T as the sub pattern type of Box<T>. | 661 | // Use T as the sub pattern type of Box<T>. |
629 | let ty = substs.at(&Interner, 0).assert_ty_ref(&Interner); | 662 | let subst_ty = substs.at(&Interner, 0).assert_ty_ref(&Interner); |
630 | Fields::from_single_pattern(wildcard_from_ty(ty)) | 663 | Fields::from_single_pattern(wildcard_from_ty(subst_ty)) |
631 | } else { | 664 | } else { |
632 | let variant_id = constructor.variant_id_for_adt(*adt, cx); | 665 | let variant_id = constructor.variant_id_for_adt(*adt); |
633 | let variant = variant_id.variant_data(cx.db.upcast()); | ||
634 | let adt_is_local = | 666 | let adt_is_local = |
635 | variant_id.module(cx.db.upcast()).krate() == cx.module.krate(); | 667 | variant_id.module(cx.db.upcast()).krate() == cx.module.krate(); |
636 | // Whether we must not match the fields of this variant exhaustively. | 668 | // Whether we must not match the fields of this variant exhaustively. |
@@ -655,8 +687,8 @@ impl Fields { | |||
655 | } | 687 | } |
656 | _ => panic!("Unexpected type for `Single` constructor: {:?}", ty), | 688 | _ => panic!("Unexpected type for `Single` constructor: {:?}", ty), |
657 | }, | 689 | }, |
658 | Slice(slice) => { | 690 | Slice(..) => { |
659 | todo!() | 691 | unimplemented!() |
660 | } | 692 | } |
661 | Str(..) | FloatRange(..) | IntRange(..) | NonExhaustive | Opaque | Missing | 693 | Str(..) | FloatRange(..) | IntRange(..) | NonExhaustive | Opaque | Missing |
662 | | Wildcard => Fields::Vec(Default::default()), | 694 | | Wildcard => Fields::Vec(Default::default()), |
@@ -722,7 +754,7 @@ impl Fields { | |||
722 | } | 754 | } |
723 | _ => PatKind::Wild, | 755 | _ => PatKind::Wild, |
724 | }, | 756 | }, |
725 | Constructor::Slice(slice) => UNHANDLED, | 757 | Constructor::Slice(_) => UNHANDLED, |
726 | Str(_) => UNHANDLED, | 758 | Str(_) => UNHANDLED, |
727 | FloatRange(..) => UNHANDLED, | 759 | FloatRange(..) => UNHANDLED, |
728 | Constructor::IntRange(_) => UNHANDLED, | 760 | Constructor::IntRange(_) => UNHANDLED, |
@@ -828,7 +860,7 @@ impl Fields { | |||
828 | pat: PatId, | 860 | pat: PatId, |
829 | cx: &MatchCheckCtx<'_>, | 861 | cx: &MatchCheckCtx<'_>, |
830 | ) -> Self { | 862 | ) -> Self { |
831 | // TODO: these alocations and clones are so unfortunate (+1 for switching to references) | 863 | // FIXME(iDawer): these alocations and clones are so unfortunate (+1 for switching to references) |
832 | let mut arena = cx.pattern_arena.borrow_mut(); | 864 | let mut arena = cx.pattern_arena.borrow_mut(); |
833 | match arena[pat].kind.as_ref() { | 865 | match arena[pat].kind.as_ref() { |
834 | PatKind::Deref { subpattern } => { | 866 | PatKind::Deref { subpattern } => { |
@@ -860,6 +892,3 @@ fn is_field_list_non_exhaustive(variant_id: VariantId, cx: &MatchCheckCtx<'_>) - | |||
860 | }; | 892 | }; |
861 | cx.db.attrs(attr_def_id).by_key("non_exhaustive").exists() | 893 | cx.db.attrs(attr_def_id).by_key("non_exhaustive").exists() |
862 | } | 894 | } |
863 | |||
864 | #[test] | ||
865 | fn it_works() {} | ||