[GHC] #15305: Erroneous "non-exhaustive pattern match" using nested GADT with strictness annotation

#15305: Erroneous "non-exhaustive pattern match" using nested GADT with strictness annotation -------------------------------------+------------------------------------- Reporter: jkoppel | Owner: (none) Type: bug | Status: new Priority: normal | Milestone: 8.6.1 Component: Compiler | Version: 8.4.3 (Type checker) | Keywords: | Operating System: Unknown/Multiple Architecture: | Type of failure: Incorrect Unknown/Multiple | error/warning at compile-time Test Case: | Blocked By: Blocking: | Related Tickets: Differential Rev(s): | Wiki Page: -------------------------------------+------------------------------------- In the following code, `fun` contains an exhaustive pattern match, but, when compiling with -Wall, ghc erroneously reports a non-exhaustive pattern match. {{{#!hs data (:+:) f g a = Inl !(f a) | Inr !(g a) data A data B data Foo l where Foo :: Foo A data Bar l where Bar :: Bar B type Sig = Foo :+: Bar fun :: Sig B -> Int fun (Inr Bar) = 1 }}} This report came from https://stackoverflow.com/questions/16225281/gadts- failed-exhaustiveness-checking . Without strictness annotations, this is indeed a failed exhaustive check, due to bottom. I spoke to Richard Eisenberg at PLDI a few days ago, and he informed me that, if this warning did not disappear with strictness annotations, it was a bug. I added strictness annotations, and it did not disappear. I've tried all combinations of using strictness annotations and/or running with `{-# LANGUAGE Strict #-}`. -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/15305 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#15305: Erroneous "non-exhaustive pattern match" using nested GADT with strictness annotation -------------------------------------+------------------------------------- Reporter: jkoppel | Owner: (none) Type: bug | Status: new Priority: normal | Milestone: 8.6.1 Component: Compiler (Type | Version: 8.4.3 checker) | Resolution: | Keywords: Operating System: Unknown/Multiple | Architecture: Type of failure: Incorrect | Unknown/Multiple error/warning at compile-time | Test Case: Blocked By: | Blocking: Related Tickets: | Differential Rev(s): Wiki Page: | -------------------------------------+------------------------------------- Changes (by jkoppel): * Attachment "Main.hs" added. -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/15305 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#15305: Erroneous "non-exhaustive pattern match" using nested GADT with strictness annotation -------------------------------------+------------------------------------- Reporter: jkoppel | Owner: (none) Type: bug | Status: new Priority: normal | Milestone: 8.6.1 Component: Compiler (Type | Version: 8.4.3 checker) | Keywords: Resolution: | PatternMatchWarnings Operating System: Unknown/Multiple | Architecture: Type of failure: Incorrect | Unknown/Multiple error/warning at compile-time | Test Case: Blocked By: | Blocking: Related Tickets: | Differential Rev(s): Wiki Page: | -------------------------------------+------------------------------------- Changes (by RyanGlScott): * keywords: => PatternMatchWarnings -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/15305#comment:1 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#15305: Erroneous "non-exhaustive pattern match" using nested GADT with strictness annotation -------------------------------------+------------------------------------- Reporter: jkoppel | Owner: (none) Type: bug | Status: new Priority: normal | Milestone: 8.6.1 Component: Compiler (Type | Version: 8.4.3 checker) | Keywords: Resolution: | PatternMatchWarnings Operating System: Unknown/Multiple | Architecture: Type of failure: Incorrect | Unknown/Multiple error/warning at compile-time | Test Case: Blocked By: | Blocking: Related Tickets: | Differential Rev(s): Wiki Page: | -------------------------------------+------------------------------------- Comment (by simonpj): Good point. Pattern-match coverage checking sorely needs a champion: someone who is willing to take ownership of the code (which is in only one module!) and give it love and care. There are a lot of open tickets: see [https://ghc.haskell.org/trac/ghc/wiki/PatternMatchCheck this summary wiki page] -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/15305#comment:2 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#15305: Erroneous "non-exhaustive pattern match" using nested GADT with strictness annotation -------------------------------------+------------------------------------- Reporter: jkoppel | Owner: (none) Type: bug | Status: new Priority: normal | Milestone: 8.6.1 Component: Compiler (Type | Version: 8.4.3 checker) | Keywords: Resolution: | PatternMatchWarnings Operating System: Unknown/Multiple | Architecture: Type of failure: Incorrect | Unknown/Multiple error/warning at compile-time | Test Case: Blocked By: | Blocking: Related Tickets: | Differential Rev(s): Wiki Page: | -------------------------------------+------------------------------------- Comment (by RyanGlScott): It's unclear to me what can even be done about this. The GADTs Meet Their Match paper is completely silent on the topic of bang patterns, so this seems like a theory bug, not an implementation one. -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/15305#comment:3 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#15305: Erroneous "non-exhaustive pattern match" using nested GADT with strictness annotation -------------------------------------+------------------------------------- Reporter: jkoppel | Owner: (none) Type: bug | Status: new Priority: normal | Milestone: 8.6.1 Component: Compiler (Type | Version: 8.4.3 checker) | Keywords: Resolution: | PatternMatchWarnings Operating System: Unknown/Multiple | Architecture: Type of failure: Incorrect | Unknown/Multiple error/warning at compile-time | Test Case: Blocked By: | Blocking: Related Tickets: | Differential Rev(s): Wiki Page: | -------------------------------------+------------------------------------- Comment (by goldfire): If this were fixed, then I believe that GHC could usefully use empty types as many of its "Trees That Grow" extension fields, make the extensions strict, and then remove many tiresome panics in redundant patterns. For example (simplified): {{{#!hs data HsImplicitBndrs pass = UsefulConstructor ... | XHsImplicitBndrs !(XXHsImplicitBndrs pass) type instance XXHsImplicitBndrs (GhcPass _) = Void }}} Note the bang. Currently, if we did this, we would ''still'' need to match against `XHsImplicitBndrs` every time. But with this patch fixed, we could avoid doing so. -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/15305#comment:4 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#15305: Erroneous "non-exhaustive pattern match" using nested GADT with strictness annotation -------------------------------------+------------------------------------- Reporter: jkoppel | Owner: (none) Type: bug | Status: new Priority: normal | Milestone: 8.6.1 Component: Compiler (Type | Version: 8.4.3 checker) | Keywords: Resolution: | PatternMatchWarnings Operating System: Unknown/Multiple | Architecture: Type of failure: Incorrect | Unknown/Multiple error/warning at compile-time | Test Case: Blocked By: | Blocking: Related Tickets: | Differential Rev(s): Wiki Page: | -------------------------------------+------------------------------------- Changes (by simonpj): * cc: alanz, sh.najd@… (added) -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/15305#comment:5 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#15305: Erroneous "non-exhaustive pattern match" using nested GADT with strictness annotation -------------------------------------+------------------------------------- Reporter: jkoppel | Owner: (none) Type: bug | Status: new Priority: normal | Milestone: 8.6.1 Component: Compiler (Type | Version: 8.4.3 checker) | Keywords: Resolution: | PatternMatchWarnings Operating System: Unknown/Multiple | Architecture: Type of failure: Incorrect | Unknown/Multiple error/warning at compile-time | Test Case: Blocked By: | Blocking: Related Tickets: | Differential Rev(s): Wiki Page: | -------------------------------------+------------------------------------- Comment (by RyanGlScott): To be clear, this is another occurrence of this phenomenon, right? {{{#!hs {-# LANGUAGE EmptyCase #-} {-# OPTIONS_GHC -Wincomplete-patterns #-} module Bug where import Data.Void data Abyss = Abyss !Void stareIntoTheAbyss :: Abyss -> a stareIntoTheAbyss a = case a of {} }}} (In that GHC warns that `stareIntoTheAbyss` in non-exhaustive, even though it shouldn't?) -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/15305#comment:6 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#15305: Erroneous "non-exhaustive pattern match" using nested GADT with strictness annotation -------------------------------------+------------------------------------- Reporter: jkoppel | Owner: (none) Type: bug | Status: new Priority: normal | Milestone: 8.6.1 Component: Compiler (Type | Version: 8.4.3 checker) | Keywords: Resolution: | PatternMatchWarnings Operating System: Unknown/Multiple | Architecture: Type of failure: Incorrect | Unknown/Multiple error/warning at compile-time | Test Case: Blocked By: | Blocking: Related Tickets: | Differential Rev(s): Wiki Page: | -------------------------------------+------------------------------------- Comment (by RyanGlScott): I tried thinking of an (ad hoc) algorithm to check for this sort of thing. This is the closest I could get: 1. In `mkOneConFull` (which generates pattern-matching information for each constructor), record the type and strictness of each constructor's fields. 2. When checking if a match on that constructor is reachable, perform the following additional checks: i. Check if all fields are strict. ii. Check if each field's type is uninhabitable. If both (i) and (ii) are true, then deem the entire constructor to be unreachable. I think this would work for the original program, but not the program in comment:6 {{{#!hs data Abyss = MkAbyss !Abyss stareIntoTheAbyss :: Abyss -> a stareIntoTheAbyss a = case a of {} }}} Why? In order to check that a match on `(MkAbyss _)` is unreachable, we need to check: i. That the field of `MkAbyss` is strict. (This is the case.) ii. That the type of the field of `MkAbyss` (`Abyss`) is uninhabitable. But in order to check that `Abyss` in uninhabitable, we need to check the `MkAbyss` constructor again, which requires repeating this algorithm. Now we've entered an infinite loop! -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/15305#comment:7 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#15305: Erroneous "non-exhaustive pattern match" using nested GADT with strictness annotation -------------------------------------+------------------------------------- Reporter: jkoppel | Owner: (none) Type: bug | Status: new Priority: normal | Milestone: Research Component: Compiler (Type | needed checker) | Version: 8.4.3 Resolution: | Keywords: | PatternMatchWarnings Operating System: Unknown/Multiple | Architecture: Type of failure: Incorrect | Unknown/Multiple error/warning at compile-time | Test Case: Blocked By: | Blocking: Related Tickets: | Differential Rev(s): Wiki Page: | -------------------------------------+------------------------------------- Changes (by RyanGlScott): * milestone: 8.6.1 => Research needed -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/15305#comment:8 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#15305: Erroneous "non-exhaustive pattern match" using nested GADT with strictness annotation -------------------------------------+------------------------------------- Reporter: jkoppel | Owner: (none) Type: bug | Status: new Priority: normal | Milestone: Research Component: Compiler (Type | needed checker) | Version: 8.4.3 Resolution: | Keywords: | PatternMatchWarnings Operating System: Unknown/Multiple | Architecture: Type of failure: Incorrect | Unknown/Multiple error/warning at compile-time | Test Case: Blocked By: | Blocking: Related Tickets: | Differential Rev(s): Wiki Page: | -------------------------------------+------------------------------------- Comment (by simonpj): My strong instinct here is to go back to the paper. In rule [UConVar] and friends we split up 'x' into a union of constructors {K x1, K2 x2 x3, K3}. Each of those split constructors comes with an extended `Delta`, which describes the constraints in scope. If these constraints prove contradicatory we can drop the entire branch. So for a strict constructor, say {{{ data T a where MkT :: a -> !a -> T a }}} we want to add a constraint to `Delta` that says that the type of that second argument is non-void; that is, it contains at least one element that terminates. So maybe we need a constraint `NonVoid( tau )`. In the OP, `NonVoid( Bar A )` would be a contradiction. In the implementation we have a simple "term oracle" that works alongside the type oracle that is implemented by the constraint solver. Maybe we could add the new constraint to the term oracle? -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/15305#comment:9 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#15305: Erroneous "non-exhaustive pattern match" using nested GADT with strictness annotation -------------------------------------+------------------------------------- Reporter: jkoppel | Owner: (none) Type: bug | Status: new Priority: normal | Milestone: Research Component: Compiler (Type | needed checker) | Version: 8.4.3 Resolution: | Keywords: | PatternMatchWarnings Operating System: Unknown/Multiple | Architecture: Type of failure: Incorrect | Unknown/Multiple error/warning at compile-time | Test Case: Blocked By: | Blocking: Related Tickets: | Differential Rev(s): Wiki Page: | -------------------------------------+------------------------------------- Comment (by RyanGlScott): I think this would work fine for simple, non-recursive examples like the original program, but I fail to see how this would work for recursive examples like the program in comment:7. If you have the constraint `NonVoid(Abyss)`, how do you conclude that that is contradictory? Presumably, you'd need some sort of way to determine if there are any terminating inhabitants of `Abyss`. Unlike the `InL` example, where the type of its field alone (`Foo B`) is enough to conclude that it is uninhabitable, nothing about the type of `MkAbyss`'s field (`Abyss`) suggests that it is uninhabitable, so you'd have to check if its field satisfies the constraint `NonVoid(Abyss)`. But that brings us back to the infinite loop in comment:7. In short, I just don't see how this is possible to do in a way that's complete. -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/15305#comment:10 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

I fail to see how this would work for recursive examples like the
#15305: Erroneous "non-exhaustive pattern match" using nested GADT with strictness annotation -------------------------------------+------------------------------------- Reporter: jkoppel | Owner: (none) Type: bug | Status: new Priority: normal | Milestone: Research Component: Compiler (Type | needed checker) | Version: 8.4.3 Resolution: | Keywords: | PatternMatchWarnings Operating System: Unknown/Multiple | Architecture: Type of failure: Incorrect | Unknown/Multiple error/warning at compile-time | Test Case: Blocked By: | Blocking: Related Tickets: | Differential Rev(s): Wiki Page: | -------------------------------------+------------------------------------- Comment (by simonpj): program in comment:7 Correct. Computing `NonVoid( tau )` is an open-ended question. For some `tau`s (lke `Bar A`) it's easy; for others we could be more ambitious. It's probably undecidable in general. So we could start with something simple and become more ambitious later. -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/15305#comment:11 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#15305: Erroneous "non-exhaustive pattern match" using nested GADT with strictness annotation -------------------------------------+------------------------------------- Reporter: jkoppel | Owner: (none) Type: bug | Status: patch Priority: normal | Milestone: Research Component: Compiler (Type | needed checker) | Version: 8.4.3 Resolution: | Keywords: | PatternMatchWarnings Operating System: Unknown/Multiple | Architecture: Type of failure: Incorrect | Unknown/Multiple error/warning at compile-time | Test Case: Blocked By: | Blocking: Related Tickets: | Differential Rev(s): Phab:D5087 Wiki Page: | -------------------------------------+------------------------------------- Changes (by RyanGlScott): * status: new => patch * differential: => Phab:D5087 Comment: Implementing a naïve `NonVoid(ty)` solver ends up being pretty darn simple: just check if there is at least one inhabitation candidate for `ty` (as determined through `inhabitationCandidates`) such that its term and type constraints are satisfiable. That's sufficient to avoid warnings in the original program and in the program in comment:4. See Phab:D5087. -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/15305#comment:12 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#15305: Erroneous "non-exhaustive pattern match" using nested GADT with strictness
annotation
-------------------------------------+-------------------------------------
Reporter: jkoppel | Owner: (none)
Type: bug | Status: patch
Priority: normal | Milestone: Research
Component: Compiler (Type | needed
checker) | Version: 8.4.3
Resolution: | Keywords:
| PatternMatchWarnings
Operating System: Unknown/Multiple | Architecture:
Type of failure: Incorrect | Unknown/Multiple
error/warning at compile-time | Test Case:
Blocked By: | Blocking:
Related Tickets: | Differential Rev(s): Phab:D5087
Wiki Page: |
-------------------------------------+-------------------------------------
Comment (by Krzysztof Gogolewski

#15305: Erroneous "non-exhaustive pattern match" using nested GADT with strictness annotation -------------------------------------+------------------------------------- Reporter: jkoppel | Owner: (none) Type: bug | Status: closed Priority: normal | Milestone: Research Component: Compiler (Type | needed checker) | Version: 8.4.3 Resolution: fixed | Keywords: | PatternMatchWarnings Operating System: Unknown/Multiple | Architecture: Type of failure: Incorrect | Unknown/Multiple error/warning at compile-time | Test Case: Blocked By: | Blocking: Related Tickets: | Differential Rev(s): Phab:D5087 Wiki Page: | -------------------------------------+------------------------------------- Changes (by monoidal): * status: patch => closed * resolution: => fixed -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/15305#comment:14 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#15305: Erroneous "non-exhaustive pattern match" using nested GADT with strictness annotation -------------------------------------+------------------------------------- Reporter: jkoppel | Owner: (none) Type: bug | Status: closed Priority: normal | Milestone: 8.8.1 Component: Compiler (Type | Version: 8.4.3 checker) | Keywords: Resolution: fixed | PatternMatchWarnings Operating System: Unknown/Multiple | Architecture: | Unknown/Multiple Type of failure: Incorrect | Test Case: error/warning at compile-time | pmcheck/should_compile/T15305 Blocked By: | Blocking: Related Tickets: | Differential Rev(s): Phab:D5087 Wiki Page: | -------------------------------------+------------------------------------- Changes (by RyanGlScott): * testcase: => pmcheck/should_compile/T15305 * milestone: Research needed => 8.8.1 -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/15305#comment:15 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler
participants (1)
-
GHC