I'm making some bindings to C from Haskell, and trying to make it safer with LiquidHaskell. I'm having some trouble with specifying the length of a bytestring in the LH type annotation.
I have an enhanced ByteString type in LiquidHaskell that includes its length:
{-@ type ByteString Blen = {v:B.ByteString | bslen v == Blen} @-}
I am getting the following error when I run Liquidhaskell:
**** RESULT: UNSAFE ************************************************************
/home/t/liquidproblem/Main.hs:47:3-22: Error: Liquid Type Mismatch
47 | Bi.PS foreignPtr 0 l
^^^^^^^^^^^^^^^^^^^^
Inferred type
VV : {v : Data.ByteString.Internal.ByteString | 0 <= bslen v
&& bslen v == stringlen v}
not a subtype of Required type
VV : {VV : Data.ByteString.Internal.ByteString | bslen VV == l}
In Context
l : {l : GHC.Types.Int | l >= 0}
Line 47 is:
44 {-@ makeBs :: ForeignPtr Word8 -> l:NonNeg -> ByteString l @-}
45 makeBs :: F.ForeignPtr F.Word8 -> Int -> B.ByteString
46 makeBs foreignPtr l =
47 Bi.PS foreignPtr 0 l
(I know this seems a bit of a silly function, but it got put in because the debugging process was to factor out bits and put LH annotations on them till I found the problem.)
The relevant imports are:
import qualified Data.ByteString.Internal as Bi
import qualified Data.ByteString as B
import qualified Foreign as F
The LH NonNeg type is
{-@ type NonNeg = {i:Int | i >= 0} @-}
LiquidHaskell doesn't know that the
ByteString
returned bymakeBs
has lengthl
, and it can't prove that from the information available to it.You know it will because you know that the third argument to the
PS
constructor is the length. So at this point you have two (or possibly one) options:PS
constructor, and give it an annotation like{-@ Bi.PS :: _ -> _ -> l:NonNeg -> ByteString l @-}
. I tried this and it didn't quite work because there are internal GHC types in the signature ofPS
that LH doesn't seem so good at handling. YMMV. Or, you can:PS
and mark your own function withassume
:{-@ assume makeBs :: ForeignPtr Word8 -> l:NonNeg -> ByteString l @-}
. Of course, this gets riskier if the function marked withassume
gets any bigger—silly though it may seem, you might want to keepmakeBs
around if you go this route.