[GHC] #11518: Test TcCoercibleFail hangs with substitution sanity checks enabled

GHC ghc-devs at haskell.org
Wed Feb 3 21:25:09 UTC 2016


#11518: Test TcCoercibleFail hangs with substitution sanity checks enabled
-------------------------------------+-------------------------------------
        Reporter:  niteria           |                Owner:  niteria
            Type:  bug               |               Status:  new
        Priority:  normal            |            Milestone:
       Component:  Compiler (Type    |              Version:  8.1
  checker)                           |
      Resolution:                    |             Keywords:
Operating System:  Unknown/Multiple  |         Architecture:
 Type of failure:  Compile-time      |  Unknown/Multiple
  performance bug                    |            Test Case:
      Blocked By:                    |             Blocking:
 Related Tickets:                    |  Differential Rev(s):
       Wiki Page:                    |
-------------------------------------+-------------------------------------

Comment (by niteria):

 No, I don't think the example from comment:14 is related.

 We have
 {{{
 newtype VoidBad a = VoidBad (VoidBad (a,a))
 foo5' = coerce :: (VoidBad ()) -> ()
 }}}

 For `coerce :: (VoidBad ()) -> ()` to typecheck we have to know if
 `VoidBad ()` and `()` have the same runtime representation.
 So we need to find out what `VoidBad ()` really is. So we start unwrapping
 the newtypes of off it.

 What do we get when we try to unwrap the newtype off of `VoidBad ()`?

 {{{
 VoidBad () = VoidBad ((),())
 = VoidBad (((),()),((),()))
 = VoidBad ((((),()),((),())),(((),()),((),())))
 = ...
 }}}

 It's clear that the type we apply to `VoidBad` grows exponentially and
 that `VoidBad ()` diverges. Before adding the ASSERT this is detected by
 `checkRecTc`. The reason that `checkRecTc` manages to do that in the
 presence of an exponentially growing argument to `VoidBad` is that
 `checkRecTc` only counts how many times we've applied `VoidBad`. It never
 looks at the exponentially growing argument.

 The reason why we are able to build an exponentially growing arguments
 with small amount of space and time has to do with sharing. When we apply
 `VoidBad a = VoidBad (a, a)` it is the *same* `a` that goes to the left
 and right component of the pair. The `a` is shared between both
 components.

 The way we apply `VoidBad a = VoidBad (a, a)` is by using the `substTy`
 function. We replace every `a` from the right hand side with `a` from the
 left hand side. But in this case we use the *same* `a`, so the size of the
 type grows exponentially, but the representation doesn't.

 The problem arises when `substTy` examines the argument we passed as `a`
 to look for the free vars to do the ASSERT. Since the type grows
 exponentially, the time to compute the free vars also grows exponentially
 and we don't get to the 100th iteration of unwrapping in any reasonable
 amount of time.

--
Ticket URL: <http://ghc.haskell.org/trac/ghc/ticket/11518#comment:15>
GHC <http://www.haskell.org/ghc/>
The Glasgow Haskell Compiler


More information about the ghc-tickets mailing list