Opened 6 years ago

Closed 21 months ago

#5316 closed bug (duplicate)

Orphan instances strike again: ghc rejects a program at first but will accept it if you repeat the same compilation command

Reported by: jcpetruzza Owned by:
Priority: low Milestone:
Component: Compiler Version: 7.0.4
Keywords: Cc: conal@…
Operating System: Unknown/Multiple Architecture: Unknown/Multiple
Type of failure: GHC rejects valid program Test Case:
Blocked By: #2182 Blocking:
Related Tickets: Differential Rev(s):
Wiki Page:


Consider these two modules (boiled down example from the checkers package):

{-# LANGUAGE ScopedTypeVariables, FlexibleContexts #-}

module T1


import Test.QuickCheck
import Text.Show.Functions ()

f :: forall m a b.  ( Arbitrary (a->b) ) => m (a,b) -> Property
f = const $ property (undefined :: (a->b) -> Bool)
module T2  where

import Control.Concurrent

g = threadDelay maxBound

I see the following interaction:

$ rm *hi *.o
$ ghc --make -c -O  T1 T2
[1 of 2] Compiling T2               ( T2.hs, T2.o )
[2 of 2] Compiling T1               ( T1.hs, T1.o )

    Overlapping instances for Show (a -> b)
      arising from a use of `property'
    Matching instances:
      instance Show (a -> b) -- Defined in Text.Show.Functions
      instance Show base:System.Event.Manager.IOCallback
        -- Defined in base:System.Event.Manager
    (The choice depends on the instantiation of `a, b'
     To pick the first instance above, use -XIncoherentInstances
     when compiling the other instance declarations)
    In the second argument of `($)', namely
      `property (undefined :: (a -> b) -> Bool)'
    In the expression: const $ property (undefined :: (a -> b) -> Bool)
    In an equation for `f':
        f = const $ property (undefined :: (a -> b) -> Bool)
$ ghc --make -c -O  T1 T2
[2 of 2] Compiling T1               ( T1.hs, T1.o )
$ ghc --make -c -O  T1 T2
$ ls
T1.hi T1.hs T1.o  T2.hi T2.hs T2.o

I see this consistent behaviour in versions 7.0.{1,2,3,4} but not with 6.12.1

Change History (5)

comment:1 Changed 6 years ago by igloo

Resolution: duplicate
Status: newclosed

Thanks for the report.

The overlapping instance in System.Event.Manager has been removed in the HEAD and 7.2, so this particular conflict won't arise again.

The poor handling of instances in different modules is a known problem: see #2356.

comment:2 Changed 6 years ago by jcpetruzza

Resolution: duplicate
Status: closednew

My apologies for reopening the ticket, but after going through #2356 I can't see the duplication. What I am reporting here is that:

1) GHC will reject these modules only if compiled with optimizations on, as witnessed by:

$ rm *hi *o
$ ghc --make -c  T1 T2
[1 of 2] Compiling T2               ( T2.hs, T2.o )
[2 of 2] Compiling T1               ( T1.hs, T1.o )

2) GHC behaves differently if part of the program was already compiled.

Moreover, #2356 is about a design decision that makes it accept more programs than the Report mandates, not less.

Since as of #2356 GHC accepts duplicated instances as long as they don't conflict, I suspect that the real bug here is that the instance defined in System.Event.Manager shouldn't be in scope at all when compiling T1 (the first time).

Am I missing something?

comment:3 Changed 6 years ago by conal

Cc: conal@… added

comment:4 Changed 6 years ago by simonpj

Milestone: _|_
Priority: normallow
Summary: ghc rejects a program at first but will accept it if one insistsOrphan instances strike again: ghc rejects a program at first but will accept it if you repeat the same compilation command

I believe I know what is happening here. Background:

  • GHC maintains a session-global "database" of instances from imported packages
  • This database only grows; when GHC needs to use functions from a module, it loads up that module and adds its instances to the database.
  • When looking up an instance, GHC looks up in this database (and in a similar data base for the package being compiled, but that's not an issue in this example).

In this case:

  • When you first say ghc --make T1 T2, GHC has to compile both modules.
  • T1 depends on Text.Show.Functions, which has a Show instance for (->)
  • T2 depends on System.Event.Manager, which also has a Show instance for (->).
  • So when it comes to T2, the second module is loaded, and the overlap is reported.
  • Without optimisation, System.Event.Manager isn't loaded at all, so its instances don't conflict.
  • When recompiling GHC starts with an empty database and, since T1 hasn't changed, it doesn't recompile it. So the database isn't as populated as before, and there's no overlap.

It's not very cool, but that's why.

Orphan instaces are a major pain. It's hard to do the Right Thing without being very inefficient, and I'm reluctant to penalise all compilations. It's a kind of bizarre case, and I'm not sure it's worth a lot of effort. So I'll make it low priority -- but if someone is finding it painful, please yell.

comment:5 Changed 21 months ago by ezyang

Blocked By: 2182 added
Resolution: duplicate
Status: newclosed

I was reviewing orphan instance tickets, and I think this one was fixed by #2182; Simon's analysis is essentially the same. I can't actually test it on the test-case given because System.Event.Manager has changed since then.

The one thing that you might find puzzling is the situation with optimization: but actually it is simple to explain: optimization simply means we might tug on more ModIfaces because we have to process unfoldings, etc. With the fix for #2182, the orphan instances that are pulled in from this process are unaffected.

Note: See TracTickets for help on using tickets.