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
63 kB 1692 lines
1//! An implementation of the algorithm described at 2//! <https://julesjacobs.com/notes/patternmatching/patternmatching.pdf>. 3//! 4//! Adapted from Yorick Peterse's implementation at 5//! <https://github.com/yorickpeterse/pattern-matching-in-rust>. Thank you Yorick! 6//! 7//! > This module comment (and all the following doc comments) are a rough 8//! > explanation. It's great to set some expectations on what to expect from 9//! > the following code and why the data looks the way it does. 10//! > If you want a more detailed explanation, the original paper is a lot more 11//! > detailed! 12//! 13//! A case to be compiled looks a bit different from the case expressions we're 14//! used to in Gleam: instead of having a variable to match on and a series of 15//! branches, a `CaseToCompile` is made up of a series of branches that can each 16//! contain multiple pattern checks. With a psedo-Gleam syntax, this is what it 17//! would look like: 18//! 19//! ```text 20//! case { 21//! a is Some, b is 1, c is _ -> todo 22//! a is wibble -> todo 23//! } 24//! ``` 25//! 26//! > You may wonder, why are we writing branches like this? Usually a case 27//! > expression matches on a single variable and each branch refers to it. For 28//! > example in gleam you'd write: 29//! > 30//! > ```gleam 31//! > case a { 32//! > Some(_) -> todo 33//! > None -> todo 34//! > } 35//! > ``` 36//! > 37//! > In out representation that would turn into: 38//! > 39//! > ```text 40//! > case { 41//! > a is Some(_) -> todo 42//! > a is None -> todo 43//! > } 44//! > ``` 45//! > 46//! > This change makes it way easier to compile the pattern matching into a 47//! > decision tree, because now we can add multiple checks on different 48//! > variables in each branch. 49//! 50//! Starting from this data structure, we'll be splitting all the branches into 51//! a decision tree that can be used to perform exhaustiveness checking and code 52//! generation. 53//! 54//! At the moment this tree is not suitable for use in code generation yet as it 55//! is incomplete. The tree is not correctly formed for: 56//! - Bit strings 57//! - String prefixes 58//! 59//! These were not implemented as they are more complex and I've not worked out 60//! a good way to do them yet. The tricky bit is that unlike the others they are 61//! not an exact match and they can overlap with other patterns. Take this 62//! example: 63//! 64//! ```text 65//! case x { 66//! "1" <> _ -> ... 67//! "12" <> _ -> ... 68//! "123" -> ... 69//! _ -> ... 70//! } 71//! ``` 72//! 73//! The decision tree needs to take into account that the first pattern is a 74//! super-pattern of the second, and the second is a super-pattern of the third. 75//! 76 77mod missing_patterns; 78pub mod printer; 79 80use crate::{ 81 ast::{AssignName, TypedClause, TypedPattern}, 82 type_::{ 83 Environment, Type, TypeValueConstructor, TypeValueConstructorField, TypeVar, 84 TypeVariantConstructors, collapse_links, error::UnreachableCaseClauseReason, 85 is_prelude_module, string, 86 }, 87}; 88use ecow::EcoString; 89use id_arena::{Arena, Id}; 90use itertools::Itertools; 91use radix_trie::{Trie, TrieCommon}; 92use std::{ 93 cell::RefCell, 94 collections::{HashMap, HashSet, VecDeque}, 95 hash::Hash, 96 sync::Arc, 97}; 98 99/// A single branch composing a `case` expression to be compiled into a decision 100/// tree. 101/// 102/// As shown in the module documentation, branches are a bit different from the 103/// usual branches we see in Gleam's case expressions. Each branch can perform 104/// multiple checks (each on a different variable, which appears in the check 105/// itself!): 106/// 107/// ```text 108/// a is Some, b is 1 if condition -> todo 109/// ─┬─────── ─┬──── ─┬────────── ─┬── 110/// │ │ │ ╰── body: an arbitrary expression 111/// │ │ ╰── guard: an additional boolean condition 112/// ╰──────────┴── checks: check that a variable matches with a pattern 113/// ─┬──────────────────────────────────── 114/// ╰── branch: one of the branches making up a pattern matching expression 115/// ``` 116/// 117/// As shown here a branch can also optionally include a guard with a boolean 118/// condition and is followed by a body that is to be executed if all the checks 119/// match (and the guard evaluates to true). 120/// 121#[derive(Clone, Eq, PartialEq, Debug)] 122struct Branch { 123 /// Each branch is identified by a numeric index, so we can nicely 124 /// report errors once we find something's wrong with a branch. 125 /// 126 clause_index: usize, 127 128 /// Each alternative pattern in an alternative pattern matching (e.g. 129 /// `one | two | three -> todo`) gets turned into its own branch in this 130 /// internal representation. So we also keep track of the index of the 131 /// alternative this comes from (0 being the first one and so on...) 132 /// 133 alternative_index: usize, 134 checks: Vec<PatternCheck>, 135 guard: Option<usize>, 136 body: Body, 137} 138 139impl Branch { 140 fn new( 141 clause_index: usize, 142 alternative_index: usize, 143 checks: Vec<PatternCheck>, 144 has_guard: bool, 145 ) -> Self { 146 Self { 147 clause_index, 148 alternative_index, 149 checks, 150 guard: if has_guard { Some(clause_index) } else { None }, 151 body: Body::new(clause_index), 152 } 153 } 154 155 /// Removes and returns a `PatternCheck` on the given variable from this 156 /// branch. 157 /// 158 fn pop_check_on_var(&mut self, var: &Variable) -> Option<PatternCheck> { 159 let index = self.checks.iter().position(|check| check.var == *var)?; 160 Some(self.checks.remove(index)) 161 } 162 163 fn add_check(&mut self, check: PatternCheck) { 164 self.checks.push(check); 165 } 166 167 /// To simplify compiling the pattern we can get rid of all catch-all 168 /// patterns that are guaranteed to match by turning those into assignments. 169 /// 170 /// What does this look like in practice? Let's go over an example. 171 /// Let's say we have this case to compile: 172 /// 173 /// ```gleam 174 /// case a { 175 /// Some(1) -> Some(2) 176 /// otherwise -> otherwise 177 /// } 178 /// ``` 179 /// 180 /// In our internal representation this would become: 181 /// 182 /// ```text 183 /// case { 184 /// a is Some(1) -> Some(2) 185 /// a is otherwise -> otherwise 186 /// ─┬──────────── 187 /// ╰── `a` will always match with this "catch all" variable pattern 188 /// } 189 /// ``` 190 /// 191 /// Focusing on the last branch, we can remove that check that always matches 192 /// by keeping track in its body of the correspondence. So it would end up 193 /// looking like this: 194 /// 195 /// ```text 196 /// case { 197 /// a is Some(1) -> Some(2) 198 /// ∅ -> { 199 /// ┬ 200 /// ╰── This represents the fact that there's no checks left for this branch! 201 /// So we can make another observation: if there's no checks left in a 202 /// branch we know it will always match and we can produce a leaf in the 203 /// decision tree (there's an exception when we have guards, but we'll 204 /// get to it later)! 205 /// 206 /// let otherwise = a 207 /// ─┬─────────────── 208 /// ╰── And now we can understand what those `bindings` at the start of 209 /// a body are: as we remove variable patterns, we will rewrite those 210 /// as assignments at the top of the body of the corresponding branch. 211 /// 212 /// otherwise 213 /// } 214 /// } 215 /// ``` 216 /// 217 fn move_unconditional_patterns(&mut self, compiler: &Compiler<'_>) { 218 self.checks.retain_mut(|check| { 219 loop { 220 match compiler.pattern(check.pattern) { 221 // Variable patterns always match, so we move those to the body 222 // and remove them from the branch's checks. 223 Pattern::Variable { name } => { 224 self.body.assign(name.clone(), check.var.clone()); 225 return false; 226 } 227 // A discard pattern always matches, but since the value is not 228 // used we can just remove it without even adding an assignment 229 // to the body! 230 Pattern::Discard => return false, 231 // Assigns are kind of special: they get turned into assignments 232 // (shocking) but then we can't discard the pattern they wrap. 233 // So we replace the assignment pattern with the one it's wrapping 234 // and try again. 235 Pattern::Assign { name, pattern } => { 236 self.body.assign(name.clone(), check.var.clone()); 237 check.pattern = *pattern; 238 continue; 239 } 240 // All other patterns are not unconditional, so we just keep them. 241 _ => return true, 242 } 243 } 244 }); 245 } 246} 247 248/// The body of a branch. It always starts with a series of variable assignments 249/// in the form: `let a = b`. As explained in `move_unconditional_patterns`' doc, 250/// each body starts with a series of assignments we keep track of as we're 251/// compiling each branch. 252/// 253#[derive(Clone, Eq, PartialEq, Debug)] 254pub struct Body { 255 /// Any variables to bind before running the code. 256 /// 257 /// The tuples are in the form `(name, value)`, so `(wibble, var)` 258 /// corresponds to `let wibble = var`. 259 /// 260 bindings: Vec<(EcoString, Variable)>, 261 262 /// The index of the clause in the case expression that should be run. 263 /// 264 clause_index: usize, 265} 266 267impl Body { 268 pub fn new(clause_index: usize) -> Self { 269 Self { 270 bindings: vec![], 271 clause_index, 272 } 273 } 274 275 /// Adds a new assignment to the body, binding `let var = value` 276 /// 277 pub fn assign(&mut self, var: EcoString, value: Variable) { 278 self.bindings.push((var, value)); 279 } 280} 281 282/// A user defined pattern such as `Some((x, 10))`. 283/// This is a bit simpler than the full fledged `TypedPattern` used for code analysis 284/// and only focuses on the relevant bits needed to perform exhaustiveness checking 285/// and code generation. 286/// 287/// Using this simplified version of a pattern for the case compiler makes it a 288/// whole lot simpler and more efficient (patterns will have to be cloned, so 289/// we use an arena to allocate those and only store ids to make this operation 290/// extra cheap). 291/// 292#[derive(Clone, Eq, PartialEq, Debug)] 293pub enum Pattern { 294 Discard, 295 Int { 296 value: EcoString, 297 }, 298 Float { 299 value: EcoString, 300 }, 301 String { 302 value: EcoString, 303 }, 304 StringPrefix { 305 prefix: EcoString, 306 rest_pattern: Id<Pattern>, 307 }, 308 Assign { 309 name: EcoString, 310 pattern: Id<Pattern>, 311 }, 312 Variable { 313 name: EcoString, 314 }, 315 Tuple { 316 elems_patterns: Vec<Id<Pattern>>, 317 }, 318 Variant { 319 index: usize, 320 args_patterns: Vec<Id<Pattern>>, 321 }, 322 List { 323 first_pattern: Id<Pattern>, 324 rest_pattern: Id<Pattern>, 325 }, 326 EmptyList, 327 // TODO: Compile the matching within the bit strings 328 BitArray { 329 value: EcoString, 330 }, 331} 332 333impl Pattern { 334 /// Each pattern (with a couple exceptions) can be turned into a 335 /// simpler `RuntimeCheck`: that is a check that can be performed at runtime 336 /// to make sure a `PatternCheck` can succeed on a specific value. 337 /// 338 fn to_runtime_check_kind(&self) -> Option<RuntimeCheckKind> { 339 let kind = match self { 340 // These patterns are unconditional: they will always match and be moved 341 // out of a branch's checks. So there's no corresponding runtime check 342 // we can perform for them. 343 Pattern::Discard | Pattern::Variable { .. } | Pattern::Assign { .. } => return None, 344 Pattern::Int { value } => RuntimeCheckKind::Int { 345 value: value.clone(), 346 }, 347 Pattern::Float { value } => RuntimeCheckKind::Float { 348 value: value.clone(), 349 }, 350 Pattern::String { value } => RuntimeCheckKind::String { 351 value: value.clone(), 352 }, 353 Pattern::StringPrefix { prefix, .. } => RuntimeCheckKind::StringPrefix { 354 prefix: prefix.clone(), 355 }, 356 Pattern::Tuple { elems_patterns } => RuntimeCheckKind::Tuple { 357 size: elems_patterns.len(), 358 }, 359 Pattern::Variant { index, .. } => RuntimeCheckKind::Variant { index: *index }, 360 Pattern::BitArray { value } => RuntimeCheckKind::BitArray { 361 value: value.clone(), 362 }, 363 Pattern::List { .. } => RuntimeCheckKind::NonEmptyList, 364 Pattern::EmptyList => RuntimeCheckKind::EmptyList, 365 }; 366 367 Some(kind) 368 } 369 370 fn is_matching_on_unreachable_variant(&self, branch_mode: &BranchMode) -> bool { 371 match (self, branch_mode) { 372 ( 373 Self::Variant { index, .. }, 374 BranchMode::NamedType { 375 inferred_variant: Some(variant), 376 .. 377 }, 378 ) if index != variant => true, 379 _ => false, 380 } 381 } 382} 383 384/// A single check making up a branch, checking that a variable matches with a 385/// given pattern. For example, the following branch has 2 checks: 386/// 387/// ```text 388/// a is Some, b is 1 -> todo 389/// ┬ ─┬── 390/// │ ╰── This is the pattern being checked 391/// ╰── This is the variable being pattern matched on 392/// ─┬─────── ─┬──── 393/// ╰─────────┴── Two `PatternCheck`s 394/// ``` 395/// 396#[derive(Clone, Eq, PartialEq, Debug)] 397struct PatternCheck { 398 var: Variable, 399 pattern: Id<Pattern>, 400} 401 402/// This is one of the checks we can take at runtime to decide how to move 403/// forward in the decision tree. 404/// 405/// After performing a successful check on a value we will discover something 406/// about its shape: it might be an int, an variant of a custom type, ... 407/// Some values (like variants and lists) might hold onto additional data we 408/// will have to pattern match on: in order to do that we need a name to refer 409/// to those new variables we've discovered after performing a check. That's 410/// what `args` is for. 411/// 412/// Let's have a look at an example. Imagine we have a pattern like this one: 413/// `a is Wibble(1, _, [])`; after performing a runtime check to make sure `a` 414/// is indeed a `Wibble`, we'll need to perform additional checks on it's 415/// arguments: that pattern will be replaced by three new ones `a0 is 1`, 416/// `a1 is _` and `a2 is []`. Those new variables are the `args`. 417/// 418#[derive(Clone, Debug)] 419pub enum RuntimeCheck { 420 Int { 421 value: EcoString, 422 }, 423 Float { 424 value: EcoString, 425 }, 426 String { 427 value: EcoString, 428 }, 429 StringPrefix { 430 prefix: EcoString, 431 rest_arg: Variable, 432 }, 433 Tuple { 434 size: usize, 435 args: Vec<Variable>, 436 }, 437 BitArray { 438 value: EcoString, 439 }, 440 Variant { 441 index: usize, 442 args: Vec<Variable>, 443 }, 444 EmptyList, 445 List { 446 first_arg: Variable, 447 rest_arg: Variable, 448 }, 449} 450 451impl RuntimeCheck { 452 fn kind(&self) -> RuntimeCheckKind { 453 match self { 454 RuntimeCheck::Int { value } => RuntimeCheckKind::Int { 455 value: value.clone(), 456 }, 457 RuntimeCheck::Float { value } => RuntimeCheckKind::Float { 458 value: value.clone(), 459 }, 460 RuntimeCheck::String { value } => RuntimeCheckKind::String { 461 value: value.clone(), 462 }, 463 RuntimeCheck::StringPrefix { 464 prefix, 465 rest_arg: _, 466 } => RuntimeCheckKind::StringPrefix { 467 prefix: prefix.clone(), 468 }, 469 RuntimeCheck::Tuple { size, args: _ } => RuntimeCheckKind::Tuple { size: *size }, 470 RuntimeCheck::BitArray { value } => RuntimeCheckKind::BitArray { 471 value: value.clone(), 472 }, 473 RuntimeCheck::Variant { index, args: _ } => RuntimeCheckKind::Variant { index: *index }, 474 RuntimeCheck::EmptyList => RuntimeCheckKind::EmptyList, 475 RuntimeCheck::List { 476 first_arg: _, 477 rest_arg: _, 478 } => RuntimeCheckKind::NonEmptyList, 479 } 480 } 481} 482 483#[derive(Eq, PartialEq, Clone, Hash, Debug)] 484pub enum RuntimeCheckKind { 485 Int { value: EcoString }, 486 Float { value: EcoString }, 487 String { value: EcoString }, 488 StringPrefix { prefix: EcoString }, 489 Tuple { size: usize }, 490 BitArray { value: EcoString }, 491 Variant { index: usize }, 492 EmptyList, 493 NonEmptyList, 494} 495 496/// A variable that can be matched on in a branch. 497/// 498#[derive(Eq, PartialEq, Clone, Debug)] 499pub struct Variable { 500 id: usize, 501 type_: Arc<Type>, 502} 503 504impl Variable { 505 fn new(id: usize, type_: Arc<Type>) -> Self { 506 Self { id, type_ } 507 } 508 509 fn is(&self, pattern: Id<Pattern>) -> PatternCheck { 510 PatternCheck { 511 var: self.clone(), 512 pattern, 513 } 514 } 515} 516 517#[derive(Debug)] 518/// Different types need to be handled differently when compiling a case expression 519/// into a decision tree. There's some types that have infinite matching patterns 520/// (like ints, strings, ...) and thus will always need a fallback option. 521/// 522/// Other types, like custom types, only have a well defined and finite number 523/// of patterns that could match: when matching on a `Result` we know that we can 524/// only have an `Ok(_)` and an `Error(_)`, anything else would end up being a 525/// type error! 526/// 527/// So this enum is used to pick the correct strategy to compile a case that's 528/// performing a `PatternCheck` on a variable with a specific type. 529/// 530enum BranchMode { 531 /// This covers numbers, functions, variables, bitarrays and strings. 532 /// 533 /// TODO)) In the future it won't be the case: strings and bitarrays will be 534 /// special cased to improve on exhaustiveness checking and to be used for 535 /// code generation. 536 /// 537 Infinite, 538 Tuple { 539 elems: Vec<Arc<Type>>, 540 }, 541 List { 542 inner_type: Arc<Type>, 543 }, 544 NamedType { 545 constructors: Vec<TypeValueConstructor>, 546 inferred_variant: Option<usize>, 547 }, 548} 549 550impl BranchMode { 551 fn needs_fallback(&self) -> bool { 552 match self { 553 BranchMode::Infinite => true, 554 BranchMode::Tuple { .. } | BranchMode::List { .. } | BranchMode::NamedType { .. } => { 555 false 556 } 557 } 558 } 559} 560 561impl Variable { 562 fn branch_mode(&self, env: &Environment<'_>) -> BranchMode { 563 match collapse_links(self.type_.clone()).as_ref() { 564 Type::Fn { .. } | Type::Var { .. } => BranchMode::Infinite, 565 Type::Named { module, name, .. } 566 if is_prelude_module(module) 567 && (name == "Int" 568 || name == "Float" 569 || name == "BitArray" 570 || name == "String") => 571 { 572 BranchMode::Infinite 573 } 574 575 Type::Named { 576 module, name, args, .. 577 } if is_prelude_module(module) && name == "List" => BranchMode::List { 578 inner_type: args.first().expect("list has a type argument").clone(), 579 }, 580 581 Type::Tuple { elems } => BranchMode::Tuple { 582 elems: elems.clone(), 583 }, 584 585 Type::Named { 586 module, 587 name, 588 args, 589 inferred_variant, 590 .. 591 } => { 592 let constructors = ConstructorSpecialiser::specialise_constructors( 593 env.get_constructors_for_type(module, name) 594 .expect("Custom type variants must exist"), 595 args.as_slice(), 596 ); 597 598 let inferred_variant = inferred_variant.map(|i| i as usize); 599 BranchMode::NamedType { 600 constructors, 601 inferred_variant, 602 } 603 } 604 } 605 } 606} 607 608/// This is the decision tree that a pattern matching expression gets turned 609/// into: it's a tree-like structure where each path to a root node contains a 610/// series of checks to perform at runtime to understand if a value matches with 611/// a given pattern. 612/// 613pub enum Decision { 614 /// This is the final node of the tree, once we get to this one we know we 615 /// have a body to run because a given pattern matched. 616 /// 617 Run { 618 // todo)) since the tree is not used for code generation, this field is unused. 619 // But it will be useful once we also use this for code gen purposes and not 620 // just for exhaustiveness checking 621 #[allow(dead_code)] 622 body: Body, 623 }, 624 625 /// We have to make this decision when we run into a branch that also has a 626 /// guard: if it is true we can finally run the body of the branch, stored in 627 /// `if_true`. 628 /// If it is false we might still have to take other decisions and so we might 629 /// have another `DecisionTree` to traverse, stored in `if_false`. 630 /// 631 Guard { 632 // todo)) since the tree is not used for code generation, the `guard` and 633 // `if_true` fields are unused. 634 // But they will be useful once we also use this for code gen purposes and not 635 // just for exhaustiveness checking 636 #[allow(dead_code)] 637 guard: usize, 638 #[allow(dead_code)] 639 if_true: Body, 640 if_false: Box<Decision>, 641 }, 642 643 /// When reaching this node we'll have to see if any of the possible checks 644 /// in `choices` will succeed on `var`. If it does, we know that's the path 645 /// we have to go down to. If none of the checks matches, then we'll have to 646 /// go down the `fallback` branch. 647 /// 648 Switch { 649 var: Variable, 650 choices: Vec<(RuntimeCheck, Box<Decision>)>, 651 fallback: Box<Decision>, 652 }, 653 654 /// This is similar to a `Switch` node: we're still picking a possible path 655 /// to follow based on a runtime check. The key difference is that we know 656 /// that one of those is always going to match and so there's no use for a 657 /// fallback branch. 658 /// 659 /// This is used when matching on custom types (and lists!) when we know 660 /// that there's a limited number of choices and exhaustiveness checking 661 /// ensures we'll always deal with all the possible cases. 662 /// 663 ExhaustiveSwitch { 664 var: Variable, 665 choices: Vec<(RuntimeCheck, Box<Decision>)>, 666 }, 667 668 /// This is a special node: it represents a missing pattern. If a tree 669 /// contains such a node, then we know that the patterns it was compiled 670 /// from are not exhaustive and the path leading to this node will describe 671 /// what kind of pattern doesn't match! 672 /// 673 Fail, 674} 675 676impl Decision { 677 pub fn run(body: Body) -> Self { 678 Decision::Run { body } 679 } 680 681 pub fn guard(guard: usize, if_true: Body, if_false: Self) -> Self { 682 Decision::Guard { 683 guard, 684 if_true, 685 if_false: Box::new(if_false), 686 } 687 } 688 689 pub fn switch( 690 var: Variable, 691 choices: Vec<(RuntimeCheck, Box<Decision>)>, 692 fallback: Decision, 693 ) -> Self { 694 Self::Switch { 695 var, 696 choices, 697 fallback: Box::new(fallback), 698 } 699 } 700 701 fn exhaustive_switch(var: Variable, choices: Vec<(RuntimeCheck, Box<Decision>)>) -> Decision { 702 Self::ExhaustiveSwitch { var, choices } 703 } 704} 705 706/// The `case` compiler itself (shocking, I know). 707/// 708#[derive(Debug)] 709struct Compiler<'a> { 710 environment: &'a Environment<'a>, 711 patterns: Arena<Pattern>, 712 variable_id: usize, 713 diagnostics: Diagnostics, 714} 715 716/// The result of compiling a pattern match expression. 717/// 718pub struct Match { 719 pub tree: Decision, 720 pub diagnostics: Diagnostics, 721 pub subject_variables: Vec<Variable>, 722} 723 724/// Whether a clause is reachable, or why it is unreachable. 725/// 726#[derive(Debug, Clone, Copy, PartialEq, Eq)] 727pub enum Reachability { 728 Reachable, 729 Unreachable(UnreachableCaseClauseReason), 730} 731 732impl Match { 733 pub fn is_reachable(&self, clause: usize) -> Reachability { 734 if self.diagnostics.reachable.contains(&clause) { 735 Reachability::Reachable 736 } else if self.diagnostics.match_impossible_variants.contains(&clause) { 737 Reachability::Unreachable(UnreachableCaseClauseReason::ImpossibleVariant) 738 } else { 739 Reachability::Unreachable(UnreachableCaseClauseReason::DuplicatePattern) 740 } 741 } 742 743 pub fn missing_patterns(&self, environment: &Environment<'_>) -> Vec<EcoString> { 744 missing_patterns::missing_patterns(self, environment) 745 } 746} 747 748/// A type for storing diagnostics produced by the decision tree compiler. 749/// 750#[derive(Debug)] 751pub struct Diagnostics { 752 /// A flag indicating the match is missing one or more pattern. 753 pub missing: bool, 754 755 /// The right-hand sides that are reachable. 756 /// If a right-hand side isn't in this list it means its pattern is 757 /// redundant. 758 pub reachable: HashSet<usize>, 759 760 /// Clauses which match on variants of a type which the compiler 761 /// can tell will never be present, due to variant inference. 762 pub match_impossible_variants: HashSet<usize>, 763} 764 765impl<'a> Compiler<'a> { 766 fn new(environment: &'a Environment<'a>, variable_id: usize, patterns: Arena<Pattern>) -> Self { 767 Self { 768 environment, 769 patterns, 770 variable_id, 771 diagnostics: Diagnostics { 772 missing: false, 773 reachable: HashSet::new(), 774 match_impossible_variants: HashSet::new(), 775 }, 776 } 777 } 778 779 fn pattern(&self, pattern_id: Id<Pattern>) -> &Pattern { 780 self.patterns.get(pattern_id).expect("unknown pattern id") 781 } 782 783 /// Returns a new fresh variable (i.e. guaranteed to have a unique `variable_id`) 784 /// with the given type. 785 /// 786 fn fresh_variable(&mut self, type_: Arc<Type>) -> Variable { 787 let var = Variable::new(self.variable_id, type_); 788 self.variable_id += 1; 789 var 790 } 791 792 fn mark_as_reached(&mut self, branch: &Branch) { 793 let _ = self.diagnostics.reachable.insert(branch.clause_index); 794 } 795 796 fn mark_as_matching_impossible_variant(&mut self, branch: &Branch) { 797 let _ = self.diagnostics.reachable.remove(&branch.clause_index); 798 let _ = self 799 .diagnostics 800 .match_impossible_variants 801 .insert(branch.clause_index); 802 } 803 804 fn compile(&mut self, mut branches: VecDeque<Branch>) -> Decision { 805 branches 806 .iter_mut() 807 .for_each(|branch| branch.move_unconditional_patterns(self)); 808 809 let Some(first_branch) = branches.front() else { 810 // If there's no branches, that means we have a pattern that is not 811 // exhaustive as there's nothing that could match! 812 self.diagnostics.missing = true; 813 return Decision::Fail; 814 }; 815 816 self.mark_as_reached(first_branch); 817 818 // In order to compile the branches, we need to pick a `PatternCheck` from 819 // the first branch, and use the variable it's pattern matching on to create 820 // a new node in the tree. All the branches will be split into different 821 // possible paths of this tree. 822 match find_pivot_check(first_branch, &branches) { 823 Some(PatternCheck { var, .. }) => self.split_and_compile_with_pivot_var(var, branches), 824 825 // If the branch has no remaining checks, it means that we've moved all 826 // its variable patterns as assignments into the body and there's no 827 // additional checks remaining. So the only thing left that could result 828 // in the match failing is the additional guard. 829 None => match first_branch.guard { 830 // If there's no guard we're in the following situation: 831 // `∅ -> body`. It means that this branch will always match no 832 // matter what, all the remaining branches are just discarded and 833 // we can produce a terminating node to run the body 834 // unconditionally. 835 None => Decision::run(first_branch.body.clone()), 836 // If we have a guard we're in this scenario: 837 // `∅ if condition -> body`. We can produce a `Guard` node: 838 // if the condition evaluates to `True` we can run its body. 839 // Otherwise, we'll have to keep looking at the remaining branches 840 // to know what to do if this branch doesn't match. 841 Some(guard) => { 842 let if_true = first_branch.body.clone(); 843 // All the remaining branches will be compiled and end up 844 // in the path of the tree to choose if the guard is false. 845 let _ = branches.pop_front(); 846 let if_false = self.compile(branches); 847 Decision::guard(guard, if_true, if_false) 848 } 849 }, 850 } 851 } 852 853 fn split_and_compile_with_pivot_var( 854 &mut self, 855 pivot_var: Variable, 856 branches: VecDeque<Branch>, 857 ) -> Decision { 858 // We first try and come up with a list of all the runtime checks we might 859 // have to perform on the variable at runtime. In most cases it's a limited 860 // number of checks that we know before hand (for example, when matching 861 // on a list, or on a custom type). 862 let branch_mode = pivot_var.branch_mode(self.environment); 863 let known_checks = match &branch_mode { 864 // If the type being matched on is infinite there's no known runtime 865 // check we could come up with in advance. So we'll build them as 866 // we go. 867 BranchMode::Infinite => vec![], 868 869 // If the type is a tuple there's only one runtime check we could 870 // perform that actually makes sense. 871 BranchMode::Tuple { elems } => vec![self.is_tuple_check(elems)], 872 873 // If the type being matched on is a list we know the resulting 874 // decision tree node is only ever going to have two different paths: 875 // one to follow if the list is empty, and one to follow if it's not. 876 BranchMode::List { inner_type } => { 877 vec![ 878 RuntimeCheck::EmptyList, 879 self.is_list_check(inner_type.clone()), 880 ] 881 } 882 883 // If we know that a specific variant is inferred we will require just 884 // that one and not all the other ones we know for sure are not going to 885 // be there. 886 BranchMode::NamedType { 887 constructors, 888 inferred_variant: Some(index), 889 } => { 890 let constructor = constructors 891 .get(*index) 892 .expect("wrong index for inferred variant"); 893 vec![self.is_variant_check(*index, constructor)] 894 } 895 896 // Otherwise we know we'll need a check for each of its possible variants. 897 BranchMode::NamedType { constructors, .. } => constructors 898 .iter() 899 .enumerate() 900 .map(|(index, constructor)| self.is_variant_check(index, constructor)) 901 .collect_vec(), 902 }; 903 904 // We then split all the branches using these checks and compile the 905 // choices they've been split up into. 906 let mut splitter = BranchSplitter::from_checks(known_checks); 907 self.split_branches(&mut splitter, branches, pivot_var.clone(), &branch_mode); 908 let choices = splitter 909 .choices 910 .into_iter() 911 .map(|(check, branches)| (check, Box::new(self.compile(branches)))) 912 .collect_vec(); 913 914 if branch_mode.needs_fallback() { 915 // If the branching is infinite, that means we always need to also have 916 // a fallback (imagine you're pattern matching on an `Int` and put no 917 // `_` at the end of the case expression). 918 let fallback = self.compile(splitter.fallback); 919 Decision::switch(pivot_var, choices, fallback) 920 } else if choices.is_empty() { 921 // If the branching doesn't need any fallback but we ended up with no 922 // checks it means we're trying to pattern match on an external type 923 // but haven't provided a catch-all case. 924 // That's never going to match, so we produce a failure node. 925 self.diagnostics.missing = true; 926 Decision::Fail 927 } else { 928 // Otherwise we know that one of the possible runtime checks is always 929 // going to succeed and there's no need to also have a fallback branch. 930 Decision::exhaustive_switch(pivot_var, choices) 931 } 932 } 933 934 fn split_branches( 935 &mut self, 936 splitter: &mut BranchSplitter, 937 branches: VecDeque<Branch>, 938 pivot_var: Variable, 939 branch_mode: &BranchMode, 940 ) { 941 for mut branch in branches { 942 let Some(pattern_check) = branch.pop_check_on_var(&pivot_var) else { 943 // If the branch doesn't perform any check on the pivot variable, it means 944 // it could still match no matter what shape `pivot_var` has. So we must 945 // add it as a fallback branch, that is a branch that is still relevant 946 // for all possible paths in the decision tree. 947 splitter.add_fallback_branch(branch); 948 continue; 949 }; 950 951 let checked_pattern = self.pattern(pattern_check.pattern).clone(); 952 if checked_pattern.is_matching_on_unreachable_variant(branch_mode) { 953 self.mark_as_matching_impossible_variant(&branch); 954 continue; 955 } 956 957 splitter.add_checked_branch(checked_pattern, branch, branch_mode, self); 958 } 959 } 960 961 /// Turns a `RuntimeCheckKind` into a new `RuntimeCheck` by coming up with 962 /// the needed new fresh variables. 963 /// All the type information needed to create these variables is in the 964 /// `branch_mode` arg. 965 /// 966 fn fresh_runtime_check( 967 &mut self, 968 kind: RuntimeCheckKind, 969 branch_mode: &BranchMode, 970 ) -> RuntimeCheck { 971 match (kind, branch_mode) { 972 (RuntimeCheckKind::Int { value }, _) => RuntimeCheck::Int { 973 value: value.clone(), 974 }, 975 (RuntimeCheckKind::Float { value }, _) => RuntimeCheck::Float { 976 value: value.clone(), 977 }, 978 (RuntimeCheckKind::String { value }, _) => RuntimeCheck::String { 979 value: value.clone(), 980 }, 981 (RuntimeCheckKind::BitArray { value }, _) => RuntimeCheck::BitArray { 982 value: value.clone(), 983 }, 984 (RuntimeCheckKind::StringPrefix { prefix }, _) => RuntimeCheck::StringPrefix { 985 prefix: prefix.clone(), 986 rest_arg: self.fresh_variable(string()), 987 }, 988 (RuntimeCheckKind::Tuple { .. }, BranchMode::Tuple { elems }) => { 989 self.is_tuple_check(elems) 990 } 991 (RuntimeCheckKind::Variant { index }, BranchMode::NamedType { constructors, .. }) => { 992 self.is_variant_check( 993 index, 994 constructors.get(index).expect("unknown variant index"), 995 ) 996 } 997 (RuntimeCheckKind::EmptyList, _) => RuntimeCheck::EmptyList, 998 (RuntimeCheckKind::NonEmptyList, BranchMode::List { inner_type }) => { 999 self.is_list_check(inner_type.clone()) 1000 } 1001 (_, _) => unreachable!("type checking should make this impossible"), 1002 } 1003 } 1004 1005 /// Comes up with new pattern cecks that have to match in case a given 1006 /// runtime check succeeds for the given pattern. 1007 /// 1008 /// Let's make an example: when we have a pattern - say `a is Wibble(1, [])` - 1009 /// we come up with a runtime check to perform on it. For our example the 1010 /// runtime check is to make sure that `a` is indeed a `Wibble` variant. 1011 /// However, after successfully performing that check we're left with much to 1012 /// do! We know that `a` is `Wibble` but now we'll have to make sure that its 1013 /// inner arguments also match the given patterns. So the new additional checks 1014 /// we have to add are `a0 is 1, a1 is []` (where `a0` and `a1` are the fresh 1015 /// variable names we use to refer to the constructor's arguments). 1016 /// 1017 fn new_checks( 1018 &mut self, 1019 for_pattern: &Pattern, 1020 after_succeding_check: &RuntimeCheck, 1021 ) -> Vec<PatternCheck> { 1022 match (for_pattern, after_succeding_check) { 1023 // These patterns never result in adding new checks. After a runtime 1024 // check matches on them there's nothing else to discover. 1025 ( 1026 Pattern::Discard 1027 | Pattern::Assign { .. } 1028 | Pattern::Variable { .. } 1029 | Pattern::Int { .. } 1030 | Pattern::Float { .. } 1031 | Pattern::BitArray { .. } 1032 | Pattern::EmptyList, 1033 _, 1034 ) 1035 | (Pattern::String { .. }, RuntimeCheck::String { .. }) => vec![], 1036 1037 // After making sure a value is not an empty list we'll have to perform 1038 // additional checks on its first item and on the tail. 1039 ( 1040 Pattern::List { 1041 first_pattern, 1042 rest_pattern, 1043 }, 1044 RuntimeCheck::List { 1045 first_arg, 1046 rest_arg, 1047 }, 1048 ) => vec![first_arg.is(*first_pattern), rest_arg.is(*rest_pattern)], 1049 1050 // After making sure a value is a specific variant we'll have to check each 1051 // of its arguments respects the given patterns (as shown in the doc example for 1052 // this function!) 1053 (Pattern::Variant { args_patterns, .. }, RuntimeCheck::Variant { args, .. }) => { 1054 (args.iter().zip(args_patterns)) 1055 .map(|(arg, pattern)| arg.is(*pattern)) 1056 .collect_vec() 1057 } 1058 1059 // Tuples are exactly the same as variants: after making sure we're dealing with 1060 // a tuple, we will have to check that each of its elements matches the given 1061 // pattern: `a is #(1, _)` will result in the following checks 1062 // `a0 is 1, a1 is _` (where `a0` and `a1` are fresh variable names we use to 1063 // refer to each of the tuple's elements). 1064 (Pattern::Tuple { elems_patterns }, RuntimeCheck::Tuple { args, .. }) => { 1065 (args.iter().zip(elems_patterns)) 1066 .map(|(arg, pattern)| arg.is(*pattern)) 1067 .collect_vec() 1068 } 1069 1070 // Strings are quite fun: if we've checked at runtime a string starts with a given 1071 // prefix and we want to check that it's some overlapping literal value we'll still 1072 // have some amount of work to perform. 1073 // 1074 // Let's have a look at an example: the pattern we care about is `a is "wibble"` 1075 // and we've just successfully ran the runtime check for `a is "wib" <> rest`. 1076 // So we know the string already starts with `"wib"` what we have to check now 1077 // is that the remaining part `rest` is `"ble"`. 1078 ( 1079 Pattern::String { value }, 1080 RuntimeCheck::StringPrefix { 1081 prefix, rest_arg, .. 1082 }, 1083 ) => { 1084 let remaining = value.strip_prefix(prefix.as_str()).unwrap_or(value); 1085 vec![rest_arg.is(self.string_pattern(remaining))] 1086 } 1087 1088 // String prefixes are similar to strings, but a bit more involved. Let's say we're 1089 // checking the pattern: 1090 // 1091 // ```text 1092 // "wibblest" <> rest1 1093 // ─┬──────── 1094 // ╰── We will refer to this as `prefix1` 1095 // ``` 1096 // 1097 // And we know that the following overlapping runtime check has already succeeded: 1098 // 1099 // ```text 1100 // "wibble" <> rest0 1101 // ─┬────── 1102 // ╰── We will refer to this as `prefix0` 1103 // ``` 1104 // 1105 // We're lucky because we now know quite a bit about the shape of the string. Since 1106 // we know it already starts with `"wibble"` we can just check that the remaining 1107 // part after that starts with the missing part of the prefix: 1108 // `prefix0 is "st" <> rest1`. 1109 ( 1110 Pattern::StringPrefix { 1111 prefix: prefix1, 1112 rest_pattern: rest1, 1113 }, 1114 RuntimeCheck::StringPrefix { 1115 prefix: prefix0, 1116 rest_arg: rest0, 1117 }, 1118 ) => { 1119 let remaining = prefix1.strip_prefix(prefix0.as_str()).unwrap_or(prefix1); 1120 vec![rest0.is(self.string_prefix_pattern(remaining, *rest1))] 1121 } 1122 1123 (_, _) => unreachable!("invalid pattern overlapping"), 1124 } 1125 } 1126 1127 /// Builds an `IsVariant` runtime check, coming up with new fresh variable names 1128 /// for its arguments. 1129 /// 1130 fn is_variant_check( 1131 &mut self, 1132 index: usize, 1133 constructor: &TypeValueConstructor, 1134 ) -> RuntimeCheck { 1135 RuntimeCheck::Variant { 1136 index, 1137 args: constructor 1138 .parameters 1139 .iter() 1140 .map(|p| p.type_.clone()) 1141 .map(|type_| self.fresh_variable(type_)) 1142 .collect_vec(), 1143 } 1144 } 1145 1146 /// Builds an `IsNonEmptyList` runtime check, coming up with fresh variable 1147 /// names for its arguments. 1148 /// 1149 fn is_list_check(&mut self, inner_type: Arc<Type>) -> RuntimeCheck { 1150 RuntimeCheck::List { 1151 first_arg: self.fresh_variable(inner_type.clone()), 1152 rest_arg: self.fresh_variable(Arc::new(Type::list(inner_type))), 1153 } 1154 } 1155 1156 /// Builds an `IsTuple` runtime check, coming up with fresh variable 1157 /// names for its arguments. 1158 /// 1159 fn is_tuple_check(&mut self, elems: &[Arc<Type>]) -> RuntimeCheck { 1160 RuntimeCheck::Tuple { 1161 size: elems.len(), 1162 args: elems 1163 .iter() 1164 .map(|type_| self.fresh_variable(type_.clone())) 1165 .collect_vec(), 1166 } 1167 } 1168 1169 /// Allocates a new `StringPattern` with the given value. 1170 /// 1171 fn string_pattern(&mut self, value: &str) -> Id<Pattern> { 1172 self.patterns.alloc(Pattern::String { 1173 value: EcoString::from(value), 1174 }) 1175 } 1176 1177 /// Allocates a new `StringPrefix` pattern with the given prefix and pattern 1178 /// for the rest of the string. 1179 /// 1180 fn string_prefix_pattern(&mut self, prefix: &str, rest_pattern: Id<Pattern>) -> Id<Pattern> { 1181 self.patterns.alloc(Pattern::StringPrefix { 1182 prefix: EcoString::from(prefix), 1183 rest_pattern, 1184 }) 1185 } 1186} 1187 1188/// Returns a pattern check from `first_branch` to be used as a pivot to split all 1189/// the `branches`. 1190/// 1191fn find_pivot_check(first_branch: &Branch, branches: &VecDeque<Branch>) -> Option<PatternCheck> { 1192 // To try and minimise code duplication, we use the following heuristic: we 1193 // choose the check matching on the variable that is referenced the most 1194 // across all checks in all branches. 1195 let mut var_references = HashMap::new(); 1196 for branch in branches { 1197 for check in &branch.checks { 1198 let _ = var_references 1199 .entry(check.var.id) 1200 .and_modify(|references| *references += 1) 1201 .or_insert(0); 1202 } 1203 } 1204 1205 first_branch 1206 .checks 1207 .iter() 1208 .max_by_key(|check| var_references.get(&check.var.id).cloned().unwrap_or(0)) 1209 .cloned() 1210} 1211 1212/// A handy data structure we use to split branches in different possible paths 1213/// based on a check. 1214/// 1215struct BranchSplitter { 1216 pub choices: Vec<(RuntimeCheck, VecDeque<Branch>)>, 1217 pub fallback: VecDeque<Branch>, 1218 /// This is used to allow quickly looking up a choice in the `choices` 1219 /// vector, without loosing track of the checks' order. 1220 indices: HashMap<RuntimeCheckKind, usize>, 1221 1222 /// This is used to store the indices of just the prefix checks as they have 1223 /// different rules from all the other `RuntimeCheckKinds` whose indices are 1224 /// instead stored in the `indices` field. 1225 /// 1226 /// We discuss this in more detail in the `index_of_overlapping_runtime_check` 1227 /// function! 1228 prefix_indices: Trie<String, usize>, 1229} 1230 1231impl BranchSplitter { 1232 /// Creates a new splitter with the given starting checks. 1233 /// 1234 fn from_checks(checks: Vec<RuntimeCheck>) -> Self { 1235 let mut choices = Vec::with_capacity(checks.len()); 1236 let mut indices = HashMap::new(); 1237 1238 for (index, runtime_check) in checks.into_iter().enumerate() { 1239 let _ = indices.insert(runtime_check.kind(), index); 1240 choices.push((runtime_check, VecDeque::new())); 1241 } 1242 1243 Self { 1244 fallback: VecDeque::new(), 1245 choices, 1246 indices, 1247 prefix_indices: Trie::new(), 1248 } 1249 } 1250 1251 /// Add a fallback branch: this is a branch that is relevant to all possible 1252 /// paths as it could still run, no matter the result of any of the `Check`s 1253 /// we've stored! 1254 /// 1255 fn add_fallback_branch(&mut self, branch: Branch) { 1256 self.choices 1257 .iter_mut() 1258 .for_each(|(_, branches)| branches.push_back(branch.clone())); 1259 self.fallback.push_back(branch); 1260 } 1261 1262 /// Given a branch and the pattern its using to check on the pivot variable, 1263 /// adds it to the paths where it's relevant, that is where we know from 1264 /// previous checks that this pattern has a chance of matching. 1265 /// 1266 fn add_checked_branch( 1267 &mut self, 1268 pattern: Pattern, 1269 branch: Branch, 1270 branch_mode: &BranchMode, 1271 compiler: &mut Compiler<'_>, 1272 ) { 1273 let kind = pattern 1274 .to_runtime_check_kind() 1275 .expect("no unconditional patterns left"); 1276 1277 let indices_of_overlapping_checks = self.indices_of_overlapping_checks(&kind); 1278 if indices_of_overlapping_checks.is_empty() { 1279 // This is a new choice we haven't yet discovered as it is not overlapping 1280 // with any of the existing ones. So we add it as a possible new path 1281 // we might have to go down to in the decision tree. 1282 self.save_index_of_new_choice(kind.clone()); 1283 let mut branches = self.fallback.clone(); 1284 branches.push_back(branch); 1285 let check = compiler.fresh_runtime_check(kind, branch_mode); 1286 self.choices.push((check, branches)); 1287 } else { 1288 // Otherwise, we know that the check for this branch overlaps with 1289 // (possibly more than one) existing checks and so is relevant only 1290 // as part of those existing paths. 1291 // We'll add the branch with its newly discovered checks only to those 1292 // paths. 1293 for index in indices_of_overlapping_checks { 1294 let (overlapping_check, branches) = self 1295 .choices 1296 .get_mut(index) 1297 .expect("check to already be a choice"); 1298 1299 let mut branch = branch.clone(); 1300 for new_check in compiler.new_checks(&pattern, overlapping_check) { 1301 branch.add_check(new_check); 1302 } 1303 branches.push_back(branch); 1304 } 1305 } 1306 } 1307 1308 fn save_index_of_new_choice(&mut self, kind: RuntimeCheckKind) { 1309 let _ = match kind { 1310 RuntimeCheckKind::Int { .. } 1311 | RuntimeCheckKind::Float { .. } 1312 | RuntimeCheckKind::String { .. } 1313 | RuntimeCheckKind::Tuple { .. } 1314 | RuntimeCheckKind::BitArray { .. } 1315 | RuntimeCheckKind::Variant { .. } 1316 | RuntimeCheckKind::EmptyList 1317 | RuntimeCheckKind::NonEmptyList => self.indices.insert(kind, self.choices.len()), 1318 1319 RuntimeCheckKind::StringPrefix { prefix } => self 1320 .prefix_indices 1321 .insert(prefix.to_string(), self.choices.len()), 1322 }; 1323 } 1324 1325 fn indices_of_overlapping_checks(&self, kind: &RuntimeCheckKind) -> Vec<usize> { 1326 match kind { 1327 // All these checks will only overlap with a check that is exactly the 1328 // same, so we just look up their index in the `indices` map using the 1329 // kind as the lookup. 1330 RuntimeCheckKind::Int { .. } 1331 | RuntimeCheckKind::Float { .. } 1332 | RuntimeCheckKind::Tuple { .. } 1333 | RuntimeCheckKind::BitArray { .. } 1334 | RuntimeCheckKind::Variant { .. } 1335 | RuntimeCheckKind::EmptyList 1336 | RuntimeCheckKind::NonEmptyList => { 1337 self.indices.get(kind).cloned().into_iter().collect_vec() 1338 } 1339 1340 // String patterns are a bit more tricky as they might end up overlapping 1341 // even if they're not exactly the same kind of check! Let's have a look 1342 // at an example. Say we're compiling these branches: 1343 // 1344 // ``` 1345 // a is "wibble" <> rest -> todo 1346 // a is "wibbler" <> rest -> todo 1347 // ``` 1348 // 1349 // We use the first (and only) check in the first branch as the pivot and 1350 // now we have to decide where to put the next branch. Is it matching with 1351 // the first one or completely unrelated? 1352 // Since `"wibbler"` starts with `"wibble"` we know it's overlapping and 1353 // it cannot possibly match if the previous one doesn't! 1354 // 1355 // So when we find a `String`/`StringPrefix` pattern we look for a prefix 1356 // among the ones we have discovered so far that could match with it. 1357 // That is, we look for a prefix of the pattern we're checking in the prefix 1358 // trie. 1359 RuntimeCheckKind::StringPrefix { prefix: value } => { 1360 ancestors_values(&self.prefix_indices, value).collect_vec() 1361 } 1362 1363 // Strings are almost exactly the same, except they could also have an exact 1364 // match with other string patterns. So a string pattern could overlap with 1365 // another string pattern (if they're matching on the same value), or with 1366 // one or more string prefix patterns with a matching prefix. 1367 RuntimeCheckKind::String { value } => { 1368 let first_index = self.indices.get(kind).cloned(); 1369 first_index 1370 .into_iter() 1371 .chain(ancestors_values(&self.prefix_indices, value)) 1372 .collect_vec() 1373 } 1374 } 1375 } 1376} 1377 1378fn ancestors_values(trie: &Trie<String, usize>, key: &str) -> impl Iterator<Item = usize> { 1379 trie.get_ancestor(key) 1380 .into_iter() 1381 .flat_map(|ancestor| ancestor.values().copied()) 1382} 1383 1384pub struct ConstructorSpecialiser { 1385 specialised_types: HashMap<u64, Arc<Type>>, 1386} 1387 1388impl ConstructorSpecialiser { 1389 fn specialise_constructors( 1390 constructors: &TypeVariantConstructors, 1391 type_arguments: &[Arc<Type>], 1392 ) -> Vec<TypeValueConstructor> { 1393 let specialiser = Self::new(constructors.type_parameters_ids.as_slice(), type_arguments); 1394 constructors 1395 .variants 1396 .iter() 1397 .map(|v| specialiser.specialise_type_value_constructor(v)) 1398 .collect_vec() 1399 } 1400 1401 fn new(parameters: &[u64], type_arguments: &[Arc<Type>]) -> Self { 1402 let specialised_types = parameters 1403 .iter() 1404 .copied() 1405 .zip(type_arguments.iter().cloned()) 1406 .collect(); 1407 Self { specialised_types } 1408 } 1409 1410 fn specialise_type_value_constructor(&self, v: &TypeValueConstructor) -> TypeValueConstructor { 1411 let TypeValueConstructor { 1412 name, 1413 parameters, 1414 documentation, 1415 } = v; 1416 let parameters = parameters 1417 .iter() 1418 .map(|p| TypeValueConstructorField { 1419 type_: self.specialise_type(p.type_.as_ref()), 1420 label: p.label.clone(), 1421 }) 1422 .collect_vec(); 1423 TypeValueConstructor { 1424 name: name.clone(), 1425 parameters, 1426 documentation: documentation.clone(), 1427 } 1428 } 1429 1430 fn specialise_type(&self, type_: &Type) -> Arc<Type> { 1431 Arc::new(match type_ { 1432 Type::Named { 1433 publicity, 1434 package, 1435 module, 1436 name, 1437 args, 1438 inferred_variant, 1439 } => Type::Named { 1440 publicity: *publicity, 1441 package: package.clone(), 1442 module: module.clone(), 1443 name: name.clone(), 1444 args: args.iter().map(|a| self.specialise_type(a)).collect(), 1445 inferred_variant: *inferred_variant, 1446 }, 1447 1448 Type::Fn { args, retrn } => Type::Fn { 1449 args: args.iter().map(|a| self.specialise_type(a)).collect(), 1450 retrn: retrn.clone(), 1451 }, 1452 1453 Type::Var { type_ } => Type::Var { 1454 type_: Arc::new(RefCell::new(self.specialise_var(type_))), 1455 }, 1456 1457 Type::Tuple { elems } => Type::Tuple { 1458 elems: elems.iter().map(|e| self.specialise_type(e)).collect(), 1459 }, 1460 }) 1461 } 1462 1463 fn specialise_var(&self, type_: &RefCell<TypeVar>) -> TypeVar { 1464 match &*type_.borrow() { 1465 TypeVar::Unbound { id } => TypeVar::Unbound { id: *id }, 1466 1467 TypeVar::Link { type_ } => TypeVar::Link { 1468 type_: self.specialise_type(type_.as_ref()), 1469 }, 1470 1471 TypeVar::Generic { id } => match self.specialised_types.get(id) { 1472 Some(type_) => TypeVar::Link { 1473 type_: type_.clone(), 1474 }, 1475 None => TypeVar::Generic { id: *id }, 1476 }, 1477 } 1478 } 1479} 1480 1481/// Intermiate data structure that's used to set up everything that's needed by 1482/// the pattern matching compiler and get a case expression ready to be compiled, 1483/// while hiding the intricacies of handling an arena to record different patterns. 1484/// 1485pub struct CaseToCompile { 1486 patterns: Arena<Pattern>, 1487 branches: Vec<Branch>, 1488 subject_variables: Vec<Variable>, 1489 /// The number of clauses in this case to compile. 1490 number_of_clauses: usize, 1491 variable_id: usize, 1492} 1493 1494impl CaseToCompile { 1495 pub fn new(subject_types: &[Arc<Type>]) -> Self { 1496 let mut variable_id = 0; 1497 let subject_variables = subject_types 1498 .iter() 1499 .map(|type_| { 1500 let id = variable_id; 1501 variable_id += 1; 1502 Variable::new(id, type_.clone()) 1503 }) 1504 .collect_vec(); 1505 1506 Self { 1507 patterns: Arena::new(), 1508 branches: vec![], 1509 number_of_clauses: 0, 1510 subject_variables, 1511 variable_id, 1512 } 1513 } 1514 1515 /// Registers a `TypedClause` as one of the branches to be compiled. 1516 /// 1517 /// If you don't have a clause and just have a simple `TypedPattern` you want 1518 /// to generate a decision tree for you can use `add_pattern`. 1519 /// 1520 pub fn add_clause(&mut self, branch: &TypedClause) { 1521 let all_patterns = 1522 std::iter::once(&branch.pattern).chain(branch.alternative_patterns.iter()); 1523 1524 for (alternative_index, patterns) in all_patterns.enumerate() { 1525 let mut checks = Vec::with_capacity(patterns.len()); 1526 1527 // We're doing the zipping ourselves instead of using iters.zip as the 1528 // borrow checker would complain and the only workaround would be to 1529 // allocate an entire new vector each time. 1530 for i in 0..patterns.len() { 1531 let pattern = self.register(patterns.get(i).expect("pattern index")); 1532 let var = self 1533 .subject_variables 1534 .get(i) 1535 .expect("wrong number of subjects"); 1536 checks.push(var.is(pattern)) 1537 } 1538 1539 let has_guard = branch.guard.is_some(); 1540 let branch = Branch::new(self.number_of_clauses, alternative_index, checks, has_guard); 1541 self.branches.push(branch); 1542 } 1543 1544 self.number_of_clauses += 1; 1545 } 1546 1547 /// Add a single pattern as a branch to be compiled. 1548 /// 1549 /// This is useful in case one wants to check exhaustiveness of a single 1550 /// pattern without having a fully fledged `TypedClause` to pass to the `add_clause` 1551 /// method. For example, in `let` destructurings. 1552 /// 1553 pub fn add_pattern(&mut self, pattern: &TypedPattern) { 1554 let pattern = self.register(pattern); 1555 let var = self 1556 .subject_variables 1557 .first() 1558 .expect("wrong number of subject variables for pattern"); 1559 let branch = Branch::new(self.number_of_clauses, 0, vec![var.is(pattern)], false); 1560 self.number_of_clauses += 1; 1561 self.branches.push(branch); 1562 } 1563 1564 pub fn compile(self, env: &Environment<'_>) -> Match { 1565 let mut compiler = Compiler::new(env, self.variable_id, self.patterns); 1566 1567 let decision = if self.branches.is_empty() { 1568 let var = self 1569 .subject_variables 1570 .first() 1571 .expect("case with no subjects") 1572 .clone(); 1573 1574 compiler.split_and_compile_with_pivot_var(var, VecDeque::new()) 1575 } else { 1576 compiler.compile(self.branches.into()) 1577 }; 1578 1579 Match { 1580 tree: decision, 1581 diagnostics: compiler.diagnostics, 1582 subject_variables: self.subject_variables, 1583 } 1584 } 1585 1586 /// Registers a typed pattern (and all its sub-patterns) into this 1587 /// `CaseToCompile`'s pattern arena, returning an id to get the pattern back. 1588 /// 1589 fn register(&mut self, pattern: &TypedPattern) -> Id<Pattern> { 1590 match pattern { 1591 TypedPattern::Invalid { .. } => self.insert(Pattern::Discard), 1592 TypedPattern::Discard { .. } => self.insert(Pattern::Discard), 1593 1594 TypedPattern::Int { value, .. } => { 1595 let value = value.clone(); 1596 self.insert(Pattern::Int { value }) 1597 } 1598 1599 TypedPattern::Float { value, .. } => { 1600 let value = value.clone(); 1601 self.insert(Pattern::Float { value }) 1602 } 1603 1604 TypedPattern::String { value, .. } => { 1605 let value = value.clone(); 1606 self.insert(Pattern::String { value }) 1607 } 1608 1609 TypedPattern::Variable { name, .. } => { 1610 let name = name.clone(); 1611 self.insert(Pattern::Variable { name }) 1612 } 1613 1614 TypedPattern::Assign { name, pattern, .. } => { 1615 let name = name.clone(); 1616 let pattern = self.register(pattern); 1617 self.insert(Pattern::Assign { name, pattern }) 1618 } 1619 1620 TypedPattern::Tuple { elems, .. } => { 1621 let elems_patterns = elems.iter().map(|elem| self.register(elem)).collect_vec(); 1622 self.insert(Pattern::Tuple { elems_patterns }) 1623 } 1624 1625 TypedPattern::List { elements, tail, .. } => { 1626 let mut list = match tail { 1627 Some(tail) => self.register(tail), 1628 None => self.insert(Pattern::EmptyList), 1629 }; 1630 for element in elements.iter().rev() { 1631 let first_pattern = self.register(element); 1632 list = self.insert(Pattern::List { 1633 first_pattern, 1634 rest_pattern: list, 1635 }); 1636 } 1637 list 1638 } 1639 1640 TypedPattern::Constructor { 1641 arguments, 1642 constructor, 1643 .. 1644 } => { 1645 let index = constructor.expect_ref("must be inferred").constructor_index as usize; 1646 let args_patterns = arguments 1647 .iter() 1648 .map(|argument| self.register(&argument.value)) 1649 .collect_vec(); 1650 self.insert(Pattern::Variant { 1651 index, 1652 args_patterns, 1653 }) 1654 } 1655 1656 TypedPattern::BitArray { location, .. } => { 1657 // TODO: in future support bit strings fully and check the 1658 // exhaustiveness of their segment patterns. 1659 // For now we use the location to give each bit string a pattern 1660 // a unique value. 1661 self.insert(Pattern::BitArray { 1662 value: format!("{}:{}", location.start, location.end).into(), 1663 }) 1664 } 1665 1666 TypedPattern::StringPrefix { 1667 left_side_string, 1668 right_side_assignment, 1669 .. 1670 } => { 1671 let prefix = left_side_string.clone(); 1672 let rest_pattern = match right_side_assignment { 1673 AssignName::Variable(name) => Pattern::Variable { name: name.clone() }, 1674 AssignName::Discard(_) => Pattern::Discard, 1675 }; 1676 let rest_pattern = self.insert(rest_pattern); 1677 self.insert(Pattern::StringPrefix { 1678 prefix, 1679 rest_pattern, 1680 }) 1681 } 1682 1683 TypedPattern::VarUsage { .. } => { 1684 unreachable!("Cannot convert VarUsage to exhaustiveness pattern") 1685 } 1686 } 1687 } 1688 1689 fn insert(&mut self, pattern: Pattern) -> Id<Pattern> { 1690 self.patterns.alloc(pattern) 1691 } 1692}