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

Configure Feed

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

Fix dependency order sorting

author
Gears
committer
Louis Pilfold
date (Apr 20, 2026, 10:57 AM +0100) commit 571158c1 parent a2f9adce change-id zolttusq
+301 -401
+6
CHANGELOG.md
··· 7 7 - New Gleam packages are now generated requiring `>= 1.0.0` of `gleam_stdlib`. 8 8 ([Surya Rose](https://github.com/GearsDatapacks)) 9 9 10 + ### Bug fixes 11 + 12 + - Fixed a bug where certain invalid programs would type check if they contained 13 + many mutually recursive functions. 14 + ([Surya Rose](https://github.com/GearsDatapacks)) 15 + 10 16 ## v1.16.0-rc2 - 2026-04-14 11 17 12 18 ### Bug fixes
+12 -4
compiler-core/src/call_graph.rs
··· 14 14 type_::Error, 15 15 }; 16 16 use itertools::Itertools; 17 - use petgraph::stable_graph::NodeIndex; 18 - use petgraph::{Directed, stable_graph::StableGraph}; 17 + use petgraph::{Directed, stable_graph::NodeIndex, stable_graph::StableGraph}; 19 18 20 19 #[derive(Debug, Default)] 21 20 struct CallGraphBuilder<'a> { ··· 462 461 module: Some(_), .. 463 462 } => (), 464 463 465 - Constant::List { elements, .. } | Constant::Tuple { elements, .. } => { 464 + Constant::List { elements, tail, .. } => { 465 + for element in elements { 466 + self.constant(element); 467 + } 468 + if let Some(tail) = tail { 469 + self.constant(tail); 470 + } 471 + } 472 + 473 + Constant::Tuple { elements, .. } => { 466 474 for element in elements { 467 475 self.constant(element); 468 476 } ··· 536 544 537 545 // Determine the order in which the functions should be compiled by looking 538 546 // at which other functions they depend on. 539 - let indices = crate::graph::into_dependency_order(graph); 547 + let indices = petgraph::algo::tarjan_scc(&graph); 540 548 541 549 // We got node indices back, so we need to map them back to the functions 542 550 // they represent.
+29 -13
compiler-core/src/call_graph/into_dependency_order_tests.rs
··· 191 191 ]; 192 192 assert_eq!( 193 193 parse_and_order(functions.as_slice(), [].as_slice()).unwrap(), 194 - vec![vec!["b"], vec!["c"], vec!["a"]] 194 + vec![vec!["c"], vec!["b"], vec!["a"]] 195 195 ); 196 196 } 197 197 ··· 204 204 ]; 205 205 assert_eq!( 206 206 parse_and_order(functions.as_slice(), [].as_slice()).unwrap(), 207 - vec![vec!["b"], vec!["c"], vec!["a"]] 207 + vec![vec!["c"], vec!["b"], vec!["a"]] 208 208 ); 209 209 } 210 210 ··· 217 217 ]; 218 218 assert_eq!( 219 219 parse_and_order(functions.as_slice(), [].as_slice()).unwrap(), 220 - vec![vec!["b"], vec!["c"], vec!["a"]] 220 + vec![vec!["c"], vec!["b"], vec!["a"]] 221 221 ); 222 222 } 223 223 ··· 230 230 ]; 231 231 assert_eq!( 232 232 parse_and_order(functions.as_slice(), [].as_slice()).unwrap(), 233 - vec![vec!["b"], vec!["c"], vec!["a"]] 233 + vec![vec!["c"], vec!["b"], vec!["a"]] 234 234 ); 235 235 } 236 236 ··· 269 269 ]; 270 270 assert_eq!( 271 271 parse_and_order(functions.as_slice(), [].as_slice()).unwrap(), 272 - vec![vec!["b"], vec!["c"], vec!["a"]] 272 + vec![vec!["c"], vec!["b"], vec!["a"]] 273 273 ); 274 274 } 275 275 ··· 295 295 ]; 296 296 assert_eq!( 297 297 parse_and_order(functions.as_slice(), [].as_slice()).unwrap(), 298 - vec![vec!["b"], vec!["c"], vec!["a"]] 298 + vec![vec!["c"], vec!["b"], vec!["a"]] 299 299 ); 300 300 } 301 301 ··· 308 308 ]; 309 309 assert_eq!( 310 310 parse_and_order(functions.as_slice(), [].as_slice()).unwrap(), 311 - vec![vec!["b"], vec!["c"], vec!["a"]] 311 + vec![vec!["c"], vec!["a"], vec!["b"]] 312 312 ); 313 313 } 314 314 ··· 321 321 ]; 322 322 assert_eq!( 323 323 parse_and_order(functions.as_slice(), [].as_slice()).unwrap(), 324 - vec![vec!["b"], vec!["c"], vec!["a"]] 324 + vec![vec!["c"], vec!["a"], vec!["b"]] 325 325 ); 326 326 } 327 327 ··· 347 347 ]; 348 348 assert_eq!( 349 349 parse_and_order(functions.as_slice(), [].as_slice()).unwrap(), 350 - vec![vec!["b"], vec!["c"], vec!["a"]] 350 + vec![vec!["c"], vec!["a"], vec!["b"]] 351 351 ); 352 352 } 353 353 ··· 491 491 ]; 492 492 assert_eq!( 493 493 parse_and_order(functions.as_slice(), [].as_slice()).unwrap(), 494 - vec![vec!["b"], vec!["c"], vec!["a"]] 494 + vec![vec!["c"], vec!["a"], vec!["b"]] 495 495 ); 496 496 } 497 497 ··· 504 504 ]; 505 505 assert_eq!( 506 506 parse_and_order(functions.as_slice(), [].as_slice()).unwrap(), 507 - vec![vec!["b"], vec!["c"], vec!["a"]] 507 + vec![vec!["c"], vec!["a"], vec!["b"]] 508 508 ); 509 509 } 510 510 ··· 543 543 ]; 544 544 assert_eq!( 545 545 parse_and_order(functions.as_slice(), [].as_slice()).unwrap(), 546 - vec![vec!["b"], vec!["c"], vec!["a"]] 546 + vec![vec!["c"], vec!["b"], vec!["a"]] 547 547 ); 548 548 } 549 549 ··· 653 653 ]; 654 654 assert_eq!( 655 655 parse_and_order(functions.as_slice(), [].as_slice()).unwrap(), 656 - vec![vec!["a2", "a3", "a1"]] 656 + vec![vec!["a3", "a2", "a1"]] 657 657 ); 658 658 } 659 659 ··· 730 730 vec![vec!["b", "a"]] 731 731 ); 732 732 } 733 + 734 + #[test] 735 + fn multiple_two_cycles() { 736 + let functions = [ 737 + ("a", [].as_slice(), r#"{ b }"#), 738 + ("b", [].as_slice(), r#"{ c1 }"#), 739 + ("c1", [].as_slice(), r#"{ c2 }"#), 740 + ("c2", [].as_slice(), r#"{ c1 d1 }"#), 741 + ("d1", [].as_slice(), r#"{ d2 }"#), 742 + ("d2", [].as_slice(), r#"{ d1 }"#), 743 + ]; 744 + assert_eq!( 745 + parse_and_order(functions.as_slice(), [].as_slice()).unwrap(), 746 + vec![vec!["d2", "d1"], vec!["c2", "c1"], vec!["b"], vec!["a"]] 747 + ); 748 + }
+5 -5
compiler-core/src/erlang/tests/snapshots/gleam_core__erlang__tests__echo__echo_with_a_function_call_and_a_message.snap
··· 18 18 -define(FILEPATH, "project/test/my/mod.gleam"). 19 19 -export([main/0]). 20 20 21 - -file("project/test/my/mod.gleam", 6). 22 - -spec wibble(integer(), integer()) -> integer(). 23 - wibble(N, M) -> 24 - N + M. 25 - 26 21 -file("project/test/my/mod.gleam", 7). 27 22 -spec message() -> binary(). 28 23 message() -> 29 24 <<"Hello!"/utf8>>. 25 + 26 + -file("project/test/my/mod.gleam", 6). 27 + -spec wibble(integer(), integer()) -> integer(). 28 + wibble(N, M) -> 29 + N + M. 30 30 31 31 -file("project/test/my/mod.gleam", 2). 32 32 -spec main() -> integer().
+5 -5
compiler-core/src/erlang/tests/snapshots/gleam_core__erlang__tests__variables__module_const_vars.snap
··· 21 21 -define(FILEPATH, "project/test/my/mod.gleam"). 22 22 -export([use_int_alias/0, use_int_identity_alias/0, use_compound/0]). 23 23 24 - -file("project/test/my/mod.gleam", 5). 25 - -spec int_identity(integer()) -> integer(). 26 - int_identity(I) -> 27 - I. 28 - 29 24 -file("project/test/my/mod.gleam", 3). 30 25 -spec use_int_alias() -> integer(). 31 26 use_int_alias() -> 32 27 42. 28 + 29 + -file("project/test/my/mod.gleam", 5). 30 + -spec int_identity(integer()) -> integer(). 31 + int_identity(I) -> 32 + I. 33 33 34 34 -file("project/test/my/mod.gleam", 7). 35 35 -spec use_int_identity_alias() -> integer().
-272
compiler-core/src/graph.rs
··· 1 - //! General functions for working with graphs. 2 - 3 - use petgraph::{Direction, prelude::NodeIndex, stable_graph::StableGraph}; 4 - 5 - /// Sort a graph into a sequence from the leaves to the roots. 6 - /// 7 - /// Nodes are returned in their smallest possible groups, which is either a leaf 8 - /// or a cycle. 9 - /// 10 - /// This function is implemented using `pop_leaf_or_cycle`. 11 - /// 12 - pub fn into_dependency_order<N, E>(mut graph: StableGraph<N, E>) -> Vec<Vec<NodeIndex>> { 13 - let mut items = vec![]; 14 - 15 - // Remove all self-edges from the graph. 16 - graph.retain_edges(|graph, edge| match graph.edge_endpoints(edge) { 17 - Some((a, b)) => a != b, 18 - None => false, 19 - }); 20 - 21 - loop { 22 - let current = pop_leaf_or_cycle(&mut graph); 23 - if current.is_empty() { 24 - return items; 25 - } else { 26 - items.push(current); 27 - } 28 - } 29 - } 30 - 31 - /// The same as `leaf_or_cycle` but removes the nodes from the graph. 32 - /// See the docs there for more details. 33 - /// 34 - /// # Panics 35 - /// 36 - /// Panics if the graph contains a self-edge. 37 - /// 38 - fn pop_leaf_or_cycle<N, E>(graph: &mut StableGraph<N, E>) -> Vec<NodeIndex> { 39 - let nodes = leaf_or_cycle(graph); 40 - for node in &nodes { 41 - _ = graph.remove_node(*node); 42 - } 43 - nodes 44 - } 45 - 46 - /// Return a leaf from the graph. If there are no leaves then the largest cycle 47 - /// is returned instead. 48 - /// 49 - /// If there are no leaves or cycles then an empty vector is returned. 50 - /// 51 - /// The nodes returned are not removed from the graph. 52 - /// 53 - /// # Panics 54 - /// 55 - /// Panics if the graph contains a self-edge. 56 - /// 57 - fn leaf_or_cycle<N, E>(graph: &StableGraph<N, E>) -> Vec<NodeIndex> { 58 - if graph.node_count() == 0 { 59 - return vec![]; 60 - } 61 - 62 - // Find a leaf, returning one if found. 63 - for node in graph.node_indices() { 64 - let mut outgoing = graph.neighbors_directed(node, Direction::Outgoing); 65 - let referenced = outgoing.next(); 66 - 67 - if referenced == Some(node) { 68 - panic!("Self edge found in graph"); 69 - } 70 - 71 - // This is a leaf. 72 - if referenced.is_none() { 73 - return vec![node]; 74 - } 75 - } 76 - 77 - // No leaves were found, so find a cycle. 78 - // We use a toposort to find the start of the cycle. 79 - let start = petgraph::algo::toposort(&graph, None) 80 - .expect_err("Non-empty graph has no leaves or cycles") 81 - .node_id(); 82 - 83 - // Then traverse the graph to find nodes in the cycle. 84 - // This traverses all possible paths to find a cycle, this can likely be 85 - // optimised. There's not a large number of functions in a module however so 86 - // this is tolerable in this specific instance. 87 - #[derive(Debug)] 88 - enum Step { 89 - Backtrack, 90 - Next(NodeIndex), 91 - } 92 - let mut path = vec![]; 93 - let mut stack = vec![Step::Next(start)]; 94 - let mut cycles = vec![]; 95 - 96 - while let Some(step) = stack.pop() { 97 - let node = match step { 98 - // We have processed all the nodes in the branch so backtrack, 99 - // popping the node off the path. 100 - Step::Backtrack => { 101 - _ = path.pop(); 102 - continue; 103 - } 104 - Step::Next(node) => node, 105 - }; 106 - 107 - if path.contains(&node) { 108 - continue; 109 - } 110 - 111 - // Add this node to the path and record the point at which we need to 112 - // backtrack in order to go back up the tree. 113 - stack.push(Step::Backtrack); 114 - path.push(node); 115 - 116 - // Check each child & add them to the stack if they are not the target. 117 - for node in graph.neighbors_directed(node, Direction::Outgoing) { 118 - if node == start { 119 - cycles.push(path.clone()); 120 - } else { 121 - stack.push(Step::Next(node)); 122 - } 123 - } 124 - } 125 - 126 - cycles 127 - .into_iter() 128 - .max_by_key(|x| x.len()) 129 - .expect("Could not find cycle for toposort returned start node") 130 - } 131 - 132 - #[cfg(test)] 133 - mod tests { 134 - use super::*; 135 - 136 - #[test] 137 - fn leaf_or_cycle_empty() { 138 - let mut graph: StableGraph<(), ()> = StableGraph::new(); 139 - assert!(pop_leaf_or_cycle(&mut graph).is_empty()); 140 - } 141 - 142 - #[test] 143 - fn leaf_or_cycle_1() { 144 - let mut graph: StableGraph<(), ()> = StableGraph::new(); 145 - let a = graph.add_node(()); 146 - assert_eq!(into_dependency_order(graph), vec![vec![a]]); 147 - } 148 - 149 - #[test] 150 - fn leaf_or_cycle_2() { 151 - let mut graph: StableGraph<(), ()> = StableGraph::new(); 152 - let a = graph.add_node(()); 153 - let b = graph.add_node(()); 154 - 155 - assert_eq!(into_dependency_order(graph), vec![vec![a], vec![b]]); 156 - } 157 - 158 - #[test] 159 - fn leaf_or_cycle_3() { 160 - let mut graph: StableGraph<(), ()> = StableGraph::new(); 161 - // Here a depends on b so b must come before a 162 - let a = graph.add_node(()); 163 - let b = graph.add_node(()); 164 - let c = graph.add_node(()); 165 - _ = graph.add_edge(a, b, ()); 166 - 167 - assert_eq!( 168 - into_dependency_order(graph), 169 - vec![vec![b], vec![a], vec![c]] 170 - ); 171 - } 172 - 173 - #[test] 174 - fn leaf_or_cycle_4() { 175 - let mut graph: StableGraph<(), ()> = StableGraph::new(); 176 - let a = graph.add_node(()); 177 - let b = graph.add_node(()); 178 - let c = graph.add_node(()); 179 - _ = graph.add_edge(a, b, ()); 180 - _ = graph.add_edge(a, c, ()); 181 - 182 - assert_eq!( 183 - into_dependency_order(graph), 184 - vec![vec![b], vec![c], vec![a]] 185 - ); 186 - } 187 - 188 - #[test] 189 - fn leaf_or_cycle_5() { 190 - let mut graph: StableGraph<(), ()> = StableGraph::new(); 191 - let a = graph.add_node(()); 192 - let b = graph.add_node(()); 193 - let c = graph.add_node(()); 194 - _ = graph.add_edge(a, b, ()); 195 - _ = graph.add_edge(b, a, ()); 196 - 197 - assert_eq!(into_dependency_order(graph), vec![vec![c], vec![b, a]]); 198 - } 199 - 200 - #[test] 201 - fn leaf_or_cycle_6() { 202 - let mut graph: StableGraph<(), ()> = StableGraph::new(); 203 - let a = graph.add_node(()); 204 - let b = graph.add_node(()); 205 - let c = graph.add_node(()); 206 - let d = graph.add_node(()); 207 - _ = graph.add_edge(a, b, ()); 208 - _ = graph.add_edge(b, c, ()); 209 - _ = graph.add_edge(c, a, ()); 210 - _ = graph.add_edge(d, a, ()); 211 - 212 - assert_eq!(into_dependency_order(graph), vec![vec![c, a, b], vec![d]]); 213 - } 214 - 215 - #[test] 216 - fn leaf_or_cycle_7() { 217 - let mut graph: StableGraph<(), ()> = StableGraph::new(); 218 - let a = graph.add_node(()); 219 - let b = graph.add_node(()); 220 - _ = graph.add_edge(a, a, ()); 221 - _ = graph.add_edge(a, b, ()); 222 - _ = graph.add_edge(b, b, ()); 223 - 224 - // Here there are no true leafs, only cycles. However, b is in a loop 225 - // with itself so counts as a leaf as far as we are concerned. 226 - 227 - assert_eq!(into_dependency_order(graph), vec![vec![b], vec![a]]); 228 - } 229 - 230 - #[test] 231 - fn leaf_or_cycle_8() { 232 - let mut graph: StableGraph<(), ()> = StableGraph::new(); 233 - let a = graph.add_node(()); 234 - let b = graph.add_node(()); 235 - _ = graph.add_edge(a, a, ()); 236 - _ = graph.add_edge(a, b, ()); 237 - _ = graph.add_edge(b, b, ()); 238 - _ = graph.add_edge(b, b, ()); 239 - _ = graph.add_edge(b, b, ()); 240 - 241 - // Here there are no true leafs, only cycles. However, b is in a loop 242 - // with itself so counts as a leaf as far as we are concerned. 243 - // This is different from the previous test as there are multiple self 244 - // references for node b. 245 - 246 - assert_eq!(into_dependency_order(graph), vec![vec![b], vec![a]]); 247 - } 248 - 249 - #[test] 250 - fn leaf_or_cycle_9() { 251 - let mut graph: StableGraph<(), ()> = StableGraph::new(); 252 - let a = graph.add_node(()); 253 - let b = graph.add_node(()); 254 - let c = graph.add_node(()); 255 - 256 - _ = graph.add_edge(a, a, ()); 257 - _ = graph.add_edge(a, b, ()); 258 - 259 - _ = graph.add_edge(b, b, ()); 260 - _ = graph.add_edge(b, c, ()); 261 - 262 - _ = graph.add_edge(c, b, ()); 263 - _ = graph.add_edge(c, c, ()); 264 - 265 - // Here there are no true leafs, only cycles. However, b is in a loop 266 - // with itself so counts as a leaf as far as we are concerned. 267 - // This is different from the previous test as there are multiple self 268 - // references for node b. 269 - 270 - assert_eq!(into_dependency_order(graph), vec![vec![c, b], vec![a]]); 271 - } 272 - }
+4 -4
compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__custom_types__custom_type_with_named_fields.snap
··· 76 76 return new Cat("Nubi", 3); 77 77 } 78 78 79 - export function access(cat) { 80 - return cat.cuteness; 81 - } 82 - 83 79 export function new_cat() { 84 80 return new Cat("Beau", 11); 85 81 } ··· 93 89 let _record$1 = box.occupant; 94 90 return new Cat(_record$1.name, box.occupant.cuteness + 1); 95 91 } 92 + 93 + export function access(cat) { 94 + return cat.cuteness; 95 + }
+4 -4
compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__echo__echo_with_a_function_call_and_a_message.snap
··· 25 25 UtfCodepoint as $UtfCodepoint, 26 26 } from "../gleam.mjs"; 27 27 28 - function wibble(n, m) { 29 - return n + m; 30 - } 31 - 32 28 function message() { 33 29 return "Hello!"; 30 + } 31 + 32 + function wibble(n, m) { 33 + return n + m; 34 34 } 35 35 36 36 export function main() {
-1
compiler-core/src/lib.rs
··· 95 95 mod dep_tree; 96 96 pub(crate) mod derivation_tree; 97 97 pub mod exhaustiveness; 98 - pub(crate) mod graph; 99 98 pub(crate) mod inline; 100 99 pub mod reference; 101 100
+85
compiler-core/src/type_/snapshots/gleam_core__type___tests__correct_type_check_for_multiple_mutually_recursive_functions2.snap
··· 1 + --- 2 + source: compiler-core/src/type_/tests.rs 3 + expression: "\npub fn main() {\n let _ = step(Leaf, Leaf)\n Nil\n}\n\ntype Tree {\n Leaf\n Node(List(Tree))\n}\n\nfn step(left: Tree, right: Tree) -> Int {\n sum(diff(left, right, []))\n}\n\nfn sum(xs: List(Int)) -> Int {\n case xs {\n [] -> 0\n [first, ..rest] -> first + sum(rest)\n }\n}\n\nfn diff(left, right, effect) {\n case left, right {\n Node(olds), Node(news) -> diff_batch(olds, news, effect)\n _, _ -> start(right, effect)\n }\n}\n\nfn diff_batch(left, right, effect) {\n case left, right {\n [old, ..left], [new, ..right] ->\n diff_batch(left, right, diff(old, new, effect))\n _, _ -> effect\n }\n}\n\nfn start(node, effect) {\n case node {\n Leaf -> \"wat\"\n Node(children) -> start_batch(children, effect)\n }\n}\n\nfn start_batch(children, effect) {\n case children {\n [] -> effect\n [child, ..rest] -> start_batch(rest, start(child, effect))\n }\n}\n" 4 + --- 5 + ----- SOURCE CODE 6 + 7 + pub fn main() { 8 + let _ = step(Leaf, Leaf) 9 + Nil 10 + } 11 + 12 + type Tree { 13 + Leaf 14 + Node(List(Tree)) 15 + } 16 + 17 + fn step(left: Tree, right: Tree) -> Int { 18 + sum(diff(left, right, [])) 19 + } 20 + 21 + fn sum(xs: List(Int)) -> Int { 22 + case xs { 23 + [] -> 0 24 + [first, ..rest] -> first + sum(rest) 25 + } 26 + } 27 + 28 + fn diff(left, right, effect) { 29 + case left, right { 30 + Node(olds), Node(news) -> diff_batch(olds, news, effect) 31 + _, _ -> start(right, effect) 32 + } 33 + } 34 + 35 + fn diff_batch(left, right, effect) { 36 + case left, right { 37 + [old, ..left], [new, ..right] -> 38 + diff_batch(left, right, diff(old, new, effect)) 39 + _, _ -> effect 40 + } 41 + } 42 + 43 + fn start(node, effect) { 44 + case node { 45 + Leaf -> "wat" 46 + Node(children) -> start_batch(children, effect) 47 + } 48 + } 49 + 50 + fn start_batch(children, effect) { 51 + case children { 52 + [] -> effect 53 + [child, ..rest] -> start_batch(rest, start(child, effect)) 54 + } 55 + } 56 + 57 + 58 + ----- ERROR 59 + error: Type mismatch 60 + ┌─ /src/one/two.gleam:13:7 61 + 62 + 13 │ sum(diff(left, right, [])) 63 + │ ^^^^^^^^^^^^^^^^^^^^^ 64 + 65 + Expected type: 66 + 67 + List(Int) 68 + 69 + Found type: 70 + 71 + String 72 + 73 + error: Type mismatch 74 + ┌─ /src/one/two.gleam:13:25 75 + 76 + 13 │ sum(diff(left, right, [])) 77 + │ ^^ 78 + 79 + Expected type: 80 + 81 + String 82 + 83 + Found type: 84 + 85 + List(a)
+58
compiler-core/src/type_/tests.rs
··· 3522 3522 ); 3523 3523 } 3524 3524 3525 + // https://github.com/gleam-lang/gleam/issues/5618 3526 + #[test] 3527 + fn correct_type_check_for_multiple_mutually_recursive_functions2() { 3528 + assert_module_error!( 3529 + r#" 3530 + pub fn main() { 3531 + let _ = step(Leaf, Leaf) 3532 + Nil 3533 + } 3534 + 3535 + type Tree { 3536 + Leaf 3537 + Node(List(Tree)) 3538 + } 3539 + 3540 + fn step(left: Tree, right: Tree) -> Int { 3541 + sum(diff(left, right, [])) 3542 + } 3543 + 3544 + fn sum(xs: List(Int)) -> Int { 3545 + case xs { 3546 + [] -> 0 3547 + [first, ..rest] -> first + sum(rest) 3548 + } 3549 + } 3550 + 3551 + fn diff(left, right, effect) { 3552 + case left, right { 3553 + Node(olds), Node(news) -> diff_batch(olds, news, effect) 3554 + _, _ -> start(right, effect) 3555 + } 3556 + } 3557 + 3558 + fn diff_batch(left, right, effect) { 3559 + case left, right { 3560 + [old, ..left], [new, ..right] -> 3561 + diff_batch(left, right, diff(old, new, effect)) 3562 + _, _ -> effect 3563 + } 3564 + } 3565 + 3566 + fn start(node, effect) { 3567 + case node { 3568 + Leaf -> "wat" 3569 + Node(children) -> start_batch(children, effect) 3570 + } 3571 + } 3572 + 3573 + fn start_batch(children, effect) { 3574 + case children { 3575 + [] -> effect 3576 + [child, ..rest] -> start_batch(rest, start(child, effect)) 3577 + } 3578 + } 3579 + "# 3580 + ); 3581 + } 3582 + 3525 3583 #[test] 3526 3584 fn prepend_constant_list() { 3527 3585 assert_module_infer!(
+31 -31
test-package-compiler/src/snapshots/test_package_compiler__generated_tests__imported_constants.snap
··· 33 33 -module(two). 34 34 -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). 35 35 -define(FILEPATH, "src/two.gleam"). 36 - -export([accessors/1, destructure_qualified/1, destructure_unqualified/1, destructure_aliased/1, qualified_fn_a/0, qualified_fn_b/0, unqualified_fn_a/0, unqualified_fn_b/0, aliased_fn_a/0, aliased_fn_b/0]). 36 + -export([qualified_fn_a/0, qualified_fn_b/0, unqualified_fn_a/0, unqualified_fn_b/0, aliased_fn_a/0, aliased_fn_b/0, accessors/1, destructure_qualified/1, destructure_unqualified/1, destructure_aliased/1]). 37 37 38 38 -if(?OTP_RELEASE >= 27). 39 39 -define(MODULEDOC(Str), -moduledoc(Str)). ··· 43 43 -define(DOC(Str), -compile([])). 44 44 -endif. 45 45 46 - -file("src/two.gleam", 45). 47 - ?DOC( 48 - " For these statements we use the accessors for the record from the other\n" 49 - " module\n" 50 - ). 51 - -spec accessors(one:user()) -> {binary(), integer()}. 52 - accessors(User) -> 53 - Name = erlang:element(2, User), 54 - Score = erlang:element(3, User), 55 - {Name, Score}. 56 - 57 - -file("src/two.gleam", 52). 58 - ?DOC(" For these statements we use destructure the record\n"). 59 - -spec destructure_qualified(one:user()) -> {binary(), integer()}. 60 - destructure_qualified(User) -> 61 - {user, Name, Score} = User, 62 - {Name, Score}. 63 - 64 - -file("src/two.gleam", 57). 65 - -spec destructure_unqualified(one:user()) -> {binary(), integer()}. 66 - destructure_unqualified(User) -> 67 - {user, Name, Score} = User, 68 - {Name, Score}. 69 - 70 - -file("src/two.gleam", 62). 71 - -spec destructure_aliased(one:user()) -> {binary(), integer()}. 72 - destructure_aliased(User) -> 73 - {user, Name, Score} = User, 74 - {Name, Score}. 75 - 76 46 -file("src/two.gleam", 6). 77 47 -spec qualified_fn_a() -> one:a(). 78 48 qualified_fn_a() -> ··· 102 72 -spec aliased_fn_b() -> one:b(). 103 73 aliased_fn_b() -> 104 74 {b, a, a}. 75 + 76 + -file("src/two.gleam", 45). 77 + ?DOC( 78 + " For these statements we use the accessors for the record from the other\n" 79 + " module\n" 80 + ). 81 + -spec accessors(one:user()) -> {binary(), integer()}. 82 + accessors(User) -> 83 + Name = erlang:element(2, User), 84 + Score = erlang:element(3, User), 85 + {Name, Score}. 86 + 87 + -file("src/two.gleam", 52). 88 + ?DOC(" For these statements we use destructure the record\n"). 89 + -spec destructure_qualified(one:user()) -> {binary(), integer()}. 90 + destructure_qualified(User) -> 91 + {user, Name, Score} = User, 92 + {Name, Score}. 93 + 94 + -file("src/two.gleam", 57). 95 + -spec destructure_unqualified(one:user()) -> {binary(), integer()}. 96 + destructure_unqualified(User) -> 97 + {user, Name, Score} = User, 98 + {Name, Score}. 99 + 100 + -file("src/two.gleam", 62). 101 + -spec destructure_aliased(one:user()) -> {binary(), integer()}. 102 + destructure_aliased(User) -> 103 + {user, Name, Score} = User, 104 + {Name, Score}. 105 105 106 106 107 107 //// /out/lib/the_package/ebin/importy.app
+21 -21
test-package-compiler/src/snapshots/test_package_compiler__generated_tests__imported_external_fns.snap
··· 58 58 -module(two). 59 59 -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). 60 60 -define(FILEPATH, "src/two.gleam"). 61 - -export([fn_reference_qualified/0, fn_reference_qualified_aliased/0, fn_reference_unqualified/0, fn_reference_unqualified_aliased/0, fn_call_qualified/0, fn_call_qualified_aliased/0, fn_call_unqualified/0, fn_call_unqualified_aliased/0, argument_reference_qualified/0, argument_reference_qualified_aliased/0, argument_reference_unqualified/0, argument_reference_unqualified_aliased/0, the_consts/0]). 61 + -export([the_consts/0, fn_reference_qualified/0, fn_reference_qualified_aliased/0, fn_reference_unqualified/0, fn_reference_unqualified_aliased/0, fn_call_qualified/0, fn_call_qualified_aliased/0, fn_call_unqualified/0, fn_call_unqualified_aliased/0, argument_reference_qualified/0, argument_reference_qualified_aliased/0, argument_reference_unqualified/0, argument_reference_unqualified_aliased/0]). 62 + 63 + -file("src/two.gleam", 24). 64 + -spec the_consts() -> nil. 65 + the_consts() -> 66 + _ = fun thing:new/0, 67 + _ = fun thing:new/0, 68 + _ = fun thing:new/0, 69 + _ = fun thing:new/0, 70 + _ = fun 'the.thing':'make.new'/0, 71 + _ = fun 'the.thing':'make.new'/0, 72 + _ = fun 'the.thing':'make.new'/0, 73 + _ = fun 'the.thing':'make.new'/0, 74 + thing:new(), 75 + thing:new(), 76 + thing:new(), 77 + thing:new(), 78 + 'the.thing':'make.new'(), 79 + 'the.thing':'make.new'(), 80 + 'the.thing':'make.new'(), 81 + 'the.thing':'make.new'(). 62 82 63 83 -file("src/two.gleam", 45). 64 84 -spec fn_reference_qualified() -> fun(() -> nil). ··· 124 144 -spec argument_reference_unqualified_aliased() -> nil. 125 145 argument_reference_unqualified_aliased() -> 126 146 x(fun 'the.thing':'make.new'/0). 127 - 128 - -file("src/two.gleam", 24). 129 - -spec the_consts() -> nil. 130 - the_consts() -> 131 - _ = fun thing:new/0, 132 - _ = fun thing:new/0, 133 - _ = fun thing:new/0, 134 - _ = fun thing:new/0, 135 - _ = fun 'the.thing':'make.new'/0, 136 - _ = fun 'the.thing':'make.new'/0, 137 - _ = fun 'the.thing':'make.new'/0, 138 - _ = fun 'the.thing':'make.new'/0, 139 - thing:new(), 140 - thing:new(), 141 - thing:new(), 142 - thing:new(), 143 - 'the.thing':'make.new'(), 144 - 'the.thing':'make.new'(), 145 - 'the.thing':'make.new'(), 146 - 'the.thing':'make.new'(). 147 147 148 148 149 149 //// /out/lib/the_package/ebin/importy.app
+41 -41
test-package-compiler/src/snapshots/test_package_compiler__generated_tests__imported_record_constructors.snap
··· 54 54 -module(two). 55 55 -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). 56 56 -define(FILEPATH, "src/two.gleam"). 57 - -export([accessors/1, destructure_qualified/1, destructure_qualified_aliased/1, destructure_unqualified/1, destructure_aliased/1, update_qualified/1, update_qualified_aliased/1, update_unqualified/1, update_aliased/1, qualified_fn_a/0, qualified_fn_b/0, qualified_aliased_fn_a/0, qualified_aliased_fn_b/0, unqualified_fn_a/0, unqualified_fn_b/0, aliased_fn_a/0, aliased_fn_b/0]). 57 + -export([qualified_fn_a/0, qualified_fn_b/0, qualified_aliased_fn_a/0, qualified_aliased_fn_b/0, unqualified_fn_a/0, unqualified_fn_b/0, aliased_fn_a/0, aliased_fn_b/0, accessors/1, destructure_qualified/1, destructure_qualified_aliased/1, destructure_unqualified/1, destructure_aliased/1, update_qualified/1, update_qualified_aliased/1, update_unqualified/1, update_aliased/1]). 58 58 59 59 -if(?OTP_RELEASE >= 27). 60 60 -define(MODULEDOC(Str), -moduledoc(Str)). ··· 63 63 -define(MODULEDOC(Str), -compile([])). 64 64 -define(DOC(Str), -compile([])). 65 65 -endif. 66 + 67 + -file("src/two.gleam", 7). 68 + -spec qualified_fn_a() -> one@one:a(). 69 + qualified_fn_a() -> 70 + a. 71 + 72 + -file("src/two.gleam", 13). 73 + -spec qualified_fn_b() -> one@one:b(). 74 + qualified_fn_b() -> 75 + {b, a, a}. 76 + 77 + -file("src/two.gleam", 19). 78 + -spec qualified_aliased_fn_a() -> one@two:a(). 79 + qualified_aliased_fn_a() -> 80 + a. 81 + 82 + -file("src/two.gleam", 25). 83 + -spec qualified_aliased_fn_b() -> one@two:b(). 84 + qualified_aliased_fn_b() -> 85 + {b, a, a}. 86 + 87 + -file("src/two.gleam", 32). 88 + -spec unqualified_fn_a() -> one@one:a(). 89 + unqualified_fn_a() -> 90 + a. 91 + 92 + -file("src/two.gleam", 38). 93 + -spec unqualified_fn_b() -> one@one:b(). 94 + unqualified_fn_b() -> 95 + {b, a, a}. 96 + 97 + -file("src/two.gleam", 46). 98 + -spec aliased_fn_a() -> one@one:a(). 99 + aliased_fn_a() -> 100 + a. 101 + 102 + -file("src/two.gleam", 52). 103 + -spec aliased_fn_b() -> one@one:b(). 104 + aliased_fn_b() -> 105 + {b, a, a}. 66 106 67 107 -file("src/two.gleam", 58). 68 108 ?DOC( ··· 120 160 -spec update_aliased(one@one:user()) -> one@one:user(). 121 161 update_aliased(User) -> 122 162 {user, <<"wibble"/utf8>>, erlang:element(3, User)}. 123 - 124 - -file("src/two.gleam", 7). 125 - -spec qualified_fn_a() -> one@one:a(). 126 - qualified_fn_a() -> 127 - a. 128 - 129 - -file("src/two.gleam", 13). 130 - -spec qualified_fn_b() -> one@one:b(). 131 - qualified_fn_b() -> 132 - {b, a, a}. 133 - 134 - -file("src/two.gleam", 19). 135 - -spec qualified_aliased_fn_a() -> one@two:a(). 136 - qualified_aliased_fn_a() -> 137 - a. 138 - 139 - -file("src/two.gleam", 25). 140 - -spec qualified_aliased_fn_b() -> one@two:b(). 141 - qualified_aliased_fn_b() -> 142 - {b, a, a}. 143 - 144 - -file("src/two.gleam", 32). 145 - -spec unqualified_fn_a() -> one@one:a(). 146 - unqualified_fn_a() -> 147 - a. 148 - 149 - -file("src/two.gleam", 38). 150 - -spec unqualified_fn_b() -> one@one:b(). 151 - unqualified_fn_b() -> 152 - {b, a, a}. 153 - 154 - -file("src/two.gleam", 46). 155 - -spec aliased_fn_a() -> one@one:a(). 156 - aliased_fn_a() -> 157 - a. 158 - 159 - -file("src/two.gleam", 52). 160 - -spec aliased_fn_b() -> one@one:b(). 161 - aliased_fn_b() -> 162 - {b, a, a}. 163 163 164 164 165 165 //// /out/lib/the_package/ebin/importy.app