Hey all - I've got a real puzzler on my hands. I have been trying to port some old AoC code to the new compiler. However, while the program passes roc build, it crashes before reaching the end of main. However, depending where, and how many dbg statements I place, the crash will occur at different places.
Also, I get some strange behavior where placing two dbg statements for the same Try value will show as the wrapped value once, and an Ok(...) the next time.
There is a third issue, which seems unrelated to the dbg issue, in which I can pipe the results of the part1() calculation to Stdout.line!(), but if I save the result of part1() as a value and then call Stdout.line!() on the value in the next line, the program crashes before reaching the Stdout.line!().
Finally, the program never finishes executing completely. It will always crash before completing, but depending on how I structure the rest of the code, I may be able to get it to print the result of part1().
I have been trying to minimize this issue, but haven't been able to due so yet.
(current code coming below)
I will give the full code below, but let me start by giving this snippet. Depending on which of these debug statements are uncommented, the program will crash in different modes, and may or may not make it back to main to execute the Stdout.line!() statement in main.
## Solve part 1
part1 : Str -> Try(Str, _)
part1 = |s| {
s.split_on("\n")
.map(extract_digits)
->keep_oks(compute_calibration)
# ->debug() # (1)
->sum()
# ->debug() # (2)
.to_str()
# ->debug() # (3)
->Ok
# ->debug() # (4)
}
Failure Modes
debug statements commented out:Roc crashed: Internal error: TypeMismatch in call_invoke_closure continuation
(1) uncommented, or (1) and (2) uncommnted:thread 43720 panic: reached unreachable code
Unable to dump stack trace: debug info stripped
Aborted (core dumped)
(2), (3), or (4) uncommented:(1) and (3) uncommented:(1) prints, but prints an empty list, even though not empty. Dbg (3) does not print. The function completes successfully, and returns to main, where depending on main, the result may print via Stdout.line!(). Finally, the program crashes with a different error message:dbg: []
209.0
Roc crashed: call_invoke_closure: value_stack empty when popping function
(1) and (4) uncommented:(1) prints, but prints an empty list, even though not empty. Dbg (4) does not print. The function completes successfully, and returns to main, where depending on main, the result may print via Stdout.line!(). Finally, the program crashes with a different error message:dbg: []
209.0
Roc crashed: non-exhaustive match
(1) uncommented, and both (3) and (4) uncommented:(1) and (3) print, but (1) prints an empty list, even though not empty. Dbg (4) does not print. The function completes successfully, and returns to main, where depending on main, the result may print via Stdout.line!(). Finally, the program crashes with a different error message:dbg: []
dbg: "209.0"
209.0
Roc crashed: non-exhaustive match
(1) and at least one of (3) or (4) uncommented, uncommenting (2) does not change the failure mode. However, instead of dbg (2) printing an integer value, it also prints an empty list as well. So uncommenting all results in the following:dbg: []
dbg: []
dbg: "209.0"
209.0
Roc crashed: non-exhaustive match
Note that debug is defined as:
debug : a -> a
debug = |v| {
dbg v
v
}
I'll come back and document some of the other issues later, but for now, here is the full code file:
app [main!] { pf: platform "https://github.com/lukewilliamboswell/roc-platform-template-zig/releases/download/0.6/2BfGn4M9uWJNhDVeMghGeXNVDFijMfPsmmVeo6M4QjKX.tar.zst" }
import pf.Stdout
main! = |_args| {
input =
\\two1nine
\\eightwothree
\\abcone2threexyz
\\xtwone3four
\\4nineeightseven2
\\zoneight234
\\7pqrstsixteen
part1(input)?->Stdout.line!()
# answer1 = part1(input)?->debug()->debug()->debug()
# dbg "answer"
# Stdout.line!(answer1)
Ok({})
}
## Solve part 1
part1 : Str -> Try(Str, _)
part1 = |s| {
s.split_on("\n")
.map(extract_digits)
->keep_oks(compute_calibration)
->debug() # (1)
->sum()
# ->debug() # (2)
.to_str()
# ->debug() # (3)
->Ok
->debug() # (4)
}
extract_digits : Str -> List(U64)
extract_digits = |s| {
s.to_utf8()
.keep_if(|c| c >= '0' and c <= '9')
.map(|c| (c - '0').to_u64())
}
compute_calibration : List(U64) -> Try(U64, _)
compute_calibration = |digits| Ok(List.first(digits)? * 10 + List.last(digits)?)
## Solve part 2
part2 : Str -> Try(Str, _)
part2 = |s| {
s.split_on("\n")
.map(extract_text_digits)
->keep_oks(compute_calibration)
->debug()
->sum()
.to_str()
->Ok
}
extract_text_digits : Str -> List(U64)
extract_text_digits = |s| {
bytes = s.to_utf8()
(0).to(bytes.len())
.fold(
[],
|ds, i| {
match bytes.drop_first(i) {
['o', 'n', 'e', ..] => ds.append(1)
['t', 'w', 'o', ..] => ds.append(2)
['t', 'h', 'r', 'e', 'e', ..] => ds.append(3)
['f', 'o', 'u', 'r', ..] => ds.append(4)
['f', 'i', 'v', 'e', ..] => ds.append(5)
['s', 'i', 'x', ..] => ds.append(6)
['s', 'e', 'v', 'e', 'n', ..] => ds.append(7)
['e', 'i', 'g', 'h', 't', ..] => ds.append(8)
['n', 'i', 'n', 'e', ..] => ds.append(9)
['z', 'e', 'r', 'o', ..] => ds.append(0)
[d, ..] => if d >= '0' and d <= '9' ds.append((d - '0').to_u64()) else ds
[] => ds
}
},
)
}
keep_oks : List(a), (a -> Try(b, e)) -> List(b)
keep_oks = |vs, f| {
vs.fold(
[],
|acc, v| {
match f(v) {
Ok(u) => acc.append(u)
Err(_) => acc
}
},
)
}
sum : List(Num) -> Num
sum = |nums| nums.fold(0, |acc, n| acc + n)
debug : a -> a
debug = |v| {
dbg v
v
}
I'll look into this!
@Ian McLerran I have a fix in https://github.com/roc-lang/roc/pull/8752/changes - just cleaning it up now
tl;dr stack corruption in the interpreter - dbg was not really related, it just triggered the bug because involved the stack :smile:
Ahh, that makes sense! Changing the debug calls just changed the stack, hence the different results.
Ian McLerran has marked this topic as resolved.
Ian McLerran has marked this topic as unresolved.
Thanks for tackling this @Richard Feldman! I built off the latest main, and I can see that most of the failure modes are fixed! (I can remove all the debugs, and the program compiles, regardless of whether I print from the pipe, or save the value and print)
Looks like this code also raises a couple other bugs if I reintroduce some debug() calls:
1) If I reintroduce (1) and (2):
- debug (2) prints an empty list, where it should print the integer value "209"
2) If I reintroduce (2), (3), or (4) only:
- crashes with: Roc crashed: Internal error: TypeMismatch in call_invoke_closure continuation
ah interesting! I'll follow up!
Last updated: Jan 12 2026 at 12:19 UTC