[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