[GHC] #13929: GHC panic with levity polymorphism
GHC
ghc-devs at haskell.org
Fri Sep 15 18:09:49 UTC 2017
#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
More information about the ghc-tickets
mailing list