[GHC] #15696: Derived Ord instance for enumerations with more than 8 elements seems to be incorrect

GHC ghc-devs at haskell.org
Mon Oct 8 22:02:52 UTC 2018


#15696: Derived Ord instance for enumerations with more than 8 elements seems to be
incorrect
-------------------------------------+-------------------------------------
        Reporter:  mrkkrp            |                Owner:  (none)
            Type:  bug               |               Status:  patch
        Priority:  highest           |            Milestone:  8.6.2
       Component:  Compiler          |              Version:  8.6.1
      Resolution:                    |             Keywords:
Operating System:  Unknown/Multiple  |         Architecture:
 Type of failure:  Incorrect result  |  Unknown/Multiple
  at runtime                         |            Test Case:
      Blocked By:                    |             Blocking:
 Related Tickets:                    |  Differential Rev(s):  Phab:D5196,
       Wiki Page:                    |  Phab:D5201
-------------------------------------+-------------------------------------

Comment (by simonpj):

 The `dataToTag#` saga has revealed several things, which I'd like to
 summarise here.

 * We have been guilty of mixing up two things:
   * A ''semantic'' property, namely that an expression, or variable, is
 guaranteed to be non-bottom.  For example, given
 {{{
 data T = MkT !Int
 f (MkT y) = ...y...
 }}}
   in the body of `f`, we know that `y` is non-bottom.
   * A ''representational'' propery, that a variable is bound to a heap
 pointer that (a) points directly to an evaluated heap-allocated value
 (e.g. a cons cell), and (b) is properly tagged, for data constructors with
 small enough tags.

   The representational property implies the semantic property, ''but not
 vice versa''.  For example, in the above code, are we guaranteed that `y`
 (being the strict field of `MkT` is bound to a properly-tagged pointer to
 an `Int`?  I used to think yes, but
 [https://ghc.haskell.org/trac/ghc/ticket/15696#comment:36 this example]
 (look at the bullets in that comment) shows that the answer is NO.

   My conclusion: we should keep the two properties separate.  The
 Simplifier can maintain the first; but the second is much more fragile,
 and is the business of the code generator alone.

 * We have never implemented #14626, but it's pretty easy to do so provided
 we focus our attention on the code generator.  The example there is
 {{{
 f x = case x of y
            Red -> Green
            DEFAULT -> y
 }}}
   The resturn contract of `case` specifies that `y` is bound to a
 properly-tagged pointer directly to the value; The code generator can
 remember this fact, in its environment; and simply ''return'' `y` in the
 DEFAULT branch rather than ''entering'' it.

 * `exprOkForSpeculation` finishes with this case (in `app_ok`)
 {{{
       _other -> isUnliftedType (idType fun)          -- c.f. the Var case
 of exprIsHNF
              || idArity fun > n_val_args             -- Partial apps
              || (n_val_args == 0 &&
                  isEvaldUnfolding (idUnfolding fun)) -- Let-bound values
 }}}
   I think the final disjunct is wrong, becuase it is fragile.  Consider
 {{{
   case x of y { DEFAULT -> ...y.... }
 }}}
   In that branch, is `y` ok-for-speculation?  It looks so, because it has
 an evald unfolding (`OtherCon`).  But the binder-swap transformation
 performed by FloatOut transforms it to
 {{{
   case x of y { DEFAULT -> ...x.... }
 }}}
   and `x` is NOT ok-for-spec.  So the binder-swap transformation might
 invalidate invariants.

   This is pretty obscure in practice, but I think we can make the compiler
 simpler and more correct by deleting those two lines.  I think it will
 have zero effect because `exprOkForSpecuation` is seldom if ever called on
 expressions of lifted type.

 * Only two primops (currently) evaluate a lifted-type argument: `SeqOp`
 and `DataToTagOp`.  The former has a special case in `app_ok` (in
 `exprOkForSpeculation` and the latter should too.

 * Once all this is done, the hack of making `DataToTagOp` have
 `can_fail=True` can be dispensed with.

 * We have related loose ends in #14677 and #15155.

-- 
Ticket URL: <http://ghc.haskell.org/trac/ghc/ticket/15696#comment:60>
GHC <http://www.haskell.org/ghc/>
The Glasgow Haskell Compiler


More information about the ghc-tickets mailing list