Skip to content

Improve error message: Reduction stack overflow when using "coerce"

EDIT: Executive summary: The error messages below are confusing and not perspicuous to users. We should fix. ticket:15928#comment:163956 has a concrete suggestion to use as a starting point, and ticket:15928#comment:163991 suggests we print out the role signature of any tycons involved.

Compiling the following snippet results in a "Reduction stack overflow" error message:

{-# Language ScopedTypeVariables #-}
{-# Language RankNTypes #-}

import Data.Functor.Identity
import Data.Coerce

newtype Stream m a =
    Stream {
        unStream :: forall r. (Stream m a -> m r) -> m r
    }

newtype SerialT m a = SerialT (Stream m a)

g :: SerialT Identity a -> Identity Bool
g m = undefined

idSerial :: SerialT Identity a -> SerialT Identity a
idSerial = id

f :: SerialT Identity a -> Identity Bool
f = g . idSerial . coerce

main = undefined

The following error message is produced on compiling this with ghc-8.6.2:

xy.hs:26:20: error:
    • Reduction stack overflow; size = 201
      When simplifying the following type:
        Coercible
          ((Stream Identity a -> Identity r) -> Identity r)
          ((Stream Identity a0 -> Identity r) -> Identity r)
      Use -freduction-depth=0 to disable this check
      (any upper bound you could choose might fail unpredictably with
       minor updates to GHC, so disabling the check is recommended if
       you're sure that type checking should terminate)
    • In the second argument of ‘(.)’, namely ‘coerce’
      In the second argument of ‘(.)’, namely ‘idSerial . coerce’
      In the expression: g . idSerial . coerce
   |
26 | f = g . idSerial . coerce
   |                    ^^^^^^

When I use an inline signature like this:

f :: SerialT Identity a -> Identity Bool
f = g . (id :: SerialT Identity a -> SerialT Identity a) . coerce

main = undefined

It again results in the same error:

xy.hs:18:60: error:
    • Reduction stack overflow; size = 201
      When simplifying the following type:
        Coercible
          ((Stream Identity a -> Identity r) -> Identity r)
          ((Stream Identity a0 -> Identity r) -> Identity r)
      Use -freduction-depth=0 to disable this check
      (any upper bound you could choose might fail unpredictably with
       minor updates to GHC, so disabling the check is recommended if
       you're sure that type checking should terminate)
    • In the second argument of ‘(.)’, namely ‘coerce’
      In the second argument of ‘(.)’, namely
        ‘(id :: SerialT Identity a -> SerialT Identity a) . coerce’
      In the expression:
        g . (id :: SerialT Identity a -> SerialT Identity a) . coerce
   |
18 | f = g . (id :: SerialT Identity a -> SerialT Identity a) . coerce
   |                                                            ^^^^^^

Everything works fine is I use an inline signature with a forall keyword like this:

f :: forall a. SerialT Identity a -> Identity Bool
f = g . (id :: SerialT Identity a -> SerialT Identity a) . coerce

I have following questions:

  1. Why the first version results in a panic? Is that a bug?
  2. The second version might possibly be incorrect code because the types do not unify, but still it should not result in a panic, because of the panic I could not figure out what the problem is. It took a long time to isolate the code and then do some trial and error on it.
Edited by Richard Eisenberg
To upload designs, you'll need to enable LFS and have an admin enable hashed storage. More information