Evaluate the arguments of an inlined call left to right
Why
Inlining a function whose body is one primitive or call put each argument where the body uses its parameter, so the arguments ran in the body's order, after the ones it ignores (#9175). A read of mutable state such as r.contents then moved past another argument's side effects or past an await, and an argument for the right operand of && or || could be skipped. Larger bodies substituted such reads the same way. Inlining now puts an argument into the body only when that cannot change what anything computes, and otherwise binds the arguments in call order. The bindings can make a while condition compile to statements, which went into a closure called on each iteration, where an await is a syntax error; such a condition now goes into the loop.
modifiedJS inlined calls
let r = ref(0)let bump = () => { r := r.contents + 10 1}let minus = (a: int, b: int) => b - alet first = (a: int, _b: int) => Math.Int.abs(a) let g = () => minus(r.contents, bump())let f = async p => minus(await p, r.contents)let h = () => first(r.contents, bump())
js
modifiedJS right operand of &&
let logTrue = () => { Console.log("logTrue") true}let both = (a: bool, b: bool) => a && blet t = both(false, logTrue())
js
modifiedJS while condition with statements
let f = async p => { let n = ref(0) while { let v = await p v > n.contents } { n := n.contents + 1 } n.contents}
js
Review
- Which arguments still go into the body? Constants, and variables that no
Lassignassigns, functions, module fields and pure operators over them (Lam_analysis.delay_safe, the Lambda counterpart of the check from #9159), always; another argument without side effects when nothing it moves past has side effects. Sominus(x, r.contents)keeps its JS. Lambda does not mark record fields mutable or not, so a read of any field counts as a read of state.Lambda.applyno longer turns((x, y) => x && y)(a, b)intoa && bunlessbis a constant or a variable. - Does the output change elsewhere? In the test corpus, inline_unused_arg_order_test.mjs, whose test pinned the old order
[2, 3, 1], and the block conditions of complex_while_loop.mjs and test_while_side_effect.mjs, which no longer use a closure. The runtime and Belt compile to the same JavaScript. Labeled arguments given out of order now run in parameter order when inlined, as they do in a call that is not. Abreakorcontinuein awhilecondition, a syntax error in the closure, now works, with a label when it targets an enclosing loop.