Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Compiler crash when checking exhaustiveness against duplicate type constructors #3272

Open
nijolas opened this issue Jun 13, 2024 · 4 comments
Labels
bug Something isn't working help wanted Contributions encouraged priority:high

Comments

@nijolas
Copy link

nijolas commented Jun 13, 2024

Getting a compiler crash via the LSP consistently:

Panic: compiler-core\src\exhaustiveness.rs:746
	Custom type variants must exist: Type { name: "FrameType", hint: AlternativeTypes(["Bool", "UtfCodepoint", "Int", "Nil", "Game", "Error", "Frame", "BitArray", "Float", "Result", "FrameType", "List", "String"]) }
Gleam version: 1.2.1
Operating system: windows

Here is the code file that's causing it:

import gleam/int
import gleam/list

pub opaque type Frame {
  Frame(rolls: List(Int), bonus: List(Int))
}

pub type Game {
  Game(frames: List(Frame))
}

pub type Error {
  InvalidPinCount
  GameComplete
  GameNotComplete
  EmptyFrame
  BadFrame
}

type FrameType {
  Incomplete
  OpenFrame
  Spare
  Strike
  BadFrame
}

pub fn roll(game: Game, knocked_pins: Int) -> Result(Game, Error) {
  case game.frames {
    // first roll
    [] -> Ok(game |> push_frame(new_frame(knocked_pins)))
    // later roll
    [frame, ..rest] -> {
      // check for incomplete frame
      case frame.rolls {
        [] -> Error(EmptyFrame)
        // only one roll has occurred
        [roll] -> Ok(game |> push_frame(frame |> push_roll(knocked_pins)))
        // the previous frame is finished
        [roll1, roll2] -> {
          // see what we rolled
          case frame_type(frame) {
            Spare -> {
              // spare adds the next roll's bonus
              let final_frame = frame |> add_bonus(knocked_pins)
              Ok(Game([new_frame(knocked_pins), final_frame, ..rest]))
            }
            _ -> Ok(game |> push_frame(new_frame(knocked_pins)))
          }
        }
        _ -> Error(BadFrame)
      }
    }
  }
}

pub fn score(game: Game) -> Result(Int, Error) {
  let result =
    game.frames
    |> list.map(fn(f) { f.rolls })
    |> list.flatten
    |> int.sum
  Ok(result)
}

fn new_frame(knocked_pins: Int) -> Frame {
  Frame([knocked_pins], [])
}

fn push_roll(f: Frame, knocked_pins: Int) -> Frame {
  Frame([knocked_pins, ..f.rolls], f.bonus)
}

fn add_bonus(f: Frame, bonus: Int) -> Frame {
  Frame(f.rolls, [bonus, ..f.bonus])
}

fn push_frame(g: Game, f: Frame) -> Game {
  Game([f, ..g.frames])
}

fn frame_type(f: Frame) -> FrameType {
  case f.rolls {
    [] -> Incomplete
    [a] -> {
      case a {
        _ if a < 10 -> Incomplete
        _ if a == 10 -> Strike
        _ -> BadFrame
      }
    }
    [a, b] -> {
      case a + b {
        c if c < 10 -> OpenFrame
        c if c == 10 -> Spare
        _ -> BadFrame
      }
    }
    _ -> BadFrame
  }
}

I'm part way through working on the Bowling exercise on Exercism. As I was working, VSCode threw up an error saying:

The Gleam Language Server server crashed 5 times in the last 3 minutes. The server will not be restarted. See the output for more information.

And that's what I found in the output. I restarted VSCode a few times, and this code seems to be triggering it consistently.

This is not an issue for me of course! But I thought it might help toward improving tooling and compiler errors 🙏

@nijolas
Copy link
Author

nijolas commented Jun 13, 2024

Looks like the issue might be that I've declared BadFrame twice - once as an Error and once as a FrameType.

@nijolas
Copy link
Author

nijolas commented Jun 13, 2024

I'm struggling to find a smaller reproduction, but I can confirm that changing FrameType.BadFrame to just FrameType.Bad makes the error disappear.

Changing it back to BadFrame immediately triggers the crash again.

@nijolas nijolas changed the title Consistently reproducible LSP crash while working on code Compiler crash when checking exhaustiveness against duplicate type constructors Jun 13, 2024
@nijolas
Copy link
Author

nijolas commented Jun 13, 2024

Possibly related to #3256

@lpil
Copy link
Member

lpil commented Jun 18, 2024

Hello! Yes I think this is a bug introduced by our fault tolerant analysis, and the exhaustiveness checker failing to handle code that is invalid, while previously it would always be valid. I think it may be the same as the other issue.

@lpil lpil added bug Something isn't working help wanted Contributions encouraged priority:high labels Jun 18, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working help wanted Contributions encouraged priority:high
Projects
None yet
Development

No branches or pull requests

2 participants