The following code will crash when running roc test
, but not when running roc check
or build
.
app "Roc_99_Problems"
packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.5.0/Cufzl36_SnJ4QbOoEmiJ5dIpUxBvdB3NEySvuH82Wio.tar.br" }
imports [pf.Stdout]
provides [main] to pf
main =
Stdout.line "Running `roc test` crashes this code"
fail : List Str -> List List Str
fail = \_list -> [[], []]
expect fail ["a","b"] == [["a"], ["b"]]
The crash looks like this:
thread 'main' panicked at 'Error in alias analysis: error in module ModName("UserApp"), function definition FuncName("\x11\x00\x00\x00\x01\x00\x00\x00\x87h\xb9Z\xedHc\x19"), definition of value binding ValueId(13): expected type '(heap_cell, bag<(heap_cell,)>)', found type '(heap_cell, bag<()>)'', crates/compiler/gen_llvm/src/llvm/build.rs:5744:19
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
The fail
function is just a dummy implementation, but it is important that it returns a nested list of lists. Using a non-nested structure correctly fails the test, as expected. Using another type other than Str
will also not crash and simply fail the test as expected.
I've tried a couple variations of this, including doing the==
test without an expect, and that fails the exact same way, so I'd figure that the error is in the ==
implementation itself.
Here is a full backtrace as well:
$ RUST_BACKTRACE=full roc test
thread 'main' panicked at 'Error in alias analysis: error in module ModName("UserApp"), function definition FuncName("\x11\x00\x00\x00\x01\x00\x00\x00\x87h\xb9Z\xedHc\x19"), definition of value binding ValueId(13): expected type '(heap_cell, bag<(heap_cell,)>)', found type '(heap_cell, bag<()>)'', crates/compiler/gen_llvm/src/llvm/build.rs:5744:19
stack backtrace:
0: 0x55eab1fa62c1 - <std::sys_common::backtrace::_print::DisplayBacktrace as core::fmt::Display>::fmt::hbd7d55b7108d2ab8
1: 0x55eab1fd3b8f - core::fmt::write::h6d54cd7c9e155ec5
2: 0x55eab1fa1a21 - std::io::Write::write_fmt::h6a453a71c692f63b
3: 0x55eab1fa60d5 - std::sys_common::backtrace::print::h4ddf81241a51b337
4: 0x55eab1fa7757 - std::panicking::default_hook::{{closure}}::hff91f1f484ade5cd
5: 0x55eab1fa7544 - std::panicking::default_hook::h21f14afd59f7aef9
6: 0x55eab1fa7c0c - std::panicking::rust_panic_with_hook::h45f66047b14c555c
7: 0x55eab1fa7b07 - std::panicking::begin_panic_handler::{{closure}}::h49d1a88ef0908eb4
8: 0x55eab1fa66f6 - std::sys_common::backtrace::__rust_end_short_backtrace::hccebf9e57f8cc425
9: 0x55eab1fa7852 - rust_begin_unwind
10: 0x55eaaf3effd3 - core::panicking::panic_fmt::h54ec9d0e3180a83d
11: 0x55eaaf6f3752 - roc_gen_llvm::llvm::build::build_procedures_help::h3bdbf853df53af30
12: 0x55eaaf6ecb58 - roc_gen_llvm::llvm::build::build_procedures_expose_expects::h472e9cfdc7ef043d
13: 0x55eaaf60b70d - roc_repl_expect::run::expect_mono_module_to_dylib::h1ae11cd75ea6252c
14: 0x55eaaf5ea6c6 - roc_cli::test::h40d1fa4542d423a5
15: 0x55eaaf4d627b - roc::main::h8c0286bbf2fcf86b
16: 0x55eaaf4cb823 - std::sys_common::backtrace::__rust_begin_short_backtrace::ha2e31af2549d8703
17: 0x55eaaf4cb8e3 - std::rt::lang_start::{{closure}}::h1a31ddf22b52a3d4
18: 0x55eab1f98535 - std::rt::lang_start_internal::hf502095b101390bb
19: 0x55eaaf4d84e5 - main
20: 0x7fabc863dace - __libc_start_call_main
21: 0x7fabc863db89 - __libc_start_main@@GLIBC_2.34
22: 0x55eaaf4ca625 - _start
23: 0x0 - <unknown>
If you use that function in main instead of in the expect, will it fail with roc build
?
Like use the expect as an if condition?
Yes it also fails in that case
main =
val = fail ["a","b"]
if val == [["a"], ["b"]] then
Stdout.line "This will crash"
else
Stdout.line ""
fail : List Str -> List List Str
fail = \_list -> [[], []]
That code gives the same failure
Cool that's what I expected. No idea why it is happening though. Probably need to dump the mono ir and investigate
Out of curiosity does assigning the empty lists to a temporary fix the issue.
Oh, also, just realized the type of failed is wrong
The output needs parens
Not sure if that is the issue, but another good test, make the output type List (List Str)
With parens it doesn't crash
Ah, so bad type and check failing to catch the type error, interesting
So there should be a failure in the type checker thats not working
What does the type checker think the type of List List Str
is without the parens? That would be intresting to see.
How would I dump the IR out of curiosity, I haven't done that yet, I have built the compiler from source if needed.
if built from source, we have a few flags for dumping it a different stages.
In this case two interesting things to try:
ROC_PRINT_IR_AFTER_REFCOUNT=1 cargo run ...
And:
ROC_CHECK_MONO_IR=1 cargo run ...
Here's what I got back from the after refcount IR
procedure : `#UserApp.3` {}
procedure = `#UserApp.3` ():
let `#UserApp.17` : Str = "a";
let `#UserApp.18` : Str = "b";
let `#UserApp.13` : List Str = Array [`#UserApp.17`, `#UserApp.18`];
let `#UserApp.7` : List List Str = CallByName `#UserApp.fail` `#UserApp.13`;
let `#UserApp.12` : Str = "a";
let `#UserApp.9` : List Str = Array [`#UserApp.12`];
let `#UserApp.11` : Str = "b";
let `#UserApp.10` : List Str = Array [`#UserApp.11`];
let `#UserApp.8` : List List Str = Array [`#UserApp.9`, `#UserApp.10`];
let `#UserApp.6` : Int1 = CallByName `Bool.structuralEq` `#UserApp.7` `#UserApp.8`;
dec `#UserApp.8`;
dec `#UserApp.7`;
expect `#UserApp.6`;
let `#UserApp.5` : {} = Struct {};
ret `#UserApp.5`;
procedure : `Bool.structuralEq` Int1
procedure = `Bool.structuralEq` (`#Attr.#arg1`: List List Str, `#Attr.#arg2`: List List Str):
let `Bool.23` : Int1 = lowlevel Eq `#Attr.#arg1` `#Attr.#arg2`;
ret `Bool.23`;
procedure : `#UserApp.fail` List List Str
procedure = `#UserApp.fail` (`#UserApp.4`: List Str):
dec `#UserApp.4`;
let `#UserApp.15` : List [] = Array [];
let `#UserApp.16` : List [] = Array [];
let `#UserApp.14` : List List Str = Array [`#UserApp.15`, `#UserApp.16`];
ret `#UserApp.14`;
thread 'main' panicked at 'Error in alias analysis: error in module ModName("UserApp"), function definition FuncName("\x11\x00\x00\x00\x01\x00\x00\x00\x87h\xb9Z\xedHc\x19"), definition of value binding ValueId(13): expected type '(heap_cell, bag<(heap_cell,)>)', found type '(heap_cell, bag<()>)'', crates/compiler/gen_llvm/src/llvm/build.rs:5744:19
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
Not familiar with the IR but my first reading looks like it might be that the definitions UserApp.15
and UserApp.16
that cause problems. The type seems to be a List
of empty lists.
When I place the parens around the return type of fail to get the correct types the only meaningful diff is
< let `#UserApp.15` : List [] = Array [];
< let `#UserApp.16` : List [] = Array [];
---
> let `#UserApp.15` : List Str = Array [];
> let `#UserApp.16` : List Str = Array [];
Where the corrected types properly give List Str
instead of this List []
type
I'm not quite sure what List []
means as a type because I assumed []
is a value rather than a type.
haha
That looks very wrong
@Ayaz Hafiz or @Folkert de Vries any ideas on this?
Almost certainly List List Str
is parsed as List (List) (Str)
and that's an unhandled error not handled in the typechecker that produces an error type, and hence the error you see
So the outer list receives two type parameters instead of one. Is the number of generic arguments passed to a type not checked?
I would guess not given this
Is there any type of existing/tracking issue for this I looked through the GirHub issues an could not find any related; would it be good to open up and issue and start looking into it?
Opening a new issue sounds good, can you also mention #6191 in the new issue? It's a similar error but with different types.
Where would types get checked for having too many arguments in the compiler pipeline? (assuming that would be desired, it might not be for some reason I am unaware of).
Also, some other strange behavior I've found while looking at this. The type Str Str
seems to be valid according to the compiler:
main =
weird : Str Str
weird = "hello"
Stdout.line weird
This compiles and prints hello, no errors. I am almost certain this is wrong, the type Str Str
makes no sense. Doing this with a type alias, like:
MyStr a : Str a
main =
weird : MyStr Str
weird = "hello"
Stdout.line weird
Also compiles and runs fine.
This might be related, as it should be rejected due to the incorrect number of type parameters.
Yeah, looks to be all the same root error
Where would types get checked for having too many arguments in the compiler pipeline
I don't know myself but I find the cursor.so editor useful to help me find stuff.
Last updated: Jul 06 2025 at 12:14 UTC