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

Configure Feed

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

fix code gen bug for js case with guards

author
Giacomo Cavalieri
committer
Louis Pilfold
date (Jan 28, 2026, 7:58 PM UTC) commit 758db417 parent 6bfbb5d4 change-id ltxwzvzx
+175 -8
+4
CHANGELOG.md
··· 110 110 check in an `assert`. 111 111 ([Giacomo Cavalieri](https://github.com/giacomocavalieri)) 112 112 113 + - Fixed a bug where the compiler would generate invalid code on the JavaScript 114 + target for some `case` expressions using clause guards. 115 + ([Giacomo Cavalieri](https://github.com/giacomocavalieri)) 116 + 113 117 - The formatter no longer stack overflows trying to format lists with many 114 118 items. 115 119 ([Giacomo Cavalieri](https://github.com/giacomocavalieri))
+44 -4
compiler-core/src/javascript/decision.rs
··· 31 31 subjects: &'a [TypedExpr], 32 32 expression_generator: &mut Generator<'_, 'a>, 33 33 ) -> Document<'a> { 34 + let scope_position = expression_generator.scope_position.clone(); 34 35 let mut variables = Variables::new(expression_generator, VariableAssignment::Declare); 35 36 let assignments = variables.assign_case_subjects(compiled_case, subjects); 36 - let decision = CasePrinter { 37 + let mut printer = CasePrinter { 37 38 variables, 38 39 assignments: &assignments, 39 40 kind: DecisionKind::Case { clauses }, 40 - } 41 - .decision(&compiled_case.tree); 41 + }; 42 42 43 - docvec![assignments_to_doc(assignments), decision.into_doc()].force_break() 43 + let decision = match &compiled_case.tree { 44 + // Printing needs extra care if we're dealing with a sort of "degenerate" 45 + // tree that immediately starts with a guard node. 46 + // Code generation for guard nodes require defining variables outside of 47 + // the safe scope of the generated `if` statement. So if we were to just 48 + // generate code like usual we run the risk of leaking variables in the 49 + // outer scope: 50 + // 51 + // ```case 52 + // case 11 { 53 + // n if n == 10 -> todo 54 + // _ -> todo 55 + // } 56 + // 57 + // let n = 12 58 + // ``` 59 + // 60 + // That case would have us generate something like this: 61 + // 62 + // ```js 63 + // let n = 11 64 + // if (n === 10) { todo } else { todo } 65 + // 66 + // // If we don't wrap it in a block that `n = 11` definition that was 67 + // // introduced would end up clashing with the `let n = 12` that comes 68 + // // later! 69 + // ``` 70 + // 71 + // So in this special case we have to wrap everything in a block. 72 + tree @ Decision::Guard { .. } if !scope_position.is_tail() => break_block( 73 + printer 74 + .inside_new_scope(|this| this.decision(tree)) 75 + .into_doc(), 76 + ), 77 + 78 + tree @ Decision::Run { .. } 79 + | tree @ Decision::Guard { .. } 80 + | tree @ Decision::Switch { .. } 81 + | tree @ Decision::Fail => printer.decision(tree).into_doc(), 82 + }; 83 + docvec![assignments_to_doc(assignments), decision].force_break() 44 84 } 45 85 46 86 /// The generated code for a decision tree.
+35
compiler-core/src/javascript/tests/case.rs
··· 887 887 "# 888 888 ) 889 889 } 890 + 891 + #[test] 892 + // https://github.com/gleam-lang/gleam/issues/5283 893 + fn duplicate_name_for_variables_used_in_guards() { 894 + assert_js!( 895 + r#" 896 + pub fn wibble() { 897 + let a = case 1337 { 898 + n if n == 1347 -> Nil 899 + _ -> Nil 900 + } 901 + let b = case 1337 { 902 + n -> Nil 903 + } 904 + }"# 905 + ) 906 + } 907 + 908 + #[test] 909 + // https://github.com/gleam-lang/gleam/issues/5283 910 + fn duplicate_name_for_variables_used_in_guards_shadowing_outer_name() { 911 + assert_js!( 912 + r#" 913 + pub fn wibble() { 914 + let n = 1 915 + let a = case 1337 { 916 + n if n == 1347 -> n 917 + _ -> n 918 + } 919 + let b = case 1337 { 920 + n -> Nil 921 + } 922 + }"# 923 + ) 924 + }
+36
compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__case__duplicate_name_for_variables_used_in_guards.snap
··· 1 + --- 2 + source: compiler-core/src/javascript/tests/case.rs 3 + expression: "\npub fn wibble() {\n let a = case 1337 {\n n if n == 1347 -> Nil\n _ -> Nil\n }\n let b = case 1337 {\n n -> Nil\n }\n}" 4 + --- 5 + ----- SOURCE CODE 6 + 7 + pub fn wibble() { 8 + let a = case 1337 { 9 + n if n == 1347 -> Nil 10 + _ -> Nil 11 + } 12 + let b = case 1337 { 13 + n -> Nil 14 + } 15 + } 16 + 17 + ----- COMPILED JAVASCRIPT 18 + export function wibble() { 19 + let _block; 20 + let $ = 1337; 21 + { 22 + let n = $; 23 + if (n === 1347) { 24 + _block = undefined; 25 + } else { 26 + _block = undefined; 27 + } 28 + } 29 + let a = _block; 30 + let _block$1; 31 + let $1 = 1337; 32 + let n = $1; 33 + _block$1 = undefined; 34 + let b = _block$1; 35 + return b; 36 + }
+38
compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__case__duplicate_name_for_variables_used_in_guards_shadowing_outer_name.snap
··· 1 + --- 2 + source: compiler-core/src/javascript/tests/case.rs 3 + expression: "\npub fn wibble() {\n let n = 1\n let a = case 1337 {\n n if n == 1347 -> n\n _ -> n\n }\n let b = case 1337 {\n n -> Nil\n }\n}" 4 + --- 5 + ----- SOURCE CODE 6 + 7 + pub fn wibble() { 8 + let n = 1 9 + let a = case 1337 { 10 + n if n == 1347 -> n 11 + _ -> n 12 + } 13 + let b = case 1337 { 14 + n -> Nil 15 + } 16 + } 17 + 18 + ----- COMPILED JAVASCRIPT 19 + export function wibble() { 20 + let n = 1; 21 + let _block; 22 + let $ = 1337; 23 + { 24 + let n$1 = $; 25 + if (n$1 === 1347) { 26 + _block = $; 27 + } else { 28 + _block = n; 29 + } 30 + } 31 + let a = _block; 32 + let _block$1; 33 + let $1 = 1337; 34 + let n$1 = $1; 35 + _block$1 = undefined; 36 + let b = _block$1; 37 + return b; 38 + }
+6 -4
compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__case__var_true.snap
··· 28 28 let $ = 0; 29 29 let _block; 30 30 let $1 = undefined; 31 - if (true_) { 32 - _block = 0; 33 - } else { 34 - _block = 1; 31 + { 32 + if (true_) { 33 + _block = 0; 34 + } else { 35 + _block = 1; 36 + } 35 37 } 36 38 let $2 = _block; 37 39 if (!($ === $2)) {
+12
test/language/test/language/clause_guard_test.gleam
··· 548 548 _ -> False 549 549 } 550 550 } 551 + 552 + // https://github.com/gleam-lang/gleam/issues/5283 553 + pub fn case_with_guard_does_not_pollute_outer_scope_test() { 554 + let a = case 1337 { 555 + n if n == 1347 -> 1 556 + _ -> 2 557 + } 558 + let b = case 1337 { 559 + n -> 2 560 + } 561 + assert a == b 562 + }