HEAD-only Core Lint error with arith-encode-1.0.2 (Type of case alternatives not the same as the annotation on case)
Originally observed on a head.hackage
CI run here.
The arith-encode-1.0.2
library on Hackage fails to build with -dcore-lint
on GHC HEAD. Here is a standalone example (which unfortunately isn't terribly minimized).
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE MagicHash #-}
module Data.ArithEncode.Util (function) where
import Control.Exception
import Data.Array.Base (unsafeAt)
import Data.Array.Unboxed hiding (accum)
import Data.Bits
import Data.List
import Data.Maybe
import GHC.Exts (Int(..), Int#, uncheckedIShiftRA#, isTrue#, int2Double#, sqrtDouble#, double2Int#, (<#), (*#), (-#))
import GHC.Integer.GMP.Internals (Integer(..), shiftLInteger, shiftRInteger, sizeofBigNat#)
import GHC.Integer.Logarithms (integerLog2#)
import Numeric.Natural (Natural)
import Prelude hiding (seq)
import qualified Data.Map as Map
nonEmptyOptionSeq :: Encoding ty
-> Encoding [Maybe ty]
nonEmptyOptionSeq enc =
let
fwdfunc Nothing = Just []
fwdfunc (Just (first, rest)) = Just (reverse (Just first : rest))
revfunc' [] = Just Nothing
revfunc' (Just first : rest) = Just (Just (first, rest))
revfunc' _ = Nothing
revfunc = revfunc' . reverse
in
wrap revfunc fwdfunc (optional (pair enc (seq (optional enc))))
nonEmptyBoundedOptionSeq :: Integer
-> Encoding ty
-> Encoding [Maybe ty]
nonEmptyBoundedOptionSeq len enc =
let
fwdfunc Nothing = Just []
fwdfunc (Just (first, rest)) = Just (reverse (Just first : rest))
revfunc' [] = Just Nothing
revfunc' (Just first : rest) = Just (Just (first, rest))
revfunc' _ = Nothing
revfunc = revfunc' . reverse
in
wrap revfunc fwdfunc (optional (pair enc (boundedSeq (len - 1) (optional enc))))
function :: Ord keyty =>
Encoding keyty
-> Encoding valty
-> Encoding (Map.Map keyty valty)
function keyenc valenc =
let
seqToMap val =
let
convertEnt (_, Nothing) = Nothing
convertEnt (key', Just val') = Just (decode keyenc key', val')
contents = catMaybes (map convertEnt (zip (iterate (+ 1) 0) val))
in
Just (Map.fromList contents)
mapToSeq val
| all (inDomain keyenc) (Map.keys val) =
let
foldfun (count, accum) (idx, val') =
(idx + 1,
Just val' : replicate (fromInteger (idx - count)) Nothing ++ accum)
sorted = sortBy (\(a, _) (b, _) -> compare a b)
(map (\(key, val') -> (encode keyenc key, val'))
(Map.assocs val))
(_, out) = foldl foldfun (0, []) sorted
reversed = reverse out
in
Just reversed
| otherwise = Nothing
innerenc =
case size keyenc of
Just finitesize -> nonEmptyBoundedOptionSeq finitesize valenc
Nothing -> nonEmptyOptionSeq valenc
in
wrap mapToSeq seqToMap innerenc
-----
-- Data.ArithEncoding.Basic
-----
data Encoding ty =
Encoding {
encEncode :: ty -> Integer,
encDecode :: Integer -> ty,
encSize :: !(Maybe Integer),
encInDomain :: ty -> Bool
}
boundedSeq :: Integer
-> Encoding ty
-> Encoding [ty]
boundedSeq len enc@Encoding { encSize = sizeval, encInDomain = indomainfunc } =
let
(newencode, newdecode) = boundedSeqCore len enc
newsize = case len of
0 -> Just 1
_ -> fmap (geometricSum len) sizeval
newindomain vals = length vals <= fromInteger len && all indomainfunc vals
in
Encoding { encEncode = newencode, encDecode = newdecode,
encSize = newsize, encInDomain = newindomain }
boundedSeqCore :: Integer -> Encoding ty -> ([ty] -> Integer, Integer -> [ty])
boundedSeqCore len Encoding { encEncode = encodefunc, encDecode = decodefunc,
encSize = sizeval } =
case sizeval of
Nothing ->
let
newencode [] = 0
newencode vals =
let
thislen = toInteger (length vals)
contentnum = fromProdList (map encodefunc vals)
in
(contentnum * len) + thislen
newdecode 0 = []
newdecode num =
let
adjusted = num - 1
(remainingEntropy, lengthEntropy) = adjusted `quotRem` len
thislen = lengthEntropy + 1
in
map decodefunc (toProdList thislen remainingEntropy)
in
(newencode, newdecode)
Just 0 -> (\[] -> 0, \0 -> [])
Just 1 -> (genericLength, flip genericReplicate (decodefunc 0))
Just finitesize ->
let
newencode [] = 0
newencode vals =
let
thislen = toInteger (length vals)
base = geometricSum (thislen - 1) finitesize
newencode' accum [] = accum
newencode' accum (first : rest) =
newencode' ((accum * finitesize) + encodefunc first) rest
in
base + (newencode' 0 (reverse vals))
newdecode 0 = []
newdecode num =
let
lowlen = ilog finitesize ((num * (finitesize - 1)) + 1) - 1
thislen = lowlen + 1
contentnum = num - (geometricSum lowlen finitesize)
newdecode' accum 1 entropy = (decodefunc entropy : accum)
newdecode' _ 0 _ = []
newdecode' accum count entropy =
let
thisentropy = entropy `mod` finitesize
restentropy = entropy `quot` finitesize
this = decodefunc thisentropy
in
newdecode' (this : accum) (count - 1) restentropy
in
reverse (newdecode' [] thislen contentnum)
in
(newencode, newdecode)
decode :: Encoding ty
-> Integer
-> ty
decode encoding num
| num < 0 =
throw (IllegalArgument ("decode argument " ++ show num ++ " is negative"))
| maybe False (<= num) (size encoding) =
throw (IllegalArgument ("decode argument " ++ show num ++
" is out of bounds"))
| otherwise = (encDecode encoding) num
encode :: Encoding ty
-> ty
-> Integer
encode encoding = encEncode encoding
ilog :: Integer -> Integer -> Integer
ilog n = toInteger . integerLogBase' n
inDomain :: Encoding ty
-> ty
-> Bool
inDomain encoding = encInDomain encoding
isqrt :: Integer -> Integer
isqrt = integerSquareRoot
geometricSum :: Integer -> Integer -> Integer
geometricSum len 1 = len + 1
geometricSum len base = (1 - base ^ (len + 1)) `quot` (1 - base)
mkPairCore :: Encoding ty1 -> Encoding ty2 ->
((ty1, ty2) -> Integer, Integer -> (ty1, ty2), Maybe Integer)
mkPairCore Encoding { encEncode = encode1, encDecode = decode1,
encSize = sizeval1 }
Encoding { encEncode = encode2, encDecode = decode2,
encSize = sizeval2 } =
let
(convertidx, decodefunc) = case (sizeval1, sizeval2) of
(Just maxval, _) ->
let
convertidx' idx1 idx2 = (idx2 * maxval) + idx1
newdecode num = (decode1 (num `mod` maxval), decode2 (num `quot` maxval))
in
(convertidx', newdecode)
(_, Just maxval) ->
let
convertidx' idx1 idx2 = (idx1 * maxval) + idx2
newdecode num = (decode1 (num `quot` maxval), decode2 (num `mod` maxval))
in
(convertidx', newdecode)
(Nothing, Nothing) ->
let
convertidx' idx1 idx2 =
let
sumval = idx1 + idx2
base = (((sumval + 1) * sumval)) `quot` 2
in
base + idx2
newdecode num =
let
sumval = (isqrt ((8 * num) + 1) - 1) `quot` 2
base = (((sumval + 1) * sumval)) `quot` 2
num2 = num - base
num1 = sumval - num2
in
(decode1 num1, decode2 num2)
in
(convertidx', newdecode)
encodefunc (val1, val2) = convertidx (encode1 val1) (encode2 val2)
sizeval =
do
size1 <- sizeval1
size2 <- sizeval2
return (size1 * size2)
in
(encodefunc, decodefunc, sizeval)
optional :: Encoding ty -> Encoding (Maybe ty)
optional Encoding { encEncode = encodefunc, encDecode = decodefunc,
encSize = sizeval, encInDomain = indomainfunc } =
let
newsize = sizeval >>= return . (+ 1)
newindomain = maybe True indomainfunc
newencode Nothing = 0
newencode (Just val) = 1 + encodefunc val
newdecode 0 = Nothing
newdecode num = Just (decodefunc (num - 1))
in
Encoding { encEncode = newencode, encDecode = newdecode,
encSize = newsize, encInDomain = newindomain }
pair :: Encoding ty1 -> Encoding ty2 -> Encoding (ty1, ty2)
pair enc1@Encoding { encInDomain = indomain1 }
enc2@Encoding { encInDomain = indomain2 } =
let
(encodefunc, decodefunc, sizeval) = mkPairCore enc1 enc2
indomainfunc (val1, val2) = indomain1 val1 && indomain2 val2
in
Encoding { encEncode = encodefunc, encDecode = decodefunc,
encSize = sizeval, encInDomain = indomainfunc }
seq :: Encoding ty -> Encoding [ty]
seq enc@Encoding { encInDomain = indomainfunc } =
let
(newEncode, newDecode) = seqCore enc
newInDomain = all indomainfunc
in
Encoding { encEncode = newEncode, encDecode = newDecode,
encSize = Nothing, encInDomain = newInDomain }
seqCore :: Encoding ty -> ([ty] -> Integer, Integer -> [ty])
seqCore Encoding { encEncode = encodefunc, encDecode = decodefunc,
encSize = sizeval } =
case sizeval of
Just finitesize ->
let
newencodefunc =
let
foldfun accum = (((accum * finitesize) + 1) +) . encodefunc
in
foldl foldfun 0
newdecodefunc =
let
newdecodefunc' accum 0 = accum
newdecodefunc' accum num =
let
decoded = decodefunc ((num - 1) `mod` finitesize)
in
newdecodefunc' (decoded : accum) ((num - 1) `quot` finitesize)
in
newdecodefunc' []
in
(newencodefunc, newdecodefunc)
Nothing ->
let
newencodefunc [] = 0
newencodefunc (first : rest) =
let
insertUnary bin val =
let
encoded = encodefunc val
shifted = bin `shiftL` (fromInteger encoded)
in
shifted .|. ((2 ^ encoded) - 1)
foldfun accum val =
let
shifted = accum `shiftL` 1
in
insertUnary shifted val
initial = insertUnary 1 first
in
foldl foldfun initial rest
newdecodefunc 0 = []
newdecodefunc num =
let
leadingOnes :: Integer -> Integer
leadingOnes =
let
leadingOnes' count n
| testBit n 0 = leadingOnes' (count + 1) (n `shiftR` 1)
| otherwise = count
in
leadingOnes' 0
extractUnary bin =
let
unaryLen = leadingOnes bin
shifted = bin `shiftR` (fromInteger (unaryLen + 1))
decoded
| shifted /= 0 = decodefunc unaryLen
| otherwise = decodefunc (unaryLen - 1)
in
(decoded, shifted)
doDecode accum 0 = accum
doDecode accum bin =
let
(val, newbin) = extractUnary bin
in
doDecode (val : accum) newbin
in
doDecode [] num
in
(newencodefunc, newdecodefunc)
size :: Encoding ty
-> Maybe Integer
size = encSize
wrap :: (a -> Maybe b)
-> (b -> Maybe a)
-> Encoding b
-> Encoding a
wrap fwd rev enc@Encoding { encEncode = encodefunc, encDecode = decodefunc,
encInDomain = indomainfunc } =
let
safefwd val =
case fwd val of
Just val' -> val'
Nothing -> throw (IllegalArgument "No mapping into underlying domain")
saferev val =
case rev val of
Just val' -> val'
Nothing -> throw (IllegalArgument "No mapping into external domain")
in
enc { encEncode = encodefunc . safefwd,
encDecode = saferev . decodefunc,
encInDomain = maybe False indomainfunc . fwd }
toProdList :: Integer -> Integer -> [Integer]
toProdList =
let
productList' accum 1 entropy = reverse (entropy : accum)
productList' _ 0 _ = []
productList' accum count entropy =
let
sumval = (isqrt ((8 * entropy) + 1) - 1) `quot` 2
base = (((sumval + 1) * sumval)) `quot` 2
num2 = entropy - base
num1 = sumval - num2
in
productList' (num1 : accum) (count - 1) num2
in
productList' []
fromProdList :: [Integer] -> Integer
fromProdList [] = 0
fromProdList vals =
let
(first : rest) = reverse vals
fromProdList' accum [] = accum
fromProdList' accum (first' : rest') =
let
sumval = accum + first'
base = (((sumval + 1) * sumval)) `quot` 2
in
fromProdList' (base + accum) rest'
in
fromProdList' first rest
data IllegalArgument = IllegalArgument !String
instance Show IllegalArgument where
show (IllegalArgument "") = "Illegal argument"
show (IllegalArgument s) = "Illegal argument: " ++ s
instance Exception IllegalArgument
-----
-- Math.NumberTheory.Roots.Squares
-----
{-# SPECIALISE integerSquareRoot :: Int -> Int,
Word -> Word,
Integer -> Integer,
Natural -> Natural
#-}
integerSquareRoot :: Integral a => a -> a
integerSquareRoot n
| n < 0 = error "integerSquareRoot: negative argument"
| otherwise = integerSquareRoot' n
{-# RULES
"integerSquareRoot'/Int" integerSquareRoot' = isqrtInt'
"integerSquareRoot'/Word" integerSquareRoot' = isqrtWord
"integerSquareRoot'/Integer" integerSquareRoot' = isqrtInteger
#-}
{-# INLINE [1] integerSquareRoot' #-}
integerSquareRoot' :: Integral a => a -> a
integerSquareRoot' = isqrtA
isqrtInt' :: Int -> Int
isqrtInt' n
| n < r*r = r-1
| otherwise = r
where
!r = (truncate :: Double -> Int) . sqrt $ fromIntegral n
isqrtWord :: Word -> Word
isqrtWord n
| n < (r*r)
|| finiteBitSize (0 :: Word) == 64 && r == 4294967296
= r-1
| otherwise = r
where
!r = (fromIntegral :: Int -> Word) . (truncate :: Double -> Int) . sqrt $ fromIntegral n
{-# INLINE isqrtInteger #-}
isqrtInteger :: Integer -> Integer
isqrtInteger = fst . karatsubaSqrt
-----
-- Math.NumberTheory.Logarithms
-----
integerLogBase' :: Integer -> Integer -> Int
integerLogBase' b n
| n < b = 0
| ln-lb < lb = 1
| b < 33 = let bi = fromInteger b
ix = 2*bi-4
u = logArr `unsafeAt` ix
v = logArr `unsafeAt` (ix+1)
ex = fromInteger ((fromIntegral u * fromIntegral ln) `quot` fromIntegral v)
in case u of
1 -> ln `quot` v
_ -> ex + integerLogBase' b (n `quot` b ^ ex)
| otherwise = let bi = fromInteger (b `shiftR` (lb-4))
ix = 2*bi-2
u = fromIntegral $ logArr `unsafeAt` ix
v = fromIntegral $ logArr `unsafeAt` (ix+1)
w = v + u*fromIntegral (lb-4)
ex = fromInteger ((u * fromIntegral ln) `quot` w)
in ex + integerLogBase' b (n `quot` b ^ ex)
where
lb = integerLog2' b
ln = integerLog2' n
integerLog2' :: Integer -> Int
integerLog2' n = I# (integerLog2# n)
logArr :: UArray Int Int
logArr = listArray (0, 61)
[ 1 , 1,
190537 , 301994,
1 , 2,
1936274 , 4495889,
190537 , 492531,
91313 , 256348,
1 , 3,
190537 , 603988,
1936274 , 6432163,
1686227 , 5833387,
190537 , 683068,
5458 , 20197,
91313 , 347661,
416263 , 1626294,
1 , 4,
32631 , 133378,
190537 , 794525,
163451 , 694328,
1936274 , 8368437,
1454590 , 6389021,
1686227 , 7519614,
785355 , 3552602,
190537 , 873605,
968137 , 4495889,
5458 , 25655,
190537 , 905982,
91313 , 438974,
390321 , 1896172,
416263 , 2042557,
709397 , 3514492,
1 , 5
]
-----
-- Math.NumberTheory.Roots.Squares.Internal
-----
{-# SPECIALISE isqrtA :: Integer -> Integer #-}
isqrtA :: Integral a => a -> a
isqrtA 0 = 0
isqrtA n = heron n (fromInteger . appSqrt . fromIntegral $ n)
{-# SPECIALISE heron :: Integer -> Integer -> Integer #-}
heron :: Integral a => a -> a -> a
heron n a = go (step a)
where
step k = (k + n `quot` k) `quot` 2
go k
| m < k = go m
| otherwise = k
where
m = step k
appSqrt :: Integer -> Integer
appSqrt (S# i#) = S# (double2Int# (sqrtDouble# (int2Double# i#)))
appSqrt n@(Jp# bn#)
| isTrue# ((sizeofBigNat# bn#) <# thresh#) =
floor (sqrt $ fromInteger n :: Double)
| otherwise = case integerLog2# n of
l# -> case uncheckedIShiftRA# l# 1# -# 47# of
h# -> case shiftRInteger n (2# *# h#) of
m -> case floor (sqrt $ fromInteger m :: Double) of
r -> shiftLInteger r h#
where
thresh# :: Int#
thresh# = if finiteBitSize (0 :: Word) == 64 then 5# else 9#
appSqrt _ = error "integerSquareRoot': negative argument"
karatsubaSqrt :: Integer -> (Integer, Integer)
karatsubaSqrt 0 = (0, 0)
karatsubaSqrt n
| lgN < 2300 =
let s = isqrtA n in (s, n - s * s)
| otherwise =
if lgN .&. 2 /= 0 then
karatsubaStep k (karatsubaSplit k n)
else
let n' = n `unsafeShiftL` 2
(s, r) = karatsubaStep k (karatsubaSplit k n')
r' | s .&. 1 == 0 = r
| otherwise = r + double s - 1
in (s `unsafeShiftR` 1, r' `unsafeShiftR` 2)
where
k = lgN `unsafeShiftR` 2 + 1
lgN = I# (integerLog2# n)
karatsubaStep :: Int -> (Integer, Integer, Integer, Integer) -> (Integer, Integer)
karatsubaStep k (a3, a2, a1, a0)
| r >= 0 = (s, r)
| otherwise = (s - 1, r + double s - 1)
where
r = cat u a0 - q * q
s = s' `unsafeShiftL` k + q
(q, u) = cat r' a1 `quotRem` double s'
(s', r') = karatsubaSqrt (cat a3 a2)
cat x y = x `unsafeShiftL` k .|. y
{-# INLINE cat #-}
karatsubaSplit :: Int -> Integer -> (Integer, Integer, Integer, Integer)
karatsubaSplit k n0 = (a3, a2, a1, a0)
where
a3 = n3
n3 = n2 `unsafeShiftR` k
a2 = n2 .&. m
n2 = n1 `unsafeShiftR` k
a1 = n1 .&. m
n1 = n0 `unsafeShiftR` k
a0 = n0 .&. m
m = 1 `unsafeShiftL` k - 1
double :: Integer -> Integer
double x = x `unsafeShiftL` 1
{-# INLINE double #-}
To reproduce the error, compile this file like so:
$ ghc-stage2 -fforce-recomp -dcore-lint -O Bug.hs
[1 of 1] Compiling Data.ArithEncode.Util ( Bug.hs, Bug.o )
*** Core Lint errors : in result of Simplifier ***
Bug.hs:65:14: warning:
Type of case alternatives not the same as the annotation on case:
Actual type: keyty_a4Ai -> Bool
Annotation on case: keyty_a4Ai -> All
Alt Rhs: ds_d55n
In the RHS of function :: forall keyty valty.
Ord keyty =>
Encoding keyty -> Encoding valty -> Encoding (Map keyty valty)
In the body of lambda with binder keyty_a4Ai :: *
In the body of lambda with binder valty_a4Aj :: *
In the body of lambda with binder $dOrd_a4Ak :: Ord keyty_a4Ai
In the body of lambda with binder keyenc_a21R :: Encoding
keyty_a4Ai
In the body of lambda with binder valenc_a21S :: Encoding
valty_a4Aj
In the body of lambda with binder val_a221 :: Map
keyty_a4Ai valty_a4Aj
In the body of letrec with binders f_s5Lp :: keyty_a4Ai -> Bool
In the body of lambda with binder k1_a5Lz :: keyty_a4Ai
In the body of lambda with binder ds_a5LA :: valty_a4Aj
In the body of lambda with binder xs_a5LB :: All
In a case alternative: (Encoding ds_d55k :: keyty_a4Ai -> Integer,
ds_d55l :: Integer -> keyty_a4Ai,
ds_d55m :: Maybe Integer,
ds_d55n :: keyty_a4Ai -> Bool)
Substitution: [TCvSubst
In scope: InScope {keyty_a4Ai valty_a4Aj}
Type env: [a4Ai :-> keyty_a4Ai, a4Aj :-> valty_a4Aj]
Co env: []]
This does not occur with GHC 8.10.1.