[GHC] #15753: Inconsistent pattern-match warnings when using guards versus case expressions
GHC
ghc-devs at haskell.org
Tue Oct 16 13:08:28 UTC 2018
#15753: Inconsistent pattern-match warnings when using guards versus case
expressions
-------------------------------------+-------------------------------------
Reporter: RyanGlScott | Owner: (none)
Type: bug | Status: new
Priority: normal | Milestone: 8.8.1
Component: Compiler | Version: 8.6.1
Keywords: | Operating System: Unknown/Multiple
PatternMatchWarnings |
Architecture: | Type of failure: Incorrect
Unknown/Multiple | error/warning at compile-time
Test Case: | Blocked By:
Blocking: | Related Tickets:
Differential Rev(s): | Wiki Page:
-------------------------------------+-------------------------------------
Consider the following code (apologies for the rather non-minimal
example):
{{{#!hs
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE EmptyCase #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}
{-# OPTIONS_GHC -Wall -Wno-unticked-promoted-constructors #-}
module Bug where
import Data.Kind
import Data.Type.Bool
import Data.Type.Equality ((:~:)(..))
import Data.Void
data family Sing :: forall k. k -> Type
data instance Sing :: Bool -> Type where
SFalse :: Sing False
STrue :: Sing True
data instance Sing :: forall a. [a] -> Type where
SNil :: Sing '[]
SCons :: Sing x -> Sing xs -> Sing (x:xs)
data instance Sing :: forall a b. (a, b) -> Type where
STuple2 :: Sing x -> Sing y -> Sing '(x, y)
newtype instance Sing (f :: k1 ~> k2) =
SLambda { (@@) :: forall t. Sing t -> Sing (f @@ t) }
data TyFun :: Type -> Type -> Type
type a ~> b = TyFun a b -> Type
infixr 0 ~>
type family (f :: k1 ~> k2) @@ (x :: k1) :: k2
infixl 9 @@
newtype Map k v = MkMap [(k, v)]
data instance Sing :: forall k v. Map k v -> Type where
SMkMap :: Sing x -> Sing (MkMap x)
type family MapEmpty :: Map k v where
MapEmpty = MkMap '[]
type family MapInsertWith (f :: v ~> v ~> v) (new_k :: k) (new_v :: v) (m
:: Map k v) :: Map k v where
MapInsertWith _ new_k new_v (MkMap '[]) = MkMap '[ '(new_k, new_v)]
MapInsertWith f new_k new_v (MkMap ('(old_k,old_v):old_kvs)) =
If (old_k == new_k)
(MkMap ('(new_k,f @@ new_v @@ old_v):old_kvs))
(Case (MapInsertWith f new_k new_v (MkMap old_kvs)) old_k old_v)
type family Case (m :: Map k v) (old_k :: k) (old_v :: v) :: Map k v where
Case (MkMap kvs) old_k old_v = MkMap ('(old_k,old_v) : kvs)
sMapInsertWith :: forall k v (f :: v ~> v ~> v) (new_k :: k) (new_v :: v)
(m :: Map k v).
SEq k
=> Sing f -> Sing new_k -> Sing new_v -> Sing m
-> Sing (MapInsertWith f new_k new_v m)
sMapInsertWith _ snew_k snew_v (SMkMap SNil) = SMkMap (STuple2 snew_k
snew_v `SCons` SNil)
sMapInsertWith sf snew_k snew_v (SMkMap (STuple2 sold_k sold_v `SCons`
sold_kvs)) =
case sold_k %== snew_k of
STrue -> SMkMap (STuple2 snew_k (sf @@ snew_v @@ sold_v) `SCons`
sold_kvs)
SFalse ->
case sMapInsertWith sf snew_k snew_v (SMkMap sold_kvs) of
SMkMap skvs -> SMkMap (STuple2 sold_k sold_v `SCons` skvs)
class PEq a where
type (x :: a) == (y :: a) :: Bool
class SEq a where
(%==) :: forall (x :: a) (y :: a).
Sing x -> Sing y -> Sing (x == y)
mapInsertWithNonEmpty1 :: forall k v (f :: v ~> v ~> v) (old_k :: k)
(old_v :: v) (old_kvs :: [(k,v)])
(new_k :: k) (new_v :: v) (m :: Map k
v).
SEq k
=> Sing f -> Sing new_k -> Sing new_v -> Sing m
-> m :~: MkMap ('(old_k,old_v) : old_kvs)
-> MapInsertWith f new_k new_v m :~: MapEmpty
-> Void
mapInsertWithNonEmpty1 sf snew_k snew_v (SMkMap sm) Refl Refl
| STuple2 sold_k _ `SCons` sold_kvs <- sm
, SFalse <- sold_k %== snew_k
= case sMapInsertWith sf snew_k snew_v (SMkMap sold_kvs) of {}
mapInsertWithNonEmpty2 :: forall k v (f :: v ~> v ~> v) (old_k :: k)
(old_v :: v) (old_kvs :: [(k,v)])
(new_k :: k) (new_v :: v) (m :: Map k
v).
SEq k
=> Sing f -> Sing new_k -> Sing new_v -> Sing m
-> m :~: MkMap ('(old_k,old_v) : old_kvs)
-> MapInsertWith f new_k new_v m :~: MapEmpty
-> Void
mapInsertWithNonEmpty2 sf snew_k snew_v (SMkMap sm) Refl Refl
| STuple2 sold_k _ `SCons` sold_kvs <- sm
= case sold_k %== snew_k of
SFalse ->
case sMapInsertWith sf snew_k snew_v (SMkMap sold_kvs) of {}
}}}
If you compile this with GHC 8.6.1 or later, you'll notice something
interesting happening with the pattern-match coverage checker warnings:
{{{
$ /opt/ghc/8.6.1/bin/ghci Bug.hs
GHCi, version 8.6.1: http://www.haskell.org/ghc/ :? for help
Loaded GHCi configuration from /home/rgscott/.ghci
[1 of 1] Compiling Bug ( Bug.hs, interpreted )
Bug.hs:78:1: warning: [-Wincomplete-patterns]
Pattern match(es) are non-exhaustive
In an equation for ‘mapInsertWithNonEmpty1’:
Patterns not matched: _ _ _ (SMkMap _) Refl Refl
|
78 | mapInsertWithNonEmpty1 sf snew_k snew_v (SMkMap sm) Refl Refl
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^...
Ok, one module loaded.
}}}
`mapInsertWithNonEmpty1` is deemed non-exhaustive, but
`mapInsertWithNonEmpty2` is deemed exhaustive. However, the code for the
two functions are functionally identical—the only difference is that
`mapInsertWithNonEmpty1` uses one more pattern guard than
`mapInsertWithNonEmpty2` does.
--
Ticket URL: <http://ghc.haskell.org/trac/ghc/ticket/15753>
GHC <http://www.haskell.org/ghc/>
The Glasgow Haskell Compiler
More information about the ghc-tickets
mailing list