sourcecode:
|
module Common where
import Control.Monad.Trans.Class ( lift )
import Control.Monad.Trans.State ( gets )
import Data.List ( elemIndex, find, maximum )
import Contract.Names
import FlatCurry.Annotated.Goodies
import FlatCurry.TypeAnnotated.TypeSubst ( substRule )
import Language.SMTLIB.Goodies
import qualified Language.SMTLIB.Types as SMT
import Curry2SMT
import ESMT
import FlatCurry.Typed.Goodies
import FlatCurry.Typed.Names
import FlatCurry.Typed.Simplify
import FlatCurry.Typed.Types
import ToolOptions
import TransState
import VerifierState
---------------------------------------------------------------------------
-- Translates a FlatCurry expression to a Boolean formula representing
-- the binding of a variable (represented by its index in the first
-- component) to the translated expression (second component).
-- The translated expression is normalized when necessary.
-- For this purpose, a "fresh variable index" is passed as a state.
-- Moreover, the returned state contains also the types of all fresh variables.
-- If the first argument is `False`, the expression is not strictly demanded,
-- i.e., possible contracts of it (if it is a function call) are ignored.
binding2SMT :: Bool -> (Int,TAExpr) -> TransStateM SMT.Term
binding2SMT demanded (resvar,exp) =
case simpExpr exp of
AVar _ i -> return $ if resvar==i then true
else tvar resvar =% tvar i
ALit _ l -> return (tvar resvar =% lit2SMT l)
AComb rtype ct (qf,_) args -> do
(bs,nargs) <- normalizeArgs args
isStrict <- lift $ getOption optStrict
-- TODO: select from 'bindexps' only demanded argument positions
bindexps <- mapM (binding2SMT $ isPrimOp qf || isStrict) bs
comb2bool qf rtype ct nargs bs bindexps
ALet _ bs e -> do
bindexps <- mapM (binding2SMT False)
(map (\ ((i,_),ae) -> (i,ae)) bs)
bexp <- binding2SMT demanded (resvar,e)
return (tand (bindexps ++ [bexp]))
AOr _ e1 e2 -> do
bexp1 <- binding2SMT demanded (resvar,e1)
bexp2 <- binding2SMT demanded (resvar,e2)
return (tor [bexp1, bexp2])
ACase _ _ e brs -> do
freshvar <- getFreshVar
addVarTypes [(freshvar, annExpr e)]
argbexp <- binding2SMT demanded (freshvar,e)
bbrs <- mapM branch2bool (map (\b->(freshvar,b)) brs)
return (tand [argbexp, tor bbrs])
ATyped _ e _ -> binding2SMT demanded (resvar,e)
AFree _ _ _ -> error "Free variables not yet supported!"
where
comb2bool qf rtype ct nargs bs bindexps
| qf == pre "otherwise"
-- specific handling for the moment since the front end inserts it
-- as the last alternative of guarded rules...
= return (tvar resvar =% true)
| ct == ConsCall
= return (tand (bindexps ++
[tvar resvar =%
(SMT.TComb (cons2SMT (null nargs) False qf rtype)
(map arg2bool nargs))]))
| qf == pre "apply"
= -- cannot translate h.o. apply: ignore it
return true
| isPrimOp qf
= return (tand (bindexps ++
[tvar resvar =%
(SMT.TComb (cons2SMT True False qf rtype)
(map arg2bool nargs))]))
| otherwise -- non-primitive operation: add contract only if demanded
= do let targs = zip (map fst bs) (map annExpr nargs)
precond <- preCondExpOf qf targs
postcond <- postCondExpOf qf (targs ++ [(resvar,rtype)])
isCon <- lift $ getOption optConFail
return (tand (bindexps ++
if demanded && isCon
then [precond,postcond]
else []))
branch2bool (cvar, ABranch p e) = do
branchbexp <- binding2SMT demanded (resvar,e)
addVarTypes patvars
return (tand [ tvar cvar =% pat2SMT p, branchbexp])
where
patvars = if isConsPattern p
then patArgs p
else []
arg2bool e = case e of AVar _ i -> tvar i
ALit _ l -> lit2SMT l
_ -> error $ "Not normalized: " ++ show e
---------------------------------------------------------------------------
-- Returns the precondition expression for a given operation
-- and its arguments (which are assumed to be variable indices).
-- Rename all local variables by adding the `freshvar` index to them.
preCondExpOf :: QName -> [(Int,TypeExpr)] -> TransStateM SMT.Term
preCondExpOf qf args = do
preconds <- lift $ gets $ preConds . trInfo
maybe (return true)
(\fd -> applyFunc fd args >>= pred2SMT)
(find (\fd -> decodeContractQName (funcName fd)
== toPreCondQName (fromNoCheckQName qf)) preconds)
-- Returns the postcondition expression for a given operation
-- and its arguments (which are assumed to be variable indices).
-- Rename all local variables by adding `freshvar` to them and
-- return the new freshvar value.
postCondExpOf :: QName -> [(Int,TypeExpr)]
-> TransStateM SMT.Term
postCondExpOf qf args = do
postconds <- lift $ gets $ postConds . trInfo
maybe (return true)
(\fd -> applyFunc fd args >>= pred2SMT)
(find (\fd -> decodeContractQName (funcName fd)
== toPostCondQName (fromNoCheckQName qf)) postconds)
-- Drops a possible "'NOCHECK" suffix from a qualified name.
fromNoCheckQName :: (String,String) -> (String,String)
fromNoCheckQName (mn,fn) =
(mn, let rf = reverse fn
in reverse (drop (if take 8 rf == "KCEHCON'" then 8 else 0) rf))
---------------------------------------------------------------------------
-- Applies a function declaration on a list of arguments,
-- which are assumed to be variable indices, and returns
-- the renamed body of the function declaration.
-- All local variables are renamed by adding `freshvar` to them.
-- Also the new fresh variable index is returned.
applyFunc :: TAFuncDecl -> [(Int,TypeExpr)] -> TransStateM TAExpr
applyFunc fdecl targs = do
fv <- getFreshVarIndex
let tsub = maybe (error $ "applyFunc: types\n" ++
show (argTypes (funcType fdecl)) ++ "\n" ++
show (map snd targs) ++ "\ndo not match!")
id
(matchTypes (argTypes (funcType fdecl)) (map snd targs))
(ARule _ orgargs orgexp) = substRule tsub (funcRule fdecl)
exp = rnmAllVars (renameRuleVar fv orgargs) orgexp
setFreshVarIndex (max fv (maximum (0 : args ++ allVars exp) + 1))
return $ simpExpr $ applyArgs exp (drop (length orgargs) args)
where
args = map fst targs
-- renaming function for variables in original rule:
renameRuleVar fv orgargs r = maybe (r + fv)
(args!!)
(elemIndex r (map fst orgargs))
applyArgs e [] = e
applyArgs e (v:vs) =
-- eta-expansion
let et@(FuncType vt rt) = annExpr e
e_v = AComb rt FuncCall (pre "apply", FuncType et vt) [e, AVar vt v]
in applyArgs e_v vs
---------------------------------------------------------------------------
-- Translates a Boolean FlatCurry expression into a Boolean formula.
-- Calls to user-defined functions are replaced by the first argument
-- (which might be true or false).
pred2SMT :: TAExpr -> TransStateM SMT.Term
pred2SMT exp = case exp of
AVar _ i -> return (tvar i)
ALit _ l -> return (lit2SMT l)
AComb _ ct (qf,ftype) args -> comb2bool qf ftype ct (length args) args
_ -> return (tcomb (show exp) []) -- TODO!
where
comb2bool qf ftype ct ar args
| qf == pre "[]" && ar == 0
= return (sortedConst "nil" (polytype2sort (annExpr exp)))
| qf == pre "not" && ar == 1
= do barg <- pred2SMT (head args)
return (tnot barg)
| qf == pre "null" && ar == 1
= do let arg = head args
barg <- pred2SMT arg
typeOfVar arg >>= return . (barg =%) . maybe (tcomb "nil" [])
(sortedConst "nil" . polytype2sort)
| qf == pre "apply"
= do -- cannot translate h.o. apply: replace it by new variable
fvar <- getFreshVar
addVarTypes [(fvar,annExpr exp)]
return (tvar fvar)
| qf == pre "/="
= do be <- comb2bool (pre "==") ftype ct ar args
return (tnot be)
| otherwise
= do bargs <- mapM pred2SMT args
return (SMT.TComb (cons2SMT (ct /= ConsCall || not (null bargs))
False qf ftype)
bargs)
typeOfVar e =
case e of
AVar _ i -> getVarTypes >>= return . lookup i . map (\(x,y,_) -> (x,y))
_ -> return $ Just $ annExpr e
-- might not be correct due to applyFunc!
---------------------------------------------------------------------------
normalizeArgs :: [TAExpr] -> TransStateM ([(Int,TAExpr)],[TAExpr])
normalizeArgs [] = return ([],[])
normalizeArgs (e:es) = case e of
AVar _ i -> do (bs,nes) <- normalizeArgs es
return ((i,e):bs, e:nes)
_ -> do fvar <- getFreshVar
addVarTypes [(fvar,annExpr e)]
(bs,nes) <- normalizeArgs es
return ((fvar,e):bs, AVar (annExpr e) fvar : nes)
|