Skip to content

Type inference regression in GHC HEAD

First noticed in #12790 (closed)##12936 (closed). This causes parsec-3.1.11 to fail to build with GHC HEAD.

Here is as small of a test case that I can manage, with no dependencies:

{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE MultiParamTypeClasses #-}
module Parsec (makeTokenParser) where

import Data.Char (digitToInt)

data ParsecT s u (m :: * -> *) a

instance Functor (ParsecT s u m) where
    fmap = undefined

instance Applicative (ParsecT s u m) where
    pure = undefined
    (<*>) = undefined

instance Monad (ParsecT s u m) where
    return = undefined
    (>>=)  = undefined
    fail   = undefined

parserZero :: ParsecT s u m a
parserZero = undefined

infixr 1 <|>
(<|>) :: (ParsecT s u m a) -> (ParsecT s u m a) -> (ParsecT s u m a)
(<|>) = undefined

class (Monad m) => Stream s m t | s -> t where

digit :: (Stream s m Char) => ParsecT s u m Char
digit = undefined

hexDigit :: (Stream s m Char) => ParsecT s u m Char
hexDigit = undefined

octDigit :: (Stream s m Char) => ParsecT s u m Char
octDigit = undefined

option :: (Stream s m t) => a -> ParsecT s u m a -> ParsecT s u m a
option = undefined

many1 :: (Stream s m t) => ParsecT s u m a -> ParsecT s u m [a]
many1 = undefined

data GenTokenParser s u m
    = TokenParser {
        float            :: ParsecT s u m Double,
        naturalOrFloat   :: ParsecT s u m (Either Integer Double)
    }

makeTokenParser :: (Stream s m Char) => GenTokenParser s u m
makeTokenParser
    = TokenParser{
                   float = float_
                 , naturalOrFloat = naturalOrFloat_
                 }
    where

    -----------------------------------------------------------
    -- Numbers
    -----------------------------------------------------------
    naturalOrFloat_ = lexeme natFloat

    float_          = lexeme floating

    -- floats
    floating        = do{ n <- decimal
                        ; fractExponent n
                        }


    natFloat        =     zeroNumFloat
                      <|> decimalFloat

    zeroNumFloat    =  do{ n <- hexadecimal <|> octal
                         ; return (Left n)
                         }
                    <|> decimalFloat

    decimalFloat    = do{ n <- decimal
                        ; option (Left n)
                                 (fractFloat n)
                        }

    fractFloat n    = do{ f <- fractExponent n
                        ; return (Right f)
                        }

    fractExponent n = do{ fract <- fraction
                        ; expo  <- option "" exponent'
                        ; readDouble (show n ++ fract ++ expo)
                        }
                    <|>
                      do{ expo <- exponent'
                        ; readDouble (show n ++ expo)
                        }
                      where
                        readDouble s =
                          case reads s of
                            [(x, "")] -> return x
                            _         -> parserZero

    fraction        = do{ digits <- many1 digit
                        ; return ('.' : digits)
                        }

    exponent'       = do{ e <- decimal
                        ; return (show e)
                        }

    -- integers and naturals
    decimal         = number 10 digit
    hexadecimal     = number 16 hexDigit
    octal           = number 8 octDigit

    number base baseDigit
        = do{ digits <- many1 baseDigit
            ; let n = foldl (\x d -> base*x + toInteger (digitToInt d)) 0 digits
            ; seq n (return n)
            }

    -----------------------------------------------------------
    -- White space & symbols
    -----------------------------------------------------------
    lexeme p = do{ x <- p; whiteSpace; return x  }
    whiteSpace = return ()

In GHC 8.0.1 and 8.0.2, this compiles without issue. But on GHC HEAD:

$ ~/Software/ghc/inplace/bin/ghc-stage2 Parsec.hs
[1 of 1] Compiling Parsec           ( Parsec.hs, Parsec.o )

Parsec.hs:83:27: error:
    • Could not deduce (Stream s m t0) arising from a use of ‘option’
      from the context: Stream s m Char
        bound by the type signature for:
                   makeTokenParser :: Stream s m Char => GenTokenParser s u m
        at Parsec.hs:53:1-60
      The type variable ‘t0’ is ambiguous
      Relevant bindings include
        decimalFloat :: ParsecT s u1 m (Either Integer Double)
          (bound at Parsec.hs:82:5)
        fractFloat :: forall b a1 u a2.
                      (Read b, Show a2) =>
                      a2 -> ParsecT s u m (Either a1 b)
          (bound at Parsec.hs:87:5)
        fractExponent :: forall a1 u a2.
                         (Show a2, Read a1) =>
                         a2 -> ParsecT s u m a1
          (bound at Parsec.hs:91:5)
        fraction :: forall u. ParsecT s u m [Char]
          (bound at Parsec.hs:105:5)
        exponent' :: forall u. ParsecT s u m String
          (bound at Parsec.hs:109:5)
        decimal :: forall u. ParsecT s u m Integer
          (bound at Parsec.hs:114:5)
        lexeme :: forall b. ParsecT s u m b -> ParsecT s u m b
          (bound at Parsec.hs:127:5)
        (Some bindings suppressed; use -fmax-relevant-binds=N or -fno-max-relevant-binds)
    • In a stmt of a 'do' block: option (Left n) (fractFloat n)
      In the expression:
        do { n <- decimal;
             option (Left n) (fractFloat n) }
      In an equation for ‘decimalFloat’:
          decimalFloat
            = do { n <- decimal;
                   option (Left n) (fractFloat n) }
Trac metadata
Trac field Value
Version 8.1
Type Bug
TypeOfFailure OtherFailure
Priority high
Resolution Unresolved
Component Compiler (Type checker)
Test case
Differential revisions
BlockedBy
Related
Blocking
CC
Operating system
Architecture
To upload designs, you'll need to enable LFS and have an admin enable hashed storage. More information