
#9404: tcInferRho muddies the waters -------------------------------------+------------------------------------- Reporter: goldfire | Owner: goldfire Type: bug | Status: new Priority: normal | Milestone: Component: Compiler | Version: 7.9 Keywords: | Operating System: Architecture: Unknown/Multiple | Unknown/Multiple Difficulty: Unknown | Type of failure: Blocked By: | None/Unknown Related Tickets: | Test Case: | Blocking: | Differential Revisions: -------------------------------------+------------------------------------- In trying to understand the algorithm implemented in !TcExpr, I've spent some time examining `tcInferRho`. I conjecture that this function is superfluous and should be removed, in favor of `tcInfer . tcExpr`. Some of these thoughts were first written up in [http://www.haskell.org/pipermail /ghc-devs/2014-July/005733.html this thread]. After a considerable amount of staring, I've actually found a misbehavior caused by the current implementation. `tcInferRho` calls `tcInfExpr`, which has only 3 special cases before calling `tcInfer . tcExpr`: for variables, parentheses, and application. I was first drawn into this problem because these three cases seem woefully insufficient for the problem at hand. I was surprised how such a paucity of cases could have survived without causing havoc, if indeed `tcInferRho` were necessary at all. For example, only normal (prefix) application is handled; infix application is missing. In looking at differences between `tcInferApp` (which is called from the application case of `tcInfExpr`) and `tcApp` (called from `tcExpr`), I saw that the former doesn't have a special case for `seq` while the latter does. And, indeed, this difference is exploitable. This compiles fine: {{{ foo _ = case () `seq` (# #) of (# #) -> () }}} This does not: {{{ foo _ = case seq () (# #) of (# #) -> () }}} which produces {{{ Scratch.hs:15:21: Kind incompatibility when matching types: b0 :: * (# #) :: # In the second argument of ‘seq’, namely ‘(##)’ In the expression: seq () (##) }}} Looking at the code, this behavior is expected, because the first version of the code calls into `tcInfer . tcExpr`, which special-cases `seq`, while the second is caught by `tcInferApp`, which doesn't have the special case. The above example shows that this isn't just a theoretical concern about code cleanup! This all leads to another question: Why special-case `seq` at all? !MkId tells me that {{{ seq :: forall (a :: *) (b :: *). a -> b -> b }}} Why isn't it {{{ seq :: forall (a :: *) (b :: OpenKind). a -> b -> b }}} With the second type, I would imagine that `seq` wouldn't need a special case in the type-checker. Is there something wrong with the second type for `seq`? I'll post more thoughts to this ticket as I continue to explore. -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/9404 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler