Stream: bugs

Topic: Compiler crash from pattern matching on nested extended tags


view this post on Zulip Austin Davis (Jul 23 2025 at 19:07):

Let's say we have the following code:

TypeAB x : [A, B]x

TypeCD x : TypeAB [C, D]x

TypeEF x : TypeCD [E, F]x

Combined : TypeCD (TypeAB (TypeEF []))

combined_to_int : Combined -> U8
combined_to_int = |test1|
    when test1 is
        A -> 1
        B -> 2
        C -> 3
        D -> 4
        E -> 5
        F -> 6
        _ -> crash("combined_to_int() failed")

expect
  value = E
  int_value = combined_to_int(E)
  int_value == 5

The above actually works fine. However, simply removing the wildcard case from the when branch inside combined_to_int causes the compiler to fail an internal assertion. Here is the message:

---> roc test
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>
not a tag union: Alias(`Ast.IdentId(34)`, [3395], <3396>['A' , 'A' , 'B' , 'B' , 'C' , 'D' , 'E' , 'F' , ]<Openness(3)>)
Location: crates/compiler/solve/src/solve.rs:1840:34

What I would expect is for the compiler to output a type error telling me that I haven't covered all the cases. However, I also think that's a semantic bug in itself, as the Combined tag union is not extensible (all type variables are populated by concrete types), so it seems like a mistake to require a wildcard case when pattern matching on it.

view this post on Zulip Anton (Jul 25 2025 at 11:57):

I'll check this out, I made a minimal repro:

❯ roc check temp.roc
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>
not a tag union: Alias(`temp.IdentId(0)`, [3172], <3173>['A' , 'B' , ]<Any(3174)>)
Location: crates/compiler/solve/src/solve.rs:1840:34

~/Downloads/temp
❯ cat temp.roc
module []

TypeAB x : [A, B]x

TypeCD x : TypeAB [C]x

Combined : TypeCD (TypeAB [])

combined_to_int : Combined -> U8
combined_to_int = |test1|
    when test1 is
        A -> 1
        B -> 2
        C -> 3

view this post on Zulip Austin Davis (Jul 25 2025 at 16:29):

Thanks for looking into it. The minimal example might seem trivial, but I have some non-trivial use cases that would improve the quality of life for the code I'm currently writing.

view this post on Zulip Anton (Jul 25 2025 at 16:42):

We should definitely make trivial things work :)

view this post on Zulip Anton (Jul 25 2025 at 17:50):

Fix in PR#8107, it turned out these type definitions created a tag union with lots of duplicates:

['A' , 'A' , 'A' , 'B' , 'B' , 'B' , 'C' , 'C' , 'D' , 'D' , 'E' , 'F' ]

I added deduplication at the right point and all is well :tada:

That was a fun bug hunt :)

view this post on Zulip Austin Davis (Jul 25 2025 at 20:20):

Amazing, that was fast! Thanks for taking the time to fix it!


Last updated: Jul 26 2025 at 12:14 UTC