Opened 4 years ago

Closed 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 Difficulty:
Test Case: deriving/should_compile/T4220 Blocked By:
Blocking: Related Tickets:

Description

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):
            TcPat.checkArgs

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 (8)

comment:1 Changed 4 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 4 years ago by BMeph

  • Cc black.meph@… added
  • Version changed from 6.12.1 to 6.12.3

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

comment:3 Changed 4 years ago by BMeph

  • Owner set to conal

comment:4 Changed 4 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?

Simon

comment:5 Changed 4 years ago by conal

  • Owner conal deleted

comment:6 Changed 4 years ago by igloo

  • Milestone set to 6.14.1
  • Owner set to simonpj

comment:7 Changed 4 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 4 years ago by simonpj

  • Resolution set to fixed
  • Status changed from new to closed
  • Test Case set to deriving/should_compile/T4220

In the end I did the simplest thing. Fixed by

Thu Aug 12 14:13:19 BST 2010  simonpj@microsoft.com
  * 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.

Simon

Note: See TracTickets for help on using tickets.