GHC generates a wrong code for the case-match on GADT with polymorphic type,
here is a minimal example attached, expected results are 'A' in all test cases
(this is case for ghc-HEAD), however in:
test0 - result is missing as matches are removed from code (according to core)
test1 - returns A as expected
test2 - output 'o_O' without any checks
Trac metadata
Trac field
Value
Version
7.8.3
Type
Bug
TypeOfFailure
OtherFailure
Priority
normal
Resolution
Unresolved
Component
Compiler
Test case
Differential revisions
BlockedBy
Related
Blocking
CC
Operating system
Architecture
To upload designs, you'll need to enable LFS and have an admin enable hashed storage. More information
Child items
0
Show closed items
No child items are currently assigned. Use child items to break down this issue into smaller parts.
Linked items
0
Link issues together to show that they're related or that one is blocking others.
Learn more.
test0 = let s = cast A (SomeS (S 0)) in case viewV0 s of V0A{} -> putStrLn "test0 - A" V0B{} -> putStrLn "test0 - B"
Remember that cast :: forall a. M -> SomeS -> S a, so that s is a polymorphic value: s :: forall (x::M). S x.
Now at the occurrence of s on the next line, GHC has to choose what type to instantiate s at; that is, what to instantiate x with. The calling context is no help, so GHC simply has to guess any old type, of kind M. It could randomly choose A or B, but that seems arbitrary. So it chooses Any M, where Any :: forall k. k is a built-in type family that has no equations. It's as if it was declared with
type family Any :: k
So now (viewV0 s) has type V0 (Any M).
The trouble is that in GHC 7.8, Any was a data type not a type family. That was wrong for many reasons, but this ticket exposes a new one. In a case expression, GHC discards any patterns that can't possibly match the scrutinee. The two patterns in the case expression in test0 have types V0 A and V0 B. Since A and Any M are different (if Any is a data type) GHC discarded that alternative, and the same for the other one.
In HEAD, Any is a type family (see #9097 (closed)), so GHC (rightly) does not know that Any M and A are distinct. So the alternative is not discarded.
I have not followed through all the other cases, but I bet that it's all attributable to that one fix.
I will add this as regression test, despite the unsavoury use of unsafeCoerce.
I don't propose to fix 7.8.4. I think there were various reasons that Any was not made a type family earlier.
Here is a bit more concise test case (derived from the original one):
{-# LANGUAGE GADTs, DataKinds, KindSignatures #-}import Unsafe.Coercedata X = A | Bdata Y (x :: X) where YA :: Y A YB :: Y B Yany :: String -> Y xview :: Y a -> Y bview = unsafeCoercemain :: IO ()main = case view YA of YA -> putStrLn "YA" YB -> putStrLn "YB" Yany x -> putStrLn x
This will cause segmentation fault on 7.8.3 due to matching YA with Yany and trying to access String that does not exist.
In HEAD it will print YA. But is it really intended behavior?
My reasoning is that this is an example of unsafe using of unsafeCoerce.
Due to view's parametricity GHC can infer that all valid implementations (except |) are of the form
view :: Y a -> Y bview = const (Yany "some string here")
And it is safe to optimize main by dropping YA and YB cases.
But if we allow such usage of unsafeCoerce then it is no longer possible to optimize cases even if view absolutely safe.