I wondered what was the reason that the first expression worked while the second one received an error . image.png
I think it is a bug in the wasm dev backend.
Interesting that changing from List.dropAt x 0
to List.dropFirst x
fixes it.
EDIT: This is actually extra confusing cause List.dropFirst
is defined as:
dropFirst = \list ->
List.dropAt list 0
Also, I get a slightly different error, which might be useful as context for someone: JsValue(CompileError: wasm validation error: at offset 18988: popping value from empty stack)
:thinking: this works for me in roc repl
on the command line
so yeah, probably a bug in the wasm dev backend
but I agree that I'm not sure what it would be, given how dropFirst
is defined :sweat_smile:
My gut feeling is it is somehow refcount/memory related. Maybe even a bug related to freeing a seamless slice, but that is definitely guessing.
Thanks for the information!
OK this is weird. The following tests passes for the Wasm backend if I insert it into gen_list.rs
And remember that tests are wrapped in a main
function in the same way as REPL inputs are.
#[test]
fn zulip_issue() {
assert_evals_to!(
r#"
x = [1,2,3]
tl = List.dropAt x 0
if Bool.true then [] else tl
"#,
RocList::empty(),
RocList<i64>
)
}
Also if I print out the IR after refcount insertion, this is what I get
procedure : `#UserApp.main` List I64
procedure = `#UserApp.main` ():
let `#UserApp.x` : List I64 = Array [1i64, 2i64, 3i64];
let `#UserApp.6` : U32 = 0i64;
let `#UserApp.tl` : List I64 = CallByName `List.dropAt` `#UserApp.x` `#UserApp.6`;
let `#UserApp.4` : Int1 = CallByName `Bool.true`;
if `#UserApp.4` then
dec `#UserApp.tl`;
let `#UserApp.5` : List I64 = Array [];
ret `#UserApp.5`;
else
ret `#UserApp.tl`;
procedure : `List.dropAt` List I64
procedure = `List.dropAt` (`#Attr.#arg1`: List I64, `#Attr.#arg2`: U32):
let `List.494` : List I64 = lowlevel ListDropAt `#Attr.#arg1` `#Attr.#arg2`;
ret `List.494`;
procedure : `Bool.true` Int1
procedure = `Bool.true` ():
let `Bool.23` : Int1 = true;
ret `Bool.23`;
There's just one decrement in the then
branch
Flipping the if
branches around moves the decrement to the branch that's not taken
OK I managed to extract a .wasm file from the browser.
The wasm instruction stream is invalid at the point where it calls the refcount decrement.
OK I figured out what's wrong. The backend tries to keep track of what's on the Wasm stack machine at each point in the program. In this example, our model of the stack machine goes wrong and we end up generating invalid Wasm instructions! This is the hardest part of the wasm backend. :sweating:
Last updated: Jul 05 2025 at 12:14 UTC