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