Opened 3 years ago

Closed 3 years ago

#12423 closed bug (fixed)

Panic with DeriveAnyClass

Reported by: knrafto Owned by:
Priority: normal Milestone: 8.2.1
Component: Compiler Version: 8.0.1
Keywords: Generics Cc:
Operating System: Unknown/Multiple Architecture: Unknown/Multiple
Type of failure: Compile-time crash Test Case: tests/deriving/should_compile/T12423
Blocked By: Blocking:
Related Tickets: #12144 Differential Rev(s): Phab:D2961
Wiki Page:

Description

With the code

{-# LANGUAGE DefaultSignatures, DeriveAnyClass #-}
class Eq1 f where
  (==#) :: Eq a => f a -> f a -> Bool
  default (==#) :: Eq (f a) => f a -> f a -> Bool
  (==#) = (==)

data Foo a = Foo (Either a a)
    deriving (Eq, Eq1)

GHC 8.0.1 and 7.10.3 both panic:

ghc: panic! (the 'impossible' happened)
  (GHC version 8.0.1 for x86_64-apple-darwin):
	in other argument

Please report this as a GHC bug:  http://www.haskell.org/ghc/reportabug

Change History (9)

comment:1 Changed 3 years ago by dfeuer

DefaultSignatures isn't actually relevant here; DeriveAnyClass seems to be solely responsible. I get the same error using

{-# LANGUAGE DeriveAnyClass #-}

class Eq1 f where
  (==#) :: Eq a => f a -> f a -> Bool

data Foo a = Foo (Either a a) deriving (Eq, Eq1)

Most surprisingly, to me, these examples compile successfully if Foo is modified just a drop. Both of the following work:

data Foo2 a = Foo2 a deriving (Eq, Eq1)
data Foo3 a b = Foo3 (Either a b) deriving (Eq, Eq1)

(the latter needing the obvious Eq a => Eq1 (Either a) instance.)

comment:2 Changed 3 years ago by dfeuer

Summary: Panic with DeriveAnyClass and DefaultSignaturesPanic with DeriveAnyClass

comment:3 Changed 3 years ago by RyanGlScott

Keywords: GenericDeriving added

*sigh* I know what's happening here. The algorithm that GHC uses to infer constraints for * -> *-kinded things is co-opted from the DeriveFunctor algorithm, which doesn't know how to deal with argument types like Either a a (since it expects that last type parameter a to only ever be used as the very last type in a type application). If you tried to derive a Functor instance for data Foo a = Foo (Either a a), it'd give a proper error, since GHC has checks specifically for derived Functor instances.

On the other hand, there aren't any checks like that for things that are derived in general with DeriveAnyClass, so it falls through to some code which throws a GHC panic. Urk.

There are two ways we can proceed here. One thing we can do is to put these same checks for Functor in place for all DeriveAnyClass-derived typeclasses. That would eliminate the GHC panic, but it would simply replace one error message with another.

Another approach, which Simon suggests in this ghc-devs thread, is to instead adopt a different strategy for inferring constraints for DeriveAnyClass-derived things. Instead of using the DeriveFunctor algorithm, we'd collect all of the constraints given by the default signatures of the typeclass, and then simplify as needed.

It should be noted that even with the second approach, you still wouldn't be able to derive an Eq1 instance for data Foo a = Foo (Either a a), since you can't eta-reduce the last type variable. But you would be allowed to derive more things with DeriveAnyClass than you could before. A similar problem popped up in #12144, except that uses the last type variable in a contravariant position (another thing which the DeriveFunctor algorithm chokes on).

Even though this ticket is technically a duplicate of #12144, I'll keep this one open, since it's of a somewhat different nature and provides another nice test case.

comment:4 Changed 3 years ago by RyanGlScott

Keywords: Generics added; GenericDeriving removed

comment:5 Changed 3 years ago by RyanGlScott

I apologize, comment:3 is wrong in stating that you can't derive an Eq1 instance for Foo. It is most certainly possible to–after all, you can create the following instance!

instance Eq1 Foo

That eta-reduction stuff was me confusing the details of DeriveAnyClass with other deriving mechanisms, like GeneralizedNewtypeDeriving and DeriveFunctor.

comment:6 Changed 3 years ago by bgamari

Type of failure: None/UnknownCompile-time crash

comment:7 Changed 3 years ago by RyanGlScott

Differential Rev(s): Phab:D2961
Milestone: 8.2.1
Status: newpatch

A WIP attempt at fixing this is at ​Phab:D2961.

comment:8 Changed 3 years ago by Ryan Scott <ryan.gl.scott@…>

In 639e702/ghc:

Refactor DeriveAnyClass's instance context inference

Summary:
Currently, `DeriveAnyClass` has two glaring flaws:

* It only works on classes whose argument is of kind `*` or `* -> *` (#9821).
* The way it infers constraints makes no sense. It basically co-opts the
  algorithms used to infer contexts for `Eq` (for `*`-kinded arguments) or
  `Functor` (for `(* -> *)`-kinded arguments). This tends to produce overly
  constrained instances, which in extreme cases can lead to legitimate things
  failing to typecheck (#12594). Or even worse, it can trigger GHC panics
  (#12144 and #12423).

This completely reworks the way `DeriveAnyClass` infers constraints to fix
these two issues. It now uses the type signatures of the derived class's
methods to infer constraints (and to simplify them). A high-level description
of how this works is included in the GHC users' guide, and more technical notes
on what is going on can be found as comments (and a Note) in `TcDerivInfer`.

Fixes #9821, #12144, #12423, #12594.

Test Plan: ./validate

Reviewers: dfeuer, goldfire, simonpj, austin, bgamari

Subscribers: dfeuer, thomie

Differential Revision: https://phabricator.haskell.org/D2961

comment:9 Changed 3 years ago by RyanGlScott

Resolution: fixed
Status: patchclosed
Test Case: tests/deriving/should_compile/T12423
Note: See TracTickets for help on using tickets.