Opened 4 years ago

Closed 4 years ago

Last modified 4 years ago

#4235 closed bug (fixed)

deriving Enum fails for data instances

Reported by: nestra Owned by:
Priority: normal Milestone: 7.0.1
Component: Compiler (Type checker) Version: 6.12.3
Keywords: instance deriving, type families, GADTs Cc:
Operating System: Linux Architecture: x86_64 (amd64)
Type of failure: Compile-time performance bug Difficulty:
Test Case: indexed-types/should_run/T4235 Blocked By:
Blocking: Related Tickets:

Description

The following sample module does not compile:

{-# LANGUAGE TypeFamilies #-}
module Foo where

data family Foo a

data instance Foo Int
  = A | B
  deriving (Enum)

GHC gives 5 error messages, all to line 8 (the deriving clause), in terms of some internal representations of variables and types. I think that an error message should not be of this kind even if the code really contains an error. But here I even do not understand why the code should not be valid. In the case of deriving Eq, Ord, Bounded or Show for the same Foo Int, everything works fine; and also, old good instance declaration instead of deriving gives a normally working code. (Analogous errors are produced when deriving Ix.)

Here is the output of GHCi -v in the case of the above module (GHC gives a similar one):

Prelude> :l Foo
*** Chasing dependencies:
Chasing modules from:
Stable obj: []
Stable BCO: []
unload: retaining objs []
unload: retaining bcos []
Ready for upsweep []
Upsweep completely successful.
*** Deleting temp files:
Deleting:
*** Chasing dependencies:
Chasing modules from: *Foo.hs
Stable obj: []
Stable BCO: []
unload: retaining objs []
unload: retaining bcos []
Ready for upsweep
  [NONREC
      ModSummary {
         ms_hs_date = Mon Aug  2 14:30:44 UTC 2010
         ms_mod = main:Foo,
         ms_imps = []
         ms_srcimps = []
      }]
compile: input file Foo.hs
*** Checking old interface for main:Foo:
[1 of 1] Compiling Foo              ( Foo.hs, interpreted )
*** Parser:
*** Renamer/typechecker:

Foo.hs:8:12:
    Couldn't match expected type `Foo Int'
           against inferred type `Foo.R:FooInt'
      NB: `Foo' is a type function
    In the expression: Foo.$tag2con_R:FooInt ((+) (GHC.Types.I# a#) 1)
    In the expression:
        if (==) Foo.$maxtag_R:FooInt (GHC.Types.I# a#) then
            error
              "succ{R:FooInt}: tried to take `succ' of last tag in enumeration"
        else
            Foo.$tag2con_R:FooInt ((+) (GHC.Types.I# a#) 1)
    In a case alternative:
        a#
          -> if (==) Foo.$maxtag_R:FooInt (GHC.Types.I# a#) then
                 error
                   "succ{R:FooInt}: tried to take `succ' of last tag in enumeration"
             else
                 Foo.$tag2con_R:FooInt ((+) (GHC.Types.I# a#) 1)

Foo.hs:8:12:
    Couldn't match expected type `Foo Int'
           against inferred type `Foo.R:FooInt'
      NB: `Foo' is a type function
    In the expression: Foo.$tag2con_R:FooInt ((+) (GHC.Types.I# a#) -1)
    In the expression:
        if (==) 0 (GHC.Types.I# a#) then
            error
              "pred{R:FooInt}: tried to take `pred' of first tag in enumeration"
        else
            Foo.$tag2con_R:FooInt ((+) (GHC.Types.I# a#) -1)
    In a case alternative:
        a#
          -> if (==) 0 (GHC.Types.I# a#) then
                 error
                   "pred{R:FooInt}: tried to take `pred' of first tag in enumeration"
             else
                 Foo.$tag2con_R:FooInt ((+) (GHC.Types.I# a#) -1)

Foo.hs:8:12:
    Couldn't match expected type `Foo Int'
           against inferred type `Foo.R:FooInt'
      NB: `Foo' is a type function
    In the expression: Foo.$tag2con_R:FooInt a
    In the expression:
        if (&&) ((>=) a 0) ((<=) a Foo.$maxtag_R:FooInt) then
            Foo.$tag2con_R:FooInt a
        else
            error
              ((++)
                 "toEnum{R:FooInt}: tag ("
                 (showsPrec
                    0
                    a
                    ((++)
                       ") is outside of enumeration's range (0,"
                       (showsPrec 0 Foo.$maxtag_R:FooInt ")"))))
    In the definition of `toEnum':
        toEnum a = if (&&) ((>=) a 0) ((<=) a Foo.$maxtag_R:FooInt) then
                       Foo.$tag2con_R:FooInt a
                   else
                       error
                         ((++)
                            "toEnum{R:FooInt}: tag ("
                            (showsPrec
                               0
                               a
                               ((++)
                                  ") is outside of enumeration's range (0,"
                                  (showsPrec 0 Foo.$maxtag_R:FooInt ")"))))

Foo.hs:8:12:
    Couldn't match expected type `Foo Int'
           against inferred type `Foo.R:FooInt'
      NB: `Foo' is a type function
    In the first argument of `map', namely `Foo.$tag2con_R:FooInt'
    In the expression:
        map
          Foo.$tag2con_R:FooInt
          (enumFromTo (GHC.Types.I# a#) Foo.$maxtag_R:FooInt)
    In a case alternative:
        a#
          -> map
               Foo.$tag2con_R:FooInt
               (enumFromTo (GHC.Types.I# a#) Foo.$maxtag_R:FooInt)

Foo.hs:8:12:
    Couldn't match expected type `Foo Int'
           against inferred type `Foo.R:FooInt'
      NB: `Foo' is a type function
    In the first argument of `map', namely `Foo.$tag2con_R:FooInt'
    In the expression:
        map
          Foo.$tag2con_R:FooInt
          (enumFromThenTo
             (GHC.Types.I# a#)
             (GHC.Types.I# b#)
             (if (>) (GHC.Types.I# a#) (GHC.Types.I# b#) then
                  0
              else
                  Foo.$maxtag_R:FooInt))
    In a case alternative:
        b#
          -> map
               Foo.$tag2con_R:FooInt
               (enumFromThenTo
                  (GHC.Types.I# a#)
                  (GHC.Types.I# b#)
                  (if (>) (GHC.Types.I# a#) (GHC.Types.I# b#) then
                       0
                   else
                       Foo.$maxtag_R:FooInt))
*** Deleting temp files:
Deleting:
Upsweep partially successful.
*** Deleting temp files:
Deleting:
Failed, modules loaded: none.

Making deriving stand-alone does not help (the error messages would be more or less similar). It seems that the automatically derived code for methods is buggy and does not type-check.

Using GADTs also does not help, but the feedback is different. In the case of GADTs, (stand-alone) deriving does not work for other type classes either. For example, if the module is

{-# LANGUAGE GADTs, StandaloneDeriving, FlexibleInstances #-}
module Foo where

data Foo a where
  A :: Foo Int
  B :: Foo Int

deriving instance (Eq (Foo Int))

then GHCi says the following:

Foo.hs:1:0:
    GADT pattern match in non-rigid context for `A'
      Probable solution: add a type signature for `Foo.$con2tag_Foo'
    In the pattern: A
    In the definition of `Foo.$con2tag_Foo': Foo.$con2tag_Foo A = 0#

Note the location of the error according to the message!
Anyway, after deleting the stand-alone deriving, GHCi is satisfied. By the way, :i A then gives:

data Foo a where
  A :: (a ~ Int) => Foo Int
  ...

However, :t A gives:

A :: Foo Int

So, is the type system of GHC ambiguous? Why are the types given by :i and :t different?

Change History (4)

comment:1 Changed 4 years ago by igloo

  • Milestone set to 6.14.1

Thanks for the report. HEAD has the same problem.

:i gives information about how something it defined, whereas :t tells you what type it would be inferred to have as an expression.

comment:2 in reply to: ↑ description Changed 4 years ago by nestra

I discovered that this bug occurs even with the following example:

module Foo where

data Foo a
  = A | B
  deriving (Enum)
Foo.hs:1:0:
    Expecting an ordinary type, but found a type of kind * -> *
    In an expression type signature: Foo
    In the expression: GHC.Prim.tagToEnum# a :: Foo
    In the definition of `Foo.$tag2con_Foo':
        Foo.$tag2con_Foo (GHC.Types.I# a) = GHC.Prim.tagToEnum# a :: Foo

So, the bug is not specific to the extensions (although the examples
revealing it occur in practice more probably when programming with type
families or GADTs).

The same behaviour is observed in the case of deriving Ix.

comment:3 Changed 4 years ago by simonpj

  • Resolution set to fixed
  • Status changed from new to closed
  • Test Case set to indexed-types/should_run/T4235

Quite right. I've fixed all of these bugs in the new typechecker branch, which will merge to HEAD in the next few weeks. And added a regression test. Will be in the next release.

Thanks for good examples

Simon

comment:4 Changed 4 years ago by simonpj

Turned out there was a second bug relating to enumerations that have a phantom type variable, that made the regresssion test fail with -O. Fixed by

Wed Oct  6 08:52:23 PDT 2010  simonpj@microsoft.com
  * Fix test T4235 with -O
  
  The tag2Enum rule wasn't doing the right thing for
  enumerations with a phantom type parameter, like
     data T a = A | B

    M ./compiler/prelude/PrelRules.lhs -14 +10
Note: See TracTickets for help on using tickets.