gleam-lang / gleam

⭐️ A friendly language for building type-safe, scalable systems!
https://gleam.run
Apache License 2.0
17.48k stars 726 forks source link

Compiler panics when a custom type variant doesn't exist #3288

Closed H12 closed 2 months ago

H12 commented 3 months ago

Ran into this while working on the Forth exercise on the exercism track.

It popped up when adding a custom type variant that held data of a type I had not yet created. Here's the full error message:

error: Fatal compiler bug!

This is a bug in the Gleam compiler, sorry!

Please report this crash to https://github.com/gleam-lang/gleam/issues/new
and include this error message with your report.

Panic: compiler-core/src/exhaustiveness.rs:746
    Custom type variants must exist: Type { name: "Entry", hint: AlternativeTypes(["Float", "Bool", "BitArray", "Result", "Forth", "ForthError", "Nil", "UtfCodepoint", "Int", "List", "String", "Entry"]) }
Gleam version: 1.2.0
Operating system: macos

If you can also share your code and say what file you were editing or any
steps to reproduce the crash that would be a great help.

You may also want to try again with the `GLEAM_LOG=trace` environment
variable set.

Here is the code prior to receiving the error:

import gleam/int
import gleam/list
import gleam/result
import gleam/string

pub type Forth {
  Forth(stack: List(Int))
}

pub type ForthError {
  DivisionByZero
  StackUnderflow
  InvalidWord
  UnknownWord
}

type Entry {
  Value(Int)
}

pub fn new() -> Forth {
  Forth([])
}

pub fn format_stack(f: Forth) -> String {
  case f {
    Forth([]) -> ""
    Forth(stack) ->
      stack
      |> list.reverse
      |> list.map(int.to_string)
      |> string.join(" ")
  }
}

pub fn eval(f: Forth, prog: String) -> Result(Forth, ForthError) {
  let #(entry, rest) = result.unwrap(string.split_once(prog, " "), #(prog, ""))

  case f, prog {
    Forth(stack), "" -> Ok(Forth(stack))
    Forth(stack), _ -> {
      use entry <- result.try(parse_entry(entry))
      case entry {
        Value(num) -> eval(Forth([num, ..stack]), rest)
      }
    }
  }
}

fn parse_entry(entry: String) -> Result(Entry, ForthError) {
  case int.parse(entry) {
    Ok(num) -> Ok(Value(num))
    _ ->
      case entry {
        "+" -> Ok(Value(0))
        _ -> Error(InvalidWord)
      }
  }
}

...And the code w/ the line (Word(Operator)) that causes the error:

import gleam/int
import gleam/list
import gleam/result
import gleam/string

pub type Forth {
  Forth(stack: List(Int))
}

pub type ForthError {
  DivisionByZero
  StackUnderflow
  InvalidWord
  UnknownWord
}

type Entry {
  Value(Int)
  Word(Operator)
}

pub fn new() -> Forth {
  Forth([])
}

pub fn format_stack(f: Forth) -> String {
  case f {
    Forth([]) -> ""
    Forth(stack) ->
      stack
      |> list.reverse
      |> list.map(int.to_string)
      |> string.join(" ")
  }
}

pub fn eval(f: Forth, prog: String) -> Result(Forth, ForthError) {
  let #(entry, rest) = result.unwrap(string.split_once(prog, " "), #(prog, ""))

  case f, prog {
    Forth(stack), "" -> Ok(Forth(stack))
    Forth(stack), _ -> {
      use entry <- result.try(parse_entry(entry))
      case entry {
        Value(num) -> eval(Forth([num, ..stack]), rest)
      }
    }
  }
}

fn parse_entry(entry: String) -> Result(Entry, ForthError) {
  case int.parse(entry) {
    Ok(num) -> Ok(Value(num))
    _ ->
      case entry {
        "+" -> Ok(Value(0))
        _ -> Error(InvalidWord)
      }
  }
}

It makes sense that my code would not compile, but it's a little surprising that it causes the compiler to panic.

Hope this report is helpful!

lpil commented 3 months ago

Thank you. I believe this bug was introduced in the previous release with fault tolerant analysis.

GearsDatapacks commented 3 months ago

I'll look into fixing this