Fork of daniellemaywood.uk/gleam — Wasm codegen work
2

Configure Feed

Select the types of activity you want to include in your feed.

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