Skip to content

deriving-inferred contexts for types with DatatypeContexts are not as general as they should be

Consider this code:

{-# LANGUAGE DatatypeContexts #-}
{-# OPTIONS_GHC -ddump-deriv #-}
module Bug where

data Show a => T a = MkT
  deriving Eq

Currently, GHC will generate the following instance for this:

$ /opt/ghc/9.0.1/bin/ghc Bug.hs
[1 of 1] Compiling Bug              ( Bug.hs, Bug.o )

==================== Derived instances ====================
Derived class instances:
  instance GHC.Show.Show a => GHC.Classes.Eq (Bug.T a) where
    (GHC.Classes.==) (Bug.MkT) (Bug.MkT) = GHC.Types.True

This instance is overly constrained, however, as the Show a constraint is not necessary. Due to the way DatatypeContexts work, a data constructor only acquires the datatype context if its field types mention any of the type variables used in the datatype context. Because MkT has no argument types, its type does not acquire a datatype context at all. As proof, the following hand-written instance typechecks:

instance Eq (T a) where
  MkT == MkT = True

A derived instance should generate the same code as well. The problem lies here in GHC.Tc.Deriv.Infer. The code which infers instance contexts for derived instances will simply use the overall datatype context without checking the individual data constructors to see if they actually make use of the datatype context or not. Rather than using the tyConStupidTheta, I think this code should be made to work over dataConStupidThetas instead.

I realize that DatatypeContexts is a deprecated feature, and fixing this would't seem that important at first blush. I discovered this bug in the context of investigating #20375 (closed)/#20387 (closed), however, and it turns out the current treatment of DatatypeContexts vis-à-vis deriving is somewhat of an impediment to fixing those issues.

To upload designs, you'll need to enable LFS and have an admin enable hashed storage. More information