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

GHC ghc-devs at haskell.org
Fri Feb 15 20:54:40 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):

 Step 2 isn't quite accurate. In general, saying "GHC reduces to looking
 for an instance of ..." is a decent intuition for how
 `GeneralizedNewtypeDeriving` works, but it breaks down in the particular
 case of `C`. Given the standalone `deriving` declaration you've written,
 GHC will first generate this code:

 {{{#!hs
 instance C a b => C (X String a) (X String b) where
   c :: Proxy (X String a) -> Int
   c = coerce @(Proxy (X String a) -> Int) @(Proxy (X String a) -> Int) c
 }}}

 At this point, GHC will typecheck this code. GHC has no issues with this
 code—the only class constraint that needs to be satisfies in order to
 typecheck is `C (X String a) (X String b)`, but since that's exactly the
 instance we're defining, this works. Moreover, that's why `c` loops at
 runtime, since we're recursively invoking `c` from the same instance
 without end. (Note that the `C a b =>` constraint is never really used at
 runtime—the only purpose it serves is to satisfy the functional dependency
 coverage condition.)

 All of this weirdness is ultimately due to the fact that the type of `c`
 never mentions `b` anywhere. If `c`'s type were `Proxy b -> Int`, then the
 generated code would instead be:

 {{{#!hs
 instance C a b => C (X String a) (X String b) where
   c :: Proxy (X String b) -> Int
   c = coerce @(Proxy b -> Int) @(Proxy (X String b) -> Int) c
 }}}

 In order to typecheck this, GHC would need actually need to satisfy a `C a
 b` constraint. In that scenario, it would be fair to summarize the whole
 thing as "reducing to looking for an instance of `C a b`". But in the
 program you've presented, you have an atypical corner case where the
 method's type does not mention the last type parameter of the class, so
 the usual intuition doesn't apply.

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


More information about the ghc-tickets mailing list