When I try to decode an aliased opaque type it crashes the compiler
I made this repro example:
UserName := Str implements [
Eq { isEq: userNameEq },
Decoding {
decoder: userNameDecode,
},
]
userNameEq = \@UserName a, @UserName b -> a == b
userNameDecode = Decode.custom \bytes, fmt ->
bytes
|> Decode.fromBytesPartial fmt
|> Decode.mapResult @UserName
#This is fine
Alias : { user: UserName }
expect
prog =
"""
{"user":"name"}
"""
|> Str.toUtf8
rec : Result Alias _
rec = prog |> Decode.fromBytes TotallyNotJson.json
rec == Ok { user: @UserName "name" }
# This will crash the compiler
AliasBad : UserName
expect
prog =
"name"
|> Str.toUtf8
rec : Result AliasBad _
rec = prog |> Decode.fromBytes TotallyNotJson.json
rec == @UserName "name" |>Ok
I get this error:
An internal compiler expectation was broken.
This is definitely a compiler bug.
Please file an issue here: https://github.com/roc-lang/roc/issues/new/choose
thread '<unnamed>' panicked at 'ambient functions don't unify', /home/eli/Code/roc/roc/crates/compiler/unify/src/unify.rs:201:18
stack backtrace:
0: rust_begin_unwind
at /rustc/d5c2e9c342b358556da91d61ed4133f6f50fc0c3/library/std/src/panicking.rs:593:5
1: core::panicking::panic_fmt
at /rustc/d5c2e9c342b358556da91d61ed4133f6f50fc0c3/library/core/src/panicking.rs:67:14
2: panic_display<&str>
at /rustc/d5c2e9c342b358556da91d61ed4133f6f50fc0c3/library/core/src/panicking.rs:150:5
3: roc_unify::unify::Unified<M>::expect_success
at ./crates/compiler/unify/src/unify.rs:201:18
4: compact_lambda_set<roc_late_solve::LatePhase>
at ./crates/compiler/solve/src/specialize.rs:577:73
5: compact_lambda_sets_of_vars<roc_late_solve::LatePhase>
at ./crates/compiler/solve/src/specialize.rs:434:37
6: unify
at ./crates/compiler/late_solve/src/lib.rs:403:17
7: roc_mono::ir::Env::unify
at ./crates/compiler/mono/src/ir.rs:1437:33
8: specialize_proc_help
at ./crates/compiler/mono/src/ir.rs:3506:20
9: specialize_variable
at ./crates/compiler/mono/src/ir.rs:4049:9
10: call_by_name_help
at ./crates/compiler/mono/src/ir.rs:8958:31
11: call_by_name
at ./crates/compiler/mono/src/ir.rs:8671:17
12: with_hole
at ./crates/compiler/mono/src/ir.rs:5420:21
13: assign_to_symbol
at ./crates/compiler/mono/src/ir.rs:8486:23
14: from_can
at ./crates/compiler/mono/src/ir.rs:6949:13
15: specialize_proc_help
at ./crates/compiler/mono/src/ir.rs:3541:32
16: specialize_variable
at ./crates/compiler/mono/src/ir.rs:4049:9
17: specialize_external_help
at ./crates/compiler/mono/src/ir.rs:3198:9
18: specialize_external_specializations
at ./crates/compiler/mono/src/ir.rs:3178:13
19: specialize_all
at ./crates/compiler/mono/src/ir.rs:3040:9
20: roc_load_internal::file::make_specializations
at ./crates/compiler/load_internal/src/file.rs:5745:13
21: roc_load_internal::file::run_task
at ./crates/compiler/load_internal/src/file.rs:6498:17
22: worker_task
at ./crates/compiler/load_internal/src/file.rs:2048:34
23: roc_load_internal::file::load_multi_threaded::{{closure}}::{{closure}}
at ./crates/compiler/load_internal/src/file.rs:1788:25
24: crossbeam_utils::thread::ScopedThreadBuilder::spawn::{{closure}}
at /home/eli/.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-utils-0.8.16/src/thread.rs:440:31
25: core::ops::function::FnOnce::call_once{{vtable.shim}}
at /rustc/d5c2e9c342b358556da91d61ed4133f6f50fc0c3/library/core/src/ops/function.rs:250:5
26: <alloc::boxed::Box<F,A> as core::ops::function::FnOnce<Args>>::call_once
at /rustc/d5c2e9c342b358556da91d61ed4133f6f50fc0c3/library/alloc/src/boxed.rs:1993:9
Interestingly in my original application where this cropped up the error is different, perhaps that's because the opaque type takes type args?:
Code:
TT: Option Str
expect
prog="""{"prog":"hi"}"""|>Str.toUtf8
rec:Result {prog:TT} _
rec=prog|>Decode.fromBytes Core.json
rec==Ok {prog:Union.some "hi" }
Makes this error:
thread 'main' panicked at 'Error in alias analysis: error in module ModName("UserApp"), function definition FuncName("\x02\x00\x00\x00%\x00\x00\x00y0za5-\xc3Y"), definition of value binding ValueId(22): could not find func in module ModName("UserApp") with name FuncName("\x15\x00\x00\x00\x14\x00\x00\x00\xa8,\xfbK\xdc\xea\xc1\xe8")', crates/compiler/gen_llvm/src/llvm/build.rs:5761:19
stack backtrace:
0: rust_begin_unwind
at /rustc/d5c2e9c342b358556da91d61ed4133f6f50fc0c3/library/std/src/panicking.rs:593:5
1: core::panicking::panic_fmt
at /rustc/d5c2e9c342b358556da91d61ed4133f6f50fc0c3/library/core/src/panicking.rs:67:14
2: build_procedures_help
at ./crates/compiler/gen_llvm/src/llvm/build.rs:5761:19
3: build_procedures_expose_expects
at ./crates/compiler/gen_llvm/src/llvm/build.rs:5675:25
4: expect_mono_module_to_dylib
at ./crates/repl_expect/src/run.rs:675:24
5: test
at ./crates/cli/src/lib.rs:518:43
6: main
at ./crates/cli/src/main.rs:80:17
7: core::ops::function::FnOnce::call_once
at /rustc/d5c2e9c342b358556da91d61ed4133f6f50fc0c3/library/core/src/ops/function.rs:250:5
ouch, that’s a nasty bug
I've got a few more for you too ;)
optionToEncode : Option val -> Encoder fmt where val implements Encoding,fmt implements EncoderFormatting
optionToEncode = \@Option val ->
when val is
Some contents ->
Encode.custom \bytes, fmt ->
bytes |> Encode.append contents fmt
None -> Encode.custom \bytes, fmt -> bytes
expect
encoded =
dat:{maybe:Option u8,other:Str}
dat={maybe: none {},other:"hi" }
Encode.toBytes dat Core.json
|> Str.fromUtf8
expected = Ok "{\"other\":\"hi\"}"
expected == encoded
This causes:
thread '<unnamed>' panicked at 'unspecialized lambda sets left over during resolution:
LambdaSet([] + (<939>FlexAble(fmt, [`Encode.Encoding`]):`Encode.toEncoder`:2), ^<942>),
UlsOfVar(VecMap { keys: [939, 971], values: [VecSet { elements: [938, 941] }, VecSet { elements: [970, 973] }] })',
crates/compiler/mono/src/layout.rs:2065:17
Interestingly encoding a Some
value works fine.
I've modified the Core.json record encoder slightly so that it just doesn't include the field if it gets a 0 byte response but even just encoding the Option on it's own fails with the exact same error:
expect
encoded =
dat:Option u8
dat=@Option None
Encode.toBytes dat Core.json
|> Str.fromUtf8
expected = Ok ""
expected == encoded
Interestingly with some more testing i found that if i remove the encode from the Some
encoder I don't get that error.
optionToEncode : Option val -> Encoder fmt where val implements Encoding,fmt implements EncoderFormatting
optionToEncode = \@Option val ->
when val is
Some contents ->
Encode.custom \bytes, fmt ->
bytes |>List.concat ("hi"|>Str.toUtf8)
None -> Encode.custom \bytes, fmt ->
bytes |>List.concat ("hello"|>Str.toUtf8)
I was able to run the tests, but it mostly throws this error depending on how long the list i return in the encoder is.
thread 'main' panicked at 'misaligned pointer dereference: address must be a multiple of 0x8 but is 0x7ffff7e2c03c', crates/repl_expect/src/app.rs:57:45
thread 'main' panicked at 'misaligned pointer dereference: address must be a multiple of 0x8 but is 0x7ffff7e2c03c', crates/repl_expect/src/app.rs:57:45
Does this also happen with a release compiler?
Oh, haven't tested that, I can do that shortly though.
Thanks @Anton I got some improvement there.
The version that just returns lists no longer has weird pointer errors.
However if i actually try to encode in the Some
branch it still gives me this error.
thread '<unnamed>' panicked at 'called `Option::unwrap()` on a `None` value', crates/compiler/mono/src/ir.rs:6143:56
Which looks like it's the same as the lambda set error from before, it just isn't being caught by the debug asserts
The debug asserts probably fail earlier, at the first panic you got
Also, pretty worrying that we get a bad pointer deref in dev but not in release... we should fix the one in dev
Well I figured out what the second set of errors were.. in my type annotations I had u8 instead of U8 :face_palm::face_palm::face_palm::face_palm:
Last updated: Jul 06 2025 at 12:14 UTC