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
recursion via data-types or references makes termination checker unsound #159
Comments
Aargh. Yes, negative-mu’s are always a hassle (also with plain sub typing since one cannot be covariant…) Good catch. Will have to deal with this… On December 2, 2013 at 6:04:36 PM, Niki Vazou (notifications@github.com) wrote:
|
Another (maybe extreme?) example where our termination checker is unsound is in recursion via references:
The credits for both the above sources of unsound termination checking go to Daan Leijen. |
That does seem quite extreme, as you'd have to infer that foo f == g before On Wednesday, December 4, 2013, Niki Vazou wrote:
|
Haha interesting! Surprised that LH does not crash on this program... :)
|
Actually my first attempt to implement it crashed, but was fixed after @gridaphobe 's edit for partial application of TyCon. It is extreme, but I added this example mostly to document a source of unsoundness. |
I agree this is extremely good to know. Actually I would also add a link to this in the README...
|
I would actually suggest splitting the |
I updated the title of the issue for now. It is not clear to me how to resolve any of them. |
This seems related to the covariant/contravariant discussion from earlier today? |
Is there a plan here? Does LH currently have a concept of unsafe types that it rejects? I guess you only want to reject these types in proofs, but not in all programs, or something... BTW, higher rank polymorphism can also lead to recursion, so a plain positivity-checker is not sufficient. |
No current plan. I think Rustan has worked around this in Dafny, but as far as I know, has not published his technique. An idea is to always generate termination error when you have negative or higher rank types? |
Yeah, that’d be the conservative approach. But it is a policy decision on your (i.e. the Liquid Haskell authors) to weigh soundness-in-all-cases vs, practicality. If you document that “Liquid Haskell is not sound in the presence of negative types and higher rank types”, then there is no bug at least, and people who shoot in their foot are welcome to do so :-) |
The conservative approach sounds good to me. We can have some mechanism to
“relax” the checks for such cases if the programmer so desires?
…On Tue, Apr 3, 2018 at 2:06 PM Joachim Breitner ***@***.***> wrote:
Yeah, that’d be the conservative approach. But it is a policy decision on
your (i.e. the Liquid Haskell authors) to weigh soundness-in-all-cases vs,
practicality. If you document that “Liquid Haskell is not sound in the
presence of negative types and higher rank types”, then there is no bug at
least, and people who shoot in their foot are welcome to do so :-)
—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub
<#159 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/ABkuOCvpNeRZtvVm1kiYnWK00gAA2nqiks5tk-OugaJpZM4BRVE1>
.
|
We independently ran into this (per email); posting the file here for posterity. A negative occurrence check would rule out the following program: module Omega where
import qualified Data.Map as Map
import Data.Map (Map, (!))
-- | Standard domain for LC + Bool
data D = B Bool | L (D -> D) | Wrong
instance Show D where
show (B b) = show b
show (L _) = "<function>"
show Wrong = "oops"
-- | Syntax for Lambda calculus
type VarName = String
data LC =
Var VarName
| App LC LC
| Lam VarName LC
| Bool Bool
| If LC LC LC
deriving (Eq, Ord, Show)
{-@ data LC [size] @-}
-- | Simple size measure
--
-- >>> size (Var "x")
-- 1
-- >>> size littleOmega
-- 4
-- >>> size omega
-- 9
size :: LC -> Int
size (Var _) = 1
size (App e1 e2) = 1 + size e1 + size e2
size (Lam x e) = 1 + size e
size (Bool _) = 1
size (If e1 e2 e3) = 1 + size e1 + size e2 + size e3
{-@ measure size @-}
{-@ size :: e:LC -> {v : Nat | v = size e} @-}
type Env = Map VarName D
-- | Standard denotational model of LC + Bool
--
-- >>> eval Map.empty (Lam "x" (Var "x"))
-- <function>
-- >>> eval Map.empty (App (Lam "x" (Var "x")) (Bool True))
-- True
{-@ eval :: Env -> e:LC -> D / [ size e ] @-}
eval :: Env -> LC -> D
eval env (Var x) = env ! x
eval env (App e1 e2) =
case eval env e1 of
L f -> f $ eval env e2
_ -> Wrong
eval env (Lam x e) = L $ \v -> eval (Map.insert x v env) e
eval _ (Bool b) = B b
eval env (If e1 e2 e3) =
case eval env e1 of
B b -> eval env (if b then e2 else e3)
_ -> Wrong
-- | We go off the rails... Liquid Haskell said we'd be safe! :(
littleOmega, omega :: LC
littleOmega = Lam "x" (App (Var "x") (Var "x"))
omega = App littleOmega littleOmega
oops = eval Map.empty omega |
High time we buttoned down and implemented this... [thanks for bumping it up @mgree !] |
Side note: that file passes as safe when I use the github version, but Ron's version from cabal breaks with a bunch of weird subtype errors. |
Adding super simple example due to Gan Shen on slack: https://liquidhaskell.slack.com/archives/C54QAL9RR/p1615244385057800 module Evil where
data Evil a = Very (Evil a -> a)
{-@ type Bot = {v: () | false} @-}
{-@ bad :: Evil Bot -> Bot @-}
bad :: Evil () -> ()
bad (Very f) = f (Very f)
{-@ worse :: Bot @-}
worse :: ()
worse = bad (Very bad) |
@nikivazou I think it should be easy to add @mgree 's suggestion: reject datatypes with negative occurrences (maybe with an escape hatch to allow it e.g. |
It seems that we can encode the fixpoint or the Y combinator (ie, recursion) with data types[1]:
Since
y
is not a recursive function liquidHaskell will trivially assume that y always terminates.With
y
we can define our unsound example (see tests/todo/Recursion.hs):So, maybe in the termination checker we should reason about data types like Rec,
ie recursive types that appear in negative positions. Concretely, functions that open such types should be considered as diverging.
[1] http://en.wikipedia.org/wiki/Fixed-point_combinator
The text was updated successfully, but these errors were encountered: