
#13929: GHC panic with levity polymorphism -------------------------------------+------------------------------------- Reporter: vagarenko | Owner: (none) Type: bug | Status: new Priority: high | Milestone: 8.2.2 Component: Compiler | Version: 8.2.1-rc2 Resolution: | Keywords: TypeInType, | LevityPolymorphism Operating System: Unknown/Multiple | Architecture: | Unknown/Multiple Type of failure: None/Unknown | Test Case: Blocked By: | Blocking: Related Tickets: | Differential Rev(s): Wiki Page: | -------------------------------------+------------------------------------- Comment (by bgamari): The problem is the result type of `gunbox x`. Namely, we have {{{#!hs class GUnbox (f :: Type -> Type) (r :: RuntimeRep) where type GUnboxed f r :: TYPE r gunbox :: f p -> GUnboxed f r }}} and {{{#!hs f :: Type -> Type p :: Type x :: f p }}} therefore {{{#!hs gunbox x :: GUnboxed f rf }}} Which is levity polymorphic. My understanding is that this wouldn't be the problem if this were at the head of the RHS; however, in this case we need to build an unboxed tuple from it, which is problematic. In short: returning levity polymorphic things generally isn't a problem. However, the moment we actually need to manipulate such a value we have a problem. I find it helpful to consider why this is from the standpoint of a concrete operational model. You can think of the `RuntimeRep` of a function's return type as being a description of which machine register(s) the result can be found in. In the case that we have a function like, {{{#!hs ($) :: forall (r :: RuntimeRep) (a :: TYPE r) (b :: Type). (b -> a) -> b -> a f $ x = f x }}} The `($)` function never needs to //do// anything with the levity- polymorphic value: it simply sets up the call to `f` and jumps. In fact, flow of control will never even return to `($)` after this jump. Consequently, the code that we generate for `($)` can be entirely agnostic to the `RuntimeRep` that it is used at. Now let's consider at your function, {{{#!hs gunbox :: (GUnbox f rf, GUnbox g rg) => (f :*: g) -> (# GUnboxed f rf, GUnboxed g rg #) gunbox (x :*: y) = (# gunbox x, gunbox y #) }}} Specifically, let's consider the case where `f ~ Double` and `g ~ Maybe Int` (with instances such that `rf ~ DoubleRep` and `rg ~ LiftedPtrRep`). In this case `gunbox` will need to first evaluate `gunbox x`, which will save its result in one of the machine's floating point registers. Then it will need to evaluate `gunbox y`, which will save its result in a pointer register. We can then return, having "constructed" our unboxed tuple. This case was easy as there was no "overlap" between the two registers used to return the result. However, let's consider another case, where `f ~ Double` and `g ~ Double` (therefore `rf ~ DoubleRep` and `rg ~ DoubleRep`). In this case `gunbox` will need to evaluate both `gunbox x` and `gunbox y`, but it must take care to move the result of one out of the way before evaluating the other since both will return their result in the same floating point register. This, of course, means that `gunbox` would need to behave differently depending upon the `RuntimeRep`s that it was working with. Our code generation strategy does not currently allow for this. -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/13929#comment:14 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler