aboutsummaryrefslogtreecommitdiff
path: root/crates/hir_ty/src/diagnostics/pattern/deconstruct_pat.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/hir_ty/src/diagnostics/pattern/deconstruct_pat.rs')
-rw-r--r--crates/hir_ty/src/diagnostics/pattern/deconstruct_pat.rs145
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
1use std::{ 45use 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
7use hir_def::{ 51use hir_def::{EnumVariantId, HasModule, LocalFieldId, VariantId};
8 expr::{Expr, Literal, RecordFieldPat},
9 type_ref::Mutability,
10 AttrDefId, EnumVariantId, HasModule, LocalFieldId, VariantId,
11};
12use smallvec::{smallvec, SmallVec}; 52use smallvec::{smallvec, SmallVec};
13 53
14use crate::{AdtId, Interner, Scalar, Ty, TyExt, TyKind}; 54use crate::{AdtId, Interner, Scalar, Ty, TyExt, TyKind};
@@ -20,9 +60,21 @@ use super::{
20 60
21use self::Constructor::*; 61use 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)]
24pub(super) enum ToDo {} 66pub(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)]
27pub(super) struct IntRange { 79pub(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)]
190pub(super) struct Slice { 242pub(super) struct Slice {
191 todo: ToDo, 243 _unimplemented: Void,
192} 244}
193 245
194impl Slice { 246impl 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)]
209pub(super) enum Constructor { 262pub(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 {
411impl SplitWildcard { 463impl 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]
560fn 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)]
565pub(super) enum FilteredField {
566 Kept(PatId),
567 Hidden,
568}
569
570impl 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]
865fn it_works() {}