-
Notifications
You must be signed in to change notification settings - Fork 62
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
Unfold-unfolded definitions when the extentionality flag is active #704
Conversation
I have ran all the tests in the liquid-haskell repo and they are passing |
👋 Hello @AlecsFerra. The following works for me. {-@ LIQUID "--ple" @-}
module Example where
{-@ reflect f @-}
f :: Int -> Int
f x = 13
{-@ reflect g @-}
g :: Int -> Int -> Int
g x y = f y
{-@ reflect k @-}
k :: Int -> Int -> Int
k x y = 13
{-@ complexProof' :: x:Int -> y:Int -> { g x y = k x y } @-}
complexProof' :: Int -> Int -> ()
complexProof' _ _ = () Enabling I don't know how to deal with the lambda in the first lemma. But my intuition is that functions like |
The thing is that the extensionality flag is adding some "fake" variables that prevent the unfoldings |
So it should pass without adding the extra arguments to the proof |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the PR.
I need some help understanding the problem, I think. My comment below contains my first questions.
Also, before merging, looks like some new tests are going to be needed here and in liquidhaskell.
src/Language/Fixpoint/Solver/PLE.hs
Outdated
@@ -458,6 +460,8 @@ data EvalEnv = EvalEnv | |||
-- an expression | |||
, evSMTCache :: M.HashMap Expr Bool -- ^ Whether an expression is valid or its negation | |||
, evFuel :: FuelCount | |||
, unfoldedDefinitions :: S.HashSet Symbol -- ^ Functions that we have already unfolded |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thinking about documentation:
unfoldedDefinitions
seems to be used to inhibit unfolding of some saturated function application, for instance length xs
. However, it would also inhibit unfolding of length ys
. Is this necessary?
Also, the scope in which a function is inhibited from unfolding needs to be made explicit. It seems to be inhibited only when scanning for more applications to unfold, in the result of the first unfolding (eval γ ctx et e3'
below). Why?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Technically we could keep in the unfolded set of also arguments of the call (Symbol, [Expr])
but I'm worried that it could be tricked to end in an infinite loop somehow since the termination checking happens after this code is ran
I don't understand your second comment sorry
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
but I'm worried that it could be tricked to end in an infinite loop somehow
How would PLE enter an infinite loop if unfoldedDefinitions
isn't introduced at all?
I don't understand your second comment sorry
This ties to the motivation of unfoldedDefinitions
which isn't clear to me yet. Symbols aren't kept in unfoldedDefinitions
forever, they are removed with unsetUnfolded
. And I'm asking how did you choose when to call unsetUnfolded
.
For the proofs I wanted to wait for some comments about the PR (both on the implementation and on the idea) hence why is marked as a draft,
I hadn't noticed this PR was marked as a draft. Collecting feedback sounds good.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Take as an example
{-@ LIQUID "--reflection" @-}
{-@ LIQUID "--ple" @-}
{-@ LIQUID "--full" @-}
{-@ LIQUID "--extensionality" @-}
{-@ LIQUID "--ple-with-undecided-guards" @-}
module Example where
{-@ reflect f @-}
f :: Int -> Int
f n = if n > 0 then f n + n else 0
{-@ reflect g @-}
g :: Int -> Int -> Int
g x y = f 3
{-@ complexProof :: { g = (\x:Int -> (\y:Int -> 3)) } @-}
complexProof :: ()
complexProof = ()
Even tough LH is able to infer that f is non terminating, LH will try to look for some other errors so when trying to prove complexProof
will start with the unfolding of g so it will try to unfold f 3 so it will try to unfold then then branch of the body of f and so on till we finish the space for the call stack.
You can try it by your self by commenting away L807.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Cool thing :) we have infinite loops only in non terminating programs we dont have infinite loops in programs that are safe
{-@ LIQUID "--reflection" @-}
{-@ LIQUID "--ple" @-}
{-@ LIQUID "--full" @-}
{-@ LIQUID "--extensionality" @-}
{-@ LIQUID "--ple-with-undecided-guards" @-}
module Example where
{-@ reflect f @-}
f :: Int -> Int
f n = if n > 0 then f (n - 1) + n else 0
{-@ reflect g @-}
g :: Int -> Int -> Int
g x y = f 3
{-@ complexProof :: { g = (\x:Int -> (\y:Int -> 6)) } @-}
complexProof :: ()
complexProof = ()
For example this passes even without keeping track of the unfolded definitions
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If its the case then I guess we can perform the same modification in the case I added at line 868, removing the recursive call call and modifying the return
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we can perform the same modification in the case I added at line 868
The return in that case looks correct to me.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah but I'm calling eval inside that case maybe it isn't needed if PLE decides by looking at what is returned it it wants to unfold again
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Btw regarding making PLE diverge i think its our job to make LH terminate anyway, also before I added the infinite loop check, some test in the liquidhaskell repo was entering in the same infinite loop
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Happy to discuss that further, but I don't think it affects the contribution in this PR. I'd propose to open a separate issue or PR for that sake.
The problem is the following: morally {-@ complexProof' :: x:Int -> y:Int -> { g x y = k x y } @-}
complexProof' :: Int -> Int -> ()
complexProof' _ _ = () and {-@ complexProof' :: { g = k } @-}
complexProof' :: ()
complexProof' = () Have two different statements. we would like to LH to prove by itself the second statement as it's a common class of proofs that we have to do when performing higher order reasoning in LH. As part of a project I'm proving the correctness of compiling terms of the lambda calculus to the SKI combinators: See: https://gist.github.com/AlecsFerra/9d48ae08f63d8192b2267d0e378f8c09 |
For the proofs I wanted to wait for some comments about the PR (both on the implementation and on the idea) hence why is marked as a draft, I've already discussed about this with @nikivazou |
Also forgot to mention this code works even if we drop the check for the extensionality flag, I've already tested running the tests in the main liquidhaskell repo, is just making the typechecker slower |
Great! Let's not merge yet then, and keep it as draft until we come up with more tests.
The tests on LH should not be slower if the extra unfoldings happen only when the extensionality flag is on, correct? |
After the discussion with @facundominguez yesterday the fact that PLE wasn't unfolding recursively was a bug in PLE, so I decided to deleted my code that was dealing with function application and implement the fix that was suggested in the comments above. I left the code that handles lambdas untouched and behind the extensionality flag. |
Let me know if i should squash the commits |
Regarding tests I think that we can reuse all the snippets in the discussion + the whole SKI compilation file for the liquid haskell main repo. For the liquid fixpoint repo first I have to study the file format |
I modified the case for the lambda application to make it behave the same as normal function application, I don't know if we have to handle the undecided guards thing |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The code is looking good to me.
Edited this answer after posting, see next comment. |
hm, length ys = if null ys then 0 else length ys + 1 unless the For lambda expressions (\xs -> if null xs then e0 else e1) ys I think the question is: does it help most of the time to add an equality (\xs -> if null xs then e0 else e1) ys = if null ys then e0 else e1 when the condition |
No It helps as that equality is encoding "eta-equivalence" that before my PR it wasn't provable in LH |
How do I get the .fq files? I can't find them in the |
|
Let me know if those tests are enough |
When trying to prove pretty trivial statements PLE gives up because is not unfolding enough the definitions or lambdas: see the following examples: