Opened 5 years ago
Closed 5 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: | Differential Revisions: |
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 5 years ago by conal
comment:2 Changed 5 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 5 years ago by BMeph
- Owner set to conal
comment:4 Changed 5 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 5 years ago by conal
- Owner conal deleted
comment:6 Changed 5 years ago by igloo
- Milestone set to 6.14.1
- Owner set to simonpj
comment:7 Changed 5 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 5 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 [email protected] * 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
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.