{-# LANGUAGE CPP #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE NoMonomorphismRestriction #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE UndecidableInstances #-} {-# LANGUAGE ViewPatterns #-} {-# OPTIONS_GHC -Wno-orphans #-} {-# LANGUAGE TupleSections #-} {-# LANGUAGE InstanceSigs #-} module Language.Fixpoint.Smt.Theories ( -- * Convert theory applications TODO: merge with smt2symbol smt2App -- * Convert theory sorts , sortSmtSort -- * Convert theory symbols , smt2Symbol -- * Preamble to initialize SMT , preamble -- * Bit Vector Operations , sizeBv -- , toInt -- * Theory Symbols , theorySymbols , dataDeclSymbols -- * Theories , setEmpty, setEmp, setSng, setAdd, setMem, setCard , setCom, setCap, setCup, setDif, setSub , mapDef, mapSel, mapSto , bagEmpty, bagSng, bagCount, bagSub, bagCup, bagMax, bagMin -- * Z3 theory array encodings , arrConstM, arrStoreM, arrSelectM , arrConstS, arrStoreS, arrSelectS , arrMapNotS, arrMapOrS, arrMapAndS, arrMapImpS , arrConstB, arrStoreB, arrSelectB , arrMapPlusB, arrMapLeB, arrMapGtB, arrMapIteB -- * CVC5 finite fields , ffVal, ffAdd, ffMul -- * Query Theories , isSmt2App , axiomLiterals , maxLamArg ) where import Prelude hiding (map) import Control.Monad.State import Data.ByteString.Builder (Builder) import Language.Fixpoint.Types.Sorts import Language.Fixpoint.Types.Config import Language.Fixpoint.Types import Language.Fixpoint.Smt.Types -- import qualified Data.HashMap.Strict as M import Data.Maybe (catMaybes) -- import Data.Text.Format import qualified Data.Text import Data.String (IsString(..)) import Text.Printf (printf) import Language.Fixpoint.Utils.Builder {- | [NOTE:Adding-Theories] To add new (SMTLIB supported) theories to liquid-fixpoint and upstream, grep for "Map_default" and then add your corresponding symbol in all those places. This is currently far more complicated than it needs to be. -} -------------------------------------------------------------------------------- -- | Theory Symbols ------------------------------------------------------------ -------------------------------------------------------------------------------- ---- Size changes bvConcatName, bvExtractName, bvRepeatName, bvZeroExtName, bvSignExtName :: Symbol bvConcatName = "concat" bvExtractName = "extract" bvRepeatName = "repeat" bvZeroExtName = "zero_extend" bvSignExtName = "sign_extend" -- Unary Logic bvNotName, bvNegName :: Symbol bvNotName = "bvnot" bvNegName = "bvneg" -- Binary Logic bvAndName, bvNandName, bvOrName, bvNorName, bvXorName, bvXnorName :: Symbol bvAndName = "bvand" bvNandName = "bvnand" bvOrName = "bvor" bvNorName = "bvnor" bvXorName = "bvxor" bvXnorName = "bvxnor" -- Shifts bvShlName, bvLShrName, bvAShrName, bvLRotName, bvRRotName :: Symbol bvShlName = "bvshl" bvLShrName = "bvlshr" bvAShrName = "bvashr" bvLRotName = "rotate_left" bvRRotName = "rotate_right" -- Arithmetic bvAddName, bvSubName, bvMulName, bvUDivName :: Symbol bvURemName, bvSDivName, bvSRemName, bvSModName :: Symbol bvAddName = "bvadd" bvSubName = "bvsub" bvMulName = "bvmul" bvUDivName = "bvudiv" bvURemName = "bvurem" bvSDivName = "bvsdiv" bvSRemName = "bvsrem" bvSModName = "bvsmod" -- Comparisons bvCompName, bvULtName, bvULeName, bvUGtName, bvUGeName :: Symbol bvSLtName, bvSLeName, bvSGtName, bvSGeName :: Symbol bvCompName = "bvcomp" bvULtName = "bvult" bvULeName = "bvule" bvUGtName = "bvugt" bvUGeName = "bvuge" bvSLtName = "bvslt" bvSLeName = "bvsle" bvSGtName = "bvsgt" bvSGeName = "bvsge" mapDef, mapSel, mapSto :: (IsString a) => a mapDef = "Map_default" mapSel = "Map_select" mapSto = "Map_store" setCard, setEmpty, setEmp, setCap, setSub, setAdd, setMem, setCom, setCup, setDif, setSng :: (IsString a) => a setCard = "Set_card" setEmpty = "Set_empty" setEmp = "Set_emp" setCap = "Set_cap" setSub = "Set_sub" setAdd = "Set_add" setMem = "Set_mem" setCom = "Set_com" setCup = "Set_cup" setDif = "Set_dif" setSng = "Set_sng" bagEmpty, bagSng, bagCount, bagSub, bagCup, bagMax, bagMin :: (IsString a) => a bagEmpty = "Bag_empty" bagSng = "Bag_sng" bagCount = "Bag_count" bagSub = "Bag_sub" bagCup = "Bag_union" bagMax = "Bag_union_max" -- See [Bag max and min] bagMin = "Bag_inter_min" -- [Bag max and min] -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- Functions bagMax and bagMin: Union/intersect two bags, combining the elements by -- taking either the greatest (bagMax) or the least (bagMin) of them. -- bagMax, bagMin : Map v Int -> Map v Int -> Map v Int --- Array operations for polymorphic maps arrConstM, arrStoreM, arrSelectM :: Symbol arrConstM = "arr_const_m" arrStoreM = "arr_store_m" arrSelectM = "arr_select_m" --- Array operations for sets (Z3) arrConstS, arrStoreS, arrSelectS, arrMapNotS, arrMapOrS, arrMapAndS, arrMapImpS :: Symbol arrConstS = "arr_const_s" arrStoreS = "arr_store_s" arrSelectS = "arr_select_s" arrMapNotS = "arr_map_not" arrMapOrS = "arr_map_or" arrMapAndS = "arr_map_and" arrMapImpS = "arr_map_imp" --- Array operations for bags (Z3) arrConstB, arrStoreB, arrSelectB :: Symbol arrConstB = "arr_const_b" arrStoreB = "arr_store_b" arrSelectB = "arr_select_b" arrMapPlusB, arrMapLeB, arrMapGtB, arrMapIteB :: Symbol arrMapPlusB = "arr_map_plus" arrMapLeB = "arr_map_le" arrMapGtB = "arr_map_gt" arrMapIteB = "arr_map_ite" -- Finite field operations ffVal, ffAdd, ffMul :: (IsString a) => a -- Symbol ffVal = "FF_val" ffAdd = "FF_add" ffMul = "FF_mul" strLen, strSubstr, strConcat, strConcat', strPrefixOf, strSuffixOf, strContains :: (IsString a) => a -- Symbol strLen = "strLen" strSubstr = "subString" strConcat = "concatString" strConcat' = "strConcat" strPrefixOf = "strPrefixOf" strSuffixOf = "strSuffixOf" strContains = "strContains" smtlibStrLen, smtlibStrSubstr, smtlibStrConcat, smtlibStrPrefixOf, smtlibStrSuffixOf, smtlibStrContains :: Raw smtlibStrLen = "str.len" smtlibStrSubstr = "str.substr" smtlibStrConcat = "str.++" smtlibStrPrefixOf = "str.prefixof" smtlibStrSuffixOf = "str.suffixof" smtlibStrContains = "str.contains" strLenSort, substrSort, concatstrSort, strCompareSort :: Sort strLenSort = FFunc strSort intSort substrSort = mkFFunc 0 [strSort, intSort, intSort, strSort] concatstrSort = mkFFunc 0 [strSort, strSort, strSort] strCompareSort = mkFFunc 0 [strSort, strSort, boolSort] string :: Raw string = strConName bFun :: Raw -> [(Builder, Builder)] -> Builder -> Builder -> Builder bFun name xts out body = key "define-fun" (seqs [fromText name, args, out, body]) where args = parenSeqs [parens (x <+> t) | (x, t) <- xts] bFun' :: Raw -> [Builder] -> Builder -> Builder bFun' name ts out = key "declare-fun" (seqs [fromText name, args, out]) where args = parenSeqs ts bSort :: Raw -> Builder -> Builder bSort name def = key "define-sort" (fromText name <+> "()" <+> def) -- RJ: Am changing this to `Int` not `Real` as (1) we usually want `Int` and -- (2) have very different semantics. TODO: proper overloading, post genEApp uifDef :: Config -> Data.Text.Text -> Data.Text.Text -> Builder uifDef cfg f op | onlyLinearArith cfg -- linear cfg || Z3 /= solver cfg = bFun' f ["Int", "Int"] "Int" | otherwise = bFun f [("x", "Int"), ("y", "Int")] "Int" (key2 (fromText op) "x" "y") onlyLinearArith :: Config -> Bool onlyLinearArith cfg = linear cfg || solver cfg `notElem` [Z3, Z3mem, Cvc5] preamble :: Config -> SMTSolver -> [Builder] preamble cfg s = snd <$> filter (matchesCondition s . fst) (solverPreamble cfg) matchesCondition :: SMTSolver -> PreambleCondition -> Bool matchesCondition _ SAll = True matchesCondition s (SOnly ss) = s `elem` ss solverPreamble :: Config -> [Preamble] solverPreamble cfg = [ (SOnly [Z3, Z3mem], "(set-option :auto-config false)") , (SOnly [Z3, Z3mem], "(set-option :model true)") , (SOnly [Cvc4], "(set-logic ALL_SUPPORTED)") , (SOnly [Cvc5], "(set-logic ALL)") , (SOnly [Cvc4, Cvc5], "(set-option :incremental true)") ] ++ setPreamble cfg ++ boolPreamble cfg ++ arithPreamble cfg ++ stringPreamble cfg type Preamble = (PreambleCondition, Builder) data PreambleCondition = SAll | SOnly [SMTSolver] deriving (Eq, Show) setPreamble :: Config -> [Preamble] -- Z3 does not support cardinality on sets, which is defined to be uninterpreted function setPreamble _ = [ (SOnly [Z3, Z3mem], bFun' "set.card" ["(Array Int Bool)"] "Int") ] boolPreamble :: Config -> [Preamble] boolPreamble _ = [ (SAll, bFun boolToIntName [("b", "Bool")] "Int" "(ite b 1 0)") ] arithPreamble :: Config -> [Preamble] arithPreamble cfg = (SAll,) <$> [ uifDef cfg (symbolText mulFuncName) "*" , uifDef cfg (symbolText divFuncName) "div" ] stringPreamble :: Config -> [Preamble] stringPreamble cfg | not (noStringTheory cfg) = [ (SAll, bSort string "String") , (SAll, bFun strLen [("s", fromText string)] "Int" (key (fromText smtlibStrLen) "s")) , (SAll, bFun strSubstr [("s", fromText string), ("i", "Int"), ("j", "Int")] (fromText string) (key (fromText smtlibStrSubstr) "s i j")) , (SAll, bFun strConcat [("x", fromText string), ("y", fromText string)] (fromText string) (key (fromText smtlibStrConcat) "x y")) ] stringPreamble _ = [ (SAll, bSort string "Int") , (SAll, bFun' strLen [fromText string] "Int") , (SAll, bFun' strSubstr [fromText string, "Int", "Int"] (fromText string)) , (SAll, bFun' strConcat [fromText string, fromText string] (fromText string)) ] -------------------------------------------------------------------------------- -- | Exported API -------------------------------------------------------------- -------------------------------------------------------------------------------- smt2Symbol :: SymEnv -> Symbol -> Maybe Builder smt2Symbol env x = fromText . tsRaw <$> symEnvTheory x env instance SMTLIB2 SmtSort where smt2 s = pure $ smt2SmtSort s smt2SmtSort :: SmtSort -> Builder smt2SmtSort SInt = "Int" smt2SmtSort SReal = "Real" smt2SmtSort SBool = "Bool" smt2SmtSort SString = fromText string smt2SmtSort (SSet a) = key "Set" (smt2SmtSort a) smt2SmtSort (SBag a) = key "Bag" (smt2SmtSort a) smt2SmtSort (SArray a b) = key2 "Array" (smt2SmtSort a) (smt2SmtSort b) smt2SmtSort (SFFld n) = key "_ FiniteField" (bShow n) smt2SmtSort (SBitVec n) = key "_ BitVec" (bShow n) smt2SmtSort (SVar n) = "T" <> bShow n smt2SmtSort (SData c []) = symbolBuilder c smt2SmtSort (SData c ts) = parenSeqs [symbolBuilder c, smt2SmtSorts ts] -- smt2SmtSort (SApp ts) = build "({} {})" (symbolBuilder tyAppName, smt2SmtSorts ts) smt2SmtSorts :: [SmtSort] -> Builder smt2SmtSorts = seqs . fmap smt2SmtSort type VarAs = Symbol -> Sort -> SymM Builder -------------------------------------------------------------------------------- smt2App :: VarAs -> Expr -> [Builder] -> SymM (Maybe Builder) -------------------------------------------------------------------------------- smt2App _ ex@(dropECst -> EVar f) [d] | f == arrConstS || f == arrConstB || f == arrConstM = do env <- get pure $ Just $ key (key "as const" (getTarget env ex)) d | f == setEmpty = do env <- get pure $ Just $ key "as set.empty" (getTarget env ex) | f == bagEmpty = do env <- get pure $ Just $ key "as bag.empty" (getTarget env ex) | f == ffVal = do env <- get pure $ Just $ key ("as ff" <> d) (getTarget env ex) where getTarget :: SymEnv -> Expr -> Builder -- const is a function, but SMT expects only the output sort getTarget env (ECst _ t) = smt2SmtSort $ sortSmtSort True (seData env) (ffuncOut t) getTarget _ e = bShow e smt2App k ex (builder:builders) = do a <- smt2AppArg k ex pure $ (\fb -> key fb (builder <> mconcat [ " " <> d | d <- builders])) <$> a smt2App _ _ [] = pure Nothing smt2AppArg :: VarAs -> Expr -> SymM (Maybe Builder) smt2AppArg k (ECst (dropECst -> EVar f) t) = do env <- get case symEnvTheory f env of Just fThy -> if isPolyCtor fThy t then Just <$> k f (ffuncOut t) else pure $ Just $ fromText (tsRaw fThy) Nothing -> pure Nothing smt2AppArg _ _ = pure Nothing isPolyCtor :: TheorySymbol -> Sort -> Bool isPolyCtor fThy t = isPolyInst (tsSort fThy) t && tsInterp fThy == Ctor ffuncOut :: Sort -> Sort ffuncOut t = maybe t (last . snd) (bkFFunc t) -------------------------------------------------------------------------------- isSmt2App :: SEnv TheorySymbol -> Expr -> Maybe Int -------------------------------------------------------------------------------- isSmt2App g (dropECst -> EVar f) = lookupSEnv f g >>= thyAppInfo isSmt2App _ _ = Nothing thyAppInfo :: TheorySymbol -> Maybe Int thyAppInfo ti = case tsInterp ti of Field -> Just 1 _ -> sortAppInfo (tsSort ti) sortAppInfo :: Sort -> Maybe Int sortAppInfo t = case bkFFunc t of Just (_, ts) -> Just (length ts - 1) Nothing -> Nothing -------------------------------------------------------------------------------- -- | Theory Symbols : `uninterpSEnv` should be disjoint from see `interpSEnv` -- to avoid duplicate SMT definitions. `uninterpSEnv` is for uninterpreted -- symbols, and `interpSEnv` is for interpreted symbols. -------------------------------------------------------------------------------- instance TheorySymbols SMTSolver where theorySymbols :: SMTSolver -> SEnv TheorySymbol theorySymbols = fromListSEnv . interpSymbols instance TheorySymbols [DataDecl] where theorySymbols :: [DataDecl] -> SEnv TheorySymbol theorySymbols = fromListSEnv . concatMap dataDeclSymbols instance TheorySymbols [Equation] where theorySymbols = fromListSEnv . fmap equationSymbol instance TheorySymbols DefinedFuns where theorySymbols (MkDefinedFuns eqns) = theorySymbols eqns equationSymbol :: Equation -> (Symbol, TheorySymbol) equationSymbol eq = (sym, Thy sym (symbolRaw sym) sort Defined) where sym = eqName eq sort = mkFFunc 0 ((snd <$> eqArgs eq) <> [eqSort eq]) -------------------------------------------------------------------------------- interpSymbols :: SMTSolver -> [(Symbol, TheorySymbol)] -------------------------------------------------------------------------------- interpSymbols cfg = [ -- maps interpSym mapDef mapDef mapDefSort , interpSym mapSel mapSel mapSelSort , interpSym mapSto mapSto mapStoSort , interpSym arrConstM "const" (FAbs 0 $ FFunc (FVar 1) mapArrSort) , interpSym arrSelectM "select" (FAbs 0 $ FFunc mapArrSort $ FFunc (FVar 0) (FVar 1)) , interpSym arrStoreM "store" (FAbs 0 $ FFunc mapArrSort $ FFunc (FVar 0) $ FFunc (FVar 1) mapArrSort) -- CVC5 sets , interpSym setCard "set.card" (FAbs 0 $ FFunc (setSort $ FVar 0) intSort) , interpSym setEmp "set.is_empty" (FAbs 0 $ FFunc (setSort $ FVar 0) boolSort) , interpSym setEmpty "set.empty" (FAbs 0 $ FFunc intSort (setSort $ FVar 0)) , interpSym setSng "set.singleton" (FAbs 0 $ FFunc (FVar 0) (setSort $ FVar 0)) , interpSym setAdd "set.insert" (FAbs 0 $ FFunc (FVar 0) $ FFunc (setSort $ FVar 0) (setSort $ FVar 0)) , interpSym setMem "set.member" (FAbs 0 $ FFunc (FVar 0) $ FFunc (setSort $ FVar 0) boolSort) , interpSym setCup "set.union" setBopSort , interpSym setCap "set.inter" setBopSort , interpSym setDif "set.minus" setBopSort , interpSym setSub "set.subset" (FAbs 0 $ FFunc (setSort $ FVar 0) $ FFunc (setSort $ FVar 0) boolSort) , interpSym setCom "set.complement" (FAbs 0 $ FFunc (setSort $ FVar 0) (setSort $ FVar 0)) -- CVC5 bags , interpSym bagEmpty "bag.empty" (FAbs 0 $ FFunc intSort (bagSort $ FVar 0)) , interpSym bagSng "bag" (FAbs 0 $ FFunc (FVar 0) $ FFunc intSort (bagSort $ FVar 0)) , interpSym bagCount "bag.count" (FAbs 0 $ FFunc (FVar 0) $ FFunc (bagSort $ FVar 0) intSort) , interpSym bagCup "bag.union_disjoint" bagBopSort , interpSym bagMax "bag.union_max" bagBopSort , interpSym bagMin "bag.inter_min" bagBopSort , interpSym bagSub "bag.subbag" (FAbs 0 $ FFunc (bagSort $ FVar 0) $ FFunc (bagSort $ FVar 0) boolSort) -- Strings , interpSym strLen strLen strLenSort , interpSym strSubstr strSubstr substrSort , interpSym strConcat strConcat concatstrSort , interpSym strConcat' smtlibStrConcat concatstrSort , interpSym strPrefixOf smtlibStrPrefixOf strCompareSort , interpSym strSuffixOf smtlibStrSuffixOf strCompareSort , interpSym strContains smtlibStrContains strCompareSort , interpSym boolInt boolInt (FFunc boolSort intSort) -- Function mappings for indexed identifier functions , interpSym' "_" iiSort , interpSym "app" "" appSort , interpSym' bvConcatName bvConcatSort , interpSym' bvExtractName (FFunc FInt bvExtendSort) , interpBvExt bvRepeatName , interpBvExt bvZeroExtName , interpBvExt bvSignExtName , interpBvUop bvNotName , interpBvUop bvNegName , interpBvBop bvAndName , interpBvBop bvNandName , interpBvBop bvOrName , interpBvBop bvNorName , interpBvBop bvXorName , interpBvBop bvXnorName , interpBvBop bvShlName , interpBvBop bvLShrName , interpBvBop bvAShrName , interpBvRot bvLRotName , interpBvRot bvRRotName , interpBvBop bvAddName , interpBvBop bvSubName , interpBvBop bvMulName , interpBvBop bvUDivName , interpBvBop bvURemName , interpBvBop bvSDivName , interpBvBop bvSRemName , interpBvBop bvSModName , interpSym' bvCompName bvEqSort , interpBvCmp bvULtName , interpBvCmp bvULeName , interpBvCmp bvUGtName , interpBvCmp bvUGeName , interpBvCmp bvSLtName , interpBvCmp bvSLeName , interpBvCmp bvSGtName , interpBvCmp bvSGeName -- int to bv Conversions , interpSym intbv32Name "(_ int2bv 32)" (FFunc intSort bv32) , interpSym intbv64Name "(_ int2bv 64)" (FFunc intSort bv64) , interpSym bv32intName (bv2i cfg 32) (FFunc bv32 intSort) , interpSym bv64intName (bv2i cfg 64) (FFunc bv64 intSort) , interpSym intbv8Name "(_ int2bv 8)" (FFunc intSort bv8) , interpSym intbv16Name "(_ int2bv 16)" (FFunc intSort bv16) , interpSym bv8intName (bv2i cfg 32) (FFunc bv8 intSort) , interpSym bv16intName (bv2i cfg 64) (FFunc bv16 intSort) ] ++ if cfg == Z3 || cfg == Z3mem then [ -- Z3 sets (arrays of bools) interpSym arrConstS "const" (FAbs 0 $ FFunc boolSort setArrSort) , interpSym arrSelectS "select" (FAbs 0 $ FFunc setArrSort $ FFunc (FVar 0) boolSort) , interpSym arrStoreS "store" (FAbs 0 $ FFunc setArrSort $ FFunc (FVar 0) $ FFunc boolSort setArrSort) , interpSym arrMapNotS "(_ map not)" (FAbs 0 $ FFunc setArrSort setArrSort) , interpSym arrMapOrS "(_ map or)" (FAbs 0 $ FFunc setArrSort $ FFunc setArrSort setArrSort) , interpSym arrMapAndS "(_ map and)" (FAbs 0 $ FFunc setArrSort $ FFunc setArrSort setArrSort) , interpSym arrMapImpS "(_ map =>)" (FAbs 0 $ FFunc setArrSort $ FFunc setArrSort setArrSort) -- Z3 bags (arrays of ints) , interpSym arrConstB "const" (FAbs 0 $ FFunc intSort bagArrSort) , interpSym arrSelectB "select" (FAbs 0 $ FFunc bagArrSort $ FFunc (FVar 0) intSort) , interpSym arrStoreB "store" (FAbs 0 $ FFunc bagArrSort $ FFunc (FVar 0) $ FFunc intSort bagArrSort) , interpSym arrMapPlusB "(_ map (+ (Int Int) Int))" (FAbs 0 $ FFunc bagArrSort $ FFunc bagArrSort bagArrSort) , interpSym arrMapLeB "(_ map (<= (Int Int) Bool))" (FAbs 0 $ FFunc bagArrSort $ FFunc bagArrSort setArrSort) , interpSym arrMapGtB "(_ map (> (Int Int) Bool))" (FAbs 0 $ FFunc bagArrSort $ FFunc bagArrSort setArrSort) , interpSym arrMapIteB "(_ map (ite (Bool Int Int) Int))" (FAbs 0 $ FFunc setArrSort $ FFunc bagArrSort $ FFunc bagArrSort bagArrSort) ] else if cfg == Cvc5 then [ -- CVC5 finite fields interpSym ffVal ffVal (FAbs 0 $ FFunc intSort (finfieldSort (FVar 0))) , interpSym ffAdd "ff.add" (FAbs 0 $ FFunc (finfieldSort (FVar 0)) $ FFunc (finfieldSort (FVar 0)) (finfieldSort (FVar 0))) , interpSym ffMul "ff.mul" (FAbs 0 $ FFunc (finfieldSort (FVar 0)) $ FFunc (finfieldSort (FVar 0)) (finfieldSort (FVar 0))) ] else [] where mapArrSort = arraySort (FVar 0) (FVar 1) setArrSort = arraySort (FVar 0) boolSort bagArrSort = arraySort (FVar 0) intSort bv8 = sizedBitVecSort "Size8" bv16 = sizedBitVecSort "Size16" bv32 = sizedBitVecSort "Size32" bv64 = sizedBitVecSort "Size64" boolInt = boolToIntName mapDefSort = FAbs 0 $ FAbs 1 $ FFunc (FVar 1) (mapSort (FVar 0) (FVar 1)) -- select :: forall k v. Map k v -> k -> v mapSelSort = FAbs 0 $ FAbs 1 $ FFunc (mapSort (FVar 0) (FVar 1)) $ FFunc (FVar 0) (FVar 1) -- store :: forall k v. Map k v -> k -> v -> Map k v mapStoSort = FAbs 0 $ FAbs 1 $ FFunc (mapSort (FVar 0) (FVar 1)) $ FFunc (FVar 0) $ FFunc (FVar 1) (mapSort (FVar 0) (FVar 1)) setBopSort = FAbs 0 $ FFunc (setSort $ FVar 0) $ FFunc (setSort $ FVar 0) (setSort $ FVar 0) bagBopSort = FAbs 0 $ FFunc (bagSort $ FVar 0) $ FFunc (bagSort $ FVar 0) (bagSort $ FVar 0) bv2i :: SMTSolver -> Int -> Raw bv2i Cvc4 _ = "bv2nat" bv2i Cvc5 _ = "bv2nat" bv2i _ n = Data.Text.pack $ printf "(_ bv2nat %d)" n interpBvUop :: Symbol -> (Symbol, TheorySymbol) interpBvUop name = interpSym' name bvUopSort interpBvBop :: Symbol -> (Symbol, TheorySymbol) interpBvBop name = interpSym' name bvBopSort interpBvCmp :: Symbol -> (Symbol, TheorySymbol) interpBvCmp name = interpSym' name bvCmpSort interpBvExt :: Symbol -> (Symbol, TheorySymbol) interpBvExt name = interpSym' name bvExtendSort interpBvRot :: Symbol -> (Symbol, TheorySymbol) interpBvRot name = interpSym' name bvRotSort interpSym' :: Symbol -> Sort -> (Symbol, TheorySymbol) interpSym' name = interpSym name (Data.Text.pack $ symbolString name) -- Indexed Identifier sort. -- Together with 'app', this allows one to write indexed identifier -- functions (smtlib2 specific functions). (e.g. ((_ sign_extend 1) bv)) -- -- The idea here is that 'app' is elaborated to the empty string, -- and '_' does the typelit application as it does in smtlib2. -- -- Then if we write, (app (_ sign_extend 1) bv), LF will elaborate -- it as ( (_ sign_extend 1) bv). Fitting the smtlib2 format exactly! -- -- One thing to note, is that any indexed identifier function (like -- sign_extend) has to have no FAbs in it. Otherwise, they will be -- elaborated like e.g. ( (_ (as sign_extend Int) 1) bv), which is wrong! -- -- _ :: forall a b c. (a -> b -> c) -> a -> (b -> c) iiSort :: Sort iiSort = FAbs 0 $ FAbs 1 $ FAbs 2 $ FFunc (FFunc (FVar 0) $ FFunc (FVar 1) (FVar 2)) (FFunc (FVar 0) $ FFunc (FVar 1) (FVar 2)) -- Simple application, used for indexed identifier function, check '_'. -- -- app :: forall a b. (a -> b) -> a -> b appSort :: Sort appSort = FAbs 0 $ FAbs 1 $ FFunc (FFunc (FVar 0) (FVar 1)) (FFunc (FVar 0) (FVar 1)) -- Indexed identifier operation, purposely didn't place FAbs! -- -- extend :: Int -> BitVec a -> BitVec b bvExtendSort :: Sort bvExtendSort = FFunc FInt $ FFunc (bitVecSort 1) (bitVecSort 2) -- Indexed identifier operation, purposely didn't place FAbs! -- -- rot :: Int -> BitVec a -> BitVec a bvRotSort :: Sort bvRotSort = FFunc FInt $ FFunc (bitVecSort 0) (bitVecSort 0) -- uOp :: forall a. BitVec a -> BitVec a bvUopSort :: Sort bvUopSort = FAbs 0 $ FFunc (bitVecSort 0) (bitVecSort 0) -- bOp :: forall a. BitVec a -> BitVec a -> BitVec a bvBopSort :: Sort bvBopSort = FAbs 0 $ FFunc (bitVecSort 0) $ FFunc (bitVecSort 0) (bitVecSort 0) -- bvBopSort = FAbs 0 $ FFunc (bitVecSort (FVar 0)) (FFunc (bitVecSort (FVar 0)) (bitVecSort (FVar 0))) -- cmp :: forall a. BitVec a -> BitVec a -> Bool bvCmpSort :: Sort bvCmpSort = FAbs 0 $ FFunc (bitVecSort 0) $ FFunc (bitVecSort 0) boolSort -- eq :: forall a. BitVec a -> BitVec a -> BitVec 1 bvEqSort :: Sort bvEqSort = FAbs 0 $ FFunc (bitVecSort 0) $ FFunc (bitVecSort 0) (sizedBitVecSort "Size1") -- concat :: forall a b c. BitVec a -> BitVec b -> BitVec c bvConcatSort :: Sort bvConcatSort = FAbs 0 $ FAbs 1 $ FAbs 2 $ FFunc (bitVecSort 0) $ FFunc (bitVecSort 1) (bitVecSort 2) interpSym :: Symbol -> Raw -> Sort -> (Symbol, TheorySymbol) interpSym x n t = (x, Thy x n t Theory) -- This variable is used to generate the lambda names `lam_arg$n` in -- `Interface.hs` that will be used during defunctionalization in -- `Defunctionalize.hs`, is a pretty gross hack as if the user types in the -- program or PLE generates a term that has more than `maxLamArg` lambda binders -- one inside the other, the SMT will crash complaining that -- `lam_arg${maxLamArg + k}` was not declared. maxLamArg :: Int maxLamArg = 20 axiomLiterals :: Config -> [(Symbol, Sort)] -> [Expr] axiomLiterals cfg | noStringTheory cfg = lenAxiomLiterals | otherwise = strAxiomLiterals strAxiomLiterals :: [(Symbol, Sort)] -> [Expr] strAxiomLiterals lts = catMaybes [ strAxiom l | (l, t) <- lts, isString t ] where strAxiom l = do sym <- unLitSymbol l pure (EEq (expr l) (ECon $ L (symbolText sym) strSort)) lenAxiomLiterals :: [(Symbol, Sort)] -> [Expr] lenAxiomLiterals lts = catMaybes [ lenAxiom l <$> litLen l | (l, t) <- lts, isString t ] where lenAxiom l n = EEq (EApp (expr (strLen :: Symbol)) (expr l)) (expr n `ECst` intSort) litLen = fmap (Data.Text.length . symbolText) . unLitSymbol -------------------------------------------------------------------------------- -- | Constructors, Selectors and Tests from 'DataDecl'arations. -------------------------------------------------------------------------------- dataDeclSymbols :: DataDecl -> [(Symbol, TheorySymbol)] dataDeclSymbols d = ctorSymbols d ++ testSymbols d ++ selectSymbols d -- | 'selfSort d' returns the _self-sort_ of 'd' :: 'DataDecl'. -- See [NOTE:DataDecl] for details. selfSort :: DataDecl -> Sort selfSort (DDecl c n _) = fAppTC c (FVar <$> [0..(n-1)]) -- | 'fldSort d t' returns the _real-sort_ of 'd' if 't' is the _self-sort_ -- and otherwise returns 't'. See [NOTE:DataDecl] for details. fldSort :: DataDecl -> Sort -> Sort fldSort d (FTC c) | c == ddTyCon d = selfSort d fldSort _ s = s -------------------------------------------------------------------------------- ctorSymbols :: DataDecl -> [(Symbol, TheorySymbol)] -------------------------------------------------------------------------------- ctorSymbols d = ctorSort d <$> ddCtors d ctorSort :: DataDecl -> DataCtor -> (Symbol, TheorySymbol) ctorSort d ctor = (x, Thy x (symbolRaw x) t Ctor) where x = symbol ctor t = mkFFunc n (ts ++ [selfSort d]) n = ddVars d ts = fldSort d . dfSort <$> dcFields ctor -------------------------------------------------------------------------------- testSymbols :: DataDecl -> [(Symbol, TheorySymbol)] -------------------------------------------------------------------------------- testSymbols d = testTheory t . symbol <$> ddCtors d where t = mkFFunc (ddVars d) [selfSort d, boolSort] testTheory :: Sort -> Symbol -> (Symbol, TheorySymbol) testTheory t x = (sx, Thy sx raw t Test) where sx = testSymbol x raw = "is-" <> symbolRaw x symbolRaw :: Symbol -> Data.Text.Text symbolRaw = symbolSafeText -------------------------------------------------------------------------------- selectSymbols :: DataDecl -> [(Symbol, TheorySymbol)] -------------------------------------------------------------------------------- selectSymbols d = theorify <$> concatMap (ctorSelectors d) (ddCtors d) -- | 'theorify' converts the 'Sort' into a full 'TheorySymbol' theorify :: (Symbol, Sort) -> (Symbol, TheorySymbol) theorify (x, t) = (x, Thy x (symbolRaw x) t Field) ctorSelectors :: DataDecl -> DataCtor -> [(Symbol, Sort)] ctorSelectors d ctor = fieldSelector d <$> dcFields ctor fieldSelector :: DataDecl -> DataField -> (Symbol, Sort) fieldSelector d f = (symbol f, mkFFunc n [selfSort d, ft]) where ft = fldSort d $ dfSort f n = ddVars d {- | [NOTE:DataDecl] This note explains the set of symbols generated for the below data-declaration: data Vec 1 = [ | nil { } | cons { vHead : @(0), vTail : Vec} ] We call 'Vec' the _self-sort_ of the data-type, and we want to ensure that in all constructors, tests and selectors, the _self-sort_ is replaced with the actual sort, namely, 'Vec @(0)'. Constructors // ctor : (fld-sorts) => me nil : func(1, [Vec @(0)]) cons : func(1, [@(0); Vec @(0); Vec @(0)]) Tests // is#ctor : (me) => bool is#nil : func(1, [Vec @(0); bool]) is#cons : func(1, [Vec @(0); bool]) Selectors // fld : (me) => fld-sort vHead : func(1, [Vec @(0); @(0)]) vTail : func(1, [Vec @(0); Vec @(0)]) -}