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.
1
+ //! As explained in [`super::usefulness`], values and patterns are made from constructors applied to
2
+ //! fields. This file defines a `Constructor` enum, a `Fields` struct, and various operations to
3
+ //! manipulate them and convert them from/to patterns.
5
4
//!
6
- //! There's one idea that is not detailed in [`super::usefulness`] because the details are not
7
- //! needed there: _constructor splitting_.
5
+ //! There are two important bits of core logic in this file: constructor inclusion and constructor
6
+ //! splitting. Constructor inclusion, i.e. whether a constructor is included in/covered by another,
7
+ //! is straightforward and defined in [`Constructor::is_covered_by`].
8
8
//!
9
- //! # Constructor splitting
9
+ //! Constructor splitting is mentioned in [`super::usefulness`] but not detailed. We describe it
10
+ //! precisely here.
10
11
//!
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
12
//!
13
+ //! # Constructor grouping and splitting
14
+ //!
15
+ //! As explained in the corresponding section in [`super::usefulness`], to make usefulness tractable
16
+ //! we need to group together constructors that have the same effect when they are used to
17
+ //! specialize the matrix.
18
+ //!
19
+ //! Example:
16
20
//! ```compile_fail,E0004
17
21
//! match (0, false) {
18
- //! (0 ..=100, true) => {} // `p_1`
19
- //! (50..=150, false) => {} // `p_2`
20
- //! (0 ..=200, _) => {} // `q`
22
+ //! (0 ..=100, true) => {}
23
+ //! (50..=150, false) => {}
24
+ //! (0 ..=200, _) => {}
21
25
//! }
22
26
//! ```
23
27
//!
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.
28
+ //! In this example we can restrict specialization to 5 cases: `0..50`, `50..=100`, `101..=150`,
29
+ //! `151..=200` and `200..`.
30
+ //!
31
+ //! In [`super::usefulness`], we had said that `specialize` only takes value-only constructors. We
32
+ //! now relax this restriction: we allow `specialize` to take constructors like `0..50` as long as
33
+ //! we're careful to only do that with constructors that make sense. For example, `specialize(0..50,
34
+ //! (0..=100, true))` is sensible, but `specialize(50..=200, (0..=100, true))` is not.
35
+ //!
36
+ //! Constructor splitting looks at the constructors in the first column of the matrix and constructs
37
+ //! such a sensible set of constructors. Formally, we want to find a smallest disjoint set of
38
+ //! constructors:
39
+ //! - Whose union covers the whole type, and
40
+ //! - That have no non-trivial intersection with any of the constructors in the column (i.e. they're
41
+ //! each either disjoint with or covered by any given column constructor).
42
+ //!
43
+ //! We compute this in two steps: first [`ConstructorSet::for_ty`] determines the set of all
44
+ //! possible constructors for the type. Then [`ConstructorSet::split`] looks at the column of
45
+ //! constructors and splits the set into groups accordingly. The precise invariants of
46
+ //! [`ConstructorSet::split`] is described in [`SplitConstructorSet`].
47
+ //!
48
+ //! Constructor splitting has two interesting special cases: integer range splitting (see
49
+ //! [`IntRange::split`]) and slice splitting (see [`Slice::split`]).
29
50
//!
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
51
//!
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.
52
+ //! # The `Missing` constructor
53
+ //!
54
+ //! We detail a special case of constructor splitting that is a bit subtle. Take the following:
55
+ //!
56
+ //! ```
57
+ //! enum Direction { North, South, East, West }
58
+ //! # let wind = (Direction::North, 0u8);
59
+ //! match wind {
60
+ //! (Direction::North, 50..) => {}
61
+ //! (_, _) => {}
62
+ //! }
63
+ //! ```
64
+ //!
65
+ //! Here we expect constructor splitting to output two cases: `North`, and "everything else". This
66
+ //! "everything else" is represented by [`Constructor::Missing`]. Unlike other constructors, it's a
67
+ //! bit contextual: to know the exact list of constructors it represents we have to look at the
68
+ //! column. In practice however we don't need to, because by construction it only matches rows that
69
+ //! have wildcards. This is how this constructor is special: the only constructor that covers it is
70
+ //! `Wildcard`.
71
+ //!
72
+ //! The only place where we care about which constructors `Missing` represents is in diagnostics
73
+ //! (see `super::usefulness::WitnessMatrix::apply_constructor`).
74
+ //!
75
+ //! Extra special implementation detail: in fact, in the case where all the constructors are
76
+ //! missing, we replace `Missing` with `Wildcard` to signal this. It only makes a difference for
77
+ //! diagnostics: for `Missing` we list the missing constructors; for `Wildcard` we only output `_`.
78
+ //!
79
+ //! FIXME(Nadrieril): maybe `Missing { report_all: bool }` would be less confusing.
80
+ //!
81
+ //! We choose whether to specialize with `Missing`/`Wildcard` in
82
+ //! `super::usefulness::compute_exhaustiveness_and_reachability`.
83
+ //!
39
84
//!
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 [`Constructor::split`]; for integer ranges, see
43
- //! [`IntRange::split`]; for slices, see [`Slice::split`].
44
85
//!
45
86
//! ## Opaque patterns
46
87
//!
47
- //! Some patterns, such as TODO, cannot be inspected, which we handle with `Constructor::Opaque`.
48
- //! Since we know nothing of these patterns, we assume they never cover each other. In order to
49
- //! respect the invariants of [`SplitConstructorSet`], we give each `Opaque` constructor a unique id
50
- //! so we can recognize it.
88
+ //! Some patterns, such as constants that are not allowed to be matched structurally, cannot be
89
+ //! inspected, which we handle with `Constructor::Opaque`. Since we know nothing of these patterns,
90
+ //! we assume they never cover each other. In order to respect the invariants of
91
+ //! [`SplitConstructorSet`], we give each `Opaque` constructor a unique id so we can recognize it.
51
92
52
93
use std:: cell:: Cell ;
53
94
use std:: cmp:: { self , max, min, Ordering } ;
@@ -645,8 +686,8 @@ impl OpaqueId {
645
686
/// `Fields`.
646
687
#[ derive( Clone , Debug , PartialEq ) ]
647
688
pub ( super ) enum Constructor < ' tcx > {
648
- /// The constructor for patterns that have a single constructor, like tuples, struct patterns
649
- /// and fixed -length arrays.
689
+ /// The constructor for patterns that have a single constructor, like tuples, struct patterns,
690
+ /// and references. Fixed -length arrays are treated separately with `Slice` .
650
691
Single ,
651
692
/// Enum variants.
652
693
Variant ( VariantIdx ) ,
@@ -678,8 +719,8 @@ pub(super) enum Constructor<'tcx> {
678
719
/// We use this for variants behind an unstable gate as well as
679
720
/// `#[doc(hidden)]` ones.
680
721
Hidden ,
681
- /// Fake extra constructor for constructors that are not seen in the matrix, as explained in the
682
- /// code for [`Constructor::split`] .
722
+ /// Fake extra constructor for constructors that are not seen in the matrix, as explained at the
723
+ /// top of the file .
683
724
Missing ,
684
725
}
685
726
@@ -761,104 +802,12 @@ impl<'tcx> Constructor<'tcx> {
761
802
}
762
803
}
763
804
764
- /// Some constructors (namely `Wildcard`, `IntRange` and `Slice`) actually stand for a set of
765
- /// actual constructors (like variants, integers or fixed-sized slices). When specializing for
766
- /// these constructors, we want to be specialising for the actual underlying constructors.
767
- /// Naively, we would simply return the list of constructors they correspond to. We instead are
768
- /// more clever: if there are constructors that we know will behave the same w.r.t. the current
769
- /// matrix, we keep them grouped. For example, all slices of a sufficiently large length will
770
- /// either be all useful or all non-useful with a given matrix.
771
- ///
772
- /// See the branches for details on how the splitting is done.
773
- ///
774
- /// This function may discard some irrelevant constructors if this preserves behavior. Eg. for
775
- /// the `_` case, we ignore the constructors already present in the column, unless all of them
776
- /// are.
777
- pub ( super ) fn split < ' a > (
778
- & self ,
779
- pcx : & PatCtxt < ' _ , ' _ , ' tcx > ,
780
- ctors : impl Iterator < Item = & ' a Constructor < ' tcx > > + Clone ,
781
- ) -> SmallVec < [ Self ; 1 ] >
782
- where
783
- ' tcx : ' a ,
784
- {
785
- match self {
786
- Wildcard => {
787
- let split_set = ConstructorSet :: for_ty ( pcx. cx , pcx. ty ) . split ( pcx, ctors) ;
788
- if !split_set. missing . is_empty ( ) {
789
- // We are splitting a wildcard in order to compute its usefulness. Some constructors are
790
- // not present in the column. The first thing we note is that specializing with any of
791
- // the missing constructors would select exactly the rows with wildcards. Moreover, they
792
- // would all return equivalent results. We can therefore group them all into a
793
- // fictitious `Missing` constructor.
794
- //
795
- // As an important optimization, this function will skip all the present constructors.
796
- // This is correct because specializing with any of the present constructors would
797
- // select a strict superset of the wildcard rows, and thus would only find witnesses
798
- // already found with the `Missing` constructor.
799
- // This does mean that diagnostics are incomplete: in
800
- // ```
801
- // match x {
802
- // Some(true) => {}
803
- // }
804
- // ```
805
- // we report `None` as missing but not `Some(false)`.
806
- //
807
- // When all the constructors are missing we can equivalently return the `Wildcard`
808
- // constructor on its own. The difference between `Wildcard` and `Missing` will then
809
- // only be in diagnostics.
810
-
811
- // If some constructors are missing, we typically want to report those constructors,
812
- // e.g.:
813
- // ```
814
- // enum Direction { N, S, E, W }
815
- // let Direction::N = ...;
816
- // ```
817
- // we can report 3 witnesses: `S`, `E`, and `W`.
818
- //
819
- // However, if the user didn't actually specify a constructor
820
- // in this arm, e.g., in
821
- // ```
822
- // let x: (Direction, Direction, bool) = ...;
823
- // let (_, _, false) = x;
824
- // ```
825
- // we don't want to show all 16 possible witnesses `(<direction-1>, <direction-2>,
826
- // true)` - we are satisfied with `(_, _, true)`. So if all constructors are missing we
827
- // prefer to report just a wildcard `_`.
828
- //
829
- // The exception is: if we are at the top-level, for example in an empty match, we
830
- // usually prefer to report the full list of constructors.
831
- let all_missing = split_set. present . is_empty ( ) ;
832
- let report_when_all_missing =
833
- pcx. is_top_level && !IntRange :: is_integral ( pcx. ty ) ;
834
- let ctor =
835
- if all_missing && !report_when_all_missing { Wildcard } else { Missing } ;
836
- smallvec ! [ ctor]
837
- } else {
838
- split_set. present
839
- }
840
- }
841
- // Fast-track if the range is trivial.
842
- IntRange ( this_range) if !this_range. is_singleton ( ) => {
843
- let column_ranges = ctors. filter_map ( |ctor| ctor. as_int_range ( ) ) . cloned ( ) ;
844
- this_range. split ( column_ranges) . map ( |( _, range) | IntRange ( range) ) . collect ( )
845
- }
846
- Slice ( this_slice @ Slice { kind : VarLen ( ..) , .. } ) => {
847
- let column_slices = ctors. filter_map ( |c| c. as_slice ( ) ) ;
848
- this_slice. split ( column_slices) . map ( |( _, slice) | Slice ( slice) ) . collect ( )
849
- }
850
- // Any other constructor can be used unchanged.
851
- _ => smallvec ! [ self . clone( ) ] ,
852
- }
853
- }
854
-
855
805
/// Returns whether `self` is covered by `other`, i.e. whether `self` is a subset of `other`.
856
806
/// For the simple cases, this is simply checking for equality. For the "grouped" constructors,
857
807
/// this checks for inclusion.
858
808
// We inline because this has a single call site in `Matrix::specialize_constructor`.
859
809
#[ inline]
860
810
pub ( super ) fn is_covered_by < ' p > ( & self , pcx : & PatCtxt < ' _ , ' p , ' tcx > , other : & Self ) -> bool {
861
- // This must be kept in sync with `is_covered_by_any`.
862
811
match ( self , other) {
863
812
// Wildcards cover anything
864
813
( _, Wildcard ) => true ,
@@ -943,23 +892,28 @@ pub(super) enum ConstructorSet {
943
892
/// `present` is morally the set of constructors present in the column, and `missing` is the set of
944
893
/// constructors that exist in the type but are not present in the column.
945
894
///
946
- /// More formally, they respect the following constraints:
947
- /// - the union of `present` and `missing` covers the whole type
948
- /// - `present` and `missing` are disjoint
949
- /// - neither contains wildcards
950
- /// - each constructor in `present` is covered by some non-wildcard constructor in the column
951
- /// - together, the constructors in `present` cover all the non-wildcard constructor in the column
952
- /// - non-wildcards in the column do no cover anything in `missing`
953
- /// - constructors in `present` and `missing` are split for the column; in other words, they are
954
- /// either fully included in or disjoint from each constructor in the column. This avoids
955
- /// non-trivial intersections like between `0..10` and `5..15`.
895
+ /// More formally, if we discard wildcards from the column, this respects the following constraints:
896
+ /// 1. the union of `present` and `missing` covers the whole type
897
+ /// 2. each constructor in `present` is covered by something in the column
898
+ /// 3. no constructor in `missing` is covered by anything in the column
899
+ /// 4. each constructor in the column is equal to the union of one or more constructors in `present`
900
+ /// 5. `missing` does not contain empty constructors (see discussion about emptiness at the top of
901
+ /// the file);
902
+ /// 6. constructors in `present` and `missing` are split for the column; in other words, they are
903
+ /// either fully included in or fully disjoint from each constructor in the column. In other
904
+ /// words, there are no non-trivial intersections like between `0..10` and `5..15`.
905
+ ///
906
+ /// We must be particularly careful with weird constructors like `Opaque`: they're not formally part
907
+ /// of the `ConstructorSet` for the type, yet if we forgot to include them in `present` we would be
908
+ /// ignoring any row with `Opaque`s in the algorithm. Hence the importance of point 4.
956
909
#[ derive( Debug ) ]
957
910
pub ( super ) struct SplitConstructorSet < ' tcx > {
958
911
pub ( super ) present : SmallVec < [ Constructor < ' tcx > ; 1 ] > ,
959
912
pub ( super ) missing : Vec < Constructor < ' tcx > > ,
960
913
}
961
914
962
915
impl ConstructorSet {
916
+ /// Creates a set that represents all the constructors of `ty`.
963
917
#[ instrument( level = "debug" , skip( cx) , ret) ]
964
918
pub ( super ) fn for_ty < ' p , ' tcx > ( cx : & MatchCheckCtxt < ' p , ' tcx > , ty : Ty < ' tcx > ) -> Self {
965
919
let make_range = |start, end| {
@@ -1095,9 +1049,10 @@ impl ConstructorSet {
1095
1049
}
1096
1050
}
1097
1051
1098
- /// This is the core logical operation of exhaustiveness checking. This analyzes a column a
1099
- /// constructors to 1/ determine which constructors of the type (if any) are missing; 2/ split
1100
- /// constructors to handle non-trivial intersections e.g. on ranges or slices.
1052
+ /// This analyzes a column of constructors to 1/ determine which constructors of the type (if
1053
+ /// any) are missing; 2/ split constructors to handle non-trivial intersections e.g. on ranges
1054
+ /// or slices. This can get subtle; see [`SplitConstructorSet`] for details of this operation
1055
+ /// and its invariants.
1101
1056
#[ instrument( level = "debug" , skip( self , pcx, ctors) , ret) ]
1102
1057
pub ( super ) fn split < ' a , ' tcx > (
1103
1058
& self ,
@@ -1244,19 +1199,6 @@ impl ConstructorSet {
1244
1199
1245
1200
SplitConstructorSet { present, missing }
1246
1201
}
1247
-
1248
- /// Compute the set of constructors missing from this column.
1249
- /// This is only used for reporting to the user.
1250
- pub ( super ) fn compute_missing < ' a , ' tcx > (
1251
- & self ,
1252
- pcx : & PatCtxt < ' _ , ' _ , ' tcx > ,
1253
- ctors : impl Iterator < Item = & ' a Constructor < ' tcx > > + Clone ,
1254
- ) -> Vec < Constructor < ' tcx > >
1255
- where
1256
- ' tcx : ' a ,
1257
- {
1258
- self . split ( pcx, ctors) . missing
1259
- }
1260
1202
}
1261
1203
1262
1204
/// A value can be decomposed into a constructor applied to some fields. This struct represents
@@ -1422,6 +1364,8 @@ impl<'p, 'tcx> DeconstructedPat<'p, 'tcx> {
1422
1364
DeconstructedPat { ctor, fields, ty, span, reachable : Cell :: new ( false ) }
1423
1365
}
1424
1366
1367
+ /// Note: the input patterns must have been lowered through
1368
+ /// `super::check_match::MatchVisitor::lower_pattern`.
1425
1369
pub ( crate ) fn from_pat ( cx : & MatchCheckCtxt < ' p , ' tcx > , pat : & Pat < ' tcx > ) -> Self {
1426
1370
let mkpat = |pat| DeconstructedPat :: from_pat ( cx, pat) ;
1427
1371
let ctor;
@@ -1625,6 +1569,7 @@ impl<'p, 'tcx> DeconstructedPat<'p, 'tcx> {
1625
1569
pub ( super ) fn is_or_pat ( & self ) -> bool {
1626
1570
matches ! ( self . ctor, Or )
1627
1571
}
1572
+ /// Expand this (possibly-nested) or-pattern into its alternatives.
1628
1573
pub ( super ) fn flatten_or_pat ( & ' p self ) -> SmallVec < [ & ' p Self ; 1 ] > {
1629
1574
if self . is_or_pat ( ) {
1630
1575
self . iter_fields ( ) . flat_map ( |p| p. flatten_or_pat ( ) ) . collect ( )
@@ -1697,7 +1642,17 @@ impl<'p, 'tcx> DeconstructedPat<'p, 'tcx> {
1697
1642
self . reachable . set ( true )
1698
1643
}
1699
1644
pub ( super ) fn is_reachable ( & self ) -> bool {
1700
- self . reachable . get ( )
1645
+ if self . reachable . get ( ) {
1646
+ true
1647
+ } else if self . is_or_pat ( ) && self . iter_fields ( ) . any ( |f| f. is_reachable ( ) ) {
1648
+ // We always expand or patterns in the matrix, so we will never see the actual
1649
+ // or-pattern (the one with constructor `Or`) in the column. As such, it will not be
1650
+ // marked as reachable itself, only its children will. We recover this information here.
1651
+ self . set_reachable ( ) ;
1652
+ true
1653
+ } else {
1654
+ false
1655
+ }
1701
1656
}
1702
1657
1703
1658
/// Report the spans of subpatterns that were not reachable, if any.
@@ -1706,7 +1661,6 @@ impl<'p, 'tcx> DeconstructedPat<'p, 'tcx> {
1706
1661
self . collect_unreachable_spans ( & mut spans) ;
1707
1662
spans
1708
1663
}
1709
-
1710
1664
fn collect_unreachable_spans ( & self , spans : & mut Vec < Span > ) {
1711
1665
// We don't look at subpatterns if we already reported the whole pattern as unreachable.
1712
1666
if !self . is_reachable ( ) {
0 commit comments