Skip to content

Commit c6f0947

Browse files
committed
Improve comments
1 parent aff073e commit c6f0947

File tree

1 file changed

+68
-7
lines changed

1 file changed

+68
-7
lines changed

src/librustc_mir_build/hair/pattern/_match.rs

+68-7
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
/// Note: most tests relevant to this file can be found (at the time of writing)
2-
/// in src/tests/ui/pattern/usefulness.
1+
/// Note: most of the tests relevant to this file can be found (at the time of writing) in
2+
/// src/tests/ui/pattern/usefulness.
33
///
44
/// This file includes the logic for exhaustiveness and usefulness checking for
55
/// pattern-matching. Specifically, given a list of patterns for a type, we can
@@ -13,6 +13,8 @@
1313
/// summarise the algorithm here to hopefully save time and be a little clearer
1414
/// (without being so rigorous).
1515
///
16+
/// # Premise
17+
///
1618
/// The core of the algorithm revolves about a "usefulness" check. In particular, we
1719
/// are trying to compute a predicate `U(P, p)` where `P` is a list of patterns (we refer to this as
1820
/// a matrix). `U(P, p)` represents whether, given an existing list of patterns
@@ -27,8 +29,52 @@
2729
/// pattern to those that have come before it doesn't increase the number of values
2830
/// we're matching).
2931
///
32+
/// # Core concept
33+
///
34+
/// The idea that powers everything that is done in this file is the following: a value is made
35+
/// from a constructor applied to some fields. Examples of constructors are `Some`, `None`, `(,)`
36+
/// (the 2-tuple constructor), `Foo {..}` (the constructor for a struct `Foo`), and `2` (the
37+
/// constructor for the number `2`). Fields are just a (possibly empty) list of values.
38+
///
39+
/// Some of the constructors listed above might feel weird: `None` and `2` don't take any
40+
/// arguments. This is part of what makes constructors so general: we will consider plain values
41+
/// like numbers and string literals to be constructors that take no arguments, also called "0-ary
42+
/// constructors"; they are the simplest case of constructors. This allows us to see any value as
43+
/// made up from a tree of constructors, each having a given number of children. For example:
44+
/// `(None, Ok(0))` is made from 4 different constructors.
45+
///
46+
/// This idea can be extended to patterns: a pattern captures a set of possible values, and we can
47+
/// describe this set using constructors. For example, `Err(_)` captures all values of the type
48+
/// `Result<T, E>` that start with the `Err` constructor (for some choice of `T` and `E`). The
49+
/// wildcard `_` captures all values of the given type starting with any of the constructors for
50+
/// that type.
51+
///
52+
/// We use this to compute whether different patterns might capture a same value. Do the patterns
53+
/// `Ok("foo")` and `Err(_)` capture a common value? The answer is no, because the first pattern
54+
/// captures only values starting with the `Ok` constructor and the second only values starting
55+
/// with the `Err` constructor. Do the patterns `Some(42)` and `Some(1..10)` intersect? They might,
56+
/// since they both capture values starting with `Some`. To be certain, we need to dig under the
57+
/// `Some` constructor and continue asking the question. This is the main idea behind the
58+
/// exhaustiveness algorithm: by looking at patterns constructor-by-constructor, we can efficiently
59+
/// figure out if some new pattern might capture a value that hadn't been captured by previous
60+
/// patterns.
61+
///
62+
/// Constructors are represented by the `Constructor` enum, and its fields by the `Fields` enum.
63+
/// Most of the complexity of this file resides in transforming between patterns and
64+
/// (`Constructor`, `Fields`) pairs, handling all the special cases correctly.
65+
///
66+
/// Caveat: this constructors/fields distinction doesn't quite cover every Rust value. For example
67+
/// a value of type `Rc<u64>` doesn't fit this idea very well, nor do function pointers and various
68+
/// other things. However, the idea covers everything that can be pattern-matched, and this is all
69+
/// we need for exhaustiveness checking.
70+
///
71+
///
72+
/// # Algorithm
73+
///
74+
/// Recall that `U(P, p)` represents whether, given an existing list of patterns (aka matrix) `P`,
75+
/// adding a new pattern `p` will cover previously-uncovered values of the type.
3076
/// During the course of the algorithm, the rows of the matrix won't just be individual patterns,
31-
/// but rather partially-deconstructed patterns in the form of a list of patterns. The paper
77+
/// but rather partially-deconstructed patterns in the form of a list of fields. The paper
3278
/// calls those pattern-vectors, and we will call them pattern-stacks. The same holds for the
3379
/// new pattern `p`.
3480
///
@@ -936,6 +982,9 @@ impl<'tcx> Constructor<'tcx> {
936982
}
937983
}
938984

985+
/// Some fields need to be explicitely hidden away in certain cases; see the comment above the
986+
/// `Fields` struct. This struct represents such a potentially-hidden field. When a field is hidden
987+
/// we still keep its type around.
939988
#[derive(Debug, Copy, Clone)]
940989
enum FilteredField<'p, 'tcx> {
941990
Kept(&'p Pat<'tcx>),
@@ -972,10 +1021,17 @@ impl<'p, 'tcx> FilteredField<'p, 'tcx> {
9721021
#[derive(Debug, Clone)]
9731022
enum Fields<'p, 'tcx> {
9741023
/// Lists of patterns that don't contain any filtered fields.
1024+
/// `Slice` and `Vec` behave the same; the difference is only to avoid allocating and
1025+
/// triple-dereferences when possible. Frankly this is premature optimization, I (Nadrieril)
1026+
/// have not measured if it really made a difference.
9751027
Slice(&'p [Pat<'tcx>]),
9761028
Vec(SmallVec<[&'p Pat<'tcx>; 2]>),
977-
/// Patterns where some of the fields need to be hidden.
978-
Filtered { fields: SmallVec<[FilteredField<'p, 'tcx>; 2]>, len: usize },
1029+
/// Patterns where some of the fields need to be hidden. `len` caches the number of non-hidden
1030+
/// fields.
1031+
Filtered {
1032+
fields: SmallVec<[FilteredField<'p, 'tcx>; 2]>,
1033+
len: usize,
1034+
},
9791035
}
9801036

9811037
impl<'p, 'tcx> Fields<'p, 'tcx> {
@@ -1098,7 +1154,8 @@ impl<'p, 'tcx> Fields<'p, 'tcx> {
10981154
pats.into_iter()
10991155
}
11001156

1101-
/// Overrides some of the fields with the provided patterns.
1157+
/// Overrides some of the fields with the provided patterns. Exactly like
1158+
/// `replace_fields_indexed`, except that it takes `FieldPat`s as input.
11021159
fn replace_with_fieldpats(
11031160
&self,
11041161
new_pats: impl IntoIterator<Item = &'p FieldPat<'tcx>>,
@@ -1108,7 +1165,11 @@ impl<'p, 'tcx> Fields<'p, 'tcx> {
11081165
)
11091166
}
11101167

1111-
/// Overrides some of the fields with the provided patterns.
1168+
/// Overrides some of the fields with the provided patterns. This is used when a pattern
1169+
/// defines some fields but not all, for example `Foo { field1: Some(_), .. }`: here we start with a
1170+
/// `Fields` that is just one wildcard per field of the `Foo` struct, and override the entry
1171+
/// corresponding to `field1` with the pattern `Some(_)`. This is also used for slice patterns
1172+
/// for the same reason.
11121173
fn replace_fields_indexed(
11131174
&self,
11141175
new_pats: impl IntoIterator<Item = (usize, &'p Pat<'tcx>)>,

0 commit comments

Comments
 (0)