Type comparison in stg-lint is hopelessly broken
Today I spent a fair amount of time looking at the STG linter, having encountered a number of times in the past six months where I needed to use it but noticed it was broken.
While I thought fixing it would just be a few small tweaks, it seems that with every issue I fix another bug rears its ugly head. So far I have encountered and fixed,
The solutions to each of these has seemed rather obvious. However, now I seem to have run into a bit of a more fundamental issue:
Consider this excerpt extracted from Foreign.Storable
,
{-# LANGUAGE BangPatterns #-}
module Hi where
import GHC.Word
import GHC.Ptr
import GHC.Base
import GHC.Num
import Data.Bits
import GHC.Fingerprint.Type
peekFingerprint :: Ptr Fingerprint -> IO Fingerprint
peekFingerprint p0 = do
let peekW64 :: Ptr Word8 -> Int -> Word64 -> IO Word64
peekW64 _ 0 !i = return i
peekW64 !p !n !i = peekW64 (p `plusPtr` 1) (n-1) (i `shiftL` 8)
high <- peekW64 (castPtr p0) 8 0
low <- peekW64 (castPtr p0 `plusPtr` 8) 8 0
return (Fingerprint high low)
In particular notice the castPtr
application. This triggers the STG linter with,
ghc-stage1: panic! (the 'impossible' happened)
(GHC version 8.3.20170815 for x86_64-unknown-linux):
*** Stg Lint ErrMsgs: in Stg2Stg ***
<no location info>: warning:
[in body of lambda with binders p0_s2zB :: Ptr Fingerprint,
eta_s2zC :: State# RealWorld]
In a function application, function type doesn't match arg types:
Function type:
Ptr Word8
-> Int#
-> Word#
-> State# RealWorld
-> (# State# RealWorld, Word64 #)
Arg types:
Ptr Fingerprint
Int#
Word#
State# RealWorld
Expression: $wpeekW64 p0_s2zB 8# 0## eta_s2zC
This is because by the time we are in Core Prep the castPtr
is turned into a cast, which we discard in STG. Consequently, it seems that the comment attached to stgEqType
,
stgEqType :: Type -> Type -> Bool
-- Compare types, but crudely because we have discarded
-- both casts and type applications, so types might look
-- different but be the same. So reply "True" if in doubt.
-- "False" means that the types are definitely different.
--
-- Fundamentally this is a losing battle because of unsafeCoerce
is quite an understatement. Rather, there are exceedingly few cases where we can catch type errors in STG. I think the only case which we can reliably catch is that of two types with explicitly different primreps. It's not clear what we can/should do about this.