Expand description

super::usefulness explains most of what is happening in this file. As explained there, values and patterns are made from constructors applied to fields. This file defines a Constructor enum, a Fields struct, and various operations to manipulate them and convert them from/to patterns.

There’s one idea that is not detailed in super::usefulness because the details are not needed there: constructor splitting.

Constructor splitting

The idea is as follows: given a constructor c and a matrix, we want to specialize in turn with all the value constructors that are covered by c, and compute usefulness for each. Instead of listing all those constructors (which is intractable), we group those value constructors together as much as possible. Example:

ⓘ
match (0, false) {
    (0 ..=100, true) => {} // `p_1`
    (50..=150, false) => {} // `p_2`
    (0 ..=200, _) => {} // `q`
}

The naive approach would try all numbers in the range 0..=200. But we can be a lot more clever: 0 and 1 for example will match the exact same rows, and return equivalent witnesses. In fact all of 0..50 would. We can thus restrict our exploration to 4 constructors: 0..50, 50..=100, 101..=150 and 151..=200. That is enough and infinitely more tractable.

We capture this idea in a function split(p_1 ... p_n, c) which returns a list of constructors c' covered by c. Given such a c', we require that all value ctors c'' covered by c' return an equivalent set of witnesses after specializing and computing usefulness. In the example above, witnesses for specializing by c'' covered by 0..50 will only differ in their first element.

We usually also ask that the c' together cover all of the original c. However we allow skipping some constructors as long as it doesn’t change whether the resulting list of witnesses is empty of not. We use this in the wildcard _ case.

Splitting is implemented in the Constructor::split function. We don’t do splitting for or-patterns; instead we just try the alternatives one-by-one. For details on splitting wildcards, see Constructor::split; for integer ranges, see IntRange::split; for slices, see Slice::split.

Structs

  • Values and patterns can be represented as a constructor applied to some fields. This represents a pattern in this form. This also uses interior mutability to keep track of whether the pattern has been found reachable during analysis. For this reason they cannot be cloned. A DeconstructedPat will almost always come from user input; the only exception are some Wildcards introduced during specialization.
  • Fields 🔒
    A value can be decomposed into a constructor applied to some fields. This struct represents those fields, generalized to allow patterns in each field. See also Constructor.
  • IntRange 🔒
    An exclusive interval, used for precise integer exhaustiveness checking. IntRanges always store a contiguous range.
  • Slice 🔒
    A constructor for array and slice patterns.
  • Describes the result of analyzing the constructors in a column of a match.
  • WitnessPat 🔒
    Same idea as DeconstructedPat, except this is a fictitious pattern built up for diagnostics purposes. As such they don’t use interning and can be cloned.

Enums

  • A value can be decomposed into a constructor applied to some fields. This struct represents the constructor. See also Fields.
  • Describes the set of all constructors for a type.
  • A possibly infinite integer. Values are encoded such that the ordering on u128 matches the natural order on the original type. For example, -128i8 is encoded as 0 and 127i8 as 255. See signed_bias for details.
  • Presence 🔒
    Whether we have seen a constructor in the column or not.
  • SliceKind 🔒

Functions

  • Recursively expand this pattern into its subpatterns. Only useful for or-patterns.