Semantic Diff

#9045 Build the untagged dispatch as a plan before emitting it

2 commits

1659aee278c597fd00f46d1bde661d8bdfb841c7..pr-9045

fbfe93006 · cknitt + Claude · Oct 25, 2026, 20:21 UTC

Build the untagged dispatch as a plan before emitting it

Why

The dispatch of a switch on an untagged variant was decided in two places, the literal side in compile_switch and the payload side in compile_untagged_cases, each with part of the facts, while printing JavaScript.

Untagged_dispatch.plan builds the dispatch as a tree of tests and statements from the variant's facts and the match's arms, making today's decisions in one place; Lam_compile.compile_untagged_plan prints it. ounit tests pin plans as data. The output is unchanged except that a body of an if is no longer compiled in a switch context.

Generated code changed (a loop left from an arm of an if):

@unboxed type t = TextItem(string) | CountItem(float)for i in 0 to Array.length(values) - 1 {  switch values->Array.getUnsafe(i) {  | TextItem(text) => seen->Array.push(text)  | CountItem(_) => break  }}

Before (JS):

loop_0: for (let i = 0, i_finish = values.length; i < i_finish; ++i) {  ...  } else {    break loop_0;  }}

After (JS):

for (let i = 0, i_finish = values.length; i < i_finish; ++i) {  ...  } else {    break;  }}
d764e3d83 · cknitt + Claude · Oct 26, 2026, 06:09 UTC

Compile the bodies of an if in the order they are printed

Why

The if chain of the payload dispatch compiled its last arm first, so loop labels in its arms were numbered bottom to top. A dispatch on one typeof kind fell back to a switch for an empty case, with bodies compiled outside a switch context.

An if compiles its then branch before its else branch, so labels are numbered top to bottom, as master numbered them in the class, array and object chain; arms of the literal ifs are now numbered in that order too. A dispatch on one typeof kind is always an if.

Generated code changed (loop labels in literal if arms):

a loop needing a label in each of two literal arms printed as `if`s

Before (JS): the label of the second arm is loop_0, of the first loop_1 After (JS): the label of the first arm is loop_0, of the second loop_1