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

Configure Feed

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

tell if string patterns are unreachable

+382 -204
+26
Cargo.lock
··· 767 767 checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" 768 768 769 769 [[package]] 770 + name = "endian-type" 771 + version = "0.1.2" 772 + source = "registry+https://github.com/rust-lang/crates.io-index" 773 + checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d" 774 + 775 + [[package]] 770 776 name = "equivalent" 771 777 version = "1.0.1" 772 778 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1117 1123 "pretty_assertions", 1118 1124 "pubgrub", 1119 1125 "pulldown-cmark", 1126 + "radix_trie", 1120 1127 "rand", 1121 1128 "regex", 1122 1129 "serde", ··· 1854 1861 checksum = "defc4c55412d89136f966bbb339008b474350e5e6e78d2714439c386b3137a03" 1855 1862 1856 1863 [[package]] 1864 + name = "nibble_vec" 1865 + version = "0.1.0" 1866 + source = "registry+https://github.com/rust-lang/crates.io-index" 1867 + checksum = "77a5d83df9f36fe23f0c3648c6bbb8b0298bb5f1939c8f2704431371f4b84d43" 1868 + dependencies = [ 1869 + "smallvec", 1870 + ] 1871 + 1872 + [[package]] 1857 1873 name = "nix" 1858 1874 version = "0.29.0" 1859 1875 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2279 2295 checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" 2280 2296 dependencies = [ 2281 2297 "proc-macro2", 2298 + ] 2299 + 2300 + [[package]] 2301 + name = "radix_trie" 2302 + version = "0.2.1" 2303 + source = "registry+https://github.com/rust-lang/crates.io-index" 2304 + checksum = "c069c179fcdc6a2fe24d8d18305cf085fdbd4f922c041943e203685d6a1c58fd" 2305 + dependencies = [ 2306 + "endian-type", 2307 + "nibble_vec", 2282 2308 ] 2283 2309 2284 2310 [[package]]
+1
compiler-core/Cargo.toml
··· 49 49 num-traits = "0.2.19" 50 50 # Encryption 51 51 age = { version = "0.11", features = ["armor"] } 52 + radix_trie = "0.2.1" 52 53 53 54 async-trait.workspace = true 54 55 base16.workspace = true
+354 -203
compiler-core/src/exhaustiveness.rs
··· 88 88 use ecow::EcoString; 89 89 use id_arena::{Arena, Id}; 90 90 use itertools::Itertools; 91 + use radix_trie::{Trie, TrieCommon}; 91 92 use std::{ 92 93 cell::RefCell, 93 94 collections::{HashMap, HashSet, VecDeque}, ··· 302 303 }, 303 304 StringPrefix { 304 305 prefix: EcoString, 305 - rest: Id<Pattern>, 306 + rest_pattern: Id<Pattern>, 306 307 }, 307 308 Assign { 308 309 name: EcoString, ··· 312 313 name: EcoString, 313 314 }, 314 315 Tuple { 315 - elements: Vec<Id<Pattern>>, 316 + elems_patterns: Vec<Id<Pattern>>, 316 317 }, 317 - Constructor { 318 - variant_index: usize, 319 - arguments: Vec<Id<Pattern>>, 318 + Variant { 319 + index: usize, 320 + args_patterns: Vec<Id<Pattern>>, 320 321 }, 321 322 List { 322 - first: Id<Pattern>, 323 - rest: Id<Pattern>, 323 + first_pattern: Id<Pattern>, 324 + rest_pattern: Id<Pattern>, 324 325 }, 325 326 EmptyList, 326 327 // TODO: Compile the matching within the bit strings ··· 329 330 }, 330 331 } 331 332 333 + impl 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 + 332 384 /// A single check making up a branch, checking that a variable matches with a 333 385 /// given pattern. For example, the following branch has 2 checks: 334 386 /// ··· 347 399 pattern: Id<Pattern>, 348 400 } 349 401 350 - impl PatternCheck { 351 - fn new(var: Variable, pattern: Id<Pattern>) -> Self { 352 - Self { var, pattern } 353 - } 354 - 355 - /// Each pattern check (with a couple exceptions) can be turned into a 356 - /// simpler `RuntimeCheck`: that is a check that can be performed at runtime 357 - /// to make sure the `PatternCheck` can succeed on a specific value. 358 - /// 359 - fn to_runtime_check_kind(&self, compiler: &Compiler<'_>) -> Option<RuntimeCheckKind> { 360 - let kind = match compiler.pattern(self.pattern) { 361 - // These patterns are unconditional: they will always match and be moved 362 - // out of a branch's checks. So there's no corresponding runtime check 363 - // we can perform for them. 364 - Pattern::Discard | Pattern::Variable { .. } | Pattern::Assign { .. } => return None, 365 - Pattern::Int { value } => { 366 - let value = value.clone(); 367 - RuntimeCheckKind::Int { value } 368 - } 369 - Pattern::Float { value } => { 370 - let value = value.clone(); 371 - RuntimeCheckKind::Float { value } 372 - } 373 - Pattern::String { value } => { 374 - let value = value.clone(); 375 - RuntimeCheckKind::String { value } 376 - } 377 - Pattern::StringPrefix { prefix, .. } => { 378 - let prefix = prefix.clone(); 379 - RuntimeCheckKind::StringPrefix { prefix } 380 - } 381 - Pattern::Tuple { elements } => { 382 - let size = elements.len(); 383 - RuntimeCheckKind::Tuple { size } 384 - } 385 - Pattern::Constructor { variant_index, .. } => { 386 - let index = *variant_index; 387 - RuntimeCheckKind::Variant { index } 388 - } 389 - Pattern::BitArray { value } => { 390 - let value = value.clone(); 391 - RuntimeCheckKind::BitArray { value } 392 - } 393 - Pattern::List { .. } => RuntimeCheckKind::NonEmptyList, 394 - Pattern::EmptyList => RuntimeCheckKind::EmptyList, 395 - }; 396 - 397 - Some(kind) 398 - } 399 - } 400 - 401 402 /// This is one of the checks we can take at runtime to decide how to move 402 403 /// forward in the decision tree. 403 404 /// ··· 441 442 args: Vec<Variable>, 442 443 }, 443 444 EmptyList, 444 - NonEmptyList { 445 + List { 445 446 first_arg: Variable, 446 447 rest_arg: Variable, 447 448 }, ··· 471 472 }, 472 473 RuntimeCheck::Variant { index, args: _ } => RuntimeCheckKind::Variant { index: *index }, 473 474 RuntimeCheck::EmptyList => RuntimeCheckKind::EmptyList, 474 - RuntimeCheck::NonEmptyList { 475 + RuntimeCheck::List { 475 476 first_arg: _, 476 477 rest_arg: _, 477 478 } => RuntimeCheckKind::NonEmptyList, ··· 492 493 NonEmptyList, 493 494 } 494 495 495 - impl RuntimeCheckKind { 496 - fn is_matching_on_unreachable_variant(&self, branch_mode: &BranchMode) -> bool { 497 - match (self, branch_mode) { 498 - ( 499 - Self::Variant { index }, 500 - BranchMode::NamedType { 501 - inferred_variant: Some(variant), 502 - .. 503 - }, 504 - ) if index != variant => true, 505 - _ => false, 506 - } 507 - } 508 - } 509 - 510 496 /// A variable that can be matched on in a branch. 511 497 /// 512 498 #[derive(Eq, PartialEq, Clone, Debug)] ··· 519 505 fn new(id: usize, type_: Arc<Type>) -> Self { 520 506 Self { id, type_ } 521 507 } 508 + 509 + fn is(&self, pattern: Id<Pattern>) -> PatternCheck { 510 + PatternCheck { 511 + var: self.clone(), 512 + pattern, 513 + } 514 + } 522 515 } 523 516 524 517 #[derive(Debug)] ··· 955 948 continue; 956 949 }; 957 950 958 - // If it does check the same variable, we know that this branch is only relevant 959 - // for some specific shape of `pivot_var`, determined by the runtime check that 960 - // it is performing. 961 - let kind = pattern_check 962 - .to_runtime_check_kind(self) 963 - .expect("no var patterns left"); 964 - 965 - if kind.is_matching_on_unreachable_variant(branch_mode) { 951 + let checked_pattern = self.pattern(pattern_check.pattern).clone(); 952 + if checked_pattern.is_matching_on_unreachable_variant(branch_mode) { 966 953 self.mark_as_matching_impossible_variant(&branch); 967 954 continue; 968 955 } 969 956 970 - // We now replace the pattern check we've just removed with the new 971 - // ones we might have discovered. 972 - // 973 - // If a check is already there we don't generate a new one with 974 - // fresh variables, but reuse the existing one! 975 - // 976 - // TODO)) this might have to be slightly updated once we perform 977 - // code generation for string patterns that are a bit trickier to split 978 - // as they might be overlapping even when they're different! 979 - let runtime_check = match splitter.get_overlapping_runtime_check(&kind) { 980 - Some(runtime_check) => runtime_check, 981 - None => self.kind_to_runtime_check(kind, branch_mode), 982 - }; 983 - 984 - self.new_pattern_checks(&runtime_check, pattern_check.pattern) 985 - .into_iter() 986 - .for_each(|check| branch.add_check(check)); 987 - 988 - splitter.add_checked_branch(runtime_check, branch); 989 - } 990 - } 991 - 992 - /// Comes up with new checks to perform after the given runtime check succeeds for 993 - /// the given pattern being matched. 994 - /// 995 - /// This is a tricky part, so let's have a look at an example: let's say this is 996 - /// the pattern check `a is Wibble(1, _, [])`. 997 - /// After making the runtime check that `a` is indeed a `Wibble` we're still 998 - /// left with many things to consider! We'll need to also make sure that `a`'s 999 - /// fields match with the respective patterns `1`, `_` and `[]`. 1000 - /// So we would have to add new pattern checks to this branch: 1001 - /// `a_0 is 1`, `a_1 is _` and `a_2 is []` (where `a_n` are fresh names we use 1002 - /// to refer to `a`'s fields). 1003 - /// 1004 - fn new_pattern_checks( 1005 - &mut self, 1006 - runtime_check: &RuntimeCheck, 1007 - pattern: Id<Pattern>, 1008 - ) -> Vec<PatternCheck> { 1009 - match (runtime_check, self.pattern(pattern)) { 1010 - // None of these patterns introduces new arguments we need to be matching on! 1011 - ( 1012 - RuntimeCheck::Int { .. } 1013 - | RuntimeCheck::Float { .. } 1014 - | RuntimeCheck::String { .. } 1015 - | RuntimeCheck::BitArray { .. } 1016 - | RuntimeCheck::EmptyList, 1017 - _, 1018 - ) => vec![], 1019 - 1020 - // A check on a string prefix introduces just a single new pattern: that is 1021 - // the one on the remaining bit. 1022 - (RuntimeCheck::StringPrefix { rest_arg, .. }, Pattern::StringPrefix { rest, .. }) => { 1023 - vec![PatternCheck::new(rest_arg.clone(), *rest)] 1024 - } 1025 - (RuntimeCheck::StringPrefix { .. }, _) => vec![], 1026 - 1027 - // A check on a tuple introduces a new pattern for each of the elements making 1028 - // up the tuple. The same goes for variants, we have to introduce a new check 1029 - // for each of its fields, as shown in the doc comment above. 1030 - (RuntimeCheck::Tuple { args, .. }, Pattern::Tuple { elements: ps }) 1031 - | (RuntimeCheck::Variant { args, .. }, Pattern::Constructor { arguments: ps, .. }) => { 1032 - (args.iter().zip(ps)) 1033 - .map(|(arg, pattern)| PatternCheck::new(arg.clone(), *pattern)) 1034 - .collect_vec() 1035 - } 1036 - (RuntimeCheck::Tuple { .. } | RuntimeCheck::Variant { .. }, _) => vec![], 1037 - 1038 - // A check on a non empty list introduces two new patterns: one for the head of 1039 - // the list and one for the tail! 1040 - ( 1041 - RuntimeCheck::NonEmptyList { 1042 - first_arg, 1043 - rest_arg, 1044 - }, 1045 - Pattern::List { first, rest }, 1046 - ) => vec![ 1047 - PatternCheck::new(first_arg.clone(), *first), 1048 - PatternCheck::new(rest_arg.clone(), *rest), 1049 - ], 1050 - (RuntimeCheck::NonEmptyList { .. }, _) => vec![], 957 + splitter.add_checked_branch(checked_pattern, branch, branch_mode, self); 1051 958 } 1052 959 } 1053 960 1054 - /// Turns a `RuntimeCheckKind` into a proper `RuntimeCheck` by coming up with 961 + /// Turns a `RuntimeCheckKind` into a new `RuntimeCheck` by coming up with 1055 962 /// the needed new fresh variables. 1056 963 /// All the type information needed to create these variables is in the 1057 964 /// `branch_mode` arg. 1058 965 /// 1059 - fn kind_to_runtime_check( 966 + fn fresh_runtime_check( 1060 967 &mut self, 1061 968 kind: RuntimeCheckKind, 1062 969 branch_mode: &BranchMode, ··· 1095 1002 } 1096 1003 } 1097 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 + 1098 1127 /// Builds an `IsVariant` runtime check, coming up with new fresh variable names 1099 1128 /// for its arguments. 1100 1129 /// ··· 1118 1147 /// names for its arguments. 1119 1148 /// 1120 1149 fn is_list_check(&mut self, inner_type: Arc<Type>) -> RuntimeCheck { 1121 - RuntimeCheck::NonEmptyList { 1150 + RuntimeCheck::List { 1122 1151 first_arg: self.fresh_variable(inner_type.clone()), 1123 1152 rest_arg: self.fresh_variable(Arc::new(Type::list(inner_type))), 1124 1153 } ··· 1136 1165 .collect_vec(), 1137 1166 } 1138 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 + } 1139 1186 } 1140 1187 1141 1188 /// Returns a pattern check from `first_branch` to be used as a pivot to split all ··· 1171 1218 /// This is used to allow quickly looking up a choice in the `choices` 1172 1219 /// vector, without loosing track of the checks' order. 1173 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>, 1174 1229 } 1175 1230 1176 1231 impl BranchSplitter { ··· 1189 1244 fallback: VecDeque::new(), 1190 1245 choices, 1191 1246 indices, 1247 + prefix_indices: Trie::new(), 1192 1248 } 1193 1249 } 1194 1250 ··· 1203 1259 self.fallback.push_back(branch); 1204 1260 } 1205 1261 1206 - /// Add a branch that we know will only ever run if the `check` is true. 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. 1207 1265 /// 1208 - fn add_checked_branch(&mut self, check: RuntimeCheck, branch: Branch) { 1209 - match self.indices.get(&check.kind()) { 1210 - Some(index) => { 1211 - let (_, branches) = self 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 1212 1295 .choices 1213 - .get_mut(*index) 1296 + .get_mut(index) 1214 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 + } 1215 1303 branches.push_back(branch); 1216 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 + } 1217 1324 1218 - None => { 1219 - let _ = self.indices.insert(check.kind(), self.choices.len()); 1220 - let mut branches = self.fallback.clone(); 1221 - branches.push_back(branch); 1222 - self.choices.push((check, branches)); 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() 1223 1373 } 1224 1374 } 1225 1375 } 1376 + } 1226 1377 1227 - fn get_overlapping_runtime_check(&self, kind: &RuntimeCheckKind) -> Option<RuntimeCheck> { 1228 - let index = self.indices.get(kind)?; 1229 - let (runtime_check, _) = self.choices.get(*index)?; 1230 - Some(runtime_check.clone()) 1231 - } 1378 + fn 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()) 1232 1382 } 1233 1383 1234 1384 pub struct ConstructorSpecialiser { ··· 1382 1532 let var = self 1383 1533 .subject_variables 1384 1534 .get(i) 1385 - .expect("wrong number of subjects") 1386 - .clone(); 1387 - 1388 - checks.push(PatternCheck::new(var, pattern)) 1535 + .expect("wrong number of subjects"); 1536 + checks.push(var.is(pattern)) 1389 1537 } 1390 1538 1391 1539 let has_guard = branch.guard.is_some(); ··· 1407 1555 let var = self 1408 1556 .subject_variables 1409 1557 .first() 1410 - .expect("wrong number of subject variables for pattern") 1411 - .clone(); 1412 - let checks = vec![PatternCheck::new(var, pattern)]; 1413 - let branch = Branch::new(self.number_of_clauses, 0, checks, false); 1558 + .expect("wrong number of subject variables for pattern"); 1559 + let branch = Branch::new(self.number_of_clauses, 0, vec![var.is(pattern)], false); 1414 1560 self.number_of_clauses += 1; 1415 1561 self.branches.push(branch); 1416 1562 } ··· 1472 1618 } 1473 1619 1474 1620 TypedPattern::Tuple { elems, .. } => { 1475 - let elements = elems.iter().map(|elem| self.register(elem)).collect_vec(); 1476 - self.insert(Pattern::Tuple { elements }) 1621 + let elems_patterns = elems.iter().map(|elem| self.register(elem)).collect_vec(); 1622 + self.insert(Pattern::Tuple { elems_patterns }) 1477 1623 } 1478 1624 1479 1625 TypedPattern::List { elements, tail, .. } => { ··· 1482 1628 None => self.insert(Pattern::EmptyList), 1483 1629 }; 1484 1630 for element in elements.iter().rev() { 1485 - let first = self.register(element); 1486 - list = self.insert(Pattern::List { first, rest: list }); 1631 + let first_pattern = self.register(element); 1632 + list = self.insert(Pattern::List { 1633 + first_pattern, 1634 + rest_pattern: list, 1635 + }); 1487 1636 } 1488 1637 list 1489 1638 } ··· 1493 1642 constructor, 1494 1643 .. 1495 1644 } => { 1496 - let variant_index = 1497 - constructor.expect_ref("must be inferred").constructor_index as usize; 1498 - let arguments = arguments 1645 + let index = constructor.expect_ref("must be inferred").constructor_index as usize; 1646 + let args_patterns = arguments 1499 1647 .iter() 1500 1648 .map(|argument| self.register(&argument.value)) 1501 1649 .collect_vec(); 1502 - self.insert(Pattern::Constructor { 1503 - variant_index, 1504 - arguments, 1650 + self.insert(Pattern::Variant { 1651 + index, 1652 + args_patterns, 1505 1653 }) 1506 1654 } 1507 1655 ··· 1525 1673 AssignName::Variable(name) => Pattern::Variable { name: name.clone() }, 1526 1674 AssignName::Discard(_) => Pattern::Discard, 1527 1675 }; 1528 - let rest = self.insert(rest_pattern); 1529 - self.insert(Pattern::StringPrefix { prefix, rest }) 1676 + let rest_pattern = self.insert(rest_pattern); 1677 + self.insert(Pattern::StringPrefix { 1678 + prefix, 1679 + rest_pattern, 1680 + }) 1530 1681 } 1531 1682 1532 1683 TypedPattern::VarUsage { .. } => {
+1 -1
compiler-core/src/exhaustiveness/missing_patterns.rs
··· 164 164 } 165 165 } 166 166 167 - RuntimeCheck::NonEmptyList { 167 + RuntimeCheck::List { 168 168 first_arg, 169 169 rest_arg, 170 170 } => Term::List {