Opened 9 years ago

Closed 9 years ago

Last modified 4 years ago

#4220 closed bug (fixed)

EmptyDataDecls + DeriveFunctor == Panic!

Reported by: conal Owned by: simonpj
Priority: normal Milestone: 7.0.1
Component: Compiler Version: 6.12.3
Keywords: Cc: conal@…, black.meph@…, jules@…
Operating System: Unknown/Multiple Architecture: Unknown/Multiple
Type of failure: Compile-time crash Test Case: deriving/should_compile/T4220
Blocked By: Blocking:
Related Tickets: #4302 Differential Rev(s):
Wiki Page:


I get "ghc: panic! (the 'impossible' happened)" from GHC 6.12.1 with a simple two-line program

    {-# LANGUAGE EmptyDataDecls, DeriveFunctor #-}
    data Void a deriving Functor

Also on hpaste.

GHC says

    ghc: panic! (the 'impossible' happened)
      (GHC version 6.12.1 for i386-apple-darwin):

I asked on the #ghc IRC room and learned that

ClaudiusMaximus: conal: ghc-6.12.3 here, same error. (GHC version 6.12.3 for x86_64-unknown-linux)

Change History (9)

comment:1 Changed 9 years ago by conal

The following explicit instance works fine, though it's too bad we have to make the error case explicit, since Void can be covered exhaustively with no clauses.

instance Functor Void where
  fmap _ = error "Void fmap: no void value"  -- so ghc won't complain

comment:2 Changed 9 years ago by BMeph

Cc: black.meph@… added

If you check the referenced hpaste, you'll see that it also occurs on Windows builds.

comment:3 Changed 9 years ago by BMeph

Owner: set to conal

comment:4 Changed 9 years ago by simonpj

Ah yes. I have a fix in my tree. Conal, did becoming the "owner" mean that you intended to submit a patch?

My fix generates a catch-all equation for fmap

fmap _ a = unsafeCoerce# a

in two cases:

  • When there are no constructors
  • When there are any constructors that don't mention the "active" type variable at all

The latter case is to catch a situation like this

  data T a b = T1 b | T2 a | T3 a | T4 a | T5

The "active" type variable is the last one in type, "b" in this case. Here it seems a waste to pattern-match on T2-T5 (as the current 6.12 does).

Better to say

fmap f (T1 x) = T1 (f x)
fmap _ a      = unsafeCoerce# a

The coerce is necessary to change the type, of course.

Does that seem right?

There must be something similar for Foldable and Traversable, not just Functor, and we'd better fix them at the same time:

  • I believe the criterion is the same: that is, any data constructors whose argument types do not mention the active type variable can be treated uniformly with a no-op.
  • I'm not sure what the code for a no-op for Foldable and Traversable are. Can someone say, please?


comment:5 Changed 9 years ago by conal

Owner: conal deleted

comment:6 Changed 9 years ago by igloo

Milestone: 6.14.1
Owner: set to simonpj

comment:7 Changed 9 years ago by JulesBean

Cc: jules@… added

For Foldable it suffices to define foldMap :

foldMap :: Monoid m => (a -> m) -> t a -> m

If you "don't have" anything of type a either because it's an EmptyDataDecl or because it's a constructor which doesn't mention the type variable, then the only possible result is mempty.

foldMap _ _ = mempty

That suffices to define an instance, but for efficiency(?) you could add the other methods directly.

fold :: Monoid m => t m -> m
fold _ = mempty

foldMap :: Monoid m => (a -> m) -> t a -> m
foldMap _ _ = mempty

foldr :: (a -> b -> b) -> b -> t a -> b
foldr _ start _ = start

foldl :: (a -> b -> a) -> a -> t b -> a
foldl _ start _ = start

foldr1 :: (a -> a -> a) -> t a -> a
foldr1 _ _ = error "Foldable.foldr1"

foldl1 :: (a -> a -> a) -> t a -> a
foldl1 _ _ = error "Foldable.foldl1"

For Traversable it suffices to define traverse:

traverse :: Applicative f => (a -> f b) -> t a -> f (t b)

Similar reasoning - if you don't have any a then you can't apply the function given, and there can be no applicative effects to consider because no value f b can be constructed. So, rather like in the functor case, you do a constructor by constructor conversion, wrapped in pure for no effects.

traverse _ (T2 a) = pure (T2 a) -- n.b. these two (T2 a) expressions are at a different type

By the same process as for Functor you might choose to go for the under-the-hood more efficient pure . unsafeCoerce#, or alternatively you can "delegate" the efficiency hack to the Functor instance and use pure . fmap undefined.

Does that all sound reasonable?

comment:8 Changed 9 years ago by simonpj

Resolution: fixed
Status: newclosed
Test Case: deriving/should_compile/T4220

In the end I did the simplest thing. Fixed by

Thu Aug 12 14:13:19 BST 2010
  * Fix Trac #4220
  For deriving Functor, Foldable, Traversable with empty 
  data cons I just generate a null equation
     f _ = error "urk"
  There are probably more lurking (eg Enum) but this will do for now.


comment:9 Changed 4 years ago by thomie

commit 6efa3901fd6f1583fb654bd3659e88702dfd579a

Author: <unknown>
Date:   Thu Aug 12 13:13:19 2010 +0000

    Fix Trac #4220
    For deriving Functor, Foldable, Traversable with empty
    data cons I just generate a null equation
       f _ = error "urk"
    There are probably more lurking (eg Enum) but this will do for now.
Note: See TracTickets for help on using tickets.