Skip to content

Compiled program throws ‘Non-exhaustive patterns in case’

Summary

It's hard to say what exactly causes this bug; it's very fragile and requires a lot of newer GHC type features. But, with the below combination of unlifted data types, type data, type families, GADTs, and less interesting circumstantial details, GHC produces a program that crashes with Non-exhaustive patterns in case.

This crash is incorrect, as the case expression in question is exhaustive given the types of the GADT constructors. Furthermore, GHC doesn't warn about this at compile time with -Wincomplete-patterns. Changing minor details, like inlining the contents of the BugLib module into Main or replacing the $ in the definition of shouldBeExhaustive with parentheses, results in a program that doesn't crash.

Steps to reproduce

{-# LANGUAGE GADTs, UnliftedDatatypes #-}
module BugLib where

import GHC.Exts (UnliftedType)
import GHC.Types (Type)

data UnliftedGADTProxy :: Type -> UnliftedType where
  UGPInt :: UnliftedGADTProxy Int
  UGPBool :: UnliftedGADTProxy Bool
{-# LANGUAGE GADTs, TypeData, TypeFamilies #-}
{-# OPTIONS_GHC -Wincomplete-patterns -Werror #-}
module Main where

import GHC.Types (Type)

import BugLib

type data TDWrapper = TDWrapperCon Type

type family UnwrapTDW tdw where
  UnwrapTDW (TDWrapperCon a) = a

data ProxyBox tdw = ProxyBox (UnliftedGADTProxy (UnwrapTDW tdw))

shouldBeExhaustive :: ProxyBox (TDWrapperCon Int) -> ()
shouldBeExhaustive pb = id $ case pb of ProxyBox UGPInt -> ()

main :: IO ()
main = let
  pb :: ProxyBox (TDWrapperCon Int)
  pb = ProxyBox UGPInt
  !_ = shouldBeExhaustive pb
  in putStrLn "works"

Expected behavior

It should compile to a program that prints works.

Environment

  • GHC version used: 9.6.2

Optional:

  • Operating System: Linux (NixOS)
  • System Architecture: x86_64
To upload designs, you'll need to enable LFS and have an admin enable hashed storage. More information