Stream: bugs

Topic: Program has different behavior depending on `dbg` place...


view this post on Zulip Ian McLerran (Dec 25 2025 at 20:09):

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().

view this post on Zulip Ian McLerran (Dec 25 2025 at 20:10):

I have been trying to minimize this issue, but haven't been able to due so yet.

(current code coming below)

view this post on Zulip Ian McLerran (Dec 25 2025 at 20:14):

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)
}

view this post on Zulip Ian McLerran (Dec 25 2025 at 20:53):

Failure Modes

Roc crashed: Internal error: TypeMismatch in call_invoke_closure continuation
thread 43720 panic: reached unreachable code
Unable to dump stack trace: debug info stripped
Aborted (core dumped)
dbg: []
209.0

Roc crashed: call_invoke_closure: value_stack empty when popping function
dbg: []
209.0

Roc crashed: non-exhaustive match
dbg: []
dbg: "209.0"
209.0

Roc crashed: non-exhaustive match
dbg: []
dbg: []
dbg: "209.0"
209.0

Roc crashed: non-exhaustive match

view this post on Zulip Ian McLerran (Dec 25 2025 at 20:54):

Note that debug is defined as:

debug : a -> a
debug = |v| {
    dbg v
    v
}

view this post on Zulip Ian McLerran (Dec 25 2025 at 20:58):

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
}

view this post on Zulip Richard Feldman (Dec 25 2025 at 22:08):

I'll look into this!

view this post on Zulip Richard Feldman (Dec 26 2025 at 00:26):

@Ian McLerran I have a fix in https://github.com/roc-lang/roc/pull/8752/changes - just cleaning it up now

view this post on Zulip Richard Feldman (Dec 26 2025 at 00:27):

tl;dr stack corruption in the interpreter - dbg was not really related, it just triggered the bug because involved the stack :smile:

view this post on Zulip Ian McLerran (Dec 26 2025 at 01:16):

Ahh, that makes sense! Changing the debug calls just changed the stack, hence the different results.

view this post on Zulip Notification Bot (Dec 26 2025 at 01:40):

Ian McLerran has marked this topic as resolved.

view this post on Zulip Notification Bot (Dec 26 2025 at 02:56):

Ian McLerran has marked this topic as unresolved.

view this post on Zulip Ian McLerran (Dec 26 2025 at 03:18):

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

view this post on Zulip Richard Feldman (Dec 26 2025 at 03:19):

ah interesting! I'll follow up!


Last updated: Jan 12 2026 at 12:19 UTC