Fork of daniellemaywood.uk/gleam — Wasm codegen work
167 kB
4328 lines
1// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: 2023 The Gleam contributors
3
4//! An implementation of the algorithm described in:
5//!
6//! - How to compile pattern matching, Jules Jacobs.
7//! <https://julesjacobs.com/notes/patternmatching/patternmatching.pdf>
8//!
9//! - Efficient manipulation of binary data using pattern matching,
10//! Per Gustafsson and Konstantinos Sagonas.
11//! <https://user.it.uu.se/~kostis/Papers/JFP_06.pdf>
12//!
13//! - Compiling Pattern Matching to good Decision Trees, Luc Maranget.
14//! <https://www.cs.tufts.edu/~nr/cs257/archive/luc-maranget/jun08.pdf>
15//!
16//! The first implementation of the decision tree was adapted from Yorick
17//! Peterse's implementation at
18//! <https://github.com/yorickpeterse/pattern-matching-in-rust>.
19//! Thank you Yorick!
20//!
21//! > This module comment (and all the following doc comments) are a rough
22//! > explanation. It's great to set some expectations on what to expect from
23//! > the following code and why the data looks the way it does.
24//! > If you want a more detailed explanation, the original paper is a lot more
25//! > detailed!
26//!
27//! A case to be compiled looks a bit different from the case expressions we're
28//! used to in Gleam: instead of having a variable to match on and a series of
29//! branches, a `CaseToCompile` is made up of a series of branches that can each
30//! contain multiple pattern checks. With a psedo-Gleam syntax, this is what it
31//! would look like:
32//!
33//! ```txt
34//! case {
35//! a is Some, b is 1, c is _ -> todo
36//! a is wibble -> todo
37//! }
38//! ```
39//!
40//! > You may wonder, why are we writing branches like this? Usually a case
41//! > expression matches on a single variable and each branch refers to it. For
42//! > example in gleam you'd write:
43//! >
44//! > ```gleam
45//! > case a {
46//! > Some(_) -> todo
47//! > None -> todo
48//! > }
49//! > ```
50//! >
51//! > In out representation that would turn into:
52//! >
53//! > ```txt
54//! > case {
55//! > a is Some(_) -> todo
56//! > a is None -> todo
57//! > }
58//! > ```
59//! >
60//! > This change makes it way easier to compile the pattern matching into a
61//! > decision tree, because now we can add multiple checks on different
62//! > variables in each branch.
63//!
64//! Starting from this data structure, we'll be splitting all the branches into
65//! a decision tree that can be used to perform exhaustiveness checking and code
66//! generation.
67//!
68
69mod missing_patterns;
70pub mod printer;
71
72use crate::{
73 ast::{
74 self, AssignName, BitArraySize, Endianness, IntOperator, SrcSpan, TypedBitArraySize,
75 TypedClause, TypedPattern, TypedPatternBitArraySegment,
76 },
77 parse::LiteralFloatValue,
78 strings::{
79 convert_string_escape_chars, length_utf16, length_utf32, string_to_utf16_bytes,
80 string_to_utf32_bytes,
81 },
82 type_::{
83 Environment, Opaque, Type, TypeValueConstructor, TypeValueConstructorField, TypeVar,
84 TypeVariantConstructors, collapse_links, error::UnreachablePatternReason,
85 is_prelude_module, string,
86 },
87};
88use bitvec::{order::Msb0, slice::BitSlice, vec::BitVec, view::BitView};
89use ecow::EcoString;
90use id_arena::{Arena, Id};
91use itertools::Itertools;
92use num_bigint::{BigInt, Sign};
93use num_traits::ToPrimitive;
94use radix_trie::{Trie, TrieCommon};
95use std::{
96 cell::RefCell,
97 cmp::Ordering,
98 collections::{HashMap, HashSet, VecDeque},
99 hash::Hash,
100 sync::Arc,
101};
102
103/// A single branch composing a `case` expression to be compiled into a decision
104/// tree.
105///
106/// As shown in the module documentation, branches are a bit different from the
107/// usual branches we see in Gleam's case expressions. Each branch can perform
108/// multiple checks (each on a different variable, which appears in the check
109/// itself!):
110///
111/// ```txt
112/// a is Some, b is 1 if condition -> todo
113/// ─┬─────── ─┬──── ─┬────────── ─┬──
114/// │ │ │ ╰── body: an arbitrary expression
115/// │ │ ╰── guard: an additional boolean condition
116/// ╰──────────┴── checks: check that a variable matches with a pattern
117/// ─┬────────────────────────────────────
118/// ╰── branch: one of the branches making up a pattern matching expression
119/// ```
120///
121/// As shown here a branch can also optionally include a guard with a boolean
122/// condition and is followed by a body that is to be executed if all the checks
123/// match (and the guard evaluates to true).
124///
125#[derive(Clone, Eq, PartialEq, Debug)]
126struct Branch {
127 /// Each branch is identified by a numeric index, so we can nicely
128 /// report errors once we find something's wrong with a branch.
129 ///
130 clause_index: usize,
131
132 /// Each alternative pattern in an alternative pattern matching (e.g.
133 /// `one | two | three -> todo`) gets turned into its own branch in this
134 /// internal representation. So we also keep track of the index of the
135 /// alternative this comes from (0 being the first one and so on...)
136 ///
137 alternative_index: usize,
138 checks: Vec<PatternCheck>,
139 guard: Option<BranchGuard>,
140 body: Body,
141}
142
143/// A branch's guard, holding the index of the clause it belongs to and whether
144/// it references variables bound by the branch's patterns.
145#[derive(Clone, Copy, Eq, PartialEq, Debug)]
146struct BranchGuard {
147 clause_index: usize,
148 uses_pattern_bindings: bool,
149}
150
151impl Branch {
152 fn new(
153 clause_index: usize,
154 alternative_index: usize,
155 checks: Vec<PatternCheck>,
156 guard: Option<BranchGuard>,
157 ) -> Self {
158 Self {
159 clause_index,
160 alternative_index,
161 checks,
162 guard,
163 body: Body::new(clause_index),
164 }
165 }
166
167 /// Removes and returns a `PatternCheck` on the given variable from this
168 /// branch.
169 ///
170 fn pop_check_on_var(&mut self, var: &Variable) -> Option<PatternCheck> {
171 let index = self.checks.iter().position(|check| check.var == *var)?;
172 Some(self.checks.remove(index))
173 }
174
175 fn add_check(&mut self, check: PatternCheck) {
176 self.checks.push(check);
177 }
178
179 /// To simplify compiling the pattern we can get rid of all catch-all
180 /// patterns that are guaranteed to match by turning those into assignments.
181 ///
182 /// What does this look like in practice? Let's go over an example.
183 /// Let's say we have this case to compile:
184 ///
185 /// ```gleam
186 /// case a {
187 /// Some(1) -> Some(2)
188 /// otherwise -> otherwise
189 /// }
190 /// ```
191 ///
192 /// In our internal representation this would become:
193 ///
194 /// ```txt
195 /// case {
196 /// a is Some(1) -> Some(2)
197 /// a is otherwise -> otherwise
198 /// ─┬────────────
199 /// ╰── `a` will always match with this "catch all" variable pattern
200 /// }
201 /// ```
202 ///
203 /// Focusing on the last branch, we can remove that check that always matches
204 /// by keeping track in its body of the correspondence. So it would end up
205 /// looking like this:
206 ///
207 /// ```txt
208 /// case {
209 /// a is Some(1) -> Some(2)
210 /// ∅ -> {
211 /// ┬
212 /// ╰── This represents the fact that there's no checks left for this branch!
213 /// So we can make another observation: if there's no checks left in a
214 /// branch we know it will always match and we can produce a leaf in the
215 /// decision tree (there's an exception when we have guards, but we'll
216 /// get to it later)!
217 ///
218 /// let otherwise = a
219 /// ─┬───────────────
220 /// ╰── And now we can understand what those `bindings` at the start of
221 /// a body are: as we remove variable patterns, we will rewrite those
222 /// as assignments at the top of the body of the corresponding branch.
223 ///
224 /// otherwise
225 /// }
226 /// }
227 /// ```
228 ///
229 fn move_unconditional_patterns(&mut self, compiler: &mut Compiler<'_>) {
230 self.checks.retain_mut(|check| {
231 loop {
232 match compiler.pattern(check.pattern) {
233 // Variable patterns always match, so we move those to the body
234 // and remove them from the branch's checks.
235 Pattern::Variable { name } => {
236 self.body.assign(name.clone(), check.var.clone());
237 return false;
238 }
239 // A discard pattern always matches, but since the value is not
240 // used we can just remove it without even adding an assignment
241 // to the body!
242 Pattern::Discard => return false,
243 // Assigns are kind of special: they get turned into assignments
244 // (shocking) but then we can't discard the pattern they wrap.
245 // So we replace the assignment pattern with the one it's wrapping
246 // and try again.
247 Pattern::Assign { name, pattern } => {
248 self.body.assign(name.clone(), check.var.clone());
249 check.pattern = *pattern;
250 }
251 // There's a special case of assignments when it comes to string
252 // prefix patterns. We can give a name to a literal prefix like this:
253 // `"0" as digit <> rest`.
254 // We also want to move this special case of an assignment to the
255 // branch body!
256 Pattern::StringPrefix {
257 prefix,
258 prefix_name,
259 rest: _,
260 } => {
261 if let Some(variable) = std::mem::take(prefix_name) {
262 self.body.assign_literal_string(variable, prefix.clone());
263 }
264 return true;
265 }
266 // There's a special case of assignments when it comes to bit
267 // array patterns. We can give a name to one slice of the array and
268 // bind it to a variable to be used by later steps of the pattern
269 // like this: `<<len, payload:size(len)>>` (here we're binding
270 // two variables! `len` and `payload`).
271 //
272 // This kind of slicing will always match if it's not guarded by
273 // any size test, so if we find a `ReadAction` that is the first
274 // test to perform in a bit array pattern we know it's always
275 // going to match and can be safely moved into the branch's body.
276 Pattern::BitArray { tests } => match tests.front_mut() {
277 Some(BitArrayTest::Match(MatchTest {
278 value: BitArrayMatchedValue::Variable(name),
279 read_action,
280 })) => {
281 let bit_array = check.var.clone();
282 self.body.assign_bit_array_slice(
283 name.clone(),
284 bit_array,
285 read_action.clone(),
286 );
287 let _ = tests.pop_front();
288 }
289
290 Some(test) => match test {
291 // If we have `_ as a` we treat that as a regular variable
292 // assignment.
293 BitArrayTest::Match(MatchTest {
294 value: BitArrayMatchedValue::Assign { name, value },
295 read_action,
296 }) if value.is_discard() => {
297 *test = BitArrayTest::Match(MatchTest {
298 value: BitArrayMatchedValue::Variable(name.clone()),
299 read_action: read_action.clone(),
300 });
301 }
302
303 // Just like regular assigns, those patterns are unrefutable
304 // and will become assignments in the branch's body.
305 BitArrayTest::Match(MatchTest {
306 value: BitArrayMatchedValue::Assign { name, value },
307 read_action,
308 }) => {
309 self.body
310 .assign_segment_constant_value(name.clone(), value.as_ref());
311
312 // We will still need to check the aliased value!
313 *test = BitArrayTest::Match(MatchTest {
314 value: value.as_ref().clone(),
315 read_action: read_action.clone(),
316 });
317 }
318
319 // Discards are removed directly without even binding them
320 // in the branch's body.
321 _ if test.is_discard() => {
322 let _ = tests.pop_front();
323 }
324
325 // Otherwise there's no unconditional test to pop, we
326 // keep the pattern without changing it.
327 BitArrayTest::Size(_)
328 | BitArrayTest::Match(_)
329 | BitArrayTest::CatchAllIsBytes { .. }
330 | BitArrayTest::ReadSizeIsNotNegative { .. }
331 | BitArrayTest::SegmentIsFiniteFloat { .. } => return true,
332 },
333
334 // If a bit array pattern has no tests then it's always
335 // going to match, no matter what. We just remove it.
336 None => return false,
337 },
338
339 // A guaranteed-match slice binds the prefix name (if any) and
340 // the rest variable (if any) to the correctly sliced string.
341 Pattern::StringPrefixSlice {
342 prefix,
343 prefix_name,
344 rest_name,
345 } => {
346 let prefix = prefix.clone();
347 let prefix_name_val = std::mem::take(prefix_name);
348 let rest_name_val = std::mem::take(rest_name);
349 if let Some(name) = prefix_name_val {
350 self.body.assign_literal_string(name, prefix.clone());
351 }
352 if let Some(name) = rest_name_val {
353 self.body
354 .assign_string_slice(name, check.var.clone(), prefix);
355 }
356 return false;
357 }
358
359 // All other patterns are not unconditional, so we just keep them.
360 Pattern::Int { .. }
361 | Pattern::Float { .. }
362 | Pattern::String { .. }
363 | Pattern::Tuple { .. }
364 | Pattern::Variant { .. }
365 | Pattern::NonEmptyList { .. }
366 | Pattern::EmptyList => return true,
367 }
368 }
369 });
370 }
371}
372
373/// The body of a branch. It always starts with a series of variable assignments
374/// in the form: `let a = b`. As explained in `move_unconditional_patterns`' doc,
375/// each body starts with a series of assignments we keep track of as we're
376/// compiling each branch.
377///
378#[derive(Clone, Eq, PartialEq, Debug, serde::Serialize, serde::Deserialize)]
379pub struct Body {
380 /// Any variables to bind before running the code.
381 ///
382 /// The tuples are in the form `(name, value)`, so `(wibble, var)`
383 /// corresponds to `let wibble = var`.
384 ///
385 pub bindings: Vec<(EcoString, BoundValue)>,
386
387 /// The index of the clause in the case expression that should be run.
388 ///
389 pub clause_index: usize,
390}
391
392/// A value that can appear on the right hand side of one of the assignments we
393/// find at the top of a body.
394///
395#[derive(Clone, Eq, PartialEq, Debug, serde::Serialize, serde::Deserialize)]
396pub enum BoundValue {
397 /// `let a = variable`
398 ///
399 Variable(Variable),
400
401 /// `let a = "a literal string"`
402 ///
403 LiteralString(EcoString),
404
405 /// `let a = 123`
406 ///
407 LiteralInt(BigInt),
408
409 /// `let a = 12.2`
410 ///
411 LiteralFloat(EcoString),
412
413 /// `let a = sliceAsInt(bit_array, 0, 16, ...)`
414 ///
415 BitArraySlice {
416 bit_array: Variable,
417 read_action: ReadAction,
418 },
419
420 /// `let a = subject.slice(N)`
421 ///
422 StringSlice {
423 subject: Variable,
424 prefix: EcoString,
425 },
426}
427
428impl Body {
429 pub fn new(clause_index: usize) -> Self {
430 Self {
431 bindings: vec![],
432 clause_index,
433 }
434 }
435
436 /// Adds a new assignment to the body, binding `let variable = value`
437 ///
438 pub fn assign(&mut self, variable: EcoString, value: Variable) {
439 self.bindings.push((variable, BoundValue::Variable(value)));
440 }
441
442 fn assign_literal_string(&mut self, variable: EcoString, value: EcoString) {
443 self.bindings
444 .push((variable, BoundValue::LiteralString(value)));
445 }
446
447 fn assign_string_slice(&mut self, variable: EcoString, subject: Variable, prefix: EcoString) {
448 self.bindings
449 .push((variable, BoundValue::StringSlice { subject, prefix }));
450 }
451
452 fn assign_bit_array_slice(
453 &mut self,
454 segment_name: EcoString,
455 bit_array: Variable,
456 value: ReadAction,
457 ) {
458 self.bindings.push((
459 segment_name,
460 BoundValue::BitArraySlice {
461 bit_array,
462 read_action: value,
463 },
464 ))
465 }
466
467 fn assign_segment_constant_value(&mut self, name: EcoString, value: &BitArrayMatchedValue) {
468 let value = match value {
469 BitArrayMatchedValue::LiteralFloat(value) => BoundValue::LiteralFloat(value.clone()),
470 BitArrayMatchedValue::LiteralInt { value, .. } => BoundValue::LiteralInt(value.clone()),
471 BitArrayMatchedValue::LiteralString { value, .. } => {
472 BoundValue::LiteralString(value.clone())
473 }
474 BitArrayMatchedValue::Variable(_)
475 | BitArrayMatchedValue::Discard(_)
476 | BitArrayMatchedValue::Assign { .. } => {
477 panic!("aliased non constant value: {value:#?}")
478 }
479 };
480
481 self.bindings.push((name, value))
482 }
483}
484
485/// A user defined pattern such as `Some((x, 10))`.
486/// This is a bit simpler than the full fledged `TypedPattern` used for code analysis
487/// and only focuses on the relevant bits needed to perform exhaustiveness checking
488/// and code generation.
489///
490/// Using this simplified version of a pattern for the case compiler makes it a
491/// whole lot simpler and more efficient (patterns will have to be cloned, so
492/// we use an arena to allocate those and only store ids to make this operation
493/// extra cheap).
494///
495#[derive(Clone, Eq, PartialEq, Debug)]
496pub enum Pattern {
497 Discard,
498 Int {
499 int_value: BigInt,
500 },
501 Float {
502 float_value: LiteralFloatValue,
503 },
504 String {
505 value: EcoString,
506 },
507 StringPrefix {
508 prefix: EcoString,
509 prefix_name: Option<EcoString>,
510 rest: Id<Pattern>,
511 },
512 StringPrefixSlice {
513 prefix: EcoString,
514 prefix_name: Option<EcoString>,
515 rest_name: Option<EcoString>,
516 },
517 Assign {
518 name: EcoString,
519 pattern: Id<Pattern>,
520 },
521 Variable {
522 name: EcoString,
523 },
524 Tuple {
525 elements: Vec<Id<Pattern>>,
526 },
527 Variant {
528 index: usize,
529 used_name: EcoString,
530 name: EcoString,
531 module: Option<EcoString>,
532 fields: Vec<Id<Pattern>>,
533 },
534 NonEmptyList {
535 first: Id<Pattern>,
536 rest: Id<Pattern>,
537 },
538 EmptyList,
539 BitArray {
540 tests: VecDeque<BitArrayTest>,
541 },
542}
543
544impl Pattern {
545 /// Each pattern (with a couple exceptions) can be turned into a
546 /// simpler `RuntimeCheck`: that is a check that can be performed at runtime
547 /// to make sure a `PatternCheck` can succeed on a specific value.
548 ///
549 fn to_runtime_check_kind(&self) -> Option<RuntimeCheckKind> {
550 let kind = match self {
551 // These patterns are unconditional: they will always match and be moved
552 // out of a branch's checks. So there's no corresponding runtime check
553 // we can perform for them.
554 Pattern::Discard
555 | Pattern::Variable { .. }
556 | Pattern::Assign { .. }
557 | Pattern::StringPrefixSlice { .. } => return None,
558 Pattern::Int { int_value, .. } => RuntimeCheckKind::Int {
559 int_value: int_value.clone(),
560 },
561 Pattern::Float { float_value, .. } => RuntimeCheckKind::Float {
562 float_value: *float_value,
563 },
564 Pattern::String { value } => RuntimeCheckKind::String {
565 value: value.clone(),
566 },
567 Pattern::StringPrefix { prefix, .. } => RuntimeCheckKind::StringPrefix {
568 prefix: prefix.clone(),
569 },
570 Pattern::Tuple { elements } => RuntimeCheckKind::Tuple {
571 size: elements.len(),
572 },
573 Pattern::Variant { index, .. } => RuntimeCheckKind::Variant { index: *index },
574 Pattern::NonEmptyList { .. } => RuntimeCheckKind::NonEmptyList,
575 Pattern::EmptyList => RuntimeCheckKind::EmptyList,
576 // Bit arrays have no corresponding kind as they're dealt with in a
577 // completely different way.
578 Pattern::BitArray { .. } => return None,
579 };
580
581 Some(kind)
582 }
583
584 fn is_matching_on_unreachable_variant(&self, branch_mode: &BranchMode) -> bool {
585 match (self, branch_mode) {
586 (
587 Self::Variant { index, .. },
588 BranchMode::NamedType {
589 inferred_variant: Some(variant),
590 ..
591 },
592 ) if index != variant => true,
593 _ => false,
594 }
595 }
596
597 fn is_matching_on_impossible_segment(&self) -> Option<Vec<ImpossibleBitArraySegmentPattern>> {
598 match self {
599 Self::BitArray { tests } => {
600 let impossible_segments = tests
601 .iter()
602 .filter_map(|test| match test {
603 BitArrayTest::Size(_)
604 | BitArrayTest::CatchAllIsBytes { .. }
605 | BitArrayTest::ReadSizeIsNotNegative { .. }
606 | BitArrayTest::SegmentIsFiniteFloat { .. } => None,
607
608 BitArrayTest::Match(MatchTest { value, read_action }) => {
609 value.is_impossible_segment(read_action)
610 }
611 })
612 .collect_vec();
613
614 if impossible_segments.is_empty() {
615 None
616 } else {
617 Some(impossible_segments)
618 }
619 }
620 Self::Discard
621 | Self::Int { .. }
622 | Self::Float { .. }
623 | Self::String { .. }
624 | Self::StringPrefix { .. }
625 | Self::StringPrefixSlice { .. }
626 | Self::Assign { .. }
627 | Self::Variable { .. }
628 | Self::Tuple { .. }
629 | Self::Variant { .. }
630 | Self::NonEmptyList { .. }
631 | Self::EmptyList => None,
632 }
633 }
634}
635
636/// A single check making up a branch, checking that a variable matches with a
637/// given pattern. For example, the following branch has 2 checks:
638///
639/// ```txt
640/// a is Some, b is 1 -> todo
641/// ┬ ─┬──
642/// │ ╰── This is the pattern being checked
643/// ╰── This is the variable being pattern matched on
644/// ─┬─────── ─┬────
645/// ╰─────────┴── Two `PatternCheck`s
646/// ```
647///
648#[derive(Clone, Eq, PartialEq, Debug)]
649struct PatternCheck {
650 var: Variable,
651 pattern: Id<Pattern>,
652}
653
654/// This is one of the checks we can take at runtime to decide how to move
655/// forward in the decision tree.
656///
657/// After performing a successful check on a value we will discover something
658/// about its shape: it might be an int, an variant of a custom type, ...
659/// Some values (like variants and lists) might hold onto additional data we
660/// will have to pattern match on: in order to do that we need a name to refer
661/// to those new variables we've discovered after performing a check. That's
662/// what `args` is for.
663///
664/// Let's have a look at an example. Imagine we have a pattern like this one:
665/// `a is Wibble(1, _, [])`; after performing a runtime check to make sure `a`
666/// is indeed a `Wibble`, we'll need to perform additional checks on it's
667/// arguments: that pattern will be replaced by three new ones `a0 is 1`,
668/// `a1 is _` and `a2 is []`. Those new variables are the `args`.
669///
670#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
671pub enum RuntimeCheck {
672 Int {
673 int_value: BigInt,
674 },
675 Float {
676 float_value: LiteralFloatValue,
677 },
678 String {
679 value: EcoString,
680 },
681 StringPrefix {
682 prefix: EcoString,
683 rest: Variable,
684 },
685 Tuple {
686 size: usize,
687 elements: Vec<Variable>,
688 },
689 BitArray {
690 test: BitArrayTest,
691 },
692 Variant {
693 match_: VariantMatch,
694 index: usize,
695 labels: HashMap<usize, EcoString>,
696 fields: Vec<Variable>,
697 },
698 NonEmptyList {
699 first: Variable,
700 rest: Variable,
701 },
702 EmptyList,
703}
704
705impl RuntimeCheck {
706 fn kind(&self) -> Option<RuntimeCheckKind> {
707 let kind = match self {
708 RuntimeCheck::Int { int_value, .. } => RuntimeCheckKind::Int {
709 int_value: int_value.clone(),
710 },
711 RuntimeCheck::Float { float_value, .. } => RuntimeCheckKind::Float {
712 float_value: *float_value,
713 },
714 RuntimeCheck::String { value } => RuntimeCheckKind::String {
715 value: value.clone(),
716 },
717 RuntimeCheck::StringPrefix { prefix, rest: _ } => RuntimeCheckKind::StringPrefix {
718 prefix: prefix.clone(),
719 },
720 RuntimeCheck::Tuple { size, elements: _ } => RuntimeCheckKind::Tuple { size: *size },
721 RuntimeCheck::Variant { index, .. } => RuntimeCheckKind::Variant { index: *index },
722 RuntimeCheck::EmptyList => RuntimeCheckKind::EmptyList,
723 RuntimeCheck::NonEmptyList { first: _, rest: _ } => RuntimeCheckKind::NonEmptyList,
724 RuntimeCheck::BitArray { .. } => return None,
725 };
726 Some(kind)
727 }
728
729 pub(crate) fn is_ignored(&self) -> bool {
730 match self {
731 RuntimeCheck::Variant {
732 match_: VariantMatch::NeverExplicitlyMatchedOn { .. },
733 ..
734 } => true,
735 RuntimeCheck::Int { .. }
736 | RuntimeCheck::Float { .. }
737 | RuntimeCheck::String { .. }
738 | RuntimeCheck::StringPrefix { .. }
739 | RuntimeCheck::Tuple { .. }
740 | RuntimeCheck::BitArray { .. }
741 | RuntimeCheck::NonEmptyList { .. }
742 | RuntimeCheck::Variant { .. }
743 | RuntimeCheck::EmptyList => false,
744 }
745 }
746
747 /// Returns all the bit array segments referenced in this check.
748 /// For each segment it returns its name and the read action used to access
749 /// such segment.
750 ///
751 pub(crate) fn referenced_segment_patterns(&self) -> Vec<(&EcoString, &ReadAction)> {
752 match self {
753 RuntimeCheck::BitArray { test } => test.referenced_segment_patterns(),
754 RuntimeCheck::Int { .. }
755 | RuntimeCheck::Float { .. }
756 | RuntimeCheck::String { .. }
757 | RuntimeCheck::StringPrefix { .. }
758 | RuntimeCheck::Tuple { .. }
759 | RuntimeCheck::Variant { .. }
760 | RuntimeCheck::NonEmptyList { .. }
761 | RuntimeCheck::EmptyList => vec![],
762 }
763 }
764}
765
766#[derive(Eq, PartialEq, Clone, Hash, Debug)]
767pub enum RuntimeCheckKind {
768 Int { int_value: BigInt },
769 Float { float_value: LiteralFloatValue },
770 String { value: EcoString },
771 StringPrefix { prefix: EcoString },
772 Tuple { size: usize },
773 Variant { index: usize },
774 EmptyList,
775 NonEmptyList,
776}
777
778/// All possible variant checks are automatically generated beforehand once we
779/// know we are matching on a value with a custom type.
780/// Then if the compiled case is explicitly matching on one of those, we update
781/// it to store additional information: for example how the variant is used
782/// (if qualified or unqualified and if it is aliased).
783///
784/// This way when we get to code generation we can clump all variants that were
785/// never explicitly matched on in a single `else` block without blowing up code
786/// size!
787///
788#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
789pub enum VariantMatch {
790 ExplicitlyMatchedOn {
791 /// The locally used name, including any alias
792 used_name: EcoString,
793 /// The original name of the variant
794 name: EcoString,
795 module: Option<EcoString>,
796 },
797 NeverExplicitlyMatchedOn {
798 name: EcoString,
799 },
800}
801
802impl VariantMatch {
803 pub(crate) fn used_name(&self) -> EcoString {
804 match self {
805 VariantMatch::ExplicitlyMatchedOn { used_name, .. } => used_name.clone(),
806 VariantMatch::NeverExplicitlyMatchedOn { name } => name.clone(),
807 }
808 }
809
810 pub(crate) fn variant_name(&self) -> EcoString {
811 match self {
812 VariantMatch::ExplicitlyMatchedOn { name, .. } => name.clone(),
813 VariantMatch::NeverExplicitlyMatchedOn { name } => name.clone(),
814 }
815 }
816
817 pub(crate) fn module(&self) -> Option<EcoString> {
818 match self {
819 VariantMatch::ExplicitlyMatchedOn { module, .. } => module.clone(),
820 VariantMatch::NeverExplicitlyMatchedOn { name: _ } => None,
821 }
822 }
823}
824
825/// A variable that can be matched on in a branch.
826///
827#[derive(Eq, PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize)]
828pub struct Variable {
829 pub id: usize,
830 pub type_: Arc<Type>,
831}
832
833impl Variable {
834 fn new(id: usize, type_: Arc<Type>) -> Self {
835 Self { id, type_ }
836 }
837
838 /// Builds a `PatternCheck` that checks this variable matches the given pattern.
839 /// So we can build pattern checks the same way we informally describe them:
840 /// ```txt
841 /// var is pattern
842 /// ```
843 /// With this builder method would become:
844 /// ```rs
845 /// var.is(pattern)
846 /// ```
847 ///
848 fn is(&self, pattern: Id<Pattern>) -> PatternCheck {
849 PatternCheck {
850 var: self.clone(),
851 pattern,
852 }
853 }
854}
855
856#[derive(Debug)]
857/// Different types need to be handled differently when compiling a case expression
858/// into a decision tree. There's some types that have infinite matching patterns
859/// (like ints, strings, ...) and thus will always need a fallback option.
860///
861/// Other types, like custom types, only have a well defined and finite number
862/// of patterns that could match: when matching on a `Result` we know that we can
863/// only have an `Ok(_)` and an `Error(_)`, anything else would end up being a
864/// type error!
865///
866/// So this enum is used to pick the correct strategy to compile a case that's
867/// performing a `PatternCheck` on a variable with a specific type.
868///
869enum BranchMode {
870 /// This covers numbers, functions, variables, strings, and bitarrays.
871 Infinite,
872 Tuple {
873 elements: Vec<Arc<Type>>,
874 },
875 List {
876 inner_type: Arc<Type>,
877 },
878 NamedType {
879 constructors: Vec<TypeValueConstructor>,
880 inferred_variant: Option<usize>,
881 },
882}
883
884impl BranchMode {
885 /// Returns a heuristic estimate of the branching factor.
886 ///
887 /// This value is used by the pivot-selection to prefer splits
888 /// with fewer branches, which tends to produce smaller and shallower
889 /// decision trees.
890 fn branching_factor(&self) -> usize {
891 match self {
892 BranchMode::Infinite => usize::MAX,
893 BranchMode::Tuple { elements } => elements.len(),
894 BranchMode::List { .. } => 2,
895 BranchMode::NamedType { constructors, .. } => constructors.len(),
896 }
897 }
898
899 fn is_infinite(&self) -> bool {
900 match self {
901 BranchMode::Infinite => true,
902 BranchMode::Tuple { .. } | BranchMode::List { .. } | BranchMode::NamedType { .. } => {
903 false
904 }
905 }
906 }
907}
908
909impl Variable {
910 fn branch_mode(&self, env: &Environment<'_>) -> BranchMode {
911 match collapse_links(self.type_.clone()).as_ref() {
912 Type::Fn { .. } | Type::Var { .. } => BranchMode::Infinite,
913 Type::Named { module, name, .. }
914 if is_prelude_module(module)
915 && (name == "Int"
916 || name == "Float"
917 || name == "BitArray"
918 || name == "String") =>
919 {
920 BranchMode::Infinite
921 }
922
923 Type::Named {
924 module,
925 name,
926 arguments,
927 ..
928 } if is_prelude_module(module) && name == "List" => BranchMode::List {
929 inner_type: arguments.first().expect("list has a type argument").clone(),
930 },
931
932 Type::Tuple { elements } => BranchMode::Tuple {
933 elements: elements.clone(),
934 },
935
936 Type::Named {
937 module,
938 name,
939 arguments,
940 inferred_variant,
941 ..
942 } => {
943 let constructors = ConstructorSpecialiser::specialise_constructors(
944 env.get_constructors_for_type(module, name)
945 .expect("Custom type variants must exist"),
946 arguments.as_slice(),
947 &env.current_module,
948 module,
949 );
950
951 let inferred_variant = inferred_variant.map(|i| i as usize);
952 BranchMode::NamedType {
953 constructors,
954 inferred_variant,
955 }
956 }
957 }
958 }
959}
960
961/// When compiling a bit array pattern each segment is turned into a series of
962/// tests that all need to match in order for the pattern to succeed.
963/// Each segment might add some requirements on the total size of a bit array
964/// and/or on the value of some of its specific parts.
965///
966/// Let's look at a simple example to get started:
967///
968/// ```txt
969/// <<0:size(12), 1, rest:bits>>
970/// ─┬────────
971/// ╰── This first segment requires that the bit array must have at least
972/// 12 bits and their value must be the Int `0`. So this first
973/// segment would be turned into the following series of tests:
974/// `Size(>=, 12)` and `Match(0, ReadAction(0, 12, int))`
975/// ```
976///
977/// However, the various sizes and offsets of the different segments might not
978/// always be known at compile time and depend on previous sections of the
979/// pattern. This is no big deal: as you'll discover in more detail in the
980/// `SizeExpression`'s doc we can also represent those variable sizes:
981///
982/// ```txt
983/// <<len, payload:size(len), rest:bits>>
984/// ─┬─ ─┬───────────────
985/// │ ╰── For this segment to match the bit array must have enough bits
986/// │ for the previous segment (8 bits) and for this one (`len` bits),
987/// │ so it will turn into the following series of tests:
988/// │ `Size(>=, 8 + len)` and `Match(len, ReadAction(8, len, int))`
989/// │
990/// ╰── This first segment is 8 bits and an int (it's the default Gleam picks
991/// if no options are supplied)
992/// ```
993///
994#[derive(Clone, Eq, PartialEq, Debug, serde::Serialize, serde::Deserialize)]
995pub enum BitArrayTest {
996 Size(SizeTest),
997 Match(MatchTest),
998
999 /// This is a special test to check that the remaining part of a bit array
1000 /// has a whole number of bytes when using the `:bytes` option.
1001 ///
1002 CatchAllIsBytes {
1003 size_so_far: Offset,
1004 },
1005
1006 /// This test is made to ensure a given variable is positive: a segment
1007 /// pattern where the size is a variable with a negative value will never
1008 /// match. So we check this to make sure the test will fail.
1009 ///
1010 ReadSizeIsNotNegative {
1011 size: ReadSize,
1012 },
1013
1014 /// This test checks that the segment read by the given read action is a
1015 /// finite Float and not a `NaN` or `Infinity`.
1016 ///
1017 /// We need this check as `NaN` and `Infinity` will not match with float
1018 /// segments (like `<<_:32-float>>`) on the Erlang target and we must
1019 /// replicate the same behaviours on the JavaScript target as well.
1020 ///
1021 SegmentIsFiniteFloat {
1022 read_action: ReadAction,
1023 },
1024}
1025
1026impl BitArrayTest {
1027 fn is_discard(&self) -> bool {
1028 match self {
1029 BitArrayTest::Match(MatchTest {
1030 value: BitArrayMatchedValue::Discard(_),
1031 ..
1032 }) => true,
1033 BitArrayTest::Match(_)
1034 | BitArrayTest::Size(_)
1035 | BitArrayTest::CatchAllIsBytes { .. }
1036 | BitArrayTest::ReadSizeIsNotNegative { .. }
1037 | BitArrayTest::SegmentIsFiniteFloat { .. } => false,
1038 }
1039 }
1040
1041 pub(crate) fn referenced_segment_patterns(&self) -> Vec<(&EcoString, &ReadAction)> {
1042 match self {
1043 BitArrayTest::ReadSizeIsNotNegative { size } => {
1044 let mut references = Vec::new();
1045 size.referenced_segment_patterns(&mut references);
1046 references
1047 }
1048
1049 BitArrayTest::Size(SizeTest { operator: _, size })
1050 | BitArrayTest::CatchAllIsBytes { size_so_far: size } => {
1051 size.referenced_segment_patterns()
1052 }
1053
1054 BitArrayTest::SegmentIsFiniteFloat {
1055 read_action: ReadAction { from, size, .. },
1056 }
1057 | BitArrayTest::Match(MatchTest {
1058 read_action: ReadAction { from, size, .. },
1059 ..
1060 }) => {
1061 let mut references = vec![];
1062 references.append(&mut from.referenced_segment_patterns());
1063 size.referenced_segment_patterns(&mut references);
1064
1065 references
1066 }
1067 }
1068 }
1069}
1070
1071/// Test to make sure the bit array has a specific number of bits.
1072///
1073#[derive(Clone, Eq, PartialEq, Debug, serde::Serialize, serde::Deserialize)]
1074pub struct SizeTest {
1075 pub operator: SizeOperator,
1076 pub size: Offset,
1077}
1078
1079impl SizeTest {
1080 /// Tells us if this test is guaranteed to succeed given another test that
1081 /// we know has already succeeded.
1082 ///
1083 /// For example, ">= 5" is certainly going to succeed if ">= 6" has already
1084 /// succeeded.
1085 ///
1086 fn succeeds_if_succeeding(&self, succeeding: &SizeTest) -> Confidence {
1087 match (succeeding.operator, self.operator) {
1088 (SizeOperator::Equal, SizeOperator::Equal) if succeeding.size == self.size => {
1089 Confidence::Certain
1090 }
1091 (_, SizeOperator::GreaterEqual) => succeeding.size.greater_equal(&self.size),
1092 _ => Confidence::Uncertain,
1093 }
1094 }
1095
1096 /// Tells us if this test is guaranteed to fail given another test that
1097 /// we know has already failed.
1098 ///
1099 /// For example, ">= 5" is certainly going to fail if ">= 4" has already
1100 /// failed.
1101 ///
1102 fn fails_if_failing(&self, failing: &SizeTest) -> Confidence {
1103 match (failing.operator, self.operator) {
1104 (SizeOperator::GreaterEqual, _) => self.size.greater_equal(&failing.size),
1105 (_, _) if self == failing => Confidence::Certain,
1106 _ => Confidence::Uncertain,
1107 }
1108 }
1109
1110 /// Tells us if this test is guaranteed to fail given another test that
1111 /// we know has already succeeded.
1112 ///
1113 /// For example, "= 1" is certainly going to fail if "= 2" has already
1114 /// succeeded.
1115 ///
1116 fn fails_if_succeeding(&self, succeeding: &SizeTest) -> Confidence {
1117 match (succeeding.operator, self.operator) {
1118 (SizeOperator::GreaterEqual, SizeOperator::Equal) => {
1119 succeeding.size.greater(&self.size)
1120 }
1121 (SizeOperator::Equal, SizeOperator::GreaterEqual) => {
1122 self.size.greater(&succeeding.size)
1123 }
1124 (SizeOperator::Equal, SizeOperator::Equal) => succeeding.size.different(&self.size),
1125 _ => Confidence::Uncertain,
1126 }
1127 }
1128}
1129
1130/// Test to make sure the segment read by the specified `read_action` matches
1131/// a given value.
1132///
1133#[derive(Clone, Eq, PartialEq, Debug, serde::Serialize, serde::Deserialize)]
1134pub struct MatchTest {
1135 pub value: BitArrayMatchedValue,
1136 pub read_action: ReadAction,
1137}
1138
1139#[derive(Debug)]
1140struct Interference {
1141 interfering_bits_are_equal: bool,
1142 first_encloses_second: bool,
1143 second_encloses_first: bool,
1144}
1145
1146impl MatchTest {
1147 /// If this match test interferes with the other one, this will return
1148 /// information about how they interfere.
1149 ///
1150 ///
1151 /// # What is interference used for?
1152 ///
1153 /// Interference analysis is an optimization that we can apply to segments
1154 /// matching on a literal value with a known offset and size.
1155 /// It allows discarding many overlapping checks that we can for sure tell
1156 /// will never (or will always) match.
1157 ///
1158 /// This optimization is particularly important for network protocol
1159 /// applications where it is typical to match on some fixed patterns at the
1160 /// start of the bitarray:
1161 ///
1162 /// ```gleam
1163 /// case packet {
1164 /// <<"CONTENT_LENGTH", 0, rest:bytes>> -> todo
1165 /// <<"QUERY_STRING", 0, rest:bytes>> -> todo
1166 /// // ...
1167 /// _ -> todo
1168 /// }
1169 /// ```
1170 ///
1171 ///
1172 /// # What is interference?
1173 ///
1174 /// We say two read actions interfere with each other if:
1175 /// - They're both matching against a statically known value, we will call
1176 /// them `v1` and `v2`
1177 /// - They both start at a statically known offset, `o1` and `o2`
1178 /// - They both have a statically known size, `s1` and `s2`
1179 /// - `o1 <= o2 && o1 + s1 > o2`
1180 ///
1181 /// This is a lot of letters to say something actually quite simple, having
1182 /// a look at a graphical example will make this easier to grasp. Let's
1183 /// visualize each match action as a segment; each read action will match
1184 /// against a portion of the bit array, starting at the given offset and
1185 /// with the given number of bits (in the example I'm also showing the bits
1186 /// that the read action matches against):
1187 ///
1188 /// ```txt
1189 /// o1 (o1+s1)
1190 /// ┄├0001010110┤┄
1191 /// ┄├1101000000111011┤┄
1192 /// o2 (o2+s2)
1193 /// ```
1194 ///
1195 /// They interfere if the first one comes first (`o1 <= o2`) and the start
1196 /// of the second one falls inside the range covered by the first one
1197 /// (`o1 + s1 > p2`).
1198 /// So the example above showcases two interfering read actions, here's an
1199 /// example of two read actions that are not interfering with each other:
1200 ///
1201 /// ```txt
1202 /// o1 (o1+s1)
1203 /// ┄├0110101┤┄
1204 /// ┄├0101110101┤┄
1205 /// o2 (o2+s2)
1206 /// ```
1207 ///
1208 ///
1209 /// # How is interference useful
1210 ///
1211 /// Knowing that two read actions interfere is very useful because, knowing
1212 /// if the first one matches or not can allow us to tell for certain if an
1213 /// interfering match has no change of succeeding as well.
1214 /// Let's look at the three cases:
1215 ///
1216 /// 1. We know the first action succeeded, so the binary has the expected
1217 /// bits in the matched segment
1218 /// ```txt
1219 /// o1 (o1+s1)
1220 /// ┄├0110101111┤┄ ← This check succeded!
1221 /// ┄├0001110101┤┄
1222 /// o2 (o2+s2)
1223 /// ```
1224 /// Can the second check ever succeed? No! Since the first check
1225 /// succeeded we know for certain that the first three bits the second
1226 /// check would match against are going to be `111`, so they surely won't
1227 /// match with `000`.
1228 ///
1229 /// 2. We know the first action succeeded, and the second action is fully
1230 /// contained inside it:
1231 /// ```txt
1232 /// o1 (o1+s1)
1233 /// ┄├011010000001111┤┄ ← This check succeded!
1234 /// ┄├0100000┤┄
1235 /// o2 (o2+s2)
1236 /// ```
1237 /// In that case we know for certain that the second check will succeed
1238 /// as well (and so can be skipped) if the overlapping bits being checked
1239 /// are exactly the same.
1240 ///
1241 /// 3. We know that the first action failed, and the second one is
1242 /// fully enclosing it:
1243 /// ```txt
1244 /// o1 (o1+s1)
1245 /// ┄├010┤┄ ← This check failed!
1246 /// ┄├01000001010000001111┤┄
1247 /// o2 (o2+s2)
1248 /// ```
1249 /// Can the second check ever succeed? No! Since the first check failed we
1250 /// know for certain that the first bits are not `010`, so there's no
1251 /// chance for the second match to succeed as it requires those three bits
1252 /// to be `010` as well.
1253 ///
1254 ///
1255 /// ## What information is returned by this function
1256 ///
1257 /// If the two actions are not interfering with each other this will return
1258 /// `None`. Otherwise, it will return all the information needed by the
1259 /// three examples above (check usages of this function to see how this is
1260 /// used, hopefully it should be pretty straightforward!):
1261 /// - whether the bits in the overlapping section are the same
1262 /// - whether the first action fully encloses the second one
1263 /// - whether the second action fully encloses the first one
1264 ///
1265 fn interfering_bits(&self, other: &Self) -> Option<Interference> {
1266 // After reading the doc comment this should be pretty easy to follow:
1267 // The first requirement for interference is: `o1 <= o2`
1268 let offset_one = self.read_action.from.constant_bits()?.to_usize()?;
1269 let offset_other = other.read_action.from.constant_bits()?.to_usize()?;
1270 if offset_one > offset_other {
1271 return None;
1272 };
1273
1274 // The second requirement is that: `o1 + s1 > o2`
1275 let size_one = self.read_action.size.constant_bits()?.to_usize()?;
1276 let size_other = other.read_action.size.constant_bits()?.to_usize()?;
1277 if offset_one + size_one <= offset_other {
1278 return None;
1279 };
1280
1281 // At this point we know that both are interfering, so we compare the
1282 // overlapping slice of bits they're matching against.
1283 // A little implementation note: we're storing the matched _bytes_, not
1284 // bits, so we will be using the `view_bits` function to perform a
1285 // comparison of slices of bits.
1286 let bits_one = self.value.constant_bits()?;
1287 let bits_other = other.value.constant_bits()?;
1288 let end = (offset_other + size_other).min(offset_one + size_one);
1289
1290 let range_one = offset_one..=(offset_one + size_one);
1291 let range_other = offset_other..=(offset_other + size_other);
1292
1293 Some(Interference {
1294 interfering_bits_are_equal: bits_one[offset_other - offset_one..end - offset_one]
1295 == bits_other[0..end - offset_other],
1296 first_encloses_second: range_contains(&range_one, &range_other),
1297 second_encloses_first: range_contains(&range_other, &range_one),
1298 })
1299 }
1300}
1301
1302/// Returns true if the first range fully contains the other one.
1303fn range_contains(
1304 one: &std::ops::RangeInclusive<usize>,
1305 other: &std::ops::RangeInclusive<usize>,
1306) -> bool {
1307 one.contains(other.start()) && one.contains(other.end())
1308}
1309
1310/// A value that can be matched in a bit array pattern's segment. We do not use
1311/// a `Pattern` directly since the allowed values are actually a subset of all
1312/// the possible patterns: it can only contain literal floats, ints, strings,
1313/// a variable name, and a discard.
1314///
1315#[derive(Clone, Eq, PartialEq, Debug, serde::Serialize, serde::Deserialize)]
1316pub enum BitArrayMatchedValue {
1317 LiteralFloat(EcoString),
1318 LiteralInt {
1319 value: BigInt,
1320 /// This is carried around for better error reporting in case a segment
1321 /// is deemed unreachable: it is the location this literal value comes
1322 /// from in the whole pattern.
1323 location: SrcSpan,
1324 /// The bits representing the given literal int, with the correct
1325 /// signed- and endianness as specified in the bit array segment.
1326 bits: Result<BitVec<u8, Msb0>, IntToBitsError>,
1327 },
1328 LiteralString {
1329 value: EcoString,
1330 encoding: StringEncoding,
1331 /// The bytes representing the given literal string, with the correct
1332 /// encoding and endianness specified in the bit array segment.
1333 bytes: Vec<u8>,
1334 },
1335 Variable(EcoString),
1336 Discard(EcoString),
1337 Assign {
1338 name: EcoString,
1339 value: Box<BitArrayMatchedValue>,
1340 },
1341}
1342
1343impl BitArrayMatchedValue {
1344 /// This is an arbitrary limit beyond which interference _may_ no longer be done.
1345 /// This is necessary because people may write very silly segments like
1346 /// `<<0:9_007_199_254_740_992>>`, which would allocate around a wee petabyte of memory.
1347 /// Literal strings are already in memory, and thus ignore this limit.
1348 const MAX_BITS_INTERFERENCE: u32 = u16::MAX as u32;
1349
1350 pub(crate) fn is_literal(&self) -> bool {
1351 match self {
1352 BitArrayMatchedValue::LiteralFloat(_)
1353 | BitArrayMatchedValue::LiteralInt { .. }
1354 | BitArrayMatchedValue::LiteralString { .. } => true,
1355 BitArrayMatchedValue::Variable(..) | BitArrayMatchedValue::Discard(..) => false,
1356 BitArrayMatchedValue::Assign { value, .. } => value.is_literal(),
1357 }
1358 }
1359
1360 /// If the matched value is a literal value for which we implement
1361 /// interference pruning, this returns the bits representing it to be used
1362 /// for interference.
1363 ///
1364 fn constant_bits(&self) -> Option<&BitSlice<u8, Msb0>> {
1365 match self {
1366 BitArrayMatchedValue::LiteralString { bytes, .. } => Some(bytes.view_bits::<Msb0>()),
1367 BitArrayMatchedValue::Assign { value, .. } => value.constant_bits(),
1368 BitArrayMatchedValue::LiteralInt { bits, .. } => bits.as_deref().ok(),
1369
1370 // TODO: We could also implement the interfering optimisation for
1371 // literal floats as well, but the usefulness is questionable
1372 BitArrayMatchedValue::LiteralFloat(_)
1373 | BitArrayMatchedValue::Variable(_)
1374 | BitArrayMatchedValue::Discard(_) => None,
1375 }
1376 }
1377
1378 /// If we can statically tell the segment will never match, this will return
1379 /// the reason why it can't match. Otherwise it returns `None`.
1380 ///
1381 fn is_impossible_segment(
1382 &self,
1383 read_action: &ReadAction,
1384 ) -> Option<ImpossibleBitArraySegmentPattern> {
1385 match self {
1386 BitArrayMatchedValue::Assign { value, .. } => value.is_impossible_segment(read_action),
1387 BitArrayMatchedValue::LiteralInt {
1388 value,
1389 location,
1390 bits,
1391 } => match bits {
1392 Err(IntToBitsError::Unrepresentable { size }) => {
1393 Some(ImpossibleBitArraySegmentPattern::UnrepresentableInteger {
1394 value: value.clone(),
1395 size: *size,
1396 location: *location,
1397 signed: read_action.signed,
1398 })
1399 }
1400 _ => None,
1401 },
1402
1403 BitArrayMatchedValue::LiteralFloat(_)
1404 | BitArrayMatchedValue::LiteralString { .. }
1405 | BitArrayMatchedValue::Variable(_)
1406 | BitArrayMatchedValue::Discard(_) => None,
1407 }
1408 }
1409
1410 /// Returns true if the two `MatchedValue`s are matching for the exact same
1411 /// thing. Note how this doesn't automatically mean they will be matching
1412 /// for the exact same thing in a whole bit array pattern as they could be
1413 /// matching at different positions!
1414 ///
1415 /// ```gleam
1416 /// <<1, _>>
1417 /// <<_, 1>>
1418 /// // Both are matching on the same value but at different positions!
1419 /// ```
1420 ///
1421 fn checking_same_value(&self, other: &Self) -> bool {
1422 match (self, other) {
1423 // Ints need special care: they're carrying around information about
1424 // where they come from. But as for equality is concerned for the
1425 // pattern match compilation, they are considered equal as long as
1426 // the two values are equal, no matter where the values come from.
1427 (Self::LiteralInt { value: one, .. }, Self::LiteralInt { value: other, .. }) => {
1428 one == other
1429 }
1430 (Self::LiteralInt { .. }, _) => false,
1431
1432 // All the other cases follow the standard equality implementation.
1433 (Self::LiteralFloat(one), Self::LiteralFloat(other)) => one == other,
1434 (Self::LiteralFloat(_), _) => false,
1435
1436 // Two literal string matches are the same if they match for the
1437 // same bytes. No matter the original starting string and encoding.
1438 (Self::LiteralString { bytes: one, .. }, Self::LiteralString { bytes: other, .. }) => {
1439 one == other
1440 }
1441 (Self::LiteralString { .. }, _) => false,
1442
1443 (Self::Variable(one), Self::Variable(other)) => one == other,
1444 (Self::Variable(_), _) => false,
1445
1446 (Self::Discard(_), Self::Discard(_)) => true,
1447 (Self::Discard(_), _) => false,
1448
1449 (
1450 Self::Assign {
1451 name: _,
1452 value: one,
1453 },
1454 Self::Assign {
1455 name: _,
1456 value: other,
1457 },
1458 ) => one.checking_same_value(other),
1459 (Self::Assign { .. }, _) => false,
1460 }
1461 }
1462}
1463
1464#[derive(Clone, Copy, Eq, PartialEq, Debug, serde::Serialize, serde::Deserialize)]
1465pub enum StringEncoding {
1466 Utf8,
1467 Utf16,
1468 Utf32,
1469}
1470
1471impl BitArrayMatchedValue {
1472 pub fn is_discard(&self) -> bool {
1473 match self {
1474 BitArrayMatchedValue::Discard(_) => true,
1475 BitArrayMatchedValue::LiteralFloat(_)
1476 | BitArrayMatchedValue::LiteralInt { .. }
1477 | BitArrayMatchedValue::LiteralString { .. }
1478 | BitArrayMatchedValue::Variable(_)
1479 | BitArrayMatchedValue::Assign { .. } => false,
1480 }
1481 }
1482}
1483
1484impl BitArrayTest {
1485 /// Wether two bit array tests are equivalent, that is they are checking
1486 /// for the same thing.
1487 ///
1488 fn equivalent_to(&self, other: &Self) -> bool {
1489 match (self, other) {
1490 (BitArrayTest::Size(one), BitArrayTest::Size(other)) => one == other,
1491 (BitArrayTest::Size(_), _) => false,
1492
1493 (
1494 BitArrayTest::Match(MatchTest { value, read_action }),
1495 BitArrayTest::Match(MatchTest {
1496 value: other_value,
1497 read_action: other_read_action,
1498 }),
1499 ) => value.checking_same_value(other_value) && read_action == other_read_action,
1500 (BitArrayTest::Match(_), _) => false,
1501
1502 (
1503 BitArrayTest::CatchAllIsBytes { size_so_far: one },
1504 BitArrayTest::CatchAllIsBytes { size_so_far: other },
1505 ) => one == other,
1506 (BitArrayTest::CatchAllIsBytes { .. }, ..) => false,
1507
1508 (
1509 BitArrayTest::ReadSizeIsNotNegative { size: one },
1510 BitArrayTest::ReadSizeIsNotNegative { size: other },
1511 ) => one == other,
1512 (BitArrayTest::ReadSizeIsNotNegative { .. }, _) => false,
1513
1514 (
1515 BitArrayTest::SegmentIsFiniteFloat { read_action: one },
1516 BitArrayTest::SegmentIsFiniteFloat { read_action: other },
1517 ) => one == other,
1518 (BitArrayTest::SegmentIsFiniteFloat { .. }, _) => false,
1519 }
1520 }
1521
1522 /// Tells us if this test is guaranteed to succeed given another test that
1523 /// we know has already succeeded.
1524 ///
1525 /// For example, "size >= 5" is certainly going to succeed if "size >= 6"
1526 /// has already succeeded.
1527 ///
1528 #[must_use]
1529 fn succeeds_if_succeeding(&self, succeeding: &BitArrayTest) -> Confidence {
1530 match (succeeding, self) {
1531 (one, other) if one.equivalent_to(other) => Confidence::Certain,
1532 (BitArrayTest::Size(succeeding), BitArrayTest::Size(test)) => {
1533 test.succeeds_if_succeeding(succeeding)
1534 }
1535 (BitArrayTest::Match(succeeding), BitArrayTest::Match(test)) => {
1536 // This is example 2. in the doc comment of `interfering_bits`:
1537 // if the bits are the same and the second one is fully enclosed
1538 // in the first one, then the second one will succeed as well!
1539 if let Some(Interference {
1540 interfering_bits_are_equal: true,
1541 first_encloses_second: true,
1542 ..
1543 }) = succeeding.interfering_bits(test)
1544 {
1545 Confidence::Certain
1546 } else {
1547 Confidence::Uncertain
1548 }
1549 }
1550 // The tests are not comparable, we can't deduce any new information.
1551 _ => Confidence::Uncertain,
1552 }
1553 }
1554
1555 /// Tells us if this test is guaranteed to fail given another test that
1556 /// we know has already failed.
1557 ///
1558 /// For example, "size >= 5" is certainly going to fail if "size >= 4"
1559 /// has already failed.
1560 ///
1561 #[must_use]
1562 fn fails_if_failing(&self, failing: &BitArrayTest) -> Confidence {
1563 match (failing, self) {
1564 (one, other) if one.equivalent_to(other) => Confidence::Certain,
1565 (BitArrayTest::Size(failing), BitArrayTest::Size(test)) => {
1566 test.fails_if_failing(failing)
1567 }
1568 (BitArrayTest::Match(succeeding), BitArrayTest::Match(test)) => {
1569 // This is example 3. in the doc comment of `interfering_bits`:
1570 // if the bits are the same and the second one is fully
1571 // enclosing the first one, then the second one will fail as
1572 // well!
1573 if let Some(Interference {
1574 interfering_bits_are_equal: true,
1575 second_encloses_first: true,
1576 ..
1577 }) = succeeding.interfering_bits(test)
1578 {
1579 Confidence::Certain
1580 } else {
1581 Confidence::Uncertain
1582 }
1583 }
1584 // The tests are not comparable, we can't deduce any new information.
1585 _ => Confidence::Uncertain,
1586 }
1587 }
1588
1589 /// Tells us if this test is guaranteed to fail given another test that
1590 /// we know has already succeeded.
1591 ///
1592 /// For example, "size = 1" is certainly going to fail if "size = 2" has already
1593 /// succeeded.
1594 ///
1595 #[must_use]
1596 fn fails_if_succeeding(&self, succeeding: &BitArrayTest) -> Confidence {
1597 match (succeeding, self) {
1598 // This is an implementation of Table 6 (a) of the bit array pattern
1599 // matching paper linked at the top of this module's documentation!
1600 (BitArrayTest::Size(succeeding), BitArrayTest::Size(test)) => {
1601 test.fails_if_succeeding(succeeding)
1602 }
1603 (BitArrayTest::Match(succeeding), BitArrayTest::Match(test)) => {
1604 // This is example 1. in the doc comment of `interfering_bits`:
1605 // if the bits are the different and the first one is succeeding
1606 // then we know the second one will fail.
1607 if let Some(Interference {
1608 interfering_bits_are_equal: false,
1609 ..
1610 }) = succeeding.interfering_bits(test)
1611 {
1612 Confidence::Certain
1613 } else {
1614 Confidence::Uncertain
1615 }
1616 }
1617 // The tests are not comparable, we can't deduce any new information.
1618 _ => Confidence::Uncertain,
1619 }
1620 }
1621}
1622
1623/// When performing a size test it could require that a size is exactly some
1624/// specific number of bits (for example if we're matching the last segment
1625/// of a bit array) or that it has at least some number of bits. For example:
1626///
1627/// ```txt
1628/// <<0:size(12), 1:size(8)>>
1629/// ─┬──────── ─┬───────
1630/// │ ╰── For this segment to successfully match the bit array must
1631/// │ have exactly 20 bits: because it will need to read 8 bits
1632/// │ from bit 13 where the previous one ends: `Size(=, 20)`
1633/// │
1634/// ╰── For this segment to successfully match, the bit array must have at
1635/// least 12 bits: `Size(>, 12)`
1636/// ```
1637///
1638#[derive(Clone, Eq, PartialEq, Debug, Copy, serde::Serialize, serde::Deserialize)]
1639pub enum SizeOperator {
1640 GreaterEqual,
1641 Equal,
1642}
1643
1644/// Represents the action of reading a certain number of bits from a bit array
1645/// at a given position, returning a value with the given type.
1646///
1647/// Notice how the starting position and number of bits to read might not be
1648/// known at compile time but also include variables! For example here:
1649///
1650/// ```txt
1651// <<len, payload:size(len), rest:bits>>
1652/// ─┬─ ─┬───────────────
1653/// │ ╰── Here payload has a variable size, given by the `len` variable
1654/// │
1655/// ╰── While this segment has a constant size of 8 bits
1656/// ```
1657///
1658#[derive(Clone, Eq, PartialEq, Debug, Hash, serde::Serialize, serde::Deserialize)]
1659pub struct ReadAction {
1660 /// The offset to start reading the bits from.
1661 ///
1662 pub from: Offset,
1663 /// Number of _bits_ to read.
1664 ///
1665 pub size: ReadSize,
1666 /// The type of the read value.
1667 ///
1668 pub type_: ReadType,
1669 /// Endianness of the read value.
1670 ///
1671 pub endianness: Endianness,
1672 /// Signedness of the read value.
1673 ///
1674 pub signed: bool,
1675}
1676
1677/// Only a subset of all the possible Gleam types can be used for a pattern
1678/// segment. We enumerate those out explicitly here.
1679///
1680#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
1681pub enum ReadType {
1682 Float,
1683 Int,
1684 String,
1685 BitArray,
1686 UtfCodepoint,
1687}
1688
1689impl ReadType {
1690 pub(crate) fn is_int(&self) -> bool {
1691 match self {
1692 ReadType::Int => true,
1693 ReadType::Float | ReadType::String | ReadType::BitArray | ReadType::UtfCodepoint => {
1694 false
1695 }
1696 }
1697 }
1698
1699 pub(crate) fn is_float(&self) -> bool {
1700 match self {
1701 ReadType::Float => true,
1702 ReadType::Int | ReadType::String | ReadType::BitArray | ReadType::UtfCodepoint => false,
1703 }
1704 }
1705}
1706
1707#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1708enum Confidence {
1709 Certain,
1710 Uncertain,
1711}
1712
1713impl Confidence {
1714 fn is_certain(&self) -> bool {
1715 match self {
1716 Confidence::Certain => true,
1717 Confidence::Uncertain => false,
1718 }
1719 }
1720
1721 fn or_else(&self, fun: impl Fn() -> Confidence) -> Confidence {
1722 match self {
1723 Confidence::Certain => Confidence::Certain,
1724 Confidence::Uncertain => fun(),
1725 }
1726 }
1727}
1728
1729/// An offset, in bits, into a bit array. An offset contains three parts. For
1730/// example, in the following pattern:
1731/// ```txt
1732/// <<a, b:size(a), c:size(b - 1), payload:size(c)>>
1733/// ```
1734///
1735/// The start of the `payload` segment is the sum of the length of `a` (which we
1736/// know is 1 byte), plus the size of `b`, which is the value of `a`, plus the
1737/// size of `c`, which is 1 less than the value of `b`.
1738///
1739/// Here, `constant` would be `8`; `variables` would map `a` to `1`, because it
1740/// is referenced once, in a segment with unit `1`; `calculations` would contain
1741/// `b - 1`.
1742///
1743#[derive(Clone, Eq, PartialEq, Debug, Hash, serde::Serialize, serde::Deserialize)]
1744pub struct Offset {
1745 pub constant: BigInt,
1746 pub variables: im::HashMap<VariableUsage, usize>,
1747 pub calculations: im::Vector<OffsetCalculation>,
1748}
1749
1750#[derive(Clone, Eq, PartialEq, Debug, Hash, serde::Serialize, serde::Deserialize)]
1751pub struct OffsetCalculation {
1752 pub left: Offset,
1753 pub right: Offset,
1754 pub operator: IntOperator,
1755}
1756
1757impl Offset {
1758 pub fn constant(value: impl Into<BigInt>) -> Self {
1759 Self {
1760 constant: value.into(),
1761 variables: im::HashMap::new(),
1762 calculations: im::Vector::new(),
1763 }
1764 }
1765
1766 fn from_size(size: &ReadSize) -> Self {
1767 Self::constant(0).add_size(size)
1768 }
1769
1770 fn add_size(mut self, size: &ReadSize) -> Offset {
1771 match size {
1772 ReadSize::ConstantBits(value) => Self {
1773 constant: self.constant + value,
1774 variables: self.variables,
1775 calculations: self.calculations,
1776 },
1777 ReadSize::VariableBits { variable, unit } => Self {
1778 constant: self.constant,
1779 variables: self.variables.alter(
1780 |value| match value {
1781 Some(value) => Some(value + (*unit as usize)),
1782 None => Some(*unit as usize),
1783 },
1784 variable.as_ref().clone(),
1785 ),
1786 calculations: self.calculations,
1787 },
1788 ReadSize::RemainingBits | ReadSize::RemainingBytes => self,
1789
1790 ReadSize::BinaryOperator {
1791 left,
1792 right,
1793 operator,
1794 } => match operator {
1795 IntOperator::Add => self.add_size(left).add_size(right),
1796 IntOperator::Subtract
1797 | IntOperator::Multiply
1798 | IntOperator::Divide
1799 | IntOperator::Remainder => {
1800 self.calculations.push_back(OffsetCalculation {
1801 left: Self::from_size(left),
1802 right: Self::from_size(right),
1803 operator: *operator,
1804 });
1805 self
1806 }
1807 },
1808 }
1809 }
1810
1811 pub fn add_constant(&self, constant: impl Into<BigInt>) -> Offset {
1812 Offset {
1813 constant: self.constant.clone() + constant.into(),
1814 variables: self.variables.clone(),
1815 calculations: self.calculations.clone(),
1816 }
1817 }
1818
1819 /// Returns `true` if we can tell for certain this offset is greater than
1820 /// another one.
1821 ///
1822 fn greater(&self, other: &Offset) -> Confidence {
1823 // We can't easily tell if one calculation is greater than another, so
1824 // we can only be certain about one being greater if there are no calculations
1825 if self.constant > other.constant
1826 && self.calculations.is_empty()
1827 && other.calculations.is_empty()
1828 && superset(&self.variables, &other.variables)
1829 {
1830 Confidence::Certain
1831 } else {
1832 Confidence::Uncertain
1833 }
1834 }
1835
1836 /// Returns `true` if we can tell for certain this offset is greater or equal
1837 /// to another one.
1838 ///
1839 fn greater_equal(&self, other: &Offset) -> Confidence {
1840 // Like `greater`, we can only be certain if there are no calculations
1841 if self == other
1842 || (self.constant >= other.constant
1843 && self.calculations.is_empty()
1844 && other.calculations.is_empty()
1845 && superset(&self.variables, &other.variables))
1846 {
1847 Confidence::Certain
1848 } else {
1849 Confidence::Uncertain
1850 }
1851 }
1852
1853 /// Returns `true` if we can tell for certain this offset is different from
1854 /// another one.
1855 ///
1856 fn different(&self, other: &Offset) -> Confidence {
1857 self.greater(other).or_else(|| other.greater(self))
1858 }
1859
1860 pub(crate) fn is_zero(&self) -> bool {
1861 self.constant == BigInt::ZERO && self.variables.is_empty() && self.calculations.is_empty()
1862 }
1863
1864 /// If this offset has a constant size, returns its size in bits.
1865 ///
1866 pub(crate) fn constant_bits(&self) -> Option<BigInt> {
1867 if self.variables.is_empty() && self.calculations.is_empty() {
1868 Some(self.constant.clone())
1869 } else {
1870 None
1871 }
1872 }
1873
1874 /// If this offset has a constant size that's a whole number of bytes,
1875 /// returns its size in bytes.
1876 ///
1877 pub(crate) fn constant_bytes(&self) -> Option<BigInt> {
1878 let bits = self.constant_bits()?;
1879 if bits.clone() % 8 == BigInt::ZERO {
1880 Some(bits / 8)
1881 } else {
1882 None
1883 }
1884 }
1885
1886 fn referenced_segment_patterns(&self) -> Vec<(&EcoString, &ReadAction)> {
1887 let mut references = Vec::new();
1888
1889 for variable_usage in self.variables.keys() {
1890 match variable_usage {
1891 VariableUsage::PatternSegment(segment_name, read_action) => {
1892 references.push((segment_name, read_action));
1893 }
1894 VariableUsage::OutsideVariable(..) => {}
1895 }
1896 }
1897
1898 for calculation in self.calculations.iter() {
1899 references.extend(calculation.left.referenced_segment_patterns());
1900 references.extend(calculation.right.referenced_segment_patterns());
1901 }
1902
1903 references
1904 }
1905}
1906
1907/// The number of bits to read in a read action when reading a segment.
1908///
1909#[derive(Clone, Eq, PartialEq, Debug, Hash, serde::Serialize, serde::Deserialize)]
1910pub enum ReadSize {
1911 /// Read a constant, compile-time-known number of bits.
1912 ///
1913 /// ```txt
1914 /// <<tag:size(8)>>
1915 /// ─┬─────────
1916 /// ╰─ Here we know that we have to read exactly 8 bits
1917 /// ```
1918 ///
1919 ConstantBits(BigInt),
1920 /// Read a variable number of bits.
1921 ///
1922 /// ```txt
1923 /// <<len, payload:size(len)>>
1924 /// ─┬───────────────
1925 /// ╰─ Here we will know how many bits to read only at runtime since
1926 /// the length is a variable
1927 /// ```
1928 ///
1929 VariableBits {
1930 variable: Box<VariableUsage>,
1931 unit: u8,
1932 },
1933
1934 /// A maths expression calculating the read size from one or more variables.
1935 ///
1936 /// ```txt
1937 /// <<len, payload:size(len * 8)>>
1938 /// ─┬───────────────────
1939 /// ╰─ Here we have to calculate the length by performing the
1940 /// multiplication at runtime
1941 /// ```
1942 BinaryOperator {
1943 left: Box<ReadSize>,
1944 right: Box<ReadSize>,
1945 operator: IntOperator,
1946 },
1947
1948 /// Read all the remaining bits in the bit array when using a catch all
1949 /// pattern.
1950 ///
1951 /// ```txt
1952 /// <<_, rest:bits>>
1953 /// ─┬───────
1954 /// ╰─ We take all the remaining bits from the bit array
1955 /// ```
1956 ///
1957 RemainingBits,
1958 RemainingBytes,
1959}
1960
1961impl ReadSize {
1962 /// If this is a constant number of bits returns it wrapped in `Some`.
1963 pub(crate) fn constant_bits(&self) -> Option<BigInt> {
1964 match self {
1965 ReadSize::ConstantBits(value) => Some(value.clone()),
1966 ReadSize::VariableBits { .. }
1967 | ReadSize::BinaryOperator { .. }
1968 | ReadSize::RemainingBits
1969 | ReadSize::RemainingBytes => None,
1970 }
1971 }
1972
1973 /// If this is a constant number of bytes returns it wrapped in `Some`.
1974 pub(crate) fn constant_bytes(&self) -> Option<BigInt> {
1975 let bits = self.constant_bits()?;
1976 if bits.clone() % 8 == BigInt::ZERO {
1977 Some(bits / 8)
1978 } else {
1979 None
1980 }
1981 }
1982
1983 fn referenced_segment_patterns<'a>(
1984 &'a self,
1985 references: &mut Vec<(&'a EcoString, &'a ReadAction)>,
1986 ) {
1987 match self {
1988 ReadSize::VariableBits { variable, unit: _ } => match variable.as_ref() {
1989 VariableUsage::PatternSegment(segment_value, read_action) => {
1990 references.push((segment_value, read_action));
1991 }
1992 VariableUsage::OutsideVariable(..) => (),
1993 },
1994
1995 ReadSize::BinaryOperator { left, right, .. } => {
1996 left.referenced_segment_patterns(references);
1997 right.referenced_segment_patterns(references);
1998 }
1999
2000 ReadSize::ConstantBits(..) | ReadSize::RemainingBits | ReadSize::RemainingBytes => (),
2001 };
2002 }
2003
2004 fn can_be_negative(&self) -> bool {
2005 match self {
2006 ReadSize::ConstantBits(value) => *value < BigInt::ZERO,
2007 ReadSize::VariableBits { variable, .. } => match variable.as_ref() {
2008 VariableUsage::PatternSegment(_, read_action) => read_action.signed,
2009 VariableUsage::OutsideVariable(_) => true,
2010 },
2011 ReadSize::BinaryOperator {
2012 left,
2013 right,
2014 operator,
2015 } => {
2016 *operator == IntOperator::Subtract
2017 || left.can_be_negative()
2018 || right.can_be_negative()
2019 }
2020 ReadSize::RemainingBits | ReadSize::RemainingBytes => false,
2021 }
2022 }
2023}
2024
2025#[derive(Clone, Eq, PartialEq, Debug, Hash, serde::Serialize, serde::Deserialize)]
2026pub enum VariableUsage {
2027 /// A bit array named segment that was brought into scope in the bit array
2028 /// pattern itself and might be referenced by later segments.
2029 PatternSegment(EcoString, ReadAction),
2030 /// A variable defined somewhere else
2031 OutsideVariable(EcoString),
2032}
2033
2034impl VariableUsage {
2035 pub fn name(&self) -> &EcoString {
2036 match self {
2037 VariableUsage::PatternSegment(name, _) | VariableUsage::OutsideVariable(name) => name,
2038 }
2039 }
2040}
2041
2042/// This is the decision tree that a pattern matching expression gets turned
2043/// into: it's a tree-like structure where each path to a root node contains a
2044/// series of checks to perform at runtime to understand if a value matches with
2045/// a given pattern.
2046///
2047#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
2048pub enum Decision {
2049 /// This is the final node of the tree, once we get to this one we know we
2050 /// have a body to run because a given pattern matched.
2051 ///
2052 Run { body: Body },
2053
2054 /// We have to make this decision when we run into a branch that also has a
2055 /// guard: if it is true we can finally run the body of the branch, stored in
2056 /// `if_true`.
2057 /// If it is false we might still have to take other decisions and so we might
2058 /// have another `DecisionTree` to traverse, stored in `if_false`.
2059 ///
2060 Guard {
2061 guard: usize,
2062 if_true: Body,
2063 if_false: Box<Decision>,
2064 },
2065
2066 /// When reaching this node we'll have to see if any of the possible checks
2067 /// in `choices` will succeed on `var`. If it does, we know that's the path
2068 /// we have to go down to. If none of the checks matches, then we'll have to
2069 /// go down the `fallback` branch.
2070 ///
2071 /// The type system guarantees that all switches will always have at least
2072 /// one last choice that acts as a fallback to pick if none of the others
2073 /// matches; no matter the value we're matching on.
2074 ///
2075 /// In case we're dealing with exhaustive cases (for example on lists/custom
2076 /// types) we also keep track of the final check associated with the final
2077 /// branch: keep in mind, we don't actually have to run that check because
2078 /// we know that the type system guarantees it will always match; but it
2079 /// can hold useful informations for type generation so we keep it around.
2080 /// You can read the doc for `FallbackCheck` to find out a more in depth
2081 /// explanation and some examples!
2082 ///
2083 Switch {
2084 var: Variable,
2085 choices: Vec<(RuntimeCheck, Decision)>,
2086 fallback: Box<Decision>,
2087 fallback_check: Box<FallbackCheck>,
2088 },
2089
2090 /// This is a special node: it represents a missing pattern. If a tree
2091 /// contains such a node, then we know that the patterns it was compiled
2092 /// from are not exhaustive and the path leading to this node will describe
2093 /// what kind of pattern doesn't match!
2094 ///
2095 Fail,
2096}
2097
2098/// When we have a swith in the decision tree we know there's always going to be
2099/// at least one choice that comes last and we know is going to match no matter
2100/// what. This might fall under three different categories.
2101///
2102#[derive(Debug, Eq, PartialEq, Clone, serde::Serialize, serde::Deserialize)]
2103pub enum FallbackCheck {
2104 /// This corresponds to the catch all added at the end of a case expression
2105 /// matching on an infinite type.
2106 ///
2107 /// ```txt
2108 /// case todo {
2109 /// 1 -> todo
2110 /// _ -> todo
2111 /// ─┬───────
2112 // ╰── when matching on an infinite type there's going to be a catch all
2113 /// }
2114 ///
2115 InfiniteCatchAll,
2116
2117 /// This happens when we're matching on a variant whose checks are all
2118 /// explicitly written down:
2119 ///
2120 /// ```txt
2121 /// case todo {
2122 /// Ok(_) -> todo
2123 /// Error(Nil) -> todo
2124 /// ─┬────────────────
2125 // ╰── when matching on a custom type (or list!) we'll have to write all
2126 // variants down, so there's always going to be a last one.
2127 /// }
2128 /// ```
2129 ///
2130 /// The type system will make sure that this last check will always match,
2131 /// no matter what, if none of the other checks matches. We still keep the
2132 /// corresponding runtime check around because it's useful for code generation!
2133 ///
2134 RuntimeCheck { check: RuntimeCheck },
2135
2136 /// This is a special case for a catch all! It happens when we're matching
2137 /// on a variant and use a catch all pattern:
2138 ///
2139 /// ```txt
2140 /// case todo {
2141 /// Ok(_) -> todo
2142 /// _ -> todo
2143 /// ─┬───────
2144 /// ╰── here we know we're just skipping over the `Error` variant with
2145 /// this catch all.
2146 /// }
2147 /// ```
2148 ///
2149 /// We know exactly which variants we're skipping, so we keep track of
2150 /// those skipped checks (they will end up being useful for reporting
2151 /// missing patterns!)
2152 ///
2153 CatchAll { ignored_checks: Vec<RuntimeCheck> },
2154}
2155
2156impl Decision {
2157 pub fn run(body: Body) -> Self {
2158 Decision::Run { body }
2159 }
2160
2161 pub fn guard(guard: usize, if_true: Body, if_false: Self) -> Self {
2162 Decision::Guard {
2163 guard,
2164 if_true,
2165 if_false: Box::new(if_false),
2166 }
2167 }
2168}
2169
2170/// The `case` compiler itself (shocking, I know).
2171///
2172#[derive(Debug)]
2173struct Compiler<'a> {
2174 environment: &'a Environment<'a>,
2175 patterns: Arena<Pattern>,
2176 variable_id: usize,
2177 diagnostics: Diagnostics,
2178}
2179
2180/// The result of compiling a pattern match expression.
2181///
2182#[derive(Debug)]
2183pub struct CompileCaseResult {
2184 pub compiled_case: CompiledCase,
2185 pub diagnostics: Diagnostics,
2186}
2187
2188impl CompileCaseResult {
2189 pub fn is_reachable(&self, clause: usize, pattern_index: usize) -> Reachability {
2190 if self
2191 .diagnostics
2192 .reachable
2193 .contains(&(clause, pattern_index))
2194 {
2195 Reachability::Reachable
2196 } else if self
2197 .diagnostics
2198 .match_impossible_variants
2199 .contains(&(clause, pattern_index))
2200 {
2201 Reachability::Unreachable(UnreachablePatternReason::ImpossibleVariant)
2202 } else if let Some(segments) = self
2203 .diagnostics
2204 .match_impossible_segments
2205 .get(&(clause, pattern_index))
2206 {
2207 Reachability::Unreachable(UnreachablePatternReason::ImpossibleSegments(
2208 segments.clone(),
2209 ))
2210 } else {
2211 Reachability::Unreachable(UnreachablePatternReason::DuplicatePattern)
2212 }
2213 }
2214
2215 pub fn missing_patterns(&self, environment: &Environment<'_>) -> Vec<EcoString> {
2216 missing_patterns::missing_patterns(self, environment)
2217 }
2218}
2219
2220#[derive(Debug, PartialEq, Eq, Clone, serde::Serialize, serde::Deserialize)]
2221pub struct CompiledCase {
2222 pub tree: Decision,
2223 pub subject_variables: Vec<Variable>,
2224}
2225
2226impl CompiledCase {
2227 pub fn failure() -> Self {
2228 Self {
2229 tree: Decision::Fail,
2230 subject_variables: vec![],
2231 }
2232 }
2233
2234 /// The decision tree for simple variable assignment, such as in the following
2235 /// assignment:
2236 /// ```gleam
2237 /// let x = 10
2238 /// ```
2239 pub fn simple_variable_assignment(name: EcoString, type_: Arc<Type>) -> Self {
2240 let variable = Variable::new(0, type_);
2241 let mut body = Body::new(0);
2242 body.assign(name, variable.clone());
2243 Self {
2244 tree: Decision::Run { body },
2245 subject_variables: vec![variable],
2246 }
2247 }
2248}
2249
2250/// Whether a pattern is reachable, or why it is unreachable.
2251///
2252#[derive(Debug, Clone, PartialEq, Eq)]
2253pub enum Reachability {
2254 Reachable,
2255 Unreachable(UnreachablePatternReason),
2256}
2257
2258/// A type for storing diagnostics produced by the decision tree compiler.
2259///
2260#[derive(Debug)]
2261pub struct Diagnostics {
2262 /// A flag indicating the match is missing one or more pattern.
2263 pub missing: bool,
2264
2265 /// The patterns that are reachable. If a pattern isn't in this list it
2266 /// means it is redundant.
2267 /// Each entry is a pair of indices: The index of the clause the pattern
2268 /// belongs to, followed by the index of the pattern itself within the
2269 /// clause. For example, in this case expression:
2270 /// ```gleam
2271 /// case x {
2272 /// 1 -> todo
2273 /// 2 | 3 -> todo
2274 /// _ -> todo
2275 /// }
2276 /// ```
2277 ///
2278 /// The `3` pattern would be `(1, 1)`, because it is the second pattern of
2279 /// the second clause, and both are zero-indexed.
2280 ///
2281 pub reachable: HashSet<(usize, usize)>,
2282
2283 /// Patterns which match on variants of a type which the compiler
2284 /// can tell will never be present, due to variant inference.
2285 ///
2286 /// See `reachable` for an explanation of its structure.
2287 ///
2288 pub match_impossible_variants: HashSet<(usize, usize)>,
2289
2290 /// Patterns that are matching on bit array segments the compiler can tell
2291 /// for sure will never match.
2292 ///
2293 pub match_impossible_segments: HashMap<(usize, usize), Vec<ImpossibleBitArraySegmentPattern>>,
2294}
2295
2296#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
2297pub enum ImpossibleBitArraySegmentPattern {
2298 UnrepresentableInteger {
2299 value: BigInt,
2300 size: u32,
2301 location: SrcSpan,
2302 signed: bool,
2303 },
2304}
2305
2306impl ImpossibleBitArraySegmentPattern {
2307 pub fn location(&self) -> SrcSpan {
2308 match self {
2309 ImpossibleBitArraySegmentPattern::UnrepresentableInteger { location, .. } => *location,
2310 }
2311 }
2312}
2313
2314impl<'a> Compiler<'a> {
2315 fn new(environment: &'a Environment<'a>, variable_id: usize, patterns: Arena<Pattern>) -> Self {
2316 Self {
2317 environment,
2318 patterns,
2319 variable_id,
2320 diagnostics: Diagnostics {
2321 missing: false,
2322 reachable: HashSet::new(),
2323 match_impossible_variants: HashSet::new(),
2324 match_impossible_segments: HashMap::new(),
2325 },
2326 }
2327 }
2328
2329 fn pattern(&mut self, pattern_id: Id<Pattern>) -> &mut Pattern {
2330 self.patterns
2331 .get_mut(pattern_id)
2332 .expect("unknown pattern id")
2333 }
2334
2335 /// Returns a new fresh variable (i.e. guaranteed to have a unique `variable_id`)
2336 /// with the given type.
2337 ///
2338 fn fresh_variable(&mut self, type_: Arc<Type>) -> Variable {
2339 let var = Variable::new(self.variable_id, type_);
2340 self.variable_id += 1;
2341 var
2342 }
2343
2344 fn mark_as_reached(&mut self, branch: &Branch) {
2345 let _ = self
2346 .diagnostics
2347 .reachable
2348 .insert((branch.clause_index, branch.alternative_index));
2349 }
2350
2351 fn mark_as_matching_impossible_variant(&mut self, branch: &Branch) {
2352 let _ = self
2353 .diagnostics
2354 .reachable
2355 .remove(&(branch.clause_index, branch.alternative_index));
2356 let _ = self
2357 .diagnostics
2358 .match_impossible_variants
2359 .insert((branch.clause_index, branch.alternative_index));
2360 }
2361
2362 fn mark_as_matching_impossible_segment(
2363 &mut self,
2364 branch: &Branch,
2365 segments: Vec<ImpossibleBitArraySegmentPattern>,
2366 ) {
2367 let _ = self
2368 .diagnostics
2369 .reachable
2370 .remove(&(branch.clause_index, branch.alternative_index));
2371 let _ = self
2372 .diagnostics
2373 .match_impossible_segments
2374 .insert((branch.clause_index, branch.alternative_index), segments);
2375 }
2376
2377 fn compile(&mut self, mut branches: VecDeque<Branch>) -> Decision {
2378 branches
2379 .iter_mut()
2380 .for_each(|branch| branch.move_unconditional_patterns(self));
2381
2382 let Some(first_branch) = branches.front() else {
2383 // If there's no branches, that means we have a pattern that is not
2384 // exhaustive as there's nothing that could match!
2385 self.diagnostics.missing = true;
2386 return Decision::Fail;
2387 };
2388
2389 self.mark_as_reached(first_branch);
2390
2391 // In order to compile the branches, we need to pick a `PatternCheck` from
2392 // the first branch, and use the variable it's pattern matching on to create
2393 // a new node in the tree. All the branches will be split into different
2394 // possible paths of this tree.
2395 match find_pivot_check(first_branch, &branches, self.environment) {
2396 Some(PatternCheck { var, .. }) => self.split_and_compile_with_pivot_var(var, branches),
2397
2398 // If the branch has no remaining checks, it means that we've moved all
2399 // its variable patterns as assignments into the body and there's no
2400 // additional checks remaining. So the only thing left that could result
2401 // in the match failing is the additional guard.
2402 None => match first_branch.guard {
2403 // If there's no guard we're in the following situation:
2404 // `∅ -> body`. It means that this branch will always match no
2405 // matter what, all the remaining branches are just discarded and
2406 // we can produce a terminating node to run the body
2407 // unconditionally.
2408 None => Decision::run(first_branch.body.clone()),
2409 // If we have a guard we're in this scenario:
2410 // `∅ if condition -> body`. We can produce a `Guard` node:
2411 // if the condition evaluates to `True` we can run its body.
2412 // Otherwise, we'll have to keep looking at the remaining branches
2413 // to know what to do if this branch doesn't match.
2414 Some(BranchGuard { clause_index, .. }) => {
2415 let if_true = first_branch.body.clone();
2416 // All the remaining branches will be compiled and end up
2417 // in the path of the tree to choose if the guard is false.
2418 let _ = branches.pop_front();
2419 let if_false = self.compile(branches);
2420 Decision::guard(clause_index, if_true, if_false)
2421 }
2422 },
2423 }
2424 }
2425
2426 fn split_and_compile_with_pivot_var(
2427 &mut self,
2428 pivot_var: Variable,
2429 branches: VecDeque<Branch>,
2430 ) -> Decision {
2431 // We first try and come up with a list of all the runtime checks we might
2432 // have to perform on the variable at runtime. In most cases it's a limited
2433 // number of checks that we know before hand (for example, when matching
2434 // on a list, or on a custom type).
2435 let branch_mode = pivot_var.branch_mode(self.environment);
2436 let known_checks = match &branch_mode {
2437 // If the type being matched on is infinite there's no known runtime
2438 // check we could come up with in advance. So we'll build them as
2439 // we go.
2440 BranchMode::Infinite => vec![],
2441
2442 // If the type is a tuple there's only one runtime check we could
2443 // perform that actually makes sense.
2444 BranchMode::Tuple { elements } => vec![self.is_tuple_check(elements)],
2445
2446 // If the type being matched on is a list we know the resulting
2447 // decision tree node is only ever going to have two different paths:
2448 // one to follow if the list is empty, and one to follow if it's not.
2449 BranchMode::List { inner_type } => {
2450 vec![
2451 RuntimeCheck::EmptyList,
2452 self.is_list_check(inner_type.clone()),
2453 ]
2454 }
2455
2456 // If we know that a specific variant is inferred we will require just
2457 // that one and not all the other ones we know for sure are not going to
2458 // be there.
2459 BranchMode::NamedType {
2460 constructors,
2461 inferred_variant: Some(index),
2462 } => {
2463 let constructor = constructors
2464 .get(*index)
2465 .expect("wrong index for inferred variant");
2466 vec![self.is_variant_check(*index, constructor)]
2467 }
2468
2469 // Otherwise we know we'll need a check for each of its possible variants.
2470 BranchMode::NamedType { constructors, .. } => constructors
2471 .iter()
2472 .enumerate()
2473 .map(|(index, constructor)| self.is_variant_check(index, constructor))
2474 .collect_vec(),
2475 };
2476
2477 // We then split all the branches using these checks and compile the
2478 // choices they've been split up into.
2479 let mut splitter = BranchSplitter::from_checks(known_checks);
2480 self.split_branches(&mut splitter, branches, pivot_var.clone(), &branch_mode);
2481 if branch_mode.is_infinite() {
2482 // If the branching is infinite, that means we always need to also have
2483 // a fallback (imagine you're pattern matching on an `Int` and put no
2484 // `_` at the end of the case expression).
2485 self.splitter_to_switch(pivot_var, splitter)
2486 } else if splitter.choices.is_empty() {
2487 // If the branching doesn't need any fallback but we ended up with no
2488 // checks it means we're trying to pattern match on an external type
2489 // but haven't provided a catch-all case.
2490 // That's never going to match, so we produce a failure node.
2491 self.diagnostics.missing = true;
2492 Decision::Fail
2493 } else {
2494 // Otherwise we know that one of the possible runtime checks is always
2495 // going to succeed and there's no need to also have a fallback branch.
2496 self.splitter_to_exhaustive_switch(pivot_var, splitter)
2497 }
2498 }
2499
2500 fn split_branches(
2501 &mut self,
2502 splitter: &mut BranchSplitter,
2503 branches: VecDeque<Branch>,
2504 pivot_var: Variable,
2505 branch_mode: &BranchMode,
2506 ) {
2507 for mut branch in branches {
2508 let Some(pattern_check) = branch.pop_check_on_var(&pivot_var) else {
2509 // If the branch doesn't perform any check on the pivot variable, it means
2510 // it could still match no matter what shape `pivot_var` has. So we must
2511 // add it as a fallback branch, that is a branch that is still relevant
2512 // for all possible paths in the decision tree.
2513 splitter.add_fallback_branch(branch);
2514 continue;
2515 };
2516
2517 let checked_pattern = self.pattern(pattern_check.pattern);
2518
2519 if checked_pattern.is_matching_on_unreachable_variant(branch_mode) {
2520 self.mark_as_matching_impossible_variant(&branch);
2521 continue;
2522 } else if let Some(unreachable_segments) =
2523 checked_pattern.is_matching_on_impossible_segment()
2524 {
2525 self.mark_as_matching_impossible_segment(&branch, unreachable_segments);
2526 continue;
2527 }
2528
2529 splitter.add_checked_branch(pattern_check, branch, branch_mode, self);
2530 }
2531 }
2532
2533 /// Compiles the branches in a splitter down to a switch matching on a type
2534 /// with infinite variants (like ints, floats and strings). With a fallaback
2535 /// branch.
2536 ///
2537 fn splitter_to_switch(&mut self, var: Variable, splitter: BranchSplitter) -> Decision {
2538 let choices = self.compile_all_choices(splitter.choices);
2539 let last_choice = self.compile(splitter.fallback);
2540 Decision::Switch {
2541 var,
2542 choices,
2543 fallback: Box::new(last_choice),
2544 fallback_check: Box::new(FallbackCheck::InfiniteCatchAll),
2545 }
2546 }
2547
2548 /// Compiles the branches in a splitter down to a switch matching on a type
2549 /// with a finite known number of variants.
2550 ///
2551 fn splitter_to_exhaustive_switch(
2552 &mut self,
2553 var: Variable,
2554 splitter: BranchSplitter,
2555 ) -> Decision {
2556 let mut choices = splitter.choices;
2557 let (choices, fallback, fallback_check) =
2558 if choices.iter().any(|(check, _)| check.is_ignored()) {
2559 // If there's any check that is ignored and not explicitly being
2560 // matched on then we want to ignore all of those and clump them
2561 // into a single "fallback" branch to avoid bloating the generated
2562 // tree.
2563 let mut ignored_checks = vec![];
2564 let mut remaining_choices = vec![];
2565 for choice in choices.into_iter() {
2566 if choice.0.is_ignored() {
2567 ignored_checks.push(choice.0)
2568 } else {
2569 remaining_choices.push(choice)
2570 }
2571 }
2572
2573 let fallback_check = FallbackCheck::CatchAll { ignored_checks };
2574 (remaining_choices, splitter.fallback, fallback_check)
2575 } else {
2576 // Otherwise we just use the last check as the final one that
2577 // can be outright skipped.
2578 let (last_check, last_choice) = choices.pop().expect("at least one choice");
2579 let fallback_check = FallbackCheck::RuntimeCheck { check: last_check };
2580 (choices, last_choice, fallback_check)
2581 };
2582
2583 let choices = self.compile_all_choices(choices);
2584 let fallback = Box::new(self.compile(fallback));
2585 Decision::Switch {
2586 var,
2587 choices,
2588 fallback,
2589 fallback_check: Box::new(fallback_check),
2590 }
2591 }
2592
2593 fn compile_all_choices(
2594 &mut self,
2595 choices: Vec<(RuntimeCheck, VecDeque<Branch>)>,
2596 ) -> Vec<(RuntimeCheck, Decision)> {
2597 choices
2598 .into_iter()
2599 .map(|(check, branches)| (check, self.compile(branches)))
2600 .collect_vec()
2601 }
2602
2603 /// Turns a `RuntimeCheckKind` into a new `RuntimeCheck` by coming up with
2604 /// the needed new fresh variables.
2605 /// All the type information needed to create these variables is in the
2606 /// `branch_mode` arg.
2607 ///
2608 fn fresh_runtime_check(
2609 &mut self,
2610 kind: RuntimeCheckKind,
2611 branch_mode: &BranchMode,
2612 ) -> RuntimeCheck {
2613 match (kind, branch_mode) {
2614 (RuntimeCheckKind::Int { int_value }, _) => RuntimeCheck::Int { int_value },
2615 (RuntimeCheckKind::Float { float_value }, _) => RuntimeCheck::Float { float_value },
2616 (RuntimeCheckKind::String { value }, _) => RuntimeCheck::String { value },
2617 (RuntimeCheckKind::StringPrefix { prefix }, _) => RuntimeCheck::StringPrefix {
2618 prefix,
2619 rest: self.fresh_variable(string()),
2620 },
2621 (RuntimeCheckKind::Tuple { .. }, BranchMode::Tuple { elements }) => {
2622 self.is_tuple_check(elements)
2623 }
2624 (RuntimeCheckKind::Variant { index }, BranchMode::NamedType { constructors, .. }) => {
2625 self.is_variant_check(
2626 index,
2627 constructors.get(index).expect("unknown variant index"),
2628 )
2629 }
2630 (RuntimeCheckKind::EmptyList, _) => RuntimeCheck::EmptyList,
2631 (RuntimeCheckKind::NonEmptyList, BranchMode::List { inner_type }) => {
2632 self.is_list_check(inner_type.clone())
2633 }
2634 (_, _) => unreachable!("type checking should make this impossible"),
2635 }
2636 }
2637
2638 /// Comes up with new pattern cecks that have to match in case a given
2639 /// runtime check succeeds for the given pattern.
2640 ///
2641 /// Let's make an example: when we have a pattern - say `a is Wibble(1, [])` -
2642 /// we come up with a runtime check to perform on it. For our example the
2643 /// runtime check is to make sure that `a` is indeed a `Wibble` variant.
2644 /// However, after successfully performing that check we're left with much to
2645 /// do! We know that `a` is `Wibble` but now we'll have to make sure that its
2646 /// inner arguments also match the given patterns. So the new additional checks
2647 /// we have to add are `a0 is 1, a1 is []` (where `a0` and `a1` are the fresh
2648 /// variable names we use to refer to the constructor's arguments).
2649 ///
2650 fn new_checks(
2651 &mut self,
2652 subject: &Variable,
2653 for_pattern: &Pattern,
2654 after_succeding_check: &RuntimeCheck,
2655 ) -> Vec<PatternCheck> {
2656 match (for_pattern, after_succeding_check) {
2657 // These patterns never result in adding new checks. After a runtime
2658 // check matches on them there's nothing else to discover.
2659 (
2660 Pattern::Discard
2661 | Pattern::Assign { .. }
2662 | Pattern::Variable { .. }
2663 | Pattern::Int { .. }
2664 | Pattern::Float { .. }
2665 | Pattern::BitArray { .. }
2666 | Pattern::EmptyList,
2667 _,
2668 )
2669 | (Pattern::String { .. }, RuntimeCheck::String { .. }) => vec![],
2670
2671 // After making sure a value is not an empty list we'll have to perform
2672 // additional checks on its first item and on the tail.
2673 (
2674 Pattern::NonEmptyList {
2675 first: first_pattern,
2676 rest: rest_pattern,
2677 },
2678 RuntimeCheck::NonEmptyList {
2679 first: first_variable,
2680 rest: rest_variable,
2681 },
2682 ) => vec![
2683 first_variable.is(*first_pattern),
2684 rest_variable.is(*rest_pattern),
2685 ],
2686
2687 // After making sure a value is a specific variant we'll have to check each
2688 // of its arguments respects the given patterns (as shown in the doc example for
2689 // this function!)
2690 (
2691 Pattern::Variant {
2692 fields: patterns, ..
2693 },
2694 RuntimeCheck::Variant {
2695 fields: variables, ..
2696 },
2697 ) => (variables.iter().zip(patterns))
2698 .map(|(field, pattern)| field.is(*pattern))
2699 .collect_vec(),
2700
2701 // Tuples are exactly the same as variants: after making sure we're dealing with
2702 // a tuple, we will have to check that each of its elements matches the given
2703 // pattern: `a is #(1, _)` will result in the following checks
2704 // `a0 is 1, a1 is _` (where `a0` and `a1` are fresh variable names we use to
2705 // refer to each of the tuple's elements).
2706 (
2707 Pattern::Tuple { elements: patterns },
2708 RuntimeCheck::Tuple {
2709 elements: variables,
2710 ..
2711 },
2712 ) => (variables.iter().zip(patterns))
2713 .map(|(element, pattern)| element.is(*pattern))
2714 .collect_vec(),
2715
2716 // Strings are quite fun: if we've checked at runtime a string starts with a given
2717 // prefix and we want to check that it's some overlapping literal value we'll still
2718 // have some amount of work to perform.
2719 //
2720 // Let's have a look at an example: the pattern we care about is `a is "wibble"`
2721 // and we've just successfully ran the runtime check for `a is "wib" <> rest`.
2722 // So we know the string already starts with `"wib"` what we have to check now
2723 // is that the remaining part `rest` is `"ble"`.
2724 (Pattern::String { value }, RuntimeCheck::StringPrefix { prefix, rest, .. }) => {
2725 let remaining = value.strip_prefix(prefix.as_str()).unwrap_or(value);
2726 vec![rest.is(self.string_pattern(remaining))]
2727 }
2728
2729 // String prefixes are similar to strings, but a bit more involved. Let's say we're
2730 // checking the pattern:
2731 //
2732 // ```txt
2733 // "wibblest" <> rest1
2734 // ─┬────────
2735 // ╰── We will refer to this as `prefix1`
2736 // ```
2737 //
2738 // And we know that the following overlapping runtime check has already succeeded:
2739 //
2740 // ```txt
2741 // "wibble" <> rest0
2742 // ─┬──────
2743 // ╰── We will refer to this as `prefix0`
2744 // ```
2745 //
2746 // We're lucky because we now know quite a bit about the shape of the string. Since
2747 // we know it already starts with `"wibble"` we can just check that the remaining
2748 // part after that starts with the missing part of the prefix:
2749 // `prefix0 is "st" <> rest1`.
2750 //
2751 // The opposite can also happen. The prefix we've already checked might be longer
2752 // than (and start with) the pattern's prefix. This comes up when an earlier branch
2753 // matched the longer prefix under a guard that then failed, so control falls
2754 // through to this shorter-prefix branch. For example we might be checking the
2755 // pattern `"wib" <> rest1` knowing that `"wibble" <> rest0` already succeeded.
2756 //
2757 // This rests on an invariant established by `check_overlaps`. A shorter prefix is
2758 // only ever paired with an already-succeeded longer prefix when that longer prefix
2759 // starts with it. Two disjoint prefixes (say `"b"` reached after `"aaa" <> rest0`
2760 // succeeded) are never routed here together. So when `prefix1.strip_prefix(prefix0)`
2761 // returns `None` below it is precisely because `prefix0` starts with `prefix1`, and
2762 // the pattern's prefix is therefore already guaranteed to match. If it binds nothing
2763 // we're done. Otherwise we rebind its `rest1` to the value with the (shorter) prefix
2764 // stripped off.
2765 (
2766 Pattern::StringPrefix {
2767 prefix: prefix1,
2768 prefix_name,
2769 rest: rest1,
2770 },
2771 RuntimeCheck::StringPrefix {
2772 prefix: prefix0,
2773 rest: rest0,
2774 },
2775 ) => {
2776 if let Some(remaining) = prefix1.strip_prefix(prefix0.as_str()) {
2777 // The pattern's prefix extends (or equals) the one we've already checked.
2778 // If the prefixes are exactly the same then the only remaining check is
2779 // for the two remaining bits to be the same.
2780 if remaining.is_empty() {
2781 vec![rest0.is(*rest1)]
2782 } else {
2783 vec![rest0.is(self.string_prefix_pattern(remaining, *rest1))]
2784 }
2785 } else if prefix_name.is_none() && matches!(self.pattern(*rest1), Pattern::Discard)
2786 {
2787 // The prefix we've already checked is longer than the pattern's prefix, so
2788 // the pattern is guaranteed to match. As it binds nothing there's nothing
2789 // left to do.
2790 vec![]
2791 } else {
2792 // The pattern is guaranteed to match but it does bind something. Use
2793 // StringPrefixSlice so the backend only emits the slice, not a redundant
2794 // startsWith check.
2795 let rest_pattern = self.pattern(*rest1);
2796 let rest_name = if let Pattern::Variable { name } = rest_pattern {
2797 Some(name.clone())
2798 } else if matches!(rest_pattern, Pattern::Discard) {
2799 None
2800 } else {
2801 unreachable!("rest should be Variable or Discard, got {rest_pattern:?}")
2802 };
2803 let pattern = self.patterns.alloc(Pattern::StringPrefixSlice {
2804 prefix: prefix1.clone(),
2805 prefix_name: prefix_name.clone(),
2806 rest_name,
2807 });
2808
2809 vec![subject.is(pattern)]
2810 }
2811 }
2812
2813 (_, _) => unreachable!("invalid pattern overlapping"),
2814 }
2815 }
2816
2817 /// Builds an `IsVariant` runtime check, coming up with new fresh variable names
2818 /// for its arguments.
2819 ///
2820 fn is_variant_check(
2821 &mut self,
2822 index: usize,
2823 constructor: &TypeValueConstructor,
2824 ) -> RuntimeCheck {
2825 RuntimeCheck::Variant {
2826 index,
2827 match_: VariantMatch::NeverExplicitlyMatchedOn {
2828 name: constructor.name.clone(),
2829 },
2830 labels: constructor
2831 .parameters
2832 .iter()
2833 .enumerate()
2834 .filter_map(|(i, parameter)| Some((i, parameter.label.clone()?)))
2835 .collect(),
2836 fields: constructor
2837 .parameters
2838 .iter()
2839 .map(|parameter| parameter.type_.clone())
2840 .map(|type_| self.fresh_variable(type_))
2841 .collect_vec(),
2842 }
2843 }
2844
2845 /// Builds an `IsNonEmptyList` runtime check, coming up with fresh variable
2846 /// names for its arguments.
2847 ///
2848 fn is_list_check(&mut self, inner_type: Arc<Type>) -> RuntimeCheck {
2849 RuntimeCheck::NonEmptyList {
2850 first: self.fresh_variable(inner_type.clone()),
2851 rest: self.fresh_variable(Arc::new(Type::list(inner_type))),
2852 }
2853 }
2854
2855 /// Builds an `IsTuple` runtime check, coming up with fresh variable
2856 /// names for its arguments.
2857 ///
2858 fn is_tuple_check(&mut self, elements: &[Arc<Type>]) -> RuntimeCheck {
2859 RuntimeCheck::Tuple {
2860 size: elements.len(),
2861 elements: elements
2862 .iter()
2863 .map(|type_| self.fresh_variable(type_.clone()))
2864 .collect_vec(),
2865 }
2866 }
2867
2868 /// Allocates a new `StringPattern` with the given value.
2869 ///
2870 fn string_pattern(&mut self, value: &str) -> Id<Pattern> {
2871 self.patterns.alloc(Pattern::String {
2872 value: EcoString::from(value),
2873 })
2874 }
2875
2876 fn bit_array_pattern(&mut self, tests: VecDeque<BitArrayTest>) -> Id<Pattern> {
2877 self.patterns.alloc(Pattern::BitArray { tests })
2878 }
2879
2880 /// Allocates a new `StringPrefix` pattern with the given prefix and pattern
2881 /// for the rest of the string.
2882 ///
2883 fn string_prefix_pattern(&mut self, prefix: &str, rest: Id<Pattern>) -> Id<Pattern> {
2884 self.patterns.alloc(Pattern::StringPrefix {
2885 prefix: EcoString::from(prefix),
2886 prefix_name: None,
2887 rest,
2888 })
2889 }
2890}
2891
2892/// Returns a pattern check from `first_branch` to be used as a pivot to split all
2893/// the `branches`.
2894///
2895fn find_pivot_check(
2896 first_branch: &Branch,
2897 branches: &VecDeque<Branch>,
2898 env: &Environment<'_>,
2899) -> Option<PatternCheck> {
2900 // To try and minimise code duplication, we use the following heuristic: we
2901 // choose the check matching on the variable that is referenced the most
2902 // across all checks in all branches.
2903 let mut var_references = HashMap::new();
2904 for branch in branches {
2905 for check in &branch.checks {
2906 let _ = var_references
2907 .entry(check.var.id)
2908 .and_modify(|references| *references += 1)
2909 .or_insert(0);
2910 }
2911 }
2912
2913 // We want to picke the variable that has the smallest branching factor
2914 // possible, this tends to yield smaller, shallower decision trees.
2915 // So, for example, we will pick a list (branching factor of 2 `[]` and
2916 // `[_, ..]`) over an integer (infinitely many choices).
2917 //
2918 // In case two variables are tied we break the tie by choosing the subject
2919 // that appears in the most clauses (i.e. a test that will pay off for more
2920 // rows).
2921 //
2922 // Both parts mirror standard guidance for good match trees (small branching
2923 // factor; prioritize columns that are widely useful):
2924 // https://www.cs.tufts.edu/~nr/cs257/archive/luc-maranget/jun08.pdf
2925 first_branch
2926 .checks
2927 .iter()
2928 .min_by_key(|check| {
2929 let branching_factor = check.var.branch_mode(env).branching_factor();
2930 let references = var_references.get(&check.var.id).cloned().unwrap_or(0);
2931
2932 // Notice how we're using `-references`: we want to favour the one
2933 // that has the most references, so the one were `-references` is
2934 // the smallest.
2935 (branching_factor, -references)
2936 })
2937 .cloned()
2938}
2939
2940/// A handy data structure we use to split branches in different possible paths
2941/// based on a check.
2942///
2943struct BranchSplitter {
2944 pub choices: Vec<(RuntimeCheck, VecDeque<Branch>)>,
2945 pub fallback: VecDeque<Branch>,
2946
2947 /// This is used to allow quickly looking up a choice in the `choices`
2948 /// vector, without loosing track of the checks' order.
2949 indices: HashMap<RuntimeCheckKind, usize>,
2950
2951 /// This is used to store the indices of just the prefix checks as they have
2952 /// different rules from all the other `RuntimeCheckKinds` whose indices are
2953 /// instead stored in the `indices` field.
2954 ///
2955 /// We discuss this in more detail in the `index_of_overlapping_runtime_check`
2956 /// function!
2957 prefix_indices: Trie<String, usize>,
2958}
2959
2960impl BranchSplitter {
2961 /// Creates a new splitter with the given starting checks.
2962 ///
2963 fn from_checks(checks: Vec<RuntimeCheck>) -> Self {
2964 let mut choices = Vec::with_capacity(checks.len());
2965 let mut indices = HashMap::new();
2966
2967 for (index, runtime_check) in checks.into_iter().enumerate() {
2968 let Some(kind) = runtime_check.kind() else {
2969 continue;
2970 };
2971 let _ = indices.insert(kind, index);
2972 choices.push((runtime_check, VecDeque::new()));
2973 }
2974
2975 Self {
2976 fallback: VecDeque::new(),
2977 choices,
2978 indices,
2979 prefix_indices: Trie::new(),
2980 }
2981 }
2982
2983 /// Add a fallback branch: this is a branch that is relevant to all possible
2984 /// paths as it could still run, no matter the result of any of the `Check`s
2985 /// we've stored!
2986 ///
2987 fn add_fallback_branch(&mut self, branch: Branch) {
2988 self.choices
2989 .iter_mut()
2990 .for_each(|(_, branches)| branches.push_back(branch.clone()));
2991 self.fallback.push_back(branch);
2992 }
2993
2994 /// Given a branch and the pattern its using to check on the pivot variable,
2995 /// adds it to the paths where it's relevant, that is where we know from
2996 /// previous checks that this pattern has a chance of matching.
2997 ///
2998 fn add_checked_branch(
2999 &mut self,
3000 pattern_check: PatternCheck,
3001 mut branch: Branch,
3002 branch_mode: &BranchMode,
3003 compiler: &mut Compiler<'_>,
3004 ) {
3005 let pattern = compiler.pattern(pattern_check.pattern).clone();
3006
3007 // Bit array patterns are split in a different way that requires special
3008 // handling. Instead of reasoning on overlapping checks what we do it we
3009 // always split the decision tree in two distinct paths based on one of
3010 // the bit array pattern's tests.
3011 if let Pattern::BitArray { tests } = pattern {
3012 self.add_checked_bit_array_branch(pattern_check, tests, branch, compiler);
3013 return;
3014 };
3015
3016 let kind = pattern
3017 .to_runtime_check_kind()
3018 .expect("no unconditional patterns left");
3019
3020 let CheckOverlaps {
3021 overlapping,
3022 needs_new_choice,
3023 } = self.check_overlaps(&kind);
3024
3025 // The branch is relevant to every existing choice its check overlaps
3026 // with, so we add it (together with any newly discovered checks) to all
3027 // of those paths. For a string prefix this also includes any longer
3028 // prefixes that start with it. When one of those matched but a guard
3029 // then failed, control falls through to this shorter prefix.
3030 for index in overlapping.iter() {
3031 let (overlapping_check, branches) = self
3032 .choices
3033 .get_mut(*index)
3034 .expect("check to already be a choice");
3035
3036 let mut branch = branch.clone();
3037 for new_check in compiler.new_checks(&pattern_check.var, &pattern, overlapping_check) {
3038 branch.add_check(new_check);
3039 }
3040 branches.push_back(branch);
3041 }
3042
3043 // Unless the check is fully subsumed by an existing choice, we also add
3044 // it as a new path we might have to go down to in the decision tree.
3045 // A shorter prefix still needs its own choice even when it overlaps
3046 // earlier longer prefixes, so it can match values outside those.
3047 if needs_new_choice {
3048 self.save_index_of_new_choice(kind.clone());
3049
3050 let check = compiler.fresh_runtime_check(kind, branch_mode);
3051 for new_check in compiler.new_checks(&pattern_check.var, &pattern, &check) {
3052 branch.add_check(new_check);
3053 }
3054 let mut branches = self.fallback.clone();
3055 branches.push_back(branch);
3056
3057 self.choices.push((check, branches));
3058 }
3059
3060 // Then we have to update all variant checks with any new name/module
3061 // we might have discovered from the pattern to make sure we're using
3062 // the correct qualification to refer to each constructor.
3063 if let Pattern::Variant {
3064 used_name,
3065 name,
3066 module,
3067 ..
3068 } = pattern
3069 {
3070 for index in overlapping {
3071 let (check, _) = self
3072 .choices
3073 .get_mut(index)
3074 .expect("check to already be a choice");
3075 if let RuntimeCheck::Variant { match_, .. } = check {
3076 *match_ = VariantMatch::ExplicitlyMatchedOn {
3077 module: module.clone(),
3078 used_name: used_name.clone(),
3079 name: name.clone(),
3080 }
3081 }
3082 }
3083 }
3084 }
3085
3086 /// When we work with bit array patterns the splitter follows a different
3087 /// strategy to split branches: instead of trying to create a multiway
3088 /// decision tree with possibly many branches, we create only two branches
3089 /// based on the first segment test we run into.
3090 /// One path is going to be for the branches that can match if the test is
3091 /// successful, and the other one is going to be the usual fallback to go
3092 /// down to if it's not successful.
3093 ///
3094 /// > "And now for the tricky bit..."
3095 /// > <https://youtu.be/lKXe3HUG2l4?si=i1thYB-kjfMU8NSe&t=645>
3096 ///
3097 fn add_checked_bit_array_branch(
3098 &mut self,
3099 pattern_check: PatternCheck,
3100 tests: VecDeque<BitArrayTest>,
3101 mut branch: Branch,
3102 compiler: &mut Compiler<'_>,
3103 ) {
3104 // If we haven't found a test yet we just use the first one we find
3105 // as the pivot to split all the branches.
3106 let pivot_test = match self.choices.as_slice() {
3107 [] => {
3108 let test = tests.front().expect("empty bit array test").clone();
3109 self.choices.push((
3110 RuntimeCheck::BitArray { test: test.clone() },
3111 VecDeque::new(),
3112 ));
3113 test
3114 }
3115 [(RuntimeCheck::BitArray { test }, _)] => test.clone(),
3116 _ => unreachable!("non bit array check when splitting bit array patterns"),
3117 };
3118
3119 let pattern_fails_if_test_succeeds = tests
3120 .iter()
3121 .any(|test| test.fails_if_succeeding(&pivot_test).is_certain());
3122
3123 let pattern_fails_if_test_fails = tests
3124 .iter()
3125 .any(|test| test.fails_if_failing(&pivot_test).is_certain());
3126
3127 // The branch is still relevant for the if_true path if it still has a
3128 // chance of matching knowing that the pivot test has matched.
3129 // So if we know for certain that the pattern would fail, we don't even
3130 // bother adding it to the if_true path.
3131 if !pattern_fails_if_test_succeeds {
3132 // If the branch is relevant we can further simplify it by removing
3133 // all those tests that are guaranteed to succeed. For example,
3134 // if the succeeding pivot test is `size >= 20` there's no point
3135 // in checking that `size >= 10`, we know that's always true in this
3136 // path of the decision tree!
3137 let tests = tests
3138 .into_iter()
3139 .filter(|test| test.succeeds_if_succeeding(&pivot_test) == Confidence::Uncertain)
3140 .collect::<VecDeque<_>>();
3141
3142 let mut branch = branch.clone();
3143 let variable = &pattern_check.var;
3144 branch.add_check(variable.is(compiler.bit_array_pattern(tests)));
3145
3146 // We know that there's always going to be a single choice for the
3147 // successful check, so we get that and add the branch to it.
3148 let (_, if_true_branches) = self
3149 .choices
3150 .get_mut(0)
3151 .expect("bit array compilation with no choice");
3152 if_true_branches.push_back(branch);
3153 }
3154
3155 // Same goes for the if_false branch: knowing the pivot test has failed
3156 // we only want to keep those branches with a pattern that still have a
3157 // chance to match.
3158 if !pattern_fails_if_test_fails {
3159 // The main difference with the if true case is that we have no way
3160 // of pruning the number of needed tests, so we add this check back
3161 // exactly as it is.
3162 branch.add_check(pattern_check);
3163 self.fallback.push_back(branch);
3164 }
3165 }
3166
3167 fn save_index_of_new_choice(&mut self, kind: RuntimeCheckKind) {
3168 let _ = match kind {
3169 RuntimeCheckKind::Int { .. }
3170 | RuntimeCheckKind::Float { .. }
3171 | RuntimeCheckKind::String { .. }
3172 | RuntimeCheckKind::Tuple { .. }
3173 | RuntimeCheckKind::Variant { .. }
3174 | RuntimeCheckKind::EmptyList
3175 | RuntimeCheckKind::NonEmptyList => self.indices.insert(kind, self.choices.len()),
3176
3177 RuntimeCheckKind::StringPrefix { prefix } => self
3178 .prefix_indices
3179 .insert(prefix.to_string(), self.choices.len()),
3180 };
3181 }
3182
3183 fn choice_has_guard_using_pattern_bindings(&self, index: usize) -> bool {
3184 self.choices.get(index).is_some_and(|(_, branches)| {
3185 branches.iter().any(|branch| {
3186 branch
3187 .guard
3188 .is_some_and(|guard| guard.uses_pattern_bindings)
3189 })
3190 })
3191 }
3192
3193 fn check_overlaps(&self, kind: &RuntimeCheckKind) -> CheckOverlaps {
3194 match kind {
3195 // All these checks will only overlap with a check that is exactly the
3196 // same, so we just look up their index in the `indices` map using the
3197 // kind as the lookup. An identical existing choice fully subsumes this
3198 // one, so we only need a new choice when there isn't one already.
3199 RuntimeCheckKind::Int { .. }
3200 | RuntimeCheckKind::Float { .. }
3201 | RuntimeCheckKind::Tuple { .. }
3202 | RuntimeCheckKind::Variant { .. }
3203 | RuntimeCheckKind::EmptyList
3204 | RuntimeCheckKind::NonEmptyList => {
3205 let overlapping = self.indices.get(kind).cloned().into_iter().collect_vec();
3206 let needs_new_choice = overlapping.is_empty();
3207
3208 CheckOverlaps {
3209 overlapping,
3210 needs_new_choice,
3211 }
3212 }
3213
3214 // String patterns are a bit more tricky as they might end up overlapping
3215 // even if they're not exactly the same kind of check! Let's have a look
3216 // at an example. Say we're compiling these branches:
3217 //
3218 // ```
3219 // a is "wibble" <> rest -> todo
3220 // a is "wibbler" <> rest -> todo
3221 // ```
3222 //
3223 // We use the first (and only) check in the first branch as the pivot and
3224 // now we have to decide where to put the next branch. Is it matching with
3225 // the first one or completely unrelated?
3226 // Since `"wibbler"` starts with `"wibble"` we know it's overlapping and
3227 // it cannot possibly match if the previous one doesn't!
3228 //
3229 // So when we find a `String`/`StringPrefix` pattern we look for a prefix
3230 // among the ones we have discovered so far that could match with it.
3231 // That is, we look for a prefix of the pattern we're checking in the prefix
3232 // trie.
3233 RuntimeCheckKind::StringPrefix { prefix: value } => {
3234 // A prefix pattern overlaps with any of its own prefixes, and also with a
3235 // longer prefix it is itself a prefix of. Once that longer prefix matched,
3236 // this shorter one is guaranteed to match too.
3237 match self.prefix_indices.get_ancestor(value.as_str()) {
3238 // One of `value`'s own (shorter) prefixes is already a choice. Any
3239 // value that could match `value` is routed into that choice first, so
3240 // `value` doesn't need its own. It just joins that choice (and any
3241 // longer prefixes nested under it whose guard could fall through to it).
3242 Some(_) => {
3243 let overlapping = overlap_candidates(&self.prefix_indices, value)
3244 .filter(|&(prefix, index)| {
3245 if value.starts_with(prefix) {
3246 // An ancestor (or the prefix itself). `value` extends it,
3247 // so it always belongs in that choice.
3248 true
3249 } else {
3250 // A longer prefix which is only relevant as a fall-through
3251 // target, which only happens when it can match its prefix
3252 // but fail a guard.
3253 prefix.starts_with(value.as_str())
3254 && self.choice_has_guard_using_pattern_bindings(index)
3255 }
3256 })
3257 .map(|(_, index)| index)
3258 .collect_vec();
3259
3260 CheckOverlaps {
3261 overlapping,
3262 needs_new_choice: false,
3263 }
3264 }
3265 // No shorter prefix of `value` is a choice yet, so `value` needs its
3266 // own choice to match values outside any longer prefixes. It is added to
3267 // an existing longer prefix only when that prefix can match but fail a
3268 // guard, so control has to fall through to this shorter prefix rather
3269 // than escaping to the next choice.
3270 None => {
3271 let overlapping = descendant_candidates(&self.prefix_indices, value)
3272 .filter(|&(prefix, index)| {
3273 prefix.starts_with(value.as_str())
3274 && self.choice_has_guard_using_pattern_bindings(index)
3275 })
3276 .map(|(_, index)| index)
3277 .collect_vec();
3278
3279 CheckOverlaps {
3280 overlapping,
3281 needs_new_choice: true,
3282 }
3283 }
3284 }
3285 }
3286
3287 // Strings are almost exactly the same, except they could also have an exact
3288 // match with other string patterns. So a string pattern could overlap with
3289 // another string pattern (if they're matching on the same value), or with
3290 // one or more string prefix patterns with a matching prefix.
3291 RuntimeCheckKind::String { value } => {
3292 // Unlike a prefix pattern, an exact literal can only overlap with one of
3293 // its own prefixes. A longer prefix can never match an exact literal (the
3294 // literal is too short), so we leave those out.
3295 let first_index = self.indices.get(kind).cloned();
3296 let overlapping = first_index
3297 .into_iter()
3298 .chain(
3299 overlap_candidates(&self.prefix_indices, value)
3300 .filter(|&(prefix, _)| value.starts_with(prefix))
3301 .map(|(_, index)| index),
3302 )
3303 .collect_vec();
3304 let needs_new_choice = overlapping.is_empty();
3305
3306 CheckOverlaps {
3307 overlapping,
3308 needs_new_choice,
3309 }
3310 }
3311 }
3312 }
3313}
3314
3315/// Describes how a branch's runtime check relates to the choices discovered so
3316/// far when splitting branches.
3317struct CheckOverlaps {
3318 /// The indices of the existing choices this branch is relevant to and so
3319 /// must be added to
3320 overlapping: Vec<usize>,
3321 /// Whether the check also needs to become a new choice of its own. A new choice is a
3322 /// fresh branch of the decision tree, carrying its own runtime check.
3323 ///
3324 /// For example, suppose `"wib" <> _` is reached after `"wibble" <> _` is already a
3325 /// choice. A string can start with `"wib"` without starting with `"wibble"`
3326 /// (for example `"wibz"`), so `"wib"` is not subsumed and needs its own choice to
3327 /// match those values. With the order reversed, `"wibble"` would route through
3328 /// the existing `"wib"` choice and need no choice of its own.
3329 needs_new_choice: bool,
3330}
3331
3332fn overlap_candidates<'a>(
3333 trie: &'a Trie<String, usize>,
3334 key: &str,
3335) -> impl Iterator<Item = (&'a str, usize)> {
3336 trie.get_ancestor(key)
3337 .into_iter()
3338 .flat_map(|ancestor| ancestor.iter())
3339 .map(|(prefix, index)| (prefix.as_str(), *index))
3340}
3341
3342fn descendant_candidates<'a>(
3343 trie: &'a Trie<String, usize>,
3344 key: &str,
3345) -> impl Iterator<Item = (&'a str, usize)> {
3346 trie.get_raw_descendant(key)
3347 .into_iter()
3348 .flat_map(|descendant| descendant.iter())
3349 .map(|(prefix, index)| (prefix.as_str(), *index))
3350}
3351
3352#[derive(Debug)]
3353pub struct ConstructorSpecialiser {
3354 specialised_types: HashMap<u64, Arc<Type>>,
3355}
3356
3357impl ConstructorSpecialiser {
3358 fn specialise_constructors(
3359 constructors: &TypeVariantConstructors,
3360 type_arguments: &[Arc<Type>],
3361 current_module: &EcoString,
3362 type_module: &EcoString,
3363 ) -> Vec<TypeValueConstructor> {
3364 match constructors.opaque {
3365 // If the type is opaque and we are not in the definition module of
3366 // that type, we don't have access to any constructors so we treat
3367 // it the same as an external type.
3368 Opaque::Opaque if current_module != type_module => return Vec::new(),
3369 Opaque::Opaque | Opaque::NotOpaque => {}
3370 };
3371
3372 let specialiser = Self::new(constructors.type_parameters_ids.as_slice(), type_arguments);
3373 constructors
3374 .variants
3375 .iter()
3376 .map(|v| specialiser.specialise_type_value_constructor(v))
3377 .collect_vec()
3378 }
3379
3380 fn new(parameters: &[u64], type_arguments: &[Arc<Type>]) -> Self {
3381 let specialised_types = parameters
3382 .iter()
3383 .copied()
3384 .zip(type_arguments.iter().cloned())
3385 .collect();
3386 Self { specialised_types }
3387 }
3388
3389 fn specialise_type_value_constructor(&self, v: &TypeValueConstructor) -> TypeValueConstructor {
3390 let TypeValueConstructor {
3391 name,
3392 parameters,
3393 documentation,
3394 } = v;
3395 let parameters = parameters
3396 .iter()
3397 .map(|p| TypeValueConstructorField {
3398 type_: self.specialise_type(p.type_.as_ref()),
3399 label: p.label.clone(),
3400 documentation: p.documentation.clone(),
3401 })
3402 .collect_vec();
3403 TypeValueConstructor {
3404 name: name.clone(),
3405 parameters,
3406 documentation: documentation.clone(),
3407 }
3408 }
3409
3410 fn specialise_type(&self, type_: &Type) -> Arc<Type> {
3411 Arc::new(match type_ {
3412 Type::Named {
3413 publicity,
3414 package,
3415 module,
3416 name,
3417 arguments,
3418 inferred_variant,
3419 } => Type::Named {
3420 publicity: *publicity,
3421 package: package.clone(),
3422 module: module.clone(),
3423 name: name.clone(),
3424 arguments: arguments
3425 .iter()
3426 .map(|argument| self.specialise_type(argument))
3427 .collect(),
3428 inferred_variant: *inferred_variant,
3429 },
3430
3431 Type::Fn { arguments, return_ } => Type::Fn {
3432 arguments: arguments
3433 .iter()
3434 .map(|argument| self.specialise_type(argument))
3435 .collect(),
3436 return_: return_.clone(),
3437 },
3438
3439 Type::Var { type_ } => Type::Var {
3440 type_: Arc::new(RefCell::new(self.specialise_var(type_))),
3441 },
3442
3443 Type::Tuple { elements } => Type::Tuple {
3444 elements: elements
3445 .iter()
3446 .map(|element| self.specialise_type(element))
3447 .collect(),
3448 },
3449 })
3450 }
3451
3452 fn specialise_var(&self, type_: &RefCell<TypeVar>) -> TypeVar {
3453 match &*type_.borrow() {
3454 TypeVar::Unbound { id } => TypeVar::Unbound { id: *id },
3455
3456 TypeVar::Link { type_ } => TypeVar::Link {
3457 type_: self.specialise_type(type_.as_ref()),
3458 },
3459
3460 TypeVar::Generic { id } => match self.specialised_types.get(id) {
3461 Some(type_) => TypeVar::Link {
3462 type_: type_.clone(),
3463 },
3464 None => TypeVar::Generic { id: *id },
3465 },
3466 }
3467 }
3468}
3469
3470/// Intermiate data structure that's used to set up everything that's needed by
3471/// the pattern matching compiler and get a case expression ready to be compiled,
3472/// while hiding the intricacies of handling an arena to record different patterns.
3473///
3474#[derive(Debug)]
3475pub struct CaseToCompile {
3476 patterns: Arena<Pattern>,
3477 branches: Vec<Branch>,
3478 subject_variables: Vec<Variable>,
3479 /// The number of clauses in this case to compile.
3480 number_of_clauses: usize,
3481 variable_id: usize,
3482}
3483
3484impl CaseToCompile {
3485 pub fn new(subject_types: &[Arc<Type>]) -> Self {
3486 let mut variable_id = 0;
3487 let subject_variables = subject_types
3488 .iter()
3489 .map(|type_| {
3490 let id = variable_id;
3491 variable_id += 1;
3492 Variable::new(id, type_.clone())
3493 })
3494 .collect_vec();
3495
3496 Self {
3497 patterns: Arena::new(),
3498 branches: vec![],
3499 number_of_clauses: 0,
3500 subject_variables,
3501 variable_id,
3502 }
3503 }
3504
3505 /// Registers a `TypedClause` as one of the branches to be compiled.
3506 ///
3507 /// If you don't have a clause and just have a simple `TypedPattern` you want
3508 /// to generate a decision tree for you can use `add_pattern`.
3509 ///
3510 pub fn add_clause(&mut self, branch: &TypedClause) {
3511 let all_patterns =
3512 std::iter::once(&branch.pattern).chain(branch.alternative_patterns.iter());
3513
3514 for (alternative_index, patterns) in all_patterns.enumerate() {
3515 let mut checks = Vec::with_capacity(patterns.len());
3516
3517 // We're doing the zipping ourselves instead of using iters.zip as the
3518 // borrow checker would complain and the only workaround would be to
3519 // allocate an entire new vector each time.
3520 for i in 0..patterns.len() {
3521 let pattern = self.register(patterns.get(i).expect("pattern index"));
3522 let var = self
3523 .subject_variables
3524 .get(i)
3525 .expect("wrong number of subjects");
3526 checks.push(var.is(pattern))
3527 }
3528
3529 let guard = branch.guard.as_ref().map(|guard| {
3530 let referenced = guard.referenced_variables();
3531 let uses_pattern_bindings = patterns.iter().any(|pattern| {
3532 pattern
3533 .bound_variables()
3534 .iter()
3535 .any(|bound| referenced.contains(&bound.name()))
3536 });
3537
3538 BranchGuard {
3539 clause_index: self.number_of_clauses,
3540 uses_pattern_bindings,
3541 }
3542 });
3543
3544 let branch = Branch::new(self.number_of_clauses, alternative_index, checks, guard);
3545 self.branches.push(branch);
3546 }
3547
3548 self.number_of_clauses += 1;
3549 }
3550
3551 /// Add a single pattern as a branch to be compiled.
3552 ///
3553 /// This is useful in case one wants to check exhaustiveness of a single
3554 /// pattern without having a fully fledged `TypedClause` to pass to the `add_clause`
3555 /// method. For example, in `let` destructurings.
3556 ///
3557 pub fn add_pattern(&mut self, pattern: &TypedPattern) {
3558 let pattern = self.register(pattern);
3559 let var = self
3560 .subject_variables
3561 .first()
3562 .expect("wrong number of subject variables for pattern");
3563 let branch = Branch::new(self.number_of_clauses, 0, vec![var.is(pattern)], None);
3564 self.number_of_clauses += 1;
3565 self.branches.push(branch);
3566 }
3567
3568 pub fn compile(self, env: &Environment<'_>) -> CompileCaseResult {
3569 let mut compiler = Compiler::new(env, self.variable_id, self.patterns);
3570
3571 let decision = if self.branches.is_empty() {
3572 let var = self
3573 .subject_variables
3574 .first()
3575 .expect("case with no subjects")
3576 .clone();
3577
3578 compiler.split_and_compile_with_pivot_var(var, VecDeque::new())
3579 } else {
3580 compiler.compile(self.branches.into())
3581 };
3582
3583 CompileCaseResult {
3584 diagnostics: compiler.diagnostics,
3585 compiled_case: CompiledCase {
3586 tree: decision,
3587 subject_variables: self.subject_variables,
3588 },
3589 }
3590 }
3591
3592 /// Registers a typed pattern (and all its sub-patterns) into this
3593 /// `CaseToCompile`'s pattern arena, returning an id to get the pattern back.
3594 ///
3595 fn register(&mut self, pattern: &TypedPattern) -> Id<Pattern> {
3596 match pattern {
3597 TypedPattern::Invalid { .. } => self.insert(Pattern::Discard),
3598 TypedPattern::Discard { .. } => self.insert(Pattern::Discard),
3599
3600 TypedPattern::Int { int_value, .. } => {
3601 let int_value = int_value.clone();
3602 self.insert(Pattern::Int { int_value })
3603 }
3604
3605 TypedPattern::Float { float_value, .. } => {
3606 let float_value = *float_value;
3607 self.insert(Pattern::Float { float_value })
3608 }
3609
3610 TypedPattern::String { value, .. } => {
3611 let value = value.clone();
3612 self.insert(Pattern::String { value })
3613 }
3614
3615 TypedPattern::Variable { name, .. } => {
3616 let name = name.clone();
3617 self.insert(Pattern::Variable { name })
3618 }
3619
3620 TypedPattern::Assign { name, pattern, .. } => {
3621 let name = name.clone();
3622 let pattern = self.register(pattern);
3623 self.insert(Pattern::Assign { name, pattern })
3624 }
3625
3626 TypedPattern::Tuple { elements, .. } => {
3627 let elements = elements
3628 .iter()
3629 .map(|element| self.register(element))
3630 .collect_vec();
3631 self.insert(Pattern::Tuple { elements })
3632 }
3633
3634 TypedPattern::List { elements, tail, .. } => {
3635 let mut list = match tail {
3636 Some(tail) => self.register(&tail.pattern),
3637 None => self.insert(Pattern::EmptyList),
3638 };
3639 for element in elements.iter().rev() {
3640 let first = self.register(element);
3641 list = self.insert(Pattern::NonEmptyList { first, rest: list });
3642 }
3643 list
3644 }
3645
3646 TypedPattern::Constructor {
3647 arguments,
3648 constructor,
3649 name,
3650 module,
3651 ..
3652 } => {
3653 let constructor = constructor.expect_ref("must be inferred");
3654 let index = constructor.constructor_index as usize;
3655 let module = module.as_ref().map(|(module, _)| module.clone());
3656 let fields = arguments
3657 .iter()
3658 .map(|argument| self.register(&argument.value))
3659 .collect_vec();
3660 self.insert(Pattern::Variant {
3661 used_name: name.clone(),
3662 name: constructor.name.clone(),
3663 module,
3664 index,
3665 fields,
3666 })
3667 }
3668
3669 TypedPattern::BitArray { segments, .. } => {
3670 let tests = self.bit_array_to_tests(segments);
3671 self.insert(Pattern::BitArray { tests })
3672 }
3673
3674 TypedPattern::StringPrefix {
3675 left_side_string,
3676 left_side_assignment,
3677 right_side_assignment,
3678 ..
3679 } => {
3680 let prefix = left_side_string.clone();
3681 let prefix_name = left_side_assignment
3682 .as_ref()
3683 .map(|(label, _)| label.clone());
3684 let rest_pattern = match right_side_assignment {
3685 AssignName::Variable(name) => Pattern::Variable { name: name.clone() },
3686 AssignName::Discard(_) => Pattern::Discard,
3687 };
3688 let rest = self.insert(rest_pattern);
3689 self.insert(Pattern::StringPrefix {
3690 prefix,
3691 prefix_name,
3692 rest,
3693 })
3694 }
3695
3696 TypedPattern::BitArraySize { .. } => {
3697 unreachable!("Cannot convert BitArraySize to exhaustiveness pattern")
3698 }
3699 }
3700 }
3701
3702 fn insert(&mut self, pattern: Pattern) -> Id<Pattern> {
3703 self.patterns.alloc(pattern)
3704 }
3705
3706 fn bit_array_to_tests(
3707 &mut self,
3708 segments: &[TypedPatternBitArraySegment],
3709 ) -> VecDeque<BitArrayTest> {
3710 // If there's no segments then we just add a single check to make sure
3711 // the bit array is empty.
3712 if segments.is_empty() {
3713 let mut test = VecDeque::new();
3714 test.push_front(BitArrayTest::Size(SizeTest {
3715 operator: SizeOperator::Equal,
3716 size: Offset::constant(0),
3717 }));
3718 return test;
3719 }
3720
3721 let mut previous_end = Offset::constant(0);
3722 let mut tests = VecDeque::with_capacity(segments.len() * 2);
3723 let mut pattern_variables = HashMap::new();
3724
3725 let segments_count = segments.len();
3726 for (i, segment) in segments.iter().enumerate() {
3727 let segment_size = segment_size(segment, &pattern_variables, None);
3728
3729 // If we're reading a variable number of bits we need to make sure
3730 // that that variable is not negative!
3731 if segment_size.can_be_negative() {
3732 tests.push_back(BitArrayTest::ReadSizeIsNotNegative {
3733 size: segment_size.clone(),
3734 });
3735 }
3736
3737 // All segments but the last will require the original bit array to
3738 // have a minimum number of bits for the pattern to succeed. The
3739 // final segment is special as it could require a specific size, or
3740 // be a catch all that matches with any number of remaining bits.
3741 let is_last_segment = i + 1 == segments_count;
3742 match &segment_size {
3743 ReadSize::RemainingBits => (),
3744 ReadSize::RemainingBytes => {
3745 // `(bitSize - c) % 8 === 0` is equivalent to
3746 // `(bitSize - (c % 8)) % 8 === 0` for any constant c,
3747 // because c ≡ c%8 (mod 8). This ensures structurally
3748 // identical JS checks regardless of the accumulated offset.
3749 let mut normalized = previous_end.clone();
3750 normalized.constant = &normalized.constant % 8;
3751 tests.push_back(BitArrayTest::CatchAllIsBytes {
3752 size_so_far: normalized,
3753 });
3754 }
3755 ReadSize::ConstantBits(_)
3756 | ReadSize::VariableBits { .. }
3757 | ReadSize::BinaryOperator { .. } => {
3758 let size = previous_end.clone().add_size(&segment_size);
3759 let operator = if is_last_segment {
3760 SizeOperator::Equal
3761 } else {
3762 SizeOperator::GreaterEqual
3763 };
3764 tests.push_back(BitArrayTest::Size(SizeTest { operator, size }));
3765 }
3766 };
3767
3768 let type_ = match &segment.type_ {
3769 type_ if type_.is_int() => ReadType::Int,
3770 type_ if type_.is_float() => ReadType::Float,
3771 type_ if type_.is_string() => ReadType::String,
3772 type_ if type_.is_bit_array() => ReadType::BitArray,
3773 type_ if type_.is_utf_codepoint() => ReadType::UtfCodepoint,
3774 x => panic!("invalid segment type in exhaustiveness {x:?}"),
3775 };
3776
3777 let read_action = ReadAction {
3778 size: segment_size.clone(),
3779 from: previous_end.clone(),
3780 type_,
3781 endianness: segment.endianness(),
3782 signed: segment.signed(),
3783 };
3784
3785 // Each segment is also turned into a match test, checking the
3786 // selected bits match with the pattern's value.
3787 let value = segment_matched_value(segment, None, &read_action);
3788
3789 // Then if the matched value is a variable that is in scope for the
3790 // rest of the pattern we keep track of it, so it can be used in the
3791 // following read actions as a valid size.
3792 match &value {
3793 BitArrayMatchedValue::LiteralFloat(_)
3794 | BitArrayMatchedValue::LiteralInt { .. }
3795 | BitArrayMatchedValue::LiteralString { .. }
3796 | BitArrayMatchedValue::Discard(_) => {}
3797 BitArrayMatchedValue::Variable(name)
3798 | BitArrayMatchedValue::Assign { name, .. } => {
3799 let _ = pattern_variables.insert(name.clone(), read_action.clone());
3800 }
3801 }
3802
3803 // If we are matching on a float segment that is not a literal we want
3804 // to add an additional check to make sure that we won't match with
3805 // `NaN` and `Infinity`!
3806 if type_.is_float() && !value.is_literal() {
3807 tests.push_back(BitArrayTest::SegmentIsFiniteFloat {
3808 read_action: read_action.clone(),
3809 });
3810 }
3811 tests.push_back(BitArrayTest::Match(MatchTest { value, read_action }));
3812
3813 previous_end = previous_end.add_size(&segment_size);
3814 }
3815 tests
3816 }
3817}
3818
3819fn segment_matched_value(
3820 segment: &TypedPatternBitArraySegment,
3821 // If we are compiling an assignment pattern, we still need access to the
3822 // `type_` and `options` fields of the `segment`, so we must still pass that
3823 // in above. However, we need to check the correct sub-pattern of the original
3824 // pattern, so if they are different we set this argument to `Some`.
3825 pattern: Option<&TypedPattern>,
3826 read_action: &ReadAction,
3827) -> BitArrayMatchedValue {
3828 let pattern = pattern.unwrap_or(&segment.value);
3829 match pattern {
3830 ast::Pattern::Int {
3831 int_value,
3832 location,
3833 ..
3834 } => BitArrayMatchedValue::LiteralInt {
3835 value: int_value.clone(),
3836 location: *location,
3837 bits: int_to_bits(
3838 int_value,
3839 &read_action.size,
3840 read_action.endianness,
3841 read_action.signed,
3842 ),
3843 },
3844 ast::Pattern::Float { value, .. } => BitArrayMatchedValue::LiteralFloat(value.clone()),
3845 ast::Pattern::String { value, .. } if segment.has_utf16_option() => {
3846 BitArrayMatchedValue::LiteralString {
3847 value: value.clone(),
3848 encoding: StringEncoding::Utf16,
3849 bytes: string_to_utf16_bytes(
3850 &convert_string_escape_chars(value),
3851 read_action.endianness,
3852 ),
3853 }
3854 }
3855 ast::Pattern::String { value, .. } if segment.has_utf32_option() => {
3856 BitArrayMatchedValue::LiteralString {
3857 value: value.clone(),
3858 encoding: StringEncoding::Utf32,
3859 bytes: string_to_utf32_bytes(
3860 &convert_string_escape_chars(value),
3861 read_action.endianness,
3862 ),
3863 }
3864 }
3865 ast::Pattern::String { value, .. } => BitArrayMatchedValue::LiteralString {
3866 value: value.clone(),
3867 encoding: StringEncoding::Utf8,
3868 bytes: convert_string_escape_chars(value).as_bytes().into(),
3869 },
3870 ast::Pattern::Variable { name, .. } => BitArrayMatchedValue::Variable(name.clone()),
3871 ast::Pattern::Discard { name, .. } => BitArrayMatchedValue::Discard(name.clone()),
3872 ast::Pattern::Assign { name, pattern, .. } => BitArrayMatchedValue::Assign {
3873 name: name.clone(),
3874 value: Box::new(segment_matched_value(segment, Some(pattern), read_action)),
3875 },
3876 ast::Pattern::BitArraySize(_)
3877 | ast::Pattern::List { .. }
3878 | ast::Pattern::Constructor { .. }
3879 | ast::Pattern::Tuple { .. }
3880 | ast::Pattern::BitArray { .. }
3881 | ast::Pattern::StringPrefix { .. }
3882 | ast::Pattern::Invalid { .. } => panic!("unexpected segment value pattern {pattern:?}"),
3883 }
3884}
3885
3886fn int_to_bits(
3887 value: &BigInt,
3888 read_size: &ReadSize,
3889 endianness: Endianness,
3890 signed: bool,
3891) -> Result<BitVec<u8, Msb0>, IntToBitsError> {
3892 let size = read_size
3893 .constant_bits()
3894 .ok_or(IntToBitsError::NonConstantSize)?
3895 .to_u32()
3896 .ok_or(IntToBitsError::ExceedsMaximumSize)?;
3897
3898 if !representable_with_bits(value, size, signed) {
3899 return Err(IntToBitsError::Unrepresentable { size });
3900 } else if size > BitArrayMatchedValue::MAX_BITS_INTERFERENCE {
3901 return Err(IntToBitsError::ExceedsMaximumSize);
3902 }
3903
3904 // Pad negative numbers with 1s (true) and non-negative numbers with 0s (false)
3905 let pad_digit = value.sign() == Sign::Minus;
3906 let size = size as usize;
3907 let mut bytes = int_to_bytes(value, endianness, signed);
3908 let bytes_size = bytes.len() * 8;
3909
3910 let bits = match (endianness, bytes_size.cmp(&size)) {
3911 (_, Ordering::Equal) => BitVec::from_vec(bytes),
3912
3913 // Even though we return an error if `value` cannot be represented in `size` bits,
3914 // we may need to slice off up a couple bits if the read size is not a multiple of 8.
3915 // <3:4> yields the bytes [0b0000_0011]. We slice off 4 bits and get 0011
3916 (Endianness::Big, Ordering::Greater) => {
3917 BitVec::from_bitslice(&bytes.view_bits()[bytes_size - size..])
3918 }
3919 // If the number needs fewer bits than the read size, we pad it.
3920 // <3:9> yields the bytes [0b0000_0011]. We add one digit and get 0_0000_0011
3921 (Endianness::Big, Ordering::Less) => {
3922 let mut bits = BitVec::repeat(pad_digit, size - bytes_size);
3923 bits.extend_from_raw_slice(&bytes);
3924 bits
3925 }
3926
3927 (Endianness::Little, Ordering::Greater) => {
3928 // If the difference is greater than a byte, we returned an Error earlier
3929 let remainder = size % 8;
3930 if remainder == 0 {
3931 BitVec::from_vec(bytes)
3932 } else {
3933 // If the size is not a multiple of 8, we need to truncate the most significant bits.
3934 // As they are in the last byte, we leftshift by the appropriate amount and
3935 // truncate the final bits after conversion
3936 let last_byte = bytes.last_mut().expect("bytes must not be empty");
3937 *last_byte <<= 8 - remainder;
3938
3939 let mut bits = BitVec::from_vec(bytes);
3940 bits.truncate(size);
3941 bits
3942 }
3943 }
3944 (Endianness::Little, Ordering::Less) => {
3945 let mut bits = BitVec::from_vec(bytes);
3946 let padding: BitVec<u8, Msb0> = BitVec::repeat(pad_digit, size - bytes_size);
3947 bits.extend_from_bitslice(padding.as_bitslice());
3948 bits
3949 }
3950 };
3951 Ok(bits)
3952}
3953
3954fn int_to_bytes(value: &BigInt, endianness: Endianness, signed: bool) -> Vec<u8> {
3955 match (endianness, signed) {
3956 (Endianness::Big, false) => value.to_bytes_be().1,
3957 (Endianness::Big, true) => value.to_signed_bytes_be(),
3958 (Endianness::Little, false) => value.to_bytes_le().1,
3959 (Endianness::Little, true) => value.to_signed_bytes_le(),
3960 }
3961}
3962
3963#[derive(Clone, Copy, Eq, PartialEq, Debug, serde::Serialize, serde::Deserialize)]
3964pub enum IntToBitsError {
3965 Unrepresentable { size: u32 },
3966 ExceedsMaximumSize,
3967 NonConstantSize,
3968}
3969
3970fn segment_size(
3971 segment: &TypedPatternBitArraySegment,
3972 pattern_variables: &HashMap<EcoString, ReadAction>,
3973 // If we are compiling an assignment pattern, we still need access to the
3974 // `type_` and `options` fields of the `segment`, so we must still pass that
3975 // in above. However, we need to check the correct sub-pattern of the original
3976 // pattern, so if they are different we set this argument to `Some`.
3977 pattern: Option<&TypedPattern>,
3978) -> ReadSize {
3979 let pattern = pattern.unwrap_or(&segment.value);
3980
3981 match segment.size() {
3982 // The size of a segment must be a `BitArraySize` pattern.
3983 Some(ast::Pattern::BitArraySize(size)) => {
3984 bit_array_size(segment.unit(), pattern_variables, size)
3985 }
3986 Some(x) => panic!("invalid pattern size made it to code generation {x:?}"),
3987
3988 // If a segment has the `bits`/`bytes` option and has no size, that
3989 // means it's the final catch all segment: we'll have to read any number
3990 // of bits.
3991 _ if segment.has_bits_option() => ReadSize::RemainingBits,
3992 _ if segment.has_bytes_option() => ReadSize::RemainingBytes,
3993
3994 // If there's no size option we go for a default: 8 bits for int
3995 // segments, and 64 for anything else.
3996 None if segment.type_.is_int() => ReadSize::ConstantBits(8.into()),
3997 None => match pattern {
3998 ast::Pattern::Assign { pattern, .. } => {
3999 segment_size(segment, pattern_variables, Some(pattern))
4000 }
4001
4002 ast::Pattern::String { value, .. } if segment.has_utf16_option() => {
4003 ReadSize::ConstantBits(
4004 // Each utf16 code unit is 16 bits
4005 length_utf16(&convert_string_escape_chars(value)) * BigInt::from(16),
4006 )
4007 }
4008 ast::Pattern::String { value, .. } if segment.has_utf32_option() => {
4009 // Each utf32 code unit is 32 bits
4010 ReadSize::ConstantBits(
4011 length_utf32(&convert_string_escape_chars(value)) * BigInt::from(32),
4012 )
4013 }
4014 // If the segment is a literal string then it has an automatic size
4015 // given by its number of bytes.
4016 ast::Pattern::String { value, .. } => {
4017 ReadSize::ConstantBits(convert_string_escape_chars(value).len() * BigInt::from(8))
4018 }
4019 // In all other cases the segment is considered to be 64 bits
4020 ast::Pattern::Int { .. }
4021 | ast::Pattern::Float { .. }
4022 | ast::Pattern::Variable { .. }
4023 | ast::Pattern::BitArraySize(_)
4024 | ast::Pattern::Discard { .. }
4025 | ast::Pattern::List { .. }
4026 | ast::Pattern::Constructor { .. }
4027 | ast::Pattern::Tuple { .. }
4028 | ast::Pattern::BitArray { .. }
4029 | ast::Pattern::StringPrefix { .. }
4030 | ast::Pattern::Invalid { .. } => ReadSize::ConstantBits(64.into()),
4031 },
4032 }
4033}
4034
4035fn bit_array_size(
4036 unit: u8,
4037 pattern_variables: &HashMap<EcoString, ReadAction>,
4038 size: &TypedBitArraySize,
4039) -> ReadSize {
4040 match size {
4041 BitArraySize::Int { int_value, .. } => ReadSize::ConstantBits(int_value * unit),
4042 BitArraySize::Variable { name, .. } => {
4043 let variable = match pattern_variables.get(name) {
4044 Some(read_action) => {
4045 VariableUsage::PatternSegment(name.clone(), read_action.clone())
4046 }
4047 None => VariableUsage::OutsideVariable(name.clone()),
4048 };
4049 ReadSize::VariableBits {
4050 variable: Box::new(variable),
4051 unit,
4052 }
4053 }
4054 BitArraySize::BinaryOperator {
4055 operator,
4056 left,
4057 right,
4058 ..
4059 } => {
4060 let size = ReadSize::BinaryOperator {
4061 left: Box::new(bit_array_size(1, pattern_variables, left)),
4062 right: Box::new(bit_array_size(1, pattern_variables, right)),
4063 operator: *operator,
4064 };
4065
4066 if unit == 1 {
4067 size
4068 } else {
4069 ReadSize::BinaryOperator {
4070 left: Box::new(size),
4071 right: Box::new(ReadSize::ConstantBits(unit.into())),
4072 operator: IntOperator::Multiply,
4073 }
4074 }
4075 }
4076 BitArraySize::Block { inner, .. } => bit_array_size(unit, pattern_variables, inner),
4077 }
4078}
4079
4080/// Returns `true` if one bag is a superset of the other: that is it contains
4081/// all the keys of `other` in a quantity that's greater or equal.
4082///
4083#[must_use]
4084fn superset(
4085 one: &im::HashMap<VariableUsage, usize>,
4086 other: &im::HashMap<VariableUsage, usize>,
4087) -> bool {
4088 other
4089 .iter()
4090 .all(|(key, other_occurrences)| match one.get(key) {
4091 Some(occurrences) => occurrences >= other_occurrences,
4092 None => false,
4093 })
4094}
4095
4096#[must_use]
4097fn representable_with_bits(value: &BigInt, bits: u32, signed: bool) -> bool {
4098 // No number is representable in 0 bits.
4099 if bits == 0 {
4100 return false;
4101 };
4102
4103 let required_bits = match (value.sign(), signed) {
4104 // Zero always needs one bit.
4105 (Sign::NoSign, _) => 1,
4106
4107 // `BigInt::bits` does not consider the sign!
4108 (Sign::Plus, false) => value.bits(),
4109 // Therefore we need to add the sign bit here: `10` -> `010`
4110 (Sign::Plus, true) => value.bits() + 1,
4111
4112 // A negative number must be signed
4113 (Sign::Minus, false) => return false,
4114 (Sign::Minus, true) => {
4115 let bits_unsigned = value.bits();
4116 let trailing_zeros = value
4117 .trailing_zeros()
4118 .expect("trailing_zeros to return a value for non-zero numbers");
4119 let is_power_of_2 = trailing_zeros == bits_unsigned - 1;
4120
4121 // Negative powers of two don't need an extra sign bit. E.g. `-2 == 0b10`
4122 if is_power_of_2 {
4123 bits_unsigned
4124 } else {
4125 bits_unsigned + 1
4126 }
4127 }
4128 };
4129
4130 match required_bits.to_u32() {
4131 Some(required_bits) => bits >= required_bits,
4132 None => false,
4133 }
4134}
4135
4136#[cfg(test)]
4137mod representable_with_bits_test {
4138 use crate::exhaustiveness::representable_with_bits;
4139 use num_bigint::BigInt;
4140
4141 #[test]
4142 fn positive_number_representable_with_bits_test() {
4143 // 9 can be represented as a >=4 bits unsigned number.
4144 assert!(representable_with_bits(&9.into(), 4, false));
4145 assert!(representable_with_bits(&9.into(), 5, false));
4146 // But not in <=3 bits as an unsigned number.
4147 assert!(!representable_with_bits(&9.into(), 3, false));
4148 assert!(!representable_with_bits(&9.into(), 2, false));
4149
4150 // It can be represented as a >=5 bit signed number.
4151 assert!(representable_with_bits(&9.into(), 5, true));
4152 assert!(representable_with_bits(&9.into(), 6, true));
4153 // But not in <= 4 bits as a signed number.
4154 assert!(!representable_with_bits(&9.into(), 4, true));
4155 assert!(!representable_with_bits(&9.into(), 3, true));
4156 }
4157
4158 #[test]
4159 fn negative_number_representable_with_bits_test() {
4160 // A negative number will never be representable as an unsigned number,
4161 // no matter the number of bits!
4162 assert!(!representable_with_bits(&BigInt::from(-9), 1, false));
4163 assert!(!representable_with_bits(&BigInt::from(-9), 500, false));
4164
4165 // -9 can be represented in >=5 bits as a signed number.
4166 assert!(representable_with_bits(&BigInt::from(-9), 5, true));
4167 assert!(representable_with_bits(&BigInt::from(-9), 6, true));
4168 // But not in <= 4 bits as a signed number.
4169 assert!(!representable_with_bits(&BigInt::from(-9), 4, true));
4170 assert!(!representable_with_bits(&BigInt::from(-9), 3, true));
4171 }
4172
4173 #[test]
4174 fn zero_representable_with_bits_test() {
4175 for i in 0..12 {
4176 println!("{i}: {}", BigInt::from(i).bits());
4177 }
4178 assert!(!representable_with_bits(&BigInt::ZERO, 0, false));
4179 assert!(!representable_with_bits(&BigInt::ZERO, 0, true));
4180
4181 assert!(representable_with_bits(&BigInt::ZERO, 1, false));
4182 assert!(representable_with_bits(&BigInt::ZERO, 1, true));
4183 }
4184
4185 #[test]
4186 fn number_representable_with_sign_test() {
4187 // Sign needs one additional bit
4188 assert!(representable_with_bits(&8.into(), 4, false));
4189 assert!(!representable_with_bits(&8.into(), 4, true));
4190 assert!(representable_with_bits(&8.into(), 5, true));
4191
4192 // Negative number must be signed
4193 assert!(!representable_with_bits(&BigInt::from(-8), 100, false));
4194
4195 // Negative powers of two don't need an extra sign bit
4196 assert!(!representable_with_bits(&BigInt::from(-8), 3, true));
4197 assert!(representable_with_bits(&BigInt::from(-8), 4, true));
4198
4199 assert!(!representable_with_bits(&BigInt::from(-9), 4, true));
4200 assert!(representable_with_bits(&BigInt::from(-9), 5, true));
4201 }
4202}
4203
4204#[cfg(test)]
4205mod int_to_bits_test {
4206 use std::assert_eq;
4207
4208 use crate::{
4209 ast::Endianness,
4210 exhaustiveness::{BitArrayMatchedValue, IntToBitsError, ReadSize, int_to_bits},
4211 };
4212 use bitvec::{bitvec, order::Msb0, vec::BitVec};
4213 use num_bigint::BigInt;
4214
4215 fn read_size(size: u32) -> ReadSize {
4216 ReadSize::ConstantBits(BigInt::from(size))
4217 }
4218
4219 #[test]
4220 fn int_to_bits_size_too_big() {
4221 assert_eq!(
4222 int_to_bits(
4223 &BigInt::ZERO,
4224 &read_size(BitArrayMatchedValue::MAX_BITS_INTERFERENCE + 1),
4225 Endianness::Big,
4226 true,
4227 ),
4228 Err(IntToBitsError::ExceedsMaximumSize),
4229 );
4230 }
4231
4232 #[test]
4233 fn int_to_bits_zero() {
4234 let expect = Ok(bitvec![u8, Msb0; 0; 3]);
4235 assert_eq!(
4236 int_to_bits(&BigInt::ZERO, &read_size(3), Endianness::Big, false),
4237 expect
4238 );
4239 assert_eq!(
4240 int_to_bits(&BigInt::ZERO, &read_size(3), Endianness::Little, false),
4241 expect
4242 );
4243
4244 let expect = Ok(bitvec![u8, Msb0; 0; 10]);
4245 assert_eq!(
4246 int_to_bits(&BigInt::ZERO, &read_size(10), Endianness::Big, false),
4247 expect
4248 );
4249 assert_eq!(
4250 int_to_bits(&BigInt::ZERO, &read_size(10), Endianness::Little, false),
4251 expect
4252 );
4253 }
4254
4255 #[test]
4256 fn int_to_bits_positive() {
4257 // Exact match
4258 assert_eq!(
4259 int_to_bits(
4260 &BigInt::from(0xff00),
4261 &read_size(16),
4262 Endianness::Big,
4263 false
4264 ),
4265 Ok(BitVec::<u8, Msb0>::from_vec(vec![0xff, 0x00])),
4266 );
4267 assert_eq!(
4268 int_to_bits(
4269 &BigInt::from(0xff00),
4270 &read_size(16),
4271 Endianness::Little,
4272 false
4273 ),
4274 Ok(BitVec::<u8, Msb0>::from_vec(vec![0x00, 0xff])),
4275 );
4276
4277 assert_eq!(
4278 int_to_bits(
4279 &BigInt::from(0b11_1111_0000),
4280 &read_size(10),
4281 Endianness::Big,
4282 false
4283 ),
4284 Ok(bitvec![u8, Msb0; 1, 1, 1, 1, 1, 1, 0, 0, 0, 0]),
4285 );
4286 assert_eq!(
4287 int_to_bits(
4288 &BigInt::from(0b11_1111_0000),
4289 &read_size(10),
4290 Endianness::Little,
4291 false
4292 ),
4293 Ok(bitvec![u8, Msb0; 1, 1, 1, 1, 0, 0, 0, 0, 1, 1]),
4294 );
4295
4296 // Too few bits in int
4297 assert_eq!(
4298 int_to_bits(&BigInt::from(0xff), &read_size(12), Endianness::Big, false),
4299 Ok(bitvec![u8, Msb0; 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1]),
4300 );
4301 assert_eq!(
4302 int_to_bits(
4303 &BigInt::from(0xff),
4304 &read_size(12),
4305 Endianness::Little,
4306 false
4307 ),
4308 Ok(bitvec![u8, Msb0; 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0]),
4309 );
4310 }
4311
4312 #[test]
4313 fn int_to_bits_signed() {
4314 assert_eq!(
4315 int_to_bits(&BigInt::from(-128), &read_size(12), Endianness::Big, true),
4316 Ok(bitvec![u8, Msb0; 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0]),
4317 );
4318 assert_eq!(
4319 int_to_bits(
4320 &BigInt::from(-128),
4321 &read_size(12),
4322 Endianness::Little,
4323 true
4324 ),
4325 Ok(bitvec![u8, Msb0; 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1]),
4326 );
4327 }
4328}