[GHC] #16322: "deriving newtype instance" generates an infinite loop

GHC ghc-devs at haskell.org
Fri Feb 15 19:39:59 UTC 2019


#16322: "deriving newtype instance" generates an infinite loop
-------------------------------------+-------------------------------------
        Reporter:  paf31             |                Owner:  (none)
            Type:  bug               |               Status:  infoneeded
        Priority:  normal            |            Milestone:
       Component:  Compiler          |              Version:  8.6.3
      Resolution:                    |             Keywords:
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 RyanGlScott):

 Ah, I misinterpreted your comment—I thought that you were implying that
 the code was infinitely looping at compile-time, not runtime. My
 apologies.

 The code that `deriving newtype instance C a b => C (X String a) (X String
 b)` generates is expected behavior, as it turns out. From the
 [https://downloads.haskell.org/~ghc/8.6.3/docs/html/users_guide/glasgow_exts.html
 #extension-StandaloneDeriving users' guide section] on
 `StandaloneDeriving`:

 > The stand-alone syntax is generalised for newtypes in exactly the same
 way that ordinary deriving clauses are generalised [...]. For example:
 >
 > {{{#!hs
 > newtype Foo a = MkFoo (State Int a)
 >
 > deriving instance MonadState Int Foo
 > }}}
 >
 > GHC always treats the //last// parameter of the instance (Foo in this
 example) as the type whose instance is being derived.

 In other words, the generated code will `coerce` underneath the last type
 argument, and nothing more. In your example, you have:

 {{{#!hs
 class C a b | a -> b, b -> a where
   c :: Proxy a -> Int

 newtype X a b = X b
 deriving newtype instance C a b => C (X String a) (X String b)
 }}}

 This will `coerce` from `X String b` to `b`, and nothing more. Because the
 type of `c` happens to never mention the last type parameter of `C`, this
 results in the "identity coercion" behavior you see with `-ddump-deriv`.

 It's a bit strange, but there's a certain consistency to it. After all,
 `deriving newtype instance C a b => C (X String a) (X String b)` could
 mean three different things:

 1. Coerce underneath `X String a` only.
 2. Coerce underneath `X String b` only.
 3. Coerce underneath `X String a` and `X String b`.

 In general, if a multi-parameter type class has //n// type parameters,
 then there are 2^//n//^ - 1 different potential choices of code to
 generate. Since `deriving` clauses only `coerce` underneath the last type
 parameter, `StandaloneDeriving` picks the same convention.

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


More information about the ghc-tickets mailing list