[GHC] #13990: Core Lint error on empty case

#13990: Core Lint error on empty case -------------------------------------+------------------------------------- Reporter: mbieleck | Owner: (none) Type: bug | Status: new Priority: normal | Milestone: Component: Compiler | Version: 8.2.1-rc3 Keywords: core-lint | Operating System: Unknown/Multiple case | Architecture: | Type of failure: Compile-time Unknown/Multiple | crash or panic Test Case: | Blocked By: Blocking: | Related Tickets: Differential Rev(s): | Wiki Page: -------------------------------------+------------------------------------- This module: {{{#!hs {-# LANGUAGE EmptyCase #-} module Bug where data Void absurd :: Void -> a absurd v = case v of {} data Foo = Foo !Void absurdFoo :: Foo -> a absurdFoo (Foo x) = absurd x }}} Compiled using `ghc-8.2.0.20170704 -O -dcore-lint Bug.hs` Gives the following error: {{{ [1 of 1] Compiling Bug ( Bug.hs, Bug.o ) *** Core Lint errors : in result of Simplifier *** <no location info>: warning: In a case alternative: (Foo x_ap6 :: Void) No alternatives for a case scrutinee in head-normal form: x_ap6 *** Offending Program *** absurd :: forall a. Void -> a [LclIdX, Arity=1, Unf=Unf{Src=<vanilla>, TopLvl=True, Value=True, ConLike=True, WorkFree=True, Expandable=True, Guidance=ALWAYS_IF(arity=1,unsat_ok=True,boring_ok=True)}] absurd = \ (@ a_apU) (v_ap5 :: Void) -> case v_ap5 of { } absurdFoo :: forall a. Foo -> a [LclIdX, Arity=1, Unf=Unf{Src=<vanilla>, TopLvl=True, Value=True, ConLike=True, WorkFree=True, Expandable=True, Guidance=ALWAYS_IF(arity=1,unsat_ok=True,boring_ok=True)}] absurdFoo = \ (@ a_apY) (ds_dUn :: Foo) -> case ds_dUn of { Foo x_ap6 -> case x_ap6 of { } } -- irrelevant stuff omitted }}} When I manually inline `absurd` or remove the strictness annotation on `Foo`, the error goes away. -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/13990 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#13990: Core Lint error on empty case -------------------------------------+------------------------------------- Reporter: mbieleck | Owner: (none) Type: bug | Status: new Priority: highest | Milestone: Component: Compiler | Version: 8.2.1-rc3 Resolution: | Keywords: core-lint | case Operating System: Unknown/Multiple | Architecture: Type of failure: Compile-time | Unknown/Multiple crash or panic | Test Case: Blocked By: | Blocking: Related Tickets: | Differential Rev(s): Wiki Page: | -------------------------------------+------------------------------------- Changes (by goldfire): * priority: normal => highest Comment: Core Lint errors in a release candidate are ''bad''. -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/13990#comment:1 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#13990: Core Lint error on empty case -------------------------------------+------------------------------------- Reporter: mbieleck | Owner: (none) Type: bug | Status: new Priority: highest | Milestone: 8.2.1 Component: Compiler | Version: 8.2.1-rc3 Resolution: | Keywords: core-lint | case Operating System: Unknown/Multiple | Architecture: Type of failure: Compile-time | Unknown/Multiple crash or panic | Test Case: Blocked By: | Blocking: Related Tickets: | Differential Rev(s): Wiki Page: | -------------------------------------+------------------------------------- Changes (by goldfire): * milestone: => 8.2.1 -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/13990#comment:2 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#13990: Core Lint error on empty case -------------------------------------+------------------------------------- Reporter: mbieleck | Owner: (none) Type: bug | Status: new Priority: highest | Milestone: 8.2.1 Component: Compiler | Version: 8.2.1-rc3 Resolution: | Keywords: core-lint | case Operating System: Unknown/Multiple | Architecture: Type of failure: Compile-time | Unknown/Multiple crash or panic | Test Case: Blocked By: | Blocking: Related Tickets: | Differential Rev(s): Wiki Page: | -------------------------------------+------------------------------------- Comment (by RyanGlScott): To be fair, this wasn't introduced in GHC 8.2.1. This error also appears in 8.0 as well (but not in 7.10). -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/13990#comment:3 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#13990: Core Lint error on empty case -------------------------------------+------------------------------------- Reporter: mbieleck | Owner: (none) Type: bug | Status: new Priority: highest | Milestone: 8.2.2 Component: Compiler | Version: 8.2.1-rc3 Resolution: | Keywords: core-lint | case Operating System: Unknown/Multiple | Architecture: Type of failure: Compile-time | Unknown/Multiple crash or panic | Test Case: Blocked By: | Blocking: Related Tickets: | Differential Rev(s): Wiki Page: | -------------------------------------+------------------------------------- Changes (by bgamari): * milestone: 8.2.1 => 8.2.2 Comment: As this isn't a regression I'm going to bump this off to 8.2.2. -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/13990#comment:4 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#13990: Core Lint error on empty case -------------------------------------+------------------------------------- Reporter: mbieleck | Owner: (none) Type: bug | Status: new Priority: highest | Milestone: 8.2.2 Component: Compiler | Version: 8.2.1-rc3 Resolution: | Keywords: core-lint | case Operating System: Unknown/Multiple | Architecture: Type of failure: Compile-time | Unknown/Multiple crash or panic | Test Case: Blocked By: | Blocking: Related Tickets: | Differential Rev(s): Wiki Page: | -------------------------------------+------------------------------------- Comment (by nomeata): I believe the mistake is in `CoreLint` here. The question is: How can `CoreLint` know that `x_ap6` is both void and evaluated, hence cannot actually exist? Probably we just need to change the code {{{ do { checkL (not (exprIsHNF scrut)) (text "No alternatives for a case scrutinee in head-normal form:" <+> ppr scrut) ; checkWarnL scrut_diverges (text "No alternatives for a case scrutinee not known to diverge for sure:" <+> ppr scrut) } }}} to something that runs the first check only when `scrut_diverges` is false (if `scrut_diverges`, then it cannot be in normal form). Or find out why `exprIsHNF scrut` is `True` here. Or maybe the outer `case` of {{{ case ds_dUn of { Foo x_ap6 -> case x_ap6 of { } } }}} should be empty, as the pattern `Foo x` is already absurd (due to the bang). Many options :-) -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/13990#comment:5 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#13990: Core Lint error on empty case -------------------------------------+------------------------------------- Reporter: mbieleck | Owner: (none) Type: bug | Status: new Priority: highest | Milestone: 8.2.2 Component: Compiler | Version: 8.2.1-rc3 Resolution: | Keywords: core-lint | case Operating System: Unknown/Multiple | Architecture: Type of failure: Compile-time | Unknown/Multiple crash or panic | Test Case: Blocked By: | Blocking: Related Tickets: | Differential Rev(s): Wiki Page: | -------------------------------------+------------------------------------- Comment (by nomeata): The cleanest solution might be: * extend `CoreUtils.filterAlts` to filter out impossible alternatives, i.e. constructors with fields with an empty type and a bang pattern. This code already filters out constructors that are impossible for other reasons. * extend `CoreLint` to not complain when a `case` is empty for this particular reason. -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/13990#comment:6 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#13990: Core Lint error on empty case -------------------------------------+------------------------------------- Reporter: mbieleck | Owner: (none) Type: bug | Status: new Priority: highest | Milestone: 8.2.2 Component: Compiler | Version: 8.2.1-rc3 Resolution: | Keywords: core-lint | case Operating System: Unknown/Multiple | Architecture: Type of failure: Compile-time | Unknown/Multiple crash or panic | Test Case: Blocked By: | Blocking: Related Tickets: | Differential Rev(s): Wiki Page: | -------------------------------------+------------------------------------- Comment (by simonpj):
Or find out why exprIsHNF scrut is True here.
It's because in a case alternative {{{ K x -> ...x... }}} where `K` is strict, we know that when we unpack `K x`, the field `x` must be evaluated. So it is marked as such, and hence responds True to `exprIsHNF`. Joachim's solition is right; but it's very much a corner case and a bit hard to get right. E.g. {{{ data T a = T1 !(F a) Int | ... }}} When we pattern match on `T1 x y :: T X`, is that pattern impossible? WEll it depends on whether `(F X)` evaluates to an empty type. Hard to be sure it is fully nailed. Better, I think, to say that {{{ case x of {} }}} is fine if `x :: T` where `T` has no data constructors. That would silence the warning and is obviously correct. -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/13990#comment:7 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#13990: Core Lint error on empty case -------------------------------------+------------------------------------- Reporter: mbieleck | Owner: (none) Type: bug | Status: new Priority: highest | Milestone: 8.2.2 Component: Compiler | Version: 8.2.1-rc3 Resolution: | Keywords: core-lint | case Operating System: Unknown/Multiple | Architecture: Type of failure: Compile-time | Unknown/Multiple crash or panic | Test Case: Blocked By: | Blocking: Related Tickets: | Differential Rev(s): Wiki Page: | -------------------------------------+------------------------------------- Comment (by mbieleck): Replying to [comment:7 simonpj]:
Better, I think, to say that {{{ case x of {} }}} is fine if `x :: T` where `T` has no data constructors. That would silence the warning and is obviously correct.
Unfortunately that wouldn't fix my original problem. I encountered Core Lint errors when using [https://github.com/cdepillabout/servant-checked- exceptions/blob/master/src/Servant/Checked/Exceptions/Internal/Union.hs#L152 Servant.Checked.Exceptions.Internal.Union.absurdUnion]. Here the type involved is a GADT which __does__ have data constructors, but none of them matches the type parameter `'[]`. For the purposes of simplifying the test case I replaced it with `Void`, since they are isomorphic, but it makes a difference when linting. Here's a test case better reflecting the situation: {{{#!hs {-# LANGUAGE EmptyCase, GADTs #-} module Bug where data T a where TInt :: T Int absurd :: T Bool -> a absurd v = case v of {} data Foo = Foo !(T Bool) absurdFoo :: Foo -> a absurdFoo (Foo x) = absurd x }}} -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/13990#comment:8 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#13990: Core Lint error on empty case -------------------------------------+------------------------------------- Reporter: mbieleck | Owner: (none) Type: bug | Status: new Priority: highest | Milestone: 8.2.2 Component: Compiler | Version: 8.2.1-rc3 Resolution: | Keywords: core-lint | case Operating System: Unknown/Multiple | Architecture: Type of failure: Compile-time | Unknown/Multiple crash or panic | Test Case: Blocked By: | Blocking: Related Tickets: | Differential Rev(s): Wiki Page: | -------------------------------------+------------------------------------- Comment (by simonpj): Hmm. Let's stand back a bit. * The simplifier (via `CoreSyn.filterAlts`) can eliminate branches of a case that can't happen, because of (a) GADT matching and (b) lexically enclosing case expressions. * Lint can't guarantee to come to the same conclusion becuase of floating. Eg {{{ data T = A | B | C f x = case x of A -> e1 _ -> ...(case x of B -> e1 C -> e3)... ===> f x = let t = case x of B -> e1 C -> e3 in case x of A -> e1 _ -> ...t... }}} Before, it's clear that the inner case is exhaustive, but not so clear after the transformation. * So currently Lint doesn't attempt to complain about inexhaustive cases. * EXCEPT if there are zero alternatives. And for that, strangely, Lint makes two separate checks. First, in the `Case` equation for `lintCoreExpr`: {{{ -- See Note [No alternatives lint check] ; when (null alts) $ do { checkL (not (exprIsHNF scrut)) (text "No alternatives for a case scrutinee in head-normal form:" <+> ppr scrut) ; checkWarnL scrut_diverges (text "No alternatives for a case scrutinee not known to diverge for sure:" <+> ppr scrut) } }}} Second in `checkCaseAlts`: {{{ ; checkL (isJust maybe_deflt || not is_infinite_ty || null alts) (nonExhaustiveAltsMsg e) } }}} Clearly we should do this stuff in only one place. I now propose that we remove the "null alts" check altogether. It's just an extreme case of having filtered out all alternatives. The current code treats it specially, but this ticket has convinced me that it's not special afte all. -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/13990#comment:9 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#13990: Core Lint error on empty case -------------------------------------+------------------------------------- Reporter: mbieleck | Owner: (none) Type: bug | Status: new Priority: highest | Milestone: 8.2.2 Component: Compiler | Version: 8.2.1-rc3 Resolution: | Keywords: core-lint | case Operating System: Unknown/Multiple | Architecture: Type of failure: Compile-time | Unknown/Multiple crash or panic | Test Case: Blocked By: | Blocking: Related Tickets: | Differential Rev(s): Wiki Page: | -------------------------------------+------------------------------------- Comment (by nomeata):
this ticket has convinced me that it's not special afte all.
I wholehartedly agree with removal of this check (The second check was an error originally, we had to downgrade it to warning because of false positives, and now even that is not good enough.) I think it is inevitable that we will have some transformations making choices that – after some other transformations – are no longer verifiable by CoreLint. (Incidentally, this also means that we have transformations in GHC that produce non-well-typed Core, if we consider “well-typed” to mean “we can prove progress and preservation”. This is not a shortcoming of GHC, but rather of the notion of well-typedness, though.) -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/13990#comment:10 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#13990: Core Lint error on empty case -------------------------------------+------------------------------------- Reporter: mbieleck | Owner: (none) Type: bug | Status: new Priority: highest | Milestone: 8.2.2 Component: Compiler | Version: 8.2.1-rc3 Resolution: | Keywords: core-lint | case Operating System: Unknown/Multiple | Architecture: Type of failure: Compile-time | Unknown/Multiple crash or panic | Test Case: Blocked By: | Blocking: Related Tickets: | Differential Rev(s): Wiki Page: | -------------------------------------+------------------------------------- Comment (by goldfire): Without reading all the commentary above: I conjecture that the only way to get this right is to come up with some datatype which serves as evidence that an empty case is acceptable. There may be several constructors of the datatype depending on why GHC believes the empty case, and we may need to do transformations to elements of this type during simplification, etc. In other words, it's all quite like what `Coercion` does for type equality. Then, Lint could check various invariants of the datatype. (Also known as typing rules.) Is this worth it? It certainly doesn't seem like it. -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/13990#comment:11 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#13990: Core Lint error on empty case -------------------------------------+------------------------------------- Reporter: mbieleck | Owner: (none) Type: bug | Status: new Priority: highest | Milestone: 8.2.2 Component: Compiler | Version: 8.2.1-rc3 Resolution: | Keywords: core-lint | case Operating System: Unknown/Multiple | Architecture: Type of failure: Compile-time | Unknown/Multiple crash or panic | Test Case: Blocked By: | Blocking: Related Tickets: | Differential Rev(s): Wiki Page: | -------------------------------------+------------------------------------- Comment (by nomeata): A `Coercion` exhibits a property of a type (equal to another type). What you want is a witness of a property of a *value* – quite similar to the “value coercions” that we phantasized a few months ago. -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/13990#comment:12 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#13990: Core Lint error on empty case -------------------------------------+------------------------------------- Reporter: mbieleck | Owner: (none) Type: bug | Status: new Priority: highest | Milestone: 8.2.3 Component: Compiler | Version: 8.2.1-rc3 Resolution: | Keywords: core-lint | case Operating System: Unknown/Multiple | Architecture: Type of failure: Compile-time | Unknown/Multiple crash or panic | Test Case: Blocked By: | Blocking: Related Tickets: | Differential Rev(s): Wiki Page: | -------------------------------------+------------------------------------- Changes (by bgamari): * milestone: 8.2.2 => 8.2.3 Comment: It doesn't seem like this will happen for 8.2.2. -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/13990#comment:13 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#13990: Core Lint error on empty case -------------------------------------+------------------------------------- Reporter: mbieleck | Owner: (none) Type: bug | Status: patch Priority: highest | Milestone: 8.2.3 Component: Compiler | Version: 8.2.1-rc3 Resolution: | Keywords: core-lint | case Operating System: Unknown/Multiple | Architecture: Type of failure: Compile-time | Unknown/Multiple crash or panic | Test Case: Blocked By: | Blocking: Related Tickets: | Differential Rev(s): Phab:D4160 Wiki Page: | -------------------------------------+------------------------------------- Changes (by dfeuer): * status: new => patch * differential: => Phab:D4160 Comment: I put up a differential to just strip the tests out. -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/13990#comment:14 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#13990: Core Lint error on empty case
-------------------------------------+-------------------------------------
Reporter: mbieleck | Owner: (none)
Type: bug | Status: patch
Priority: highest | Milestone: 8.2.3
Component: Compiler | Version: 8.2.1-rc3
Resolution: | Keywords: core-lint
| case
Operating System: Unknown/Multiple | Architecture:
Type of failure: Compile-time | Unknown/Multiple
crash or panic | Test Case:
Blocked By: | Blocking:
Related Tickets: | Differential Rev(s): Phab:D4160
Wiki Page: |
-------------------------------------+-------------------------------------
Comment (by dfeuer):
Simon asked me to note here that his remark in comment:9 about
`checkCaseAlts` was a bit off. The `null alts` there is doing something a
bit different: allowing case matches with no alternatives on "nearly-
infinite" types like `Int#`. Pulling that test out actually makes the
build fail. Why? That's not actually entirely clear to me. Somewhere in
`Text` internals, we get a Core Lint error
{{{
5258 <no location info>: warning:
5259 In a case alternative: (I# n#_a4mN :: Int#)
5260 Case expression with non-exhaustive alternatives
5261 case lvl_sfty n#_a4mN of wild_00 { }
}}}
This is slightly surprising, because the relevant `lvl_sfty` seems to be
{{{
29713 lvl_sfty :: Int# -> Int#
29714 [LclId,
29715 Arity=1,
29716 Str=

#13990: Core Lint error on empty case -------------------------------------+------------------------------------- Reporter: mbieleck | Owner: (none) Type: bug | Status: patch Priority: highest | Milestone: 8.2.3 Component: Compiler | Version: 8.2.1-rc3 Resolution: | Keywords: core-lint | case Operating System: Unknown/Multiple | Architecture: Type of failure: Compile-time | Unknown/Multiple crash or panic | Test Case: Blocked By: | Blocking: Related Tickets: | Differential Rev(s): Phab:D4160 Wiki Page: | -------------------------------------+------------------------------------- Comment (by simonpj):
But should we leave the scrut_diverges warning in place?
You are referring to this code in the `Case` equation for `lintCoreExpr`: {{{ -- See Note [No alternatives lint check] ; when (null alts) $ do { checkL (not (exprIsHNF scrut)) (text "No alternatives for a case scrutinee in head-normal form:" <+> ppr scrut) ; checkWarnL scrut_diverges (text "No alternatives for a case scrutinee not known to diverge for sure:" <+> ppr scrut) } }}} Well, suppose `x :: T Bool`, where `T` is in comment:8. Then if I say something like {{{ case x of {} }}} then `x` is not sure to diverge but the case branches are legitimately empty. Maybe you can add an example like this in a `Note` explaining why there is no null-alts check here? -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/13990#comment:16 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#13990: Core Lint error on empty case
-------------------------------------+-------------------------------------
Reporter: mbieleck | Owner: (none)
Type: bug | Status: patch
Priority: highest | Milestone: 8.2.3
Component: Compiler | Version: 8.2.1-rc3
Resolution: | Keywords: core-lint
| case
Operating System: Unknown/Multiple | Architecture:
Type of failure: Compile-time | Unknown/Multiple
crash or panic | Test Case:
Blocked By: | Blocking:
Related Tickets: | Differential Rev(s): Phab:D4160
Wiki Page: |
-------------------------------------+-------------------------------------
Comment (by Ben Gamari

#13990: Core Lint error on empty case -------------------------------------+------------------------------------- Reporter: mbieleck | Owner: (none) Type: bug | Status: closed Priority: highest | Milestone: 8.2.3 Component: Compiler | Version: 8.2.1-rc3 Resolution: fixed | Keywords: core-lint | case Operating System: Unknown/Multiple | Architecture: Type of failure: Compile-time | Unknown/Multiple crash or panic | Test Case: Blocked By: | Blocking: Related Tickets: | Differential Rev(s): Phab:D4160 Wiki Page: | -------------------------------------+------------------------------------- Changes (by bgamari): * status: patch => closed * resolution: => fixed -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/13990#comment:18 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler
participants (1)
-
GHC