First of all, I don't know which version of LH you were using but I just had the exact same problem. The tutorial states that
LiquidHaskell verifies vectorSum – or, to be precise, the safety of
the vector accesses vec ! i. The verification works out because
LiquidHaskell is able to automatically infer
go :: Int -> {v:Int | 0 <= v && v <= sz} -> Int
which states that the second parameter i is between 0 and the length
of vec (inclusive). LiquidHaskell uses this and the test that i < sz
to establish that i is between 0 and (vlen vec) to prove safety.
They also state that the tutorial is not in accordance with the current version of LiquidHaskell.
It seems that the (refinement-)type inference of LH has changed a bit since the tutorial was written, probably generalizing types more than before, which results in this problem.
The problem is NOT that LH doesn't figure out the guard properly. The problem is that it fails to verify the property 0 <= v
.
The following checks fine with version 0.8.6.2 of LH:
{-@ LIQUID "--short-names" @-}
{-@ LIQUID "--no-termination" @-}
{-@ LIQUID "--reflection" @-}
import Prelude hiding (length)
import Data.Vector
{-@ go' :: v:Vector Int -> Int -> {i:Int | i>=0 } ->Int @-}
go' :: Vector Int -> Int -> Int -> Int
go' vec acc i
| i < sz = go' vec (acc + (vec ! i)) (i + 1)
| otherwise = acc
where sz = length vec
vecSum :: Vector Int -> Int
vecSum vec = go' vec 0 0
It seems that LH infers thas go is a function in its own right and might be called with an integer smaller than 0 (which it obviously isn't).
I am still playing with that example to convince LH of this fact. If anybody had more success on this please leave a comment.
EDIT:
I found the following paragraph in the same tutorial; It seems that this might have changed:
At the call loop 0 n 0 body
the parameters lo
and hi
are instantiated
with 0
and n
respectively, which, by the way is where the inference
engine deduces non-negativity.