-
Notifications
You must be signed in to change notification settings - Fork 134
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Innocent edit causes tests/pos/Map0.hs to fail #2257
Comments
I'll have to page this back in: it has to do with some strange interaction with
Does it work if you rewrite the line as: Bin _ k x l r ->
case deleteFindMax r of
(km3, vm, rm) -> (km3, vm, (balance k x l rm)) [As an aside to @nikivazou] The thing that really bugs me about this example is that it relies completely on inference: I believe there is no way to even write down the "correct" type for |
Yes, it does still pass verification if the |
module DepTriples where
{-@ type INCR3 = (Int, Int, Int)<{\a b -> a < b}, {\b a c -> b < c }>@-}
{-@ assume incr3 :: INCR3 @-}
incr3 :: (Int, Int, Int)
incr3 = undefined
{-@ uincr3 :: INCR3 @-}
uincr3 :: (Int, Int, Int)
uincr3 = let (x, y, z) = incr3
in (x + 1, y + 1, z +1 ) Now rewrite uincr3 = let (x, y, z) = incr3 in
in (case incr3 of (x1, _, _) -> x1 + 1, y + 1, z +1 )
|
@facundominguez -- here's what I believe is going on. Consider an even smaller example: {-@ type INCR2 = (Int, Int)<{\a b -> a < b}>@-}
{-@ foo :: Int -> INCR2 @-}
incr2 :: Int -> (Int, Int)
incr2 x = (x, x + 1)
{-@ type TRUE = {b: Bool | b} @-}
{-@ test_fails :: Int -> TRUE @-}
test_fails :: Int -> Bool
test_fails a =
let p = incr2 a
x = case p of
(v1,_) -> v1 -- x gets the type x: Int
y = case p of
(_, v2) -> v2 -- y gets type of `v2`, i.e.
-- exists `v1:Int. {v:Int | v1 < v}`
-- but **don't know** that `x` is `v1`!
in
x < y -- LH doesn't know that `x` is less than `y` !
{-@ test_ok :: Int -> TRUE @-}
test_ok :: Int -> Bool
test_ok a =
let p = incr2 a
case p of
(x, y) -> x < y -- `x` gets type `x:Int` and `y` gets type `{v:Int | x < v}` In brief, when you have "dependent tuples", you need to "unpack" the tuples at one place to recover the relationships between fields. Otherwise, if you "unpack" the components separately, you lose the connection. I expect this is probably a well known (?) phenomenon with "dependent pattern matching" but I'm not familiar enough with the literature to know what to cite/point at! @nikivazou do you know? This is the kind of thing Michael Greenberg would know... |
That is an interesting clue, but I have to say that verification passes for Also, the desugaring of the original
It only fails when one of the fields is inlined (e.g. x_a111) as in the example I provided. |
In any case, thanks a lot for looking at this. |
Aha, I found something... See this: liquidhaskell/liquidhaskell-boot/src/Language/Haskell/Liquid/Transforms/Rewrite.hs Lines 199 to 259 in 77895fa
I need to page the code in |
I confess the docs in that module could be significantly improved, sorry! :-( However, looking at the I would try to run the above tests with |
|
One thought I had today: maybe it would be best for the user if LH desugared {-@ uincr3 :: INCR3 @-}
uincr3 :: (Int, Int, Int)
uincr3 = let (x, y, z) = incr3
in (x + 1, y + 1, z +1 ) as {-@ uincr3 :: INCR3 @-}
uincr3 :: (Int, Int, Int)
uincr3 = case incr3 of
(x, y, z) -> (x + 1, y + 1, z +1 ) Yes, it changes the meaning of There is a simplifyPatTuple transformation that attempts to transform core lets into a case expression too. From eval_safe in eval_safe g s (EVar x) t (E_Var {}) gs
= R_Res w t wt
where
(w, (_, wt)) = lookup_safe g s x t gs the right hand side is desugared to let {
ds_d3fQ
= case lookup_safe ds_d3ei ds_d3ej x_a1Ki ds_d3el ds_d3en of
{ (w_a1Kl, ds_d3fV) ->
case ds_d3fV of { (_, wt_a1Km) -> (w_a1Kl, wt_a1Km) }
} } in
let { w_a1Kl = case ds_d3fQ of { (w_a1Kl, _) -> w_a1Kl } } in
let { wt_a1Km = case ds_d3fQ of { (_, wt_a1Km) -> wt_a1Km } } in
R_Res w_a1Kl ds_d3el wt_a1Km which is then transformed to case lookup_safe ds_d3ei ds_d3ej x_a1Ki ds_d3el ds_d3en of
{ (w_a1Kl, ds_d3fV) ->
case ds_d3fV of { (_, wt_a1Km) -> R_Res w_a1Kl ds_d3el wt_a1Km }
} This transformation might indeed break types in some unhappy cases. But it is notable that it is already quite happy to change the evaluation order, the same as my proposed desugaring. |
I finally understood that transformation and documented it in b8ca539. I'm going to try to generalize it a bit and to make it preserve types. There are cases where the desugaring of GHC doesn't match what the transformation expects as input. |
tests/pos/Map0.hs
has a functionIf we rewrite this function to
then Liquid Haskell produces errors like,
This bug is relevant because it happens when attempting to upgrade to GHC 9.8.1, where
the desugaring of the original
deleteFindMax
changes to the failing version.I haven't been able to find a smaller example, and I don't know yet how to locate the flawed code quickly.
The text was updated successfully, but these errors were encountered: