packages feed

copilot-theorem 3.2 → 3.2.1

raw patch · 35 files changed

+1553/−104 lines, 35 filesdep +bv-sizeddep +filepathdep +panicdep ~copilot-corePVP ok

version bump matches the API change (PVP)

Dependencies added: bv-sized, filepath, panic, parameterized-utils, what4

Dependency ranges changed: copilot-core

API changes (from Hackage documentation)

+ Copilot.Theorem.What4: CVC4 :: Solver
+ Copilot.Theorem.What4: DReal :: Solver
+ Copilot.Theorem.What4: Invalid :: SatResult
+ Copilot.Theorem.What4: Unknown :: SatResult
+ Copilot.Theorem.What4: Valid :: SatResult
+ Copilot.Theorem.What4: Yices :: Solver
+ Copilot.Theorem.What4: Z3 :: Solver
+ Copilot.Theorem.What4: data SatResult
+ Copilot.Theorem.What4: data Solver
+ Copilot.Theorem.What4: instance Control.Monad.Fail.MonadFail (Copilot.Theorem.What4.TransM t)
+ Copilot.Theorem.What4: instance Control.Monad.IO.Class.MonadIO (Copilot.Theorem.What4.TransM t)
+ Copilot.Theorem.What4: instance Control.Monad.State.Class.MonadState (Copilot.Theorem.What4.TransState t) (Copilot.Theorem.What4.TransM t)
+ Copilot.Theorem.What4: instance GHC.Base.Applicative (Copilot.Theorem.What4.TransM t)
+ Copilot.Theorem.What4: instance GHC.Base.Functor (Copilot.Theorem.What4.TransM t)
+ Copilot.Theorem.What4: instance GHC.Base.Monad (Copilot.Theorem.What4.TransM t)
+ Copilot.Theorem.What4: instance GHC.Show.Show (Copilot.Theorem.What4.XExpr t)
+ Copilot.Theorem.What4: instance GHC.Show.Show Copilot.Theorem.What4.SatResult
+ Copilot.Theorem.What4: instance Panic.PanicComponent Copilot.Theorem.What4.CopilotWhat4
+ Copilot.Theorem.What4: prove :: Solver -> Spec -> IO [(Name, SatResult)]

Files

CHANGELOG view
@@ -1,3 +1,7 @@+2021-03-07+        * Version bump (3.2.1). (#15).+        * Completed the documentation. (#11, #14).+ 2020-12-06         * Version bump (3.2).         * Update description, bug-reports and homepage field in cabal file
copilot-theorem.cabal view
@@ -14,7 +14,7 @@   <https://copilot-language.github.io>.  -version                   : 3.2+version                   : 3.2.1 license                   : BSD3 license-file              : LICENSE maintainer                : jonathan.laurent@ens.fr@@ -55,18 +55,23 @@   build-depends           : base          >= 4.9 && < 5                           , ansi-terminal >= 0.8 && < 0.10                           , bimap         >= 0.3 && < 0.4+                          , bv-sized      >= 1.0.2 && < 1.1                           , containers    >= 0.4 && < 0.7                           , data-default  >= 0.7 && < 0.8                           , directory     >= 1.3 && < 1.4+                          , filepath      >= 1.4.2 && < 1.5                           , mtl           >= 2.0 && < 2.3+                          , panic         >= 0.4.0 && < 0.5                           , parsec        >= 2.0 && < 3.2+                          , parameterized-utils >= 2.1.1 && < 2.2                           , pretty        >= 1.0 && < 1.2                           , process       >= 1.6 && < 1.7                           , random        >= 1.1 && < 1.2                           , transformers  >= 0.5 && < 0.6                           , xml           >= 1.3 && < 1.4+                          , what4         >= 1.0 && < 1.1 -                          , copilot-core  >= 3.2 && < 3.3+                          , copilot-core  >= 3.2.1 && < 3.3    exposed-modules         : Copilot.Theorem                           , Copilot.Theorem.Prove@@ -74,6 +79,7 @@                           , Copilot.Theorem.Prover.SMT                           -- , Copilot.Theorem.Prover.Z3                           , Copilot.Theorem.Kind2.Prover+                          , Copilot.Theorem.What4    other-modules           : Copilot.Theorem.Tactics @@ -107,3 +113,4 @@                           , Copilot.Theorem.TransSys.Invariants                           , Copilot.Theorem.TransSys.Operators                           , Copilot.Theorem.TransSys.Type+
src/Copilot/Theorem.hs view
@@ -2,6 +2,17 @@  {-# LANGUAGE Safe #-} +-- | Highly automated proof techniques are a necessary step for the widespread+-- adoption of formal methods in the software industry. Moreover, it could+-- provide a partial answer to one of its main issue which is scalability.+--+-- Copilot-theorem is a Copilot library aimed at checking automatically some+-- safety properties on Copilot programs. It includes:+--+-- * A prover producing native inputs for the Kind2 model checker.+--+-- * A What4 backend that uses SMT solvers to prove safety properties.+ module Copilot.Theorem   ( module X   , Proof
src/Copilot/Theorem/IL.hs view
@@ -2,6 +2,14 @@  {-# LANGUAGE Safe #-} +-- | Each prover first translates the Copilot specification into an+-- intermediate representation best suited for model checking.+--+-- This module and the ones in the same namespace implement the IL format. A+-- Copilot program is translated into a list of quantifier-free equations over+-- integer sequences, implicitly universally quantified by a free variable n.+-- Each sequence roughly corresponds to a stream.+ module Copilot.Theorem.IL (module X) where  import Copilot.Theorem.IL.Spec as X
src/Copilot/Theorem/IL/PrettyPrint.hs view
@@ -3,6 +3,8 @@ {-# LANGUAGE NamedFieldPuns, GADTs #-} {-# LANGUAGE Safe #-} +-- | This module implements a pretty printer for the IL format, an intermediate+-- representation used in copilot-theorem to facilitate model checking. module Copilot.Theorem.IL.PrettyPrint (prettyPrint, printConstraint) where  import Copilot.Theorem.IL.Spec@@ -13,9 +15,11 @@  -------------------------------------------------------------------------------- +-- | Pretty print an IL specification. prettyPrint :: IL -> String prettyPrint = render . ppSpec +-- | Pretty print an IL constraint expression. printConstraint :: Expr -> String printConstraint = render . ppExpr 
src/Copilot/Theorem/IL/Spec.hs view
@@ -3,6 +3,18 @@ {-# LANGUAGE ExistentialQuantification, GADTs, LambdaCase #-} {-# LANGUAGE Safe #-} +-- | This module implements the specification language for the IL format, an+-- intermediate representation used in copilot-theorem to facilitate model+-- checking.+--+-- A Copilot program is translated into a list of quantifier-free equations+-- over integer sequences, implicitly universally quantified by a free variable+-- n. Each sequence roughly corresponds to a stream.+--+-- This representation is partly inspired by the IL language described in+-- Hagen, G.E., /VERIFYING SAFETY PROPERTIES OF LUSTRE PROGRAMS: AN SMT-BASED/+-- /APPROACH/, 2008.+ module Copilot.Theorem.IL.Spec   ( Type (..)   , Op1  (..)@@ -25,11 +37,16 @@  -------------------------------------------------------------------------------- +-- | Identifier of a sequence. type SeqId    =  String -data SeqIndex = Fixed Integer | Var Integer+-- | Index within a sequence.+data SeqIndex = Fixed Integer -- ^ An absolute index in the sequence.+              | Var Integer   -- ^ An index relative to the current time-step.   deriving (Eq, Ord, Show) +-- | Idealized types. These differ from Copilot types in that, notionally,+-- reals actually denote real numbers. data Type = Bool  | Real   | SBV8 | SBV16 | SBV32 | SBV64   | BV8  | BV16 | BV32 | BV64@@ -48,19 +65,21 @@     BV32  -> "BV32"     BV64  -> "BV64" +-- | Idealized representation of a Copilot expression. data Expr-  = ConstB Bool-  | ConstR Double-  | ConstI Type Integer-  | Ite    Type Expr Expr Expr-  | Op1    Type Op1 Expr-  | Op2    Type Op2 Expr Expr-  | SVal   Type SeqId SeqIndex-  | FunApp Type String [Expr]+  = ConstB Bool                 -- ^ Constant boolean.+  | ConstR Double               -- ^ Constant real.+  | ConstI Type Integer         -- ^ Constant integer.+  | Ite    Type Expr Expr Expr  -- ^ If-then-else.+  | Op1    Type Op1 Expr        -- ^ Apply a unary operator.+  | Op2    Type Op2 Expr Expr   -- ^ Apply a binary operator.+  | SVal   Type SeqId SeqIndex  -- ^ Refer to a value in another sequence.+  | FunApp Type String [Expr]   -- ^ Function application.   deriving (Eq, Ord, Show)  -------------------------------------------------------------------------------- +-- | A description of a variable (or function) together with its type. data VarDescr = VarDescr   { varName :: String   , varType :: Type@@ -75,13 +94,16 @@  -------------------------------------------------------------------------------- +-- | Identifier for a property. type PropId = String +-- | Description of a sequence. data SeqDescr = SeqDescr   { seqId    :: SeqId   , seqType  :: Type   } +-- | An IL specification. data IL = IL   { modelInit   :: [Expr]   , modelRec    :: [Expr]@@ -91,10 +113,12 @@  -------------------------------------------------------------------------------- +-- | Unary operators. data Op1 = Not | Neg | Abs | Exp | Sqrt | Log | Sin | Tan | Cos | Asin | Atan          | Acos | Sinh | Tanh | Cosh | Asinh | Atanh | Acosh          deriving (Eq, Ord) +-- | Binary operators. data Op2 = Eq | And | Or | Le | Lt | Ge | Gt | Add | Sub | Mul | Mod | Fdiv | Pow          deriving (Eq, Ord) @@ -145,6 +169,7 @@  ------------------------------------------------------------------------------- +-- | Return the type of an expression. typeOf :: Expr -> Type typeOf e = case e of   ConstB _       -> Bool@@ -156,12 +181,15 @@   SVal   t _ _   -> t   FunApp t _ _   -> t +-- | An index to the current element of a sequence. _n_ :: SeqIndex _n_ = Var 0 +-- | An index to a future element of a sequence. _n_plus :: (Integral a) => a -> SeqIndex _n_plus d = Var (toInteger d) +-- | Evaluate an expression at specific index in the sequence. evalAt :: SeqIndex -> Expr -> Expr evalAt _ e@(ConstB _) = e evalAt _ e@(ConstR _) = e
src/Copilot/Theorem/IL/Transform.hs view
@@ -1,11 +1,17 @@ {-# LANGUAGE LambdaCase #-} {-# LANGUAGE Safe #-} +-- | Simplify IL expressions by partly evaluating operations on booleans. module Copilot.Theorem.IL.Transform ( bsimpl ) where  import Copilot.Theorem.IL.Spec --- | A transformation intended to remove boolean literals.+-- | Simplify IL expressions by partly evaluating operations on booleans,+-- eliminating some boolean literals.+--+-- For example, an if-then-else in which the condition is literally the+-- constant True or the constant False can be reduced to an operation without+-- choice in which the appropriate branch of the if-then-else is used instead. bsimpl :: Expr -> Expr bsimpl = until (\x -> bsimpl' x == x) bsimpl'   where
src/Copilot/Theorem/IL/Translate.hs view
@@ -4,6 +4,7 @@              LambdaCase #-} {-# LANGUAGE Safe #-} +-- | Translate Copilot specifications into IL specifications. module Copilot.Theorem.IL.Translate ( translate, translateWithBounds ) where  import Copilot.Theorem.IL.Spec@@ -44,11 +45,13 @@  -------------------------------------------------------------------------------- --- | Translates a Copilot specification to an IL specification-+-- | Translate a Copilot specification to an IL specification. translate :: C.Spec -> IL translate = translate' False +-- | Translate a Copilot specification to an IL specification, adding+-- constraints for limiting the values of numeric expressions to known bounds+-- based on their specific types (only for integers or natural numbers). translateWithBounds :: C.Spec -> IL translateWithBounds = translate' True @@ -273,6 +276,7 @@  -------------------------------------------------------------------------------- +-- | Translation state. data TransST = TransST   { localConstraints :: [Expr]   , muxes            :: [(Expr, (Expr, Type, Expr, Expr))]@@ -299,6 +303,7 @@           , Op2 Bool Or c (Op2 Bool Eq v e2)           ] +-- | A state monad over the translation state ('TransST'). type Trans = State TransST  fresh :: Trans Integer
src/Copilot/Theorem/Kind2.hs view
@@ -2,6 +2,9 @@  {-# LANGUAGE Safe #-} +-- | Copilot backend for the <https://kind2-mc.github.io/kind2/ Kind 2> SMT+-- based model checker.+ module Copilot.Theorem.Kind2 (module X) where  import Copilot.Theorem.Kind2.AST as X
src/Copilot/Theorem/Kind2/AST.hs view
@@ -2,35 +2,52 @@  {-# LANGUAGE Safe #-} +-- | Abstract syntax tree of Kind2 files. module Copilot.Theorem.Kind2.AST where  -------------------------------------------------------------------------------- +-- | A file is a sequence of predicates and propositions. data File = File   { filePreds     :: [PredDef]   , fileProps     :: [Prop] } +-- | A proposition is defined by a term. data Prop = Prop   { propName      :: String   , propTerm      :: Term } +-- | A predicate definition. data PredDef = PredDef-  { predId        :: String-  , predStateVars :: [StateVarDef]-  , predInit      :: Term-  , predTrans     :: Term }+  { predId        :: String         -- ^ Identifier for the predicate.+  , predStateVars :: [StateVarDef]  -- ^ Variables identifying the states in the+                                    -- underlying state transition system.+  , predInit      :: Term           -- ^ Predicate that holds for initial+                                    -- states.+  , predTrans     :: Term           -- ^ Predicate that holds for two states, if+                                    -- there is state transition between them.+  } +-- | A definition of a state variable. data StateVarDef = StateVarDef-  { varId         :: String-  , varType       :: Type-  , varFlags      :: [StateVarFlag] }+  { varId         :: String           -- ^ Name of the variable.+  , varType       :: Type             -- ^ Type of the variable.+  , varFlags      :: [StateVarFlag] } -- ^ Flags for the variable. +-- | Types used in Kind2 files to represent Copilot types.+--+-- The Kind2 backend provides functions to, additionally, constrain the range+-- of numeric values depending on their Copilot type ('Int8', 'Int16', etc.). data Type = Int | Real | Bool +-- | Possible flags for a state variable. data StateVarFlag = FConst +-- | Type of the predicate, either belonging to an initial state or a pair of+-- states with a transition. data PredType = Init | Trans +-- | Datatype to describe a term in the Kind language. data Term =     ValueLiteral  String   | PrimedStateVar String
src/Copilot/Theorem/Kind2/Output.hs view
@@ -3,6 +3,7 @@ {-# LANGUAGE RankNTypes #-} {-# LANGUAGE Safe #-} +-- | Parse output of Kind2. module Copilot.Theorem.Kind2.Output (parseOutput) where  import Text.XML.Light       hiding (findChild)@@ -15,7 +16,10 @@  simpleName s = QName s Nothing Nothing -parseOutput :: String -> String -> P.Output+-- | Parse output of Kind2.+parseOutput :: String    -- ^ Property whose validity is being checked.+            -> String    -- ^ XML output of Kind2+            -> P.Output parseOutput prop xml = fromJust $ do   root <- parseXMLDoc xml   case findAnswer . findPropTag $ root of
src/Copilot/Theorem/Kind2/PrettyPrint.hs view
@@ -2,6 +2,7 @@  {-# LANGUAGE Safe #-} +-- | Pretty print a Kind2 file defining predicates and propositions. module Copilot.Theorem.Kind2.PrettyPrint ( prettyPrint ) where  import Copilot.Theorem.Misc.SExpr@@ -12,19 +13,22 @@  -------------------------------------------------------------------------------- +-- | A tree of expressions, in which the leafs are strings. type SSExpr = SExpr String +-- | Reserved keyword prime. kwPrime = "prime"  -------------------------------------------------------------------------------- +-- | Pretty print a Kind2 file. prettyPrint :: File -> String prettyPrint =   intercalate "\n\n"   . map (SExpr.toString shouldIndent id)   . ppFile --- Defines the indentation policy of the S-Expressions+-- | Define the indentation policy of the S-Expressions shouldIndent :: SSExpr -> Bool shouldIndent (Atom _)                   = False shouldIndent (List [Atom a, Atom _])    = a `notElem` [kwPrime]@@ -32,15 +36,19 @@  -------------------------------------------------------------------------------- +-- | Convert a file into a sequence of expressions. ppFile :: File -> [SSExpr] ppFile (File preds props) = map ppPredDef preds ++ ppProps props +-- | Convert a sequence of propositions into command to check each of them. ppProps :: [Prop] -> [SSExpr] ppProps ps = [ node "check-prop" [ list $ map ppProp ps ] ] +-- | Convert a proposition into an expression. ppProp :: Prop -> SSExpr ppProp (Prop n t) = list [atom n, ppTerm t] +-- | Convert a predicate into an expression. ppPredDef :: PredDef -> SSExpr ppPredDef pd =   list [ atom "define-pred"@@ -49,15 +57,18 @@        , node "init"  [ppTerm $ predInit  pd]        , node "trans" [ppTerm $ predTrans pd] ] +-- | Convert a state variable definition into an expression. ppStateVarDef :: StateVarDef -> SSExpr ppStateVarDef svd =   list [atom (varId svd), ppType (varType svd)] +-- | Convert a type into an expression. ppType :: Type -> SSExpr ppType Int  = atom "Int" ppType Real = atom "Real" ppType Bool = atom "Bool" +-- | Convert a term into an expression. ppTerm :: Term -> SSExpr ppTerm (ValueLiteral  c) = atom c ppTerm (PrimedStateVar v) = list [atom kwPrime, atom v]
src/Copilot/Theorem/Kind2/Prover.hs view
@@ -3,6 +3,7 @@ {-# LANGUAGE LambdaCase #-} {-# LANGUAGE Trustworthy #-} +-- | A prover backend based on Kind2. module Copilot.Theorem.Kind2.Prover   ( module Data.Default   , Options (..)@@ -27,9 +28,13 @@  -------------------------------------------------------------------------------- +-- | Options for Kind2 data Options = Options-  { bmcMax :: Int }+  { bmcMax :: Int -- ^ Upper bound on the number of unrolling that base and+                  --   step will perform. A value of 0 means /unlimited/.+  } +-- | Default options with unlimited unrolling for base and step. instance Default Options where   def = Options { bmcMax = 0 } @@ -37,6 +42,9 @@   { options  :: Options   , transSys :: TS.TransSys } +-- | A prover backend based on Kind2.+--+-- The executable @kind2@ must exist and its location be in the @PATH@. kind2Prover :: Options -> Prover kind2Prover opts = Prover   { proverName =  "Kind2"
src/Copilot/Theorem/Kind2/Translate.hs view
@@ -3,6 +3,8 @@ {-# LANGUAGE RankNTypes, ViewPatterns, NamedFieldPuns, GADTs #-} {-# LANGUAGE Safe #-} +-- | Convert modular transition systems ('TransSys') into Kind2 file+-- specifications. module Copilot.Theorem.Kind2.Translate   ( toKind2   , Style (..)@@ -36,9 +38,23 @@  -------------------------------------------------------------------------------- +-- | Style of the Kind2 files produced: modular (with multiple separate nodes),+-- or all inlined (with only one node).+--+-- In the modular style, the graph is simplified to remove cycles by collapsing+-- all nodes participating in a strongly connected components.+--+-- In the inlined style, the structure of the modular transition system is+-- discarded and the graph is first turned into a /non-modular transition/+-- /system/ with only one node, which can be then converted into a Kind2 file. data Style = Inlined | Modular -toKind2 :: Style -> [PropId] -> [PropId] -> TransSys -> K.File+-- | Produce a Kind2 file that checks the properties specified.+toKind2 :: Style     -- ^ Style of the file (modular or inlined).+        -> [PropId]  -- ^ Assumptions+        -> [PropId]  -- ^ Properties to be checked+        -> TransSys  -- ^ Modular transition system holding the system spec+        -> K.File toKind2 style assumptions checkedProps spec =   addAssumptions spec assumptions   $ trSpec (complete spec') predCallsGraph assumptions checkedProps
src/Copilot/Theorem/Misc/Error.hs view
@@ -2,6 +2,7 @@  {-# LANGUAGE Safe #-} +-- | Custom functions to report error messages to users. module Copilot.Theorem.Misc.Error   ( badUse   , impossible@@ -12,21 +13,29 @@  -------------------------------------------------------------------------------- +-- | Tag used with error messages to help users locate the component that+-- failed or reports the error. errorHeader :: String errorHeader = "[Copilot-kind ERROR]  " -badUse :: String -> a+-- | Report an error due to an error detected by Copilot (e.g., user error).+badUse :: String -- ^ Description of the error.+       -> a badUse s = error $ errorHeader ++ s -impossible :: String -> a+-- | Report an error due to a bug in Copilot.+impossible :: String -- ^ Error information to attach to the message.+           -> a impossible s = error $ errorHeader ++ "Unexpected internal error : " ++ s +-- | Report an error due to a bug in Copilot. impossible_ :: a impossible_ = error $ errorHeader ++ "Unexpected internal error"  notHandled :: String -> a notHandled s = error $ errorHeader ++ "Not handled : " ++ s +-- | Report an unrecoverable error (e.g., incorrect format). fatal :: String -> a fatal = error 
src/Copilot/Theorem/Misc/SExpr.hs view
@@ -3,6 +3,8 @@ {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE Safe #-} +-- | A representation for structured expression trees, with support for pretty+-- printing and for parsing. module Copilot.Theorem.Misc.SExpr where  import Text.ParserCombinators.Parsec@@ -12,20 +14,34 @@  -------------------------------------------------------------------------------- +-- | A structured expression is either an atom, or a sequence of expressions,+-- where the first in the sequence denotes the tag or label of the tree. data SExpr a = Atom a              | List [SExpr a] -blank        = Atom ""-atom         = Atom                 -- s-unit         = List []              -- ()+-- | Empty string expression.+blank = Atom ""++-- | Atomic expression constructor.+atom = Atom                 -- s++-- | Empty expression (empty list).+unit = List []              -- ()++-- | Single expression. singleton a  = List [Atom a]        -- (s)-list         = List                 -- (ss)-node a l     = List (Atom a : l)    -- (s ss) ---------------------------------------------------------------------------------+-- | Sequence of expressions.+list = List                 -- (ss) --- A straightforward string representation+-- | Sequence of expressions with a root or main note, and a series of+-- additional expressions or arguments..+node a l = List (Atom a : l)    -- (s ss) +--------------------------------------------------------------------------------++-- A straightforward string representation for 'SExpr's of Strings that+-- parenthesizes lists of expressions. instance Show (SExpr String) where   show = PP.render . show'     where@@ -35,13 +51,22 @@  -- More advanced printing with some basic indentation +-- | Indent by a given number. indent = nest 1 -toString :: (SExpr a -> Bool) -> (a -> String) -> SExpr a -> String+-- | Pretty print a structured expression as a String.+toString :: (SExpr a -> Bool)  -- ^ True if an expression should be indented.+         -> (a -> String)      -- ^ Pretty print the value inside as 'SExpr'.+         -> SExpr a            -- ^ Root of 'SExpr' tree.+         -> String toString shouldIndent printAtom expr =   PP.render (toDoc shouldIndent printAtom expr) -toDoc :: (SExpr a -> Bool) -> (a -> String) -> SExpr a -> Doc+-- | Pretty print a structured expression as a 'Doc', or set of layouts.+toDoc :: (SExpr a -> Bool)  -- ^ True if an expression should be indented.+      -> (a -> String)      -- ^ Pretty print the value inside as 'SExpr'.+      -> SExpr a            -- ^ Root of 'SExpr' tree.+      -> Doc toDoc shouldIndent printAtom expr = case expr of   Atom a  -> text (printAtom a)   List l  -> parens (foldl renderItem empty l)@@ -54,6 +79,11 @@  -------------------------------------------------------------------------------- +-- | Parser for strings of characters separated by spaces into a structured+-- tree.+--+-- Parentheses are interpreted as grouping elements, that is, defining a+-- 'List', which may be empty. parser :: GenParser Char st (SExpr String) parser =   choice [try unitP, nodeP, leafP]@@ -72,7 +102,11 @@                         void $ char ')'                         return $ List st -+-- | Parser for strings of characters separated by spaces into a structured+-- tree.+--+-- Parentheses are interpreted as grouping elements, that is, defining a+-- 'List', which may be empty. parseSExpr :: String -> Maybe (SExpr String) parseSExpr str = case parse parser "" str of   Left s -> error (show s) -- Nothing
src/Copilot/Theorem/Misc/Utils.hs view
@@ -2,6 +2,7 @@  {-# LANGUAGE Safe #-} +-- | Utility / auxiliary functions. module Copilot.Theorem.Misc.Utils  ( isSublistOf, nub', nubBy', nubEq  , openTempFile@@ -23,22 +24,35 @@  -------------------------------------------------------------------------------- +-- | True if the given list is a subset of the second list, when both are+-- considered as sets. isSublistOf :: Ord a => [a] -> [a] -> Bool isSublistOf = Set.isSubsetOf `on` Set.fromList +-- | True if both lists contain the same elements, when both are considered as+-- sets. nubEq :: Ord a => [a] -> [a] -> Bool nubEq = (==) `on` Set.fromList --- An efficient version of 'nub'+-- | Remove duplicates from a list.+--+-- This is an efficient version of 'Data.List.nub' that works for lists with a+-- stronger constraint on the type (i.e., 'Ord', as opposed of+-- 'Data.List.nub''s 'Eq' constraint). nub' :: Ord a => [a] -> [a] nub' = map head . group . sort +-- | Variant of 'nub'' parameterized by the comparison function. nubBy' :: (a -> a -> Ordering) -> [a] -> [a] nubBy' f = map head . groupBy (\x y -> f x y == EQ) . sortBy f  -------------------------------------------------------------------------------- -openTempFile :: String -> String -> String -> IO (String, Handle)+-- | Create a temporary file and open it for writing.+openTempFile :: String  -- ^ Directory where the file should be created.+             -> String  -- ^ Base name for the file (prefix).+             -> String  -- ^ File extension.+             -> IO (String, Handle) openTempFile loc baseName extension = do    path   <- freshPath
src/Copilot/Theorem/Prove.hs view
@@ -3,6 +3,7 @@ {-# LANGUAGE NamedFieldPuns, ViewPatterns, ExistentialQuantification, GADTs #-} {-# LANGUAGE Safe #-} +-- | Connection to theorem provers. module Copilot.Theorem.Prove   ( Output  (..)   , Status  (..)@@ -24,17 +25,24 @@  -------------------------------------------------------------------------------- +-- | Output produced by a prover, containing the 'Status' of the proof and+-- additional information. data Output = Output Status [String] +-- | Status returned by a prover when given a specification and a property to+-- prove. data Status = Sat | Valid | Invalid | Unknown | Error -{- Each prover has to provide the following five functions.-   The most important is `askProver`, which takes 3 arguments :-   *  The prover descriptor-   *  A list of properties names which are assumptions-   *  A property name which has to be deduced from these assumptions--}-+-- | A connection to a prover able to check the satisfiability of+-- specifications.+--+-- The most important is `askProver`, which takes 3 arguments :+--+-- *  The prover descriptor+--+-- *  A list of properties names which are assumptions+--+-- *  A properties that have to be deduced from these assumptions data Prover = forall r . Prover   { proverName  :: String   , startProver :: Core.Spec -> IO r@@ -42,18 +50,28 @@   , closeProver :: r -> IO ()   } +-- | A unique property identifier type PropId = String +-- | Reference to a property. data PropRef a where   PropRef :: PropId -> PropRef a +-- | Empty datatype to mark proofs of universally quantified predicates. data Universal++-- | Empty datatype to mark proofs of existentially quantified predicates. data Existential +-- | A proof scheme with unit result. type Proof a = ProofScheme a () +-- | A sequence of computations that generate a trace of required prover+-- 'Action's. type UProof = Writer [Action] () +-- | A proof scheme is a sequence of computations that compute a result and+-- generate a trace of required prover 'Action's. data ProofScheme a b where   Proof :: Writer [Action] b -> ProofScheme a b @@ -68,6 +86,7 @@   (Proof p) >>= f = Proof $ p >>= (\a -> case f a of Proof p' -> p')   return a = Proof (return a) +-- | Prover actions. data Action where   Check  :: Prover -> Action   Assume :: PropId -> Action@@ -75,9 +94,14 @@  -------------------------------------------------------------------------------- +-- | Record a requirement for satisfiability checking. check :: Prover -> Proof a check prover = Proof $ tell [Check prover] +-- | Try to prove a property in a specification with a given proof scheme.+--+-- Return 'True' if a proof of satisfiability or validity is found, false+-- otherwise. prove :: Core.Spec -> PropId -> UProof -> IO Bool prove spec propId (execWriter -> actions) = do @@ -118,6 +142,13 @@           putStrLn $ propId ++ ": admitted"           processActions (propId : context) nextActions +-- | Combine two provers producing a new prover that will run both provers in+-- parallel and combine their outputs.+--+-- The results produced by the provers must be consistent. For example, if one+-- of the provers indicates that a property is 'Valid' while another indicates+-- that it is 'Invalid', the combination of both provers will return an+-- 'Error'. combine :: Prover -> Prover -> Prover combine   (Prover { proverName  = proverNameL
src/Copilot/Theorem/Prover/Backend.hs view
@@ -1,12 +1,26 @@ {-# LANGUAGE RankNTypes #-} {-# LANGUAGE Safe #-} +-- | Backend to  SMT solvers and theorem provers.+--+-- This module provides three definitions:+--+-- - A class ('SmtFormat') abstracting over the language used to communicate the+-- desired commands to an SMT solver or theorem prover.+--+-- - A class ('Backend') abstracting over the backend, which includes the name of+-- the executable, any options and flags necessary, and functions to parse the+-- results and close the communication.+--+-- - A type ('SatResult') representing a satisfiability result communicated by+-- the SMT solver or theorem prover. module Copilot.Theorem.Prover.Backend (SmtFormat(..), Backend(..), SatResult(..)) where  import Copilot.Theorem.IL  import System.IO +-- | Format of SMT-Lib commands. class Show a => SmtFormat a where    push            :: a    pop             :: a@@ -15,6 +29,7 @@    declFun         :: String -> Type -> [Type] -> a    assert          :: Expr -> a +-- | Backend to an SMT solver or theorem prover. data Backend a = Backend   { name            :: String   , cmd             :: String@@ -25,5 +40,5 @@   , interpret       :: String -> Maybe SatResult   } +-- | Satisfiability result communicated by the SMT solver or theorem prover. data SatResult = Sat | Unsat | Unknown-
src/Copilot/Theorem/Prover/SMT.hs view
@@ -3,13 +3,23 @@ {-# LANGUAGE LambdaCase, NamedFieldPuns, FlexibleInstances, RankNTypes, GADTs #-} {-# LANGUAGE Trustworthy #-} +-- | Connections to various SMT solvers and theorem provers. module Copilot.Theorem.Prover.SMT-  ( module Data.Default+  (++    -- * Backends+    Backend+  , SmtFormat+  , SmtLib+  , Tptp+  , yices, dReal, altErgo, metit, z3, cvc4, mathsat++    -- * Tactics   , Options (..)   , induction, kInduction, onlySat, onlyValidity-  , yices, dReal, altErgo, metit, z3, cvc4, mathsat-  , Backend, SmtFormat-  , SmtLib, Tptp++    -- * Auxiliary+  , module Data.Default   ) where  import Copilot.Theorem.IL.Translate@@ -42,19 +52,24 @@  -------------------------------------------------------------------------------- --- | Tactics+-- * Tactics +-- | Options to configure the provers. data Options = Options   { startK :: Word32-  -- The maximum number of steps of the k-induction algorithm the prover runs-  -- before giving up.-  , maxK   :: Word32+    -- ^ Initial @k@ for the k-induction algorithm. -  -- If `debug` is set to `True`, the SMTLib/TPTP queries produced by the-  -- prover are displayed in the standard output.-  , debug  :: Bool+  , maxK :: Word32+    -- ^ The maximum number of steps of the k-induction algorithm the prover runs+    -- before giving up.++  , debug :: Bool+    -- ^ If @debug@ is set to @True@, the SMTLib/TPTP queries produced by the+    -- prover are displayed in the standard output.   } +-- | Default 'Options' with a @0@ @k@ and a max of @10@ steps, and that produce+-- no debugging info. instance Default Options where   def = Options     { startK = 0@@ -62,6 +77,7 @@     , debug  = False     } +-- | Tactic to find only a proof of satisfiability. onlySat :: SmtFormat a => Options -> Backend a -> Proof Existential onlySat opts backend = check P.Prover   { P.proverName  = "OnlySat"@@ -70,6 +86,7 @@   , P.closeProver = const $ return ()   } +-- | Tactic to find only a proof of validity. onlyValidity :: SmtFormat a => Options -> Backend a -> Proof Universal onlyValidity opts backend = check P.Prover   { P.proverName  = "OnlyValidity"@@ -78,6 +95,9 @@   , P.closeProver = const $ return ()   } +-- | Tactic to find a proof by standard 1-induction.+--+-- The values for @startK@ and @maxK@ in the options are ignored. induction :: SmtFormat a => Options -> Backend a -> Proof Universal induction opts backend = check P.Prover   { P.proverName  = "Induction"@@ -86,6 +106,7 @@   , P.closeProver = const $ return ()   } +-- | Tactic to find a proof by k-induction. kInduction :: SmtFormat a => Options -> Backend a -> Proof Universal kInduction opts backend = check P.Prover   { P.proverName  = "K-Induction"@@ -96,8 +117,13 @@  ------------------------------------------------------------------------------- --- | Backends+-- * Backends +-- | Backend to the Yices 2 SMT solver.+--+-- It enables non-linear arithmetic (@QF_NRA@), which means MCSat will be used.+--+-- The command @yices-smt2@ must be in the user's @PATH@. yices :: Backend SmtLib yices = Backend   { name            = "Yices"@@ -109,6 +135,12 @@   , interpret       = SMTLib.interpret   } +-- | Backend to the cvc4 SMT solver.+--+-- It enables support for uninterpreted functions and mixed nonlinear+-- arithmetic (@QF_NIRA@).+--+-- The command @cvc4@ must be in the user's @PATH@. cvc4 :: Backend SmtLib cvc4 = Backend   { name            = "CVC4"@@ -120,6 +152,12 @@   , interpret       = SMTLib.interpret   } +-- | Backend to the Alt-Ergo SMT solver.+--+-- It enables support for uninterpreted functions and mixed nonlinear+-- arithmetic (@QF_NIRA@).+--+-- The command @alt-ergo.opt@ must be in the user's @PATH@. altErgo :: Backend SmtLib altErgo = Backend   { name            = "Alt-Ergo"@@ -131,6 +169,9 @@   , interpret       = SMTLib.interpret   } +-- | Backend to the Z3 theorem prover.+--+-- The command @z3@ must be in the user's @PATH@. z3 :: Backend SmtLib z3 = Backend   { name            = "Z3"@@ -142,6 +183,12 @@   , interpret       = SMTLib.interpret   } +-- | Backend to the dReal SMT solver.+--+-- It enables non-linear arithmetic (@QF_NRA@).+--+-- The libraries for dReal must be installed and @perl@ must be in the user's+-- @PATH@. dReal :: Backend SmtLib dReal = Backend   { name            = "dReal"@@ -153,6 +200,11 @@   , interpret       = SMTLib.interpret   } +-- | Backend to the Mathsat SMT solver.+--+-- It enables non-linear arithmetic (@QF_NRA@).+--+-- The command @mathsat@ must be in the user's @PATH@. mathsat :: Backend SmtLib mathsat = Backend   { name            = "MathSAT"@@ -164,7 +216,11 @@   , interpret       = SMTLib.interpret   } --- The argument is the path to the "tptp" subdirectory of the metitarski+-- | Backend to the MetiTaski theorem prover.+--+-- The command @metit@ must be in the user's @PATH@.+--+-- The argument string is the path to the @tptp@ subdirectory of the metitarski -- install location. metit :: String -> Backend Tptp metit installDir = Backend
src/Copilot/Theorem/Prover/SMTIO.hs view
@@ -3,9 +3,13 @@ {-# LANGUAGE LambdaCase, NamedFieldPuns, RankNTypes, ViewPatterns #-} {-# LANGUAGE Safe #-} +-- | Communication with SMT solvers or theorem provers.+--+-- A solver is a running process defined by a 'Backend'. module Copilot.Theorem.Prover.SMTIO   ( Solver-  , startNewSolver, assume, entailed, stop, declVars+  , startNewSolver, stop+  , assume, entailed, declVars   ) where  import Copilot.Theorem.IL@@ -21,6 +25,7 @@  -------------------------------------------------------------------------------- +-- | A connection with a running SMT solver or theorem prover. data Solver a = Solver   { solverName :: String   , inh        :: Handle@@ -34,6 +39,7 @@  -------------------------------------------------------------------------------- +-- | Output a debugging message if debugging is enabled for the solver. debug :: Bool -> Solver a -> String -> IO () debug printName s str = when (debugMode s) $   putStrLn $ (if printName then "<" ++ solverName s ++ ">  " else "") ++ str@@ -60,6 +66,10 @@  -------------------------------------------------------------------------------- +-- | Create a new solver implemented by the backend specified.+--+-- The error handle from the backend handle is immediately closed/discarded,+-- and the logic initialized as specifiied by the backend options. startNewSolver :: SmtFormat a => String -> Bool -> Backend a -> IO (Solver a) startNewSolver name dbgMode b = do   (i, o, e, p) <- runInteractiveProcess (cmd b) (cmdOpts b) Nothing Nothing@@ -68,6 +78,8 @@   send s $ setLogic $ logic b   return s +-- | Stop a solver, closing all communication handles and terminating the+-- process. stop :: Solver a -> IO () stop s = do   hClose $ inh s@@ -76,6 +88,7 @@  -------------------------------------------------------------------------------- +-- | Register the given expressions as assumptions or axioms with the solver. assume :: SmtFormat a => Solver a -> [Expr] -> IO (Solver a) assume s@(Solver { model }) cs = do   let newAxioms = elems $ fromList cs \\ model@@ -85,6 +98,8 @@ assume' :: SmtFormat a => Solver a -> [Expr] -> IO () assume' s cs = forM_ cs (send s . assert . bsimpl) +-- | Check if a series of expressions are entailed by the axioms or assumptions+-- already registered with the solver. entailed :: SmtFormat a => Solver a -> [Expr] -> IO SatResult entailed s cs = do   when (incremental $ backend s) $ send s push@@ -97,6 +112,7 @@   when (incremental $ backend s) $ send s pop   receive s +-- | Register the given variables with the solver. declVars :: SmtFormat a => Solver a -> [VarDescr] -> IO (Solver a) declVars s@(Solver { vars }) decls = do   let newVars = elems $ fromList decls \\ vars
src/Copilot/Theorem/Prover/SMTLib.hs view
@@ -3,6 +3,8 @@ {-# LANGUAGE GADTs, FlexibleInstances #-} {-# LANGUAGE Safe #-} +-- | A backend to the SMT-Lib format, enabling to produce commands for SMT-Lib+-- implementing solvers, and parse results. module Copilot.Theorem.Prover.SMTLib (SmtLib, interpret) where  import Copilot.Theorem.Prover.Backend (SmtFormat (..), SatResult (..))@@ -14,6 +16,9 @@  -------------------------------------------------------------------------------- +-- | Type used to represent SMT-lib commands.+--+-- Use the interface in 'SmtFormat' to create such commands. newtype SmtLib = SmtLib (SExpr String)  instance Show SmtLib where@@ -26,6 +31,7 @@  -------------------------------------------------------------------------------- +-- | Interface for SMT-Lib conforming backends. instance SmtFormat SmtLib where   push = SmtLib $ node "push" [atom "1"]   pop = SmtLib $ node "pop" [atom "1"]@@ -36,6 +42,7 @@     node "declare-fun" [atom name, (list $ map (atom . smtTy) args), atom (smtTy retTy)]   assert c = SmtLib $ node "assert" [expr c] +-- | Parse a satisfiability result. interpret :: String -> Maybe SatResult interpret "sat"   = Just Sat interpret "unsat" = Just Unsat
src/Copilot/Theorem/Prover/TPTP.hs view
@@ -3,6 +3,8 @@ {-# LANGUAGE GADTs, LambdaCase #-} {-# LANGUAGE Safe #-} +-- | A backend to <http://www.tptp.org/ TPTP>, enabling to produce assertions+-- and to parse the results from TPTP. module Copilot.Theorem.Prover.TPTP (Tptp, interpret) where  import Copilot.Theorem.Prover.Backend (SmtFormat (..), SatResult (..))@@ -12,6 +14,10 @@  -------------------------------------------------------------------------------- +-- | Type used to represent TPTP expressions.+--+-- Although this type implements the 'SmtFormat' interface, only 'assert' is+-- actually used. data Tptp = Ax TptpExpr | Null  data TptpExpr = Bin TptpExpr String TptpExpr | Un String TptpExpr@@ -37,6 +43,7 @@   declFun  = const $ const $ const Null   assert c = Ax $ expr c +-- | Parse a satisfiability result. interpret :: String -> Maybe SatResult interpret str   | "SZS status Unsatisfiable" `isPrefixOf` str = Just Unsat
src/Copilot/Theorem/Tactics.hs view
@@ -1,5 +1,7 @@ {-# LANGUAGE Safe #-} +-- | Utility functions to help write proof tactics.+ module Copilot.Theorem.Tactics   ( instantiate, assume, admit   ) where@@ -8,11 +10,14 @@  import Control.Monad.Writer +-- | Instantiate a universal proof into an existential proof. instantiate :: Proof Universal -> Proof Existential instantiate (Proof p) = Proof p +-- | Assume that a property, given by reference, holds. assume :: PropRef Universal -> Proof a assume (PropRef p) = Proof $ tell [Assume p] +-- | Assume that the current goal holds. admit :: Proof a admit = Proof $ tell [Admit]
src/Copilot/Theorem/TransSys.hs view
@@ -2,6 +2,15 @@  {-# LANGUAGE Safe #-} +-- | Each prover first translates the Copilot specification into an+-- intermediate representation best suited for model checking.+--+-- This module and the ones in the same namespace implement the TransSys+-- format. A Copilot program is /flattened/ and translated into a /state/+-- /transition system/.  In order to keep some structure in this+-- representation, the variables of this system are grouped by /nodes/, each+-- node exporting and importing variables. The /Kind2 prover/ uses this format,+-- which can be easily translated into the native format. module Copilot.Theorem.TransSys (module X) where  import Copilot.Theorem.TransSys.Spec as X
src/Copilot/Theorem/TransSys/Cast.hs view
@@ -3,6 +3,9 @@ {-# LANGUAGE RankNTypes, ScopedTypeVariables, GADTs #-} {-# LANGUAGE Safe #-} +-- | Casting of values with dynamic types and translating from Copilot core+-- types to Copilot theorem types.+ module Copilot.Theorem.TransSys.Cast   ( Dyn   , toDyn@@ -23,8 +26,11 @@  -------------------------------------------------------------------------------- +-- | Synonym for a dynamic type in Copilot core. type Dyn = Dynamic Type +-- | Translation of a Copilot type into Copilot theorem's internal+-- representation. castedType :: Type t -> K.U K.Type castedType t = case t of   Bool    -> K.U K.Bool@@ -39,6 +45,7 @@   Float   -> K.U K.Real   Double  -> K.U K.Real +-- | Cast a dynamic value to a given type. cast :: K.Type t -> Dyn -> t cast t v   | K.Integer <- t,  Just (vi :: Integer) <- _cast v = vi@@ -46,6 +53,8 @@   | K.Real    <- t,  Just (vr :: Double)  <- _cast v = vr   | otherwise = error "Bad type cast" +-- | Apply function to a corresponding type in Copilot theorem's internal+-- representation. casting :: Type t -> (forall t' . K.Type t' -> a) -> a casting t f = case castedType t of   K.U K.Bool    -> f K.Bool
src/Copilot/Theorem/TransSys/Invariants.hs view
@@ -1,11 +1,14 @@ {-# OPTIONS_GHC -O0 #-} {-# LANGUAGE Safe #-} +-- | Augment types with invariants.+ module Copilot.Theorem.TransSys.Invariants   ( HasInvariants (..)   , prop   ) where +-- | Type class for types with additional invariants or contraints. class HasInvariants a where    invariants :: a -> [(String, Bool)]@@ -13,5 +16,6 @@   checkInvs :: a -> Bool   checkInvs obj = all snd $ invariants obj +-- | Creates an invariant with a description. prop :: String -> Bool -> (String, Bool) prop = (,)
src/Copilot/Theorem/TransSys/Operators.hs view
@@ -4,6 +4,7 @@              RankNTypes #-} {-# LANGUAGE Safe #-} +-- | Operators in modular transition systems and their translation. module Copilot.Theorem.TransSys.Operators where  import qualified Copilot.Core as C@@ -14,6 +15,7 @@  -------------------------------------------------------------------------------- +-- | Unary operators. data Op1 a where   Not   :: Op1 Bool   Neg   :: Op1 a@@ -34,6 +36,7 @@   Atanh :: Op1 a   Acosh :: Op1 a +-- | Binary operators. data Op2 a b where   Eq     :: Op2 a    Bool   And    :: Op2 Bool Bool@@ -93,37 +96,50 @@  ------------------------------------------------------------------------------- --- | Some high level utilities to translate a Copilot operator in a standard way--- | The unhandled operators are monomorphic, and their names are labeled so--- | that each name corresponds to a unique uninterpreted function with a--- | monomorphic type.-----------------------------------------------------------------------------------+-- | Unhandled unary operator.+--+--   Unhandled operators are monomorphic, and their names are labeled so that+--   each name corresponds to a unique uninterpreted function with a+--   monomorphic type. data UnhandledOp1 = forall a b .   UnhandledOp1 String (Type a) (Type b) +-- | Unhandled binary operator.+--+--   Unhandled operators are monomorphic, and their names are labeled so that+--   each name corresponds to a unique uninterpreted function with a+--   monomorphic type. data UnhandledOp2 = forall a b c .   UnhandledOp2 String (Type a) (Type b) (Type c) +-- | Translate an Op1.+--+-- This function is parameterized so that it can be used to translate+-- in different contexts and with different targets.+--+-- 'm' is the monad in which the computation is made+--+-- 'resT' is the desired return type of the expression being translated handleOp1 ::-  -- 'm' is the monad in which the computation is made-  -- 'resT' is the desired return type of the expression being translated-  forall m expr _a _b resT. (Functor m) =>-  -- The desired return type-  Type resT ->-  -- The unary operator encountered and its argument-  (C.Op1 _a _b, C.Expr _a) ->-  -- The monadic function to translate an expression-  -- (for recursive calls to be mmadess)-  (forall t t'. Type t -> C.Expr t' -> m (expr t)) ->-  -- A function to deal with a operators not handled by copilot-kind-  (UnhandledOp1 -> m (expr resT)) ->-  -- The Op1 constructor of the 'expr' type-  (forall t . Type t -> Op1 t -> expr t -> expr t) ->+  forall m expr _a _b resT. (Functor m) -  m (expr resT)+  => Type resT+     -- ^ The desired return type +  -> (C.Op1 _a _b, C.Expr _a)+     -- ^ The unary operator encountered and its argument++  -> (forall t t'. Type t -> C.Expr t' -> m (expr t))+     -- ^ The monadic function to translate an expression++  -> (UnhandledOp1 -> m (expr resT))+      -- ^ A function to deal with a operators not handled++  -> (forall t . Type t -> Op1 t -> expr t -> expr t)+     -- ^ The Op1 constructor of the 'expr' type++  -> m (expr resT)+ handleOp1 resT (op, e) handleExpr notHandledF mkOp = case op of    C.Not      -> boolOp Not (handleExpr Bool e)@@ -181,18 +197,38 @@  -------------------------------------------------------------------------------- --- See the 'handleOp1' function for documentation+-- | Translate an Op2.+--+-- This function is parameterized so that it can be used to translate+-- in different contexts and with different targets.+--+-- 'm' is the monad in which the computation is made+--+-- 'resT' is the desired return type of the expression being translated handleOp2 ::-  forall m expr _a _b _c resT . (Monad m) =>-  Type resT ->-  (C.Op2 _a _b _c, C.Expr _a, C.Expr _b) ->-  (forall t t'. Type t -> C.Expr t' -> m (expr t)) ->-  (UnhandledOp2 -> m (expr resT)) ->-  (forall t a . Type t -> Op2 a t -> expr a -> expr a -> expr t) ->-  (expr Bool -> expr Bool) ->-  m (expr resT)+  forall m expr _a _b _c resT . (Monad m) +  => Type resT+     -- ^ The desired return type +  -> (C.Op2 _a _b _c, C.Expr _a, C.Expr _b)+     -- ^ The binary operator encountered and its arguments++  -> (forall t t'. Type t -> C.Expr t' -> m (expr t))+     -- ^ The monadic function to translate an expression++  -> (UnhandledOp2 -> m (expr resT))+      -- ^ A function to deal with a operators not handled++  -> (forall t a . Type t -> Op2 a t -> expr a -> expr a -> expr t)+     -- ^ The Op2 constructor of the 'expr' type++  -> (expr Bool -> expr Bool)+     -- ^ The Op1 for boolean negation++  -> m (expr resT)++ handleOp2 resT (op, e1, e2) handleExpr notHandledF mkOp notOp = case op of    C.And        -> boolConnector And@@ -294,6 +330,7 @@  -------------------------------------------------------------------------------- +-- | Error message for unexpected behavior / internal errors. typeErrMsg :: String typeErrMsg = "Unexpected type error in 'Misc.CoreOperators'" 
src/Copilot/Theorem/TransSys/PrettyPrint.hs view
@@ -3,6 +3,7 @@ {-# LANGUAGE NamedFieldPuns, GADTs #-} {-# LANGUAGE Safe #-} +-- | Pretty print a TransSys specification as a Kind2/Lustre specification. module Copilot.Theorem.TransSys.PrettyPrint ( prettyPrint ) where  import Copilot.Theorem.TransSys.Spec@@ -19,6 +20,7 @@ indent     = nest 4 emptyLine  = text "" +-- | Pretty print a TransSys specification as a Kind2/Lustre specification. prettyPrint :: TransSys -> String prettyPrint = render . pSpec 
src/Copilot/Theorem/TransSys/Renaming.hs view
@@ -2,6 +2,8 @@  {-# LANGUAGE Safe #-} +-- | A monad capable of keeping track of variable renames and of providing+-- fresh names for variables. module Copilot.Theorem.TransSys.Renaming   ( Renaming   , addReservedName@@ -25,18 +27,28 @@  -------------------------------------------------------------------------------- +-- | A monad capable of keeping track of variable renames and of providing+-- fresh names for variables. type Renaming = State RenamingST +-- | State needed to keep track of variable renames and reserved names. data RenamingST = RenamingST   { _reservedNames :: Set Var   , _renaming      :: Map ExtVar Var }  -------------------------------------------------------------------------------- +-- | Register a name as reserved or used. addReservedName :: Var -> Renaming () addReservedName v = modify $ \st ->     st {_reservedNames = Set.insert v (_reservedNames st)} +-- | Produce a fresh new name based on the variable names provided.+--+-- This function will try to pick a name from the given list and, if not, will+-- use one of the names in the list as a basis for new names.+--+-- PRE: the given list cannot be empty. getFreshName :: [Var] -> Renaming Var getFreshName vs = do   usedNames <- _reservedNames <$> get@@ -48,15 +60,24 @@   addReservedName v   return v -rename :: NodeId -> Var -> Var -> Renaming ()+-- | Map a name in the global namespace to a new variable name.+rename :: NodeId  -- ^ A node Id+       -> Var     -- ^ A variable within that node+       -> Var     -- ^ A new name for the variable+       -> Renaming () rename n v v' = modify $ \st ->     st {_renaming = Map.insert (ExtVar n v) v' (_renaming st)} +-- | Return a function that maps variables in the global namespace to their new+-- names if any renaming has been registered. getRenamingF :: Renaming (ExtVar -> Var) getRenamingF = do   mapping <- _renaming <$> get   return $ \extv -> fromMaybe (extVarLocalPart extv) (Map.lookup extv mapping) +-- | Run a computation in the 'Renaming' monad, providing a result and the+-- renaming function that maps variables in the global namespace to their new+-- local names. runRenaming :: Renaming a -> (a, ExtVar -> Var) runRenaming m =   evalState st' (RenamingST Set.empty Map.empty)
src/Copilot/Theorem/TransSys/Spec.hs view
@@ -3,6 +3,7 @@ {-# LANGUAGE ExistentialQuantification, GADTs, RankNTypes #-} {-# LANGUAGE Safe #-} +-- | Specification of Copilot streams as modular transition systems. module Copilot.Theorem.TransSys.Spec   ( module Copilot.Theorem.TransSys.Operators   , module Copilot.Theorem.TransSys.Type@@ -45,35 +46,56 @@  -------------------------------------------------------------------------------- +-- | Unique name that identifies a node. type NodeId = String++-- | Unique name that identifies a property. type PropId = String +-- | A modular transition system is defined by a graph of nodes and a series+-- of properties, each mapped to a variable. data TransSys = TransSys   { specNodes         :: [Node]   , specTopNodeId     :: NodeId   , specProps         :: Map PropId ExtVar } -+-- | A node is a set of variables living in a local namespace and corresponding+-- to the 'Var' type. data Node = Node   { nodeId            :: NodeId-  , nodeDependencies  :: [NodeId]-  , nodeLocalVars     :: Map Var VarDescr-  , nodeImportedVars  :: Bimap Var ExtVar+  , nodeDependencies  :: [NodeId]          -- ^ Nodes from which variables are+                                           --   imported.+  , nodeLocalVars     :: Map Var VarDescr  -- ^ Locally defined variables,+                                           --   either as the previous value of+                                           --   another variable (using 'Pre'),+                                           --   an expression involving+                                           --   variables (using 'Expr') or a+                                           --   set of constraints (using+                                           --   'Constrs').+  , nodeImportedVars  :: Bimap Var ExtVar  -- ^ Binds each imported variable to+                                           --   its local name.   , nodeConstrs       :: [Expr Bool] }  +-- | Identifer of a variable in the local (within one node) namespace. data Var      =  Var {varName :: String}   deriving (Eq, Show, Ord) +-- | Identifer of a variable in the global namespace by specifying both a node+-- name and a variable. data ExtVar   =  ExtVar {extVarNode :: NodeId, extVarLocalPart :: Var }   deriving (Eq, Ord) +-- | A description of a variable together with its type. data VarDescr = forall t . VarDescr   { varType :: Type t   , varDef  :: VarDef t } +-- | A variable definition either as a delay, an operation on variables, or+-- a constraint. data VarDef t = Pre t Var | Expr (Expr t) | Constrs [Expr Bool] +-- | A point-wise (time-wise) expression. data Expr t where   Const :: Type t -> t -> Expr t   Ite   :: Type t -> Expr Bool -> Expr t -> Expr t -> Expr t@@ -83,6 +105,7 @@  -------------------------------------------------------------------------------- +-- | Constructor for variables identifiers in the global namespace. mkExtVar node name = ExtVar node (Var name)  foldExpr :: (Monoid m) => (forall t . Expr t -> m) -> Expr a -> m@@ -97,6 +120,7 @@ foldUExpr :: (Monoid m) => (forall t . Expr t -> m) -> U Expr -> m foldUExpr f (U e) = foldExpr f e +-- | Apply an arbitrary transformation to the leafs of an expression. transformExpr :: (forall a . Expr a -> Expr a) -> Expr t -> Expr t transformExpr f = tre   where@@ -108,6 +132,8 @@  -------------------------------------------------------------------------------- +-- | The set of variables related to a node (union of the local variables and+-- the imported variables after deferencing them). nodeVarsSet :: Node -> Set Var nodeVarsSet = liftA2 Set.union   nodeLocalVarsSet@@ -157,10 +183,13 @@ specNodesIds :: TransSys -> Set NodeId specNodesIds s = Set.fromList . map nodeId $ specNodes s +-- | Given a modular transition system, produce a map from each node to its+-- dependencies. specDependenciesGraph :: TransSys -> Map NodeId [NodeId] specDependenciesGraph s =   Map.fromList [ (nodeId n, nodeDependencies n) | n <- specNodes s ] +-- | Return the top node of a modular transition system. specTopNode :: TransSys -> Node specTopNode spec = fromJust $ List.find   ((== specTopNodeId spec) . nodeId)@@ -183,6 +212,9 @@     , prop "The nodes invariants hold" $ all checkInvs (specNodes s)     ] +-- | True if the graph is topologically sorted (i.e., if the dependencies of a+-- node appear in the list of 'specNodes' before the node that depends on+-- them). isTopologicallySorted :: TransSys -> Bool isTopologicallySorted spec =   isJust $ foldM inspect Set.empty (specNodes spec)
src/Copilot/Theorem/TransSys/Transform.hs view
@@ -3,6 +3,7 @@ {-# LANGUAGE RankNTypes #-} {-# LANGUAGE Safe #-} +-- | Helper module to manipulate and simplify TransSys graphs. module Copilot.Theorem.TransSys.Transform   ( mergeNodes   , inline@@ -39,6 +40,9 @@  -------------------------------------------------------------------------------- +-- | Merge all the given nodes, replacing all references to the given node Ids+-- with a reference to a fresh node id (unless the nodes given as argument+-- contain the top node), in which case its ID is chosen instead. mergeNodes :: [NodeId] -> TransSys -> TransSys mergeNodes toMergeIds spec =   spec@@ -70,7 +74,9 @@       , id <- nodeDependencies n       , id `notElem` toMergeIds ] -    -- All the work of renaming is done in the monad with the same name+    -- All the work of renaming is done in the 'Misc.Renaming' monad. Some code+    -- complexity has been added so the variable names remains as clear as+    -- possible after merging two nodes.     (importedVars, renamingF) = runRenaming $ do       renameLocalVars toMerge       redirectLocalImports toMerge@@ -182,9 +188,27 @@  -------------------------------------------------------------------------------- +-- | Discard all the structure of a /modular transition system/ and turn it+-- into a /non-modular transition system/ with only one node. inline :: TransSys -> TransSys inline spec = mergeNodes [nodeId n | n <- specNodes spec] spec +-- | Remove cycles by merging nodes participating in strongly connected+-- components.+--+-- The transition system obtained by the 'TransSys.Translate' module is+-- perfectly consistent. However, it can't be directly translated into the+-- /Kind2 native file format/. Indeed, it is natural to bind each node to a+-- predicate but the Kind2 file format requires that each predicate only uses+-- previously defined predicates. However, some nodes in our transition system+-- could be mutually recursive. Therefore, the goal of 'removeCycles' is to+-- remove such dependency cycles.+--+-- The function 'removeCycles' computes the strongly connected components of+-- the dependency graph and merge each one into a single node using+-- 'mergeNodes'. The complexity of this process is high in the worst case (the+-- square of the total size of the system times the size of the biggest node)+-- but good in practice as few nodes are to be merged in most practical cases. removeCycles :: TransSys -> TransSys removeCycles spec =   topoSort $ foldr mergeComp spec (buildScc nodeId $ specNodes spec)@@ -202,11 +226,12 @@  -------------------------------------------------------------------------------- --- | Completes each node of a specification with imported variables such--- | that each node contains a copy of all its dependencies--- | The given specification should have its node sorted by topological--- | order.--- | The top nodes should have all the other nodes as its dependencies+-- | Completes each node of a specification with imported variables such that+-- each node contains a copy of all its dependencies.+--+-- The given specification should have its node sorted by topological order.+--+-- The top nodes should have all the other nodes as its dependencies.  complete :: TransSys -> TransSys complete spec =
src/Copilot/Theorem/TransSys/Translate.hs view
@@ -4,6 +4,41 @@              ScopedTypeVariables, GADTs, FlexibleContexts #-} {-# LANGUAGE Safe #-} +-- | Translate Copilot specifications into a modular transition system.+--+-- Each stream is associated to a node. The most significant task of this+-- translation process is to /flatten/ the copilot specification so the value+-- of all streams at time @n@ only depends on the values of all the streams at+-- time @n - 1@. For example, for the following Fibonacci implementation in+-- Copilot:+--+-- @+-- fib = [1, 1] ++ (fib + drop 1 fib)+-- @+--+-- the translation, converts it into:+--+-- @+-- fib0 = [1] ++ fib1+-- fib1 = [1] ++ (fib1 + fib0)+-- @+--+-- and then into the node:+--+-- @+-- NODE 'fib' DEPENDS ON []+-- DEFINES+--     out : Int =+--         1 -> pre out.1+--     out.1 : Int =+--         1 -> pre out.2+--     out.2 : Int =+--         (out) + (out.1)+-- @+--+-- This flattening process is made easier by the fact that the @++@ Copilot+-- operator only occurs leftmost in a stream definition after the reification+-- process. module Copilot.Theorem.TransSys.Translate ( translate ) where  import Copilot.Theorem.TransSys.Spec@@ -47,6 +82,7 @@  -------------------------------------------------------------------------------- +-- | Translate Copilot specifications into a modular transition system. translate :: C.Spec -> TransSys translate cspec = 
src/Copilot/Theorem/TransSys/Type.hs view
@@ -3,6 +3,7 @@ {-# LANGUAGE ExistentialQuantification, GADTs #-} {-# LANGUAGE Safe #-} +-- | Types suported by the modular transition systems. module Copilot.Theorem.TransSys.Type   ( Type (..)   , U (..)@@ -13,11 +14,15 @@  -------------------------------------------------------------------------------- +-- | A type at both value and type level.+--+-- Real numbers are mapped to 'Double's. data Type a where   Bool    :: Type Bool   Integer :: Type Integer   Real    :: Type Double +-- | Proofs of type equality. instance EqualType Type where   Bool    =~= Bool     = Just Refl   Integer =~= Integer  = Just Refl@@ -26,8 +31,9 @@  -------------------------------------------------------------------------------- +-- | Unknown types.+-- -- For instance, 'U Expr' is the type of an expression of unknown type- data U f = forall t . U (f t) data U2 f g = forall t . U2 (f t) (g t) 
+ src/Copilot/Theorem/What4.hs view
@@ -0,0 +1,942 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE KindSignatures #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE MultiWayIf #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE StandaloneDeriving #-}+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE TupleSections #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE TypeOperators #-}++-- |+-- Module      : Copilot.Theorem.What4+-- Description : Prove spec properties using What4.+-- Copyright   : (c) Ben Selfridge, 2020+-- Maintainer  : benselfridge@galois.com+-- Stability   : experimental+-- Portability : POSIX+--+-- Spec properties are translated into the language of SMT solvers using+-- @What4@. A backend solver is then used to prove the property is true. The+-- technique is sound, but incomplete. If a property is proved true by this+-- technique, then it can be guaranteed to be true. However, if a property is+-- not proved true, that does not mean it isn't true. Very simple specifications+-- are unprovable by this technique, including:+--+-- @+-- a = True : a+-- @+--+-- The above specification will not be proved true. The reason is that this+-- technique does not perform any sort of induction. When proving the inner @a@+-- expression, the technique merely allocates a fresh constant standing for+-- "@a@, one timestep in the past." Nothing is asserted about the fresh+-- constant.+--+-- An example of a property that is provable by this approach is:+--+-- @+-- a = True : b+-- b = not a+--+-- -- Property: a || b+-- @+--+-- By allocating a fresh constant, @b_-1@, standing for "the value of @b@ one+-- timestep in the past", the equation for @a || b@ at some arbitrary point in+-- the future reduces to @b_-1 || not b_-1@, which is always true.+--+-- In addition to proving that the stream expression is true at some arbitrary+-- point in the future, we also prove it for the first @k@ timesteps, where @k@+-- is the maximum buffer length of all streams in the given spec. This amounts+-- to simply interpreting the spec, although external variables are still+-- represented as constants with unknown values.++module Copilot.Theorem.What4+  ( prove, Solver(..), SatResult(..)+  ) where++import qualified Copilot.Core.Expr       as CE+import qualified Copilot.Core.Operators  as CE+import qualified Copilot.Core.Spec       as CS+import qualified Copilot.Core.Type       as CT+import qualified Copilot.Core.Type.Array as CT++import qualified What4.Config           as WC+import qualified What4.Expr.Builder     as WB+import qualified What4.Expr.GroundEval  as WG+import qualified What4.Interface        as WI+import qualified What4.BaseTypes        as WT+import qualified What4.Solver           as WS+import qualified What4.Solver.DReal     as WS++import qualified Control.Monad.Fail as Fail+import Control.Monad.State+import qualified Data.BitVector.Sized as BV+import Data.Foldable (foldrM)+import Data.List (elemIndex)+import Data.Maybe (fromJust)+import qualified Data.Map as Map+import Data.Parameterized.Classes+import Data.Parameterized.Context hiding (zipWithM)+import Data.Parameterized.NatRepr+import Data.Parameterized.Nonce+import Data.Parameterized.Some+import Data.Parameterized.SymbolRepr+import qualified Data.Parameterized.Vector as V+import Data.Word+import GHC.Float (castWord32ToFloat, castWord64ToDouble)+import GHC.TypeNats (KnownNat)+import qualified Panic as Panic++--------------------------------------------------------------------------------+-- 'prove' function+--+-- To prove properties of a spec, we translate them into What4 using the TransM+-- monad (transformer on top of IO), then negate each property and ask a backend+-- solver to produce a model for the negation.++-- | We assume round-near-even throughout, but this variable can be changed if+-- needed.+fpRM :: WI.RoundingMode+fpRM = WI.RNE++-- | No builder state needed.+data BuilderState a = EmptyState++-- | The solvers supported by the what4 backend.+data Solver = CVC4 | DReal | Yices | Z3++-- | The 'prove' function returns results of this form for each property in a+-- spec.+data SatResult = Valid | Invalid | Unknown+  deriving Show++type CounterExample = [(String, Some CopilotValue)]++-- | Attempt to prove all of the properties in a spec via an SMT solver (which+-- must be installed locally on the host). Return an association list mapping+-- the names of each property to the result returned by the solver.+prove :: Solver+      -- ^ Solver to use+      -> CS.Spec+      -- ^ Spec+      -> IO [(CE.Name, SatResult)]+prove solver spec = do+  -- Setup symbolic backend+  Some ng <- newIONonceGenerator+  sym <- WB.newExprBuilder WB.FloatIEEERepr EmptyState ng++  -- Solver-specific options+  case solver of+    CVC4 -> WC.extendConfig WS.cvc4Options (WI.getConfiguration sym)+    DReal -> WC.extendConfig WS.drealOptions (WI.getConfiguration sym)+    Yices -> WC.extendConfig WS.yicesOptions (WI.getConfiguration sym)+    Z3 -> WC.extendConfig WS.z3Options (WI.getConfiguration sym)++  -- Build up initial translation state+  let streamMap = Map.fromList $+        (\stream -> (CS.streamId stream, stream)) <$> CS.specStreams spec+  pow <- WI.freshTotalUninterpFn sym (WI.safeSymbol "pow") knownRepr knownRepr+  logb <- WI.freshTotalUninterpFn sym (WI.safeSymbol "logb") knownRepr knownRepr+  let st = TransState Map.empty Map.empty Map.empty streamMap pow logb++  -- Define TransM action for proving properties. Doing this in TransM rather+  -- than IO allows us to reuse the state for each property.+  let proveProperties = forM (CS.specProperties spec) $ \pr -> do+        let bufLen (CS.Stream _ buf _ _) = length buf+            maxBufLen = maximum (0 : (bufLen <$> CS.specStreams spec))+        prefix <- forM [0 .. maxBufLen - 1] $ \k -> do+          XBool p <- translateExprAt sym k (CS.propertyExpr pr)+          return p+        XBool p <- translateExpr sym 0 (CS.propertyExpr pr)+        p_and_prefix <- liftIO $ foldrM (WI.andPred sym) p prefix+        not_p_and_prefix <- liftIO $ WI.notPred sym p_and_prefix++        let clauses = [not_p_and_prefix]+        case solver of+          CVC4 -> liftIO $ WS.runCVC4InOverride sym WS.defaultLogData clauses $ \case+            WS.Sat (_ge, _) -> return (CS.propertyName pr, Invalid)+            WS.Unsat _ -> return (CS.propertyName pr, Valid)+            WS.Unknown -> return (CS.propertyName pr, Unknown)+          DReal -> liftIO $ WS.runDRealInOverride sym WS.defaultLogData clauses $ \case+            WS.Sat (_ge, _) -> return (CS.propertyName pr, Invalid)+            WS.Unsat _ -> return (CS.propertyName pr, Valid)+            WS.Unknown -> return (CS.propertyName pr, Unknown)+          Yices -> liftIO $ WS.runYicesInOverride sym WS.defaultLogData clauses $ \case+            WS.Sat _ge -> return (CS.propertyName pr, Invalid)+            WS.Unsat _ -> return (CS.propertyName pr, Valid)+            WS.Unknown -> return (CS.propertyName pr, Unknown)+          Z3 -> liftIO $ WS.runZ3InOverride sym WS.defaultLogData clauses $ \case+            WS.Sat (_ge, _) -> return (CS.propertyName pr, Invalid)+            WS.Unsat _ -> return (CS.propertyName pr, Valid)+            WS.Unknown -> return (CS.propertyName pr, Unknown)++  -- Execute the action and return the results for each property+  (res, _) <- runStateT (unTransM proveProperties) st+  return res++--------------------------------------------------------------------------------+-- What4 translation++-- | the state for translating Copilot expressions into What4 expressions. As we+-- translate, we generate fresh symbolic constants for external variables and+-- for stream variables. We need to only generate one constant per variable, so+-- we allocate them in a map. When we need the constant for a particular+-- variable, we check if it is already in the map, and return it if it is; if it+-- isn't, we generate a fresh constant at that point, store it in the map, and+-- return it.+--+-- We also store three immutable fields in this state, rather than wrap them up+-- in another monad transformer layer. These are initialized prior to+-- translation and are never modified. They are the map from stream ids to the+-- core stream definitions, a symbolic uninterpreted function for "pow", and a+-- symbolic uninterpreted function for "logb".+data TransState t = TransState {+  -- | Map of all external variables we encounter during translation. These are+  -- just fresh constants. The offset indicates how many timesteps in the past+  -- this constant represents for that stream.+  externVars :: Map.Map (CE.Name, Int) (XExpr t),+  -- | Map of external variables at specific indices (positive), rather than+  -- offset into the past. This is for interpreting streams at specific offsets.+  externVarsAt :: Map.Map (CE.Name, Int) (XExpr t),+  -- | Map from (stream id, negative offset) to fresh constant. These are all+  -- constants representing the values of a stream at some point in the past.+  -- The offset (ALWAYS NEGATIVE) indicates how many timesteps in the past+  -- this constant represents for that stream.+  streamConstants :: Map.Map (CE.Id, Int) (XExpr t),+  -- | Map from stream ids to the streams themselves. This value is never+  -- modified, but I didn't want to make this an RWS, so it's represented as a+  -- stateful value.+  streams :: Map.Map CE.Id CS.Stream,+  -- | Binary power operator, represented as an uninterpreted function.+  pow :: WB.ExprSymFn t (WB.Expr t)+         (EmptyCtx ::> WT.BaseRealType ::> WT.BaseRealType)+         WT.BaseRealType,+  -- | Binary logarithm operator, represented as an uninterpreted function.+  logb :: WB.ExprSymFn t (WB.Expr t)+          (EmptyCtx ::> WT.BaseRealType ::> WT.BaseRealType)+          WT.BaseRealType+  }++newtype TransM t a = TransM { unTransM :: StateT (TransState t) IO a }+  deriving ( Functor+           , Applicative+           , Monad+           , MonadIO+           , MonadState (TransState t)+           )++instance Fail.MonadFail (TransM t) where+  fail = error++data CopilotWhat4 = CopilotWhat4++instance Panic.PanicComponent CopilotWhat4 where+  panicComponentName _ = "Copilot/What4 translation"+  panicComponentIssues _ = "https://github.com/Copilot-Language/copilot-theorem/issues"++  {-# NOINLINE Panic.panicComponentRevision #-}+  panicComponentRevision = $(Panic.useGitRevision)++-- | Use this function rather than an error monad since it indicates that+-- copilot-core's "reify" function did something incorrectly.+panic :: MonadIO m => m a+panic = Panic.panic CopilotWhat4 "Copilot.Theorem.What4"+        [ "Ill-typed core expression" ]++-- | The What4 representation of a copilot expression. We do not attempt to+-- track the type of the inner expression at the type level, but instead lump+-- everything together into the 'XExpr t' type. The only reason this is a GADT+-- is for the array case; we need to know that the array length is strictly+-- positive.+data XExpr t where+  XBool       :: WB.Expr t WT.BaseBoolType -> XExpr t+  XInt8       :: WB.Expr t (WT.BaseBVType 8) -> XExpr t+  XInt16      :: WB.Expr t (WT.BaseBVType 16) -> XExpr t+  XInt32      :: WB.Expr t (WT.BaseBVType 32) -> XExpr t+  XInt64      :: WB.Expr t (WT.BaseBVType 64) -> XExpr t+  XWord8      :: WB.Expr t (WT.BaseBVType 8) -> XExpr t+  XWord16     :: WB.Expr t (WT.BaseBVType 16) -> XExpr t+  XWord32     :: WB.Expr t (WT.BaseBVType 32) -> XExpr t+  XWord64     :: WB.Expr t (WT.BaseBVType 64) -> XExpr t+  XFloat      :: WB.Expr t (WT.BaseFloatType WT.Prec32) -> XExpr t+  XDouble     :: WB.Expr t (WT.BaseFloatType WT.Prec64) -> XExpr t+  XEmptyArray :: XExpr t+  XArray      :: 1 <= n => V.Vector n (XExpr t) -> XExpr t+  XStruct     :: [XExpr t] -> XExpr t+  -- XArray      :: NatRepr n+  --             -> BaseTypeRepr tp+  --             -> Some (WB.Expr t)+  -- XStruct     :: Assignment BaseTypeRepr tps+  --             -> WB.Expr t (BaseStructType tps)+  --             -> XExpr t++deriving instance Show (XExpr t)++data CopilotValue a = CopilotValue { cvType :: CT.Type a+                                   , cvVal :: a+                                   }++valFromExpr :: WG.GroundEvalFn t -> XExpr t -> IO (Some CopilotValue)+valFromExpr ge xe = case xe of+  XBool e -> Some . CopilotValue CT.Bool <$> WG.groundEval ge e+  XInt8 e -> Some . CopilotValue CT.Int8 . fromBV <$> WG.groundEval ge e+  XInt16 e -> Some . CopilotValue CT.Int16 . fromBV <$> WG.groundEval ge e+  XInt32 e -> Some . CopilotValue CT.Int32 . fromBV <$> WG.groundEval ge e+  XInt64 e -> Some . CopilotValue CT.Int64 . fromBV <$> WG.groundEval ge e+  XWord8 e -> Some . CopilotValue CT.Word8 . fromBV <$> WG.groundEval ge e+  XWord16 e -> Some . CopilotValue CT.Word16 . fromBV <$> WG.groundEval ge e+  XWord32 e -> Some . CopilotValue CT.Word32 . fromBV <$> WG.groundEval ge e+  XWord64 e -> Some . CopilotValue CT.Word64 . fromBV <$> WG.groundEval ge e+  XFloat e ->+    Some . CopilotValue CT.Float . castWord32ToFloat . fromBV <$> WG.groundEval ge e+  XDouble e ->+    Some . CopilotValue CT.Double . castWord64ToDouble . fromBV <$> WG.groundEval ge e+  _ -> error "valFromExpr unhandled case"+  where fromBV :: forall a w . Num a => BV.BV w -> a+        fromBV = fromInteger . BV.asUnsigned++-- | A view of an XExpr as a bitvector expression, a natrepr for its width, its+-- signed/unsigned status, and the constructor used to reconstruct an XExpr from+-- it. This is a useful view for translation, as many of the operations can be+-- grouped together for all words\/ints\/floats.+data SomeBVExpr t where+  SomeBVExpr :: 1 <= w+             => WB.BVExpr t w+             -> NatRepr w+             -> BVSign+             -> (WB.BVExpr t w -> XExpr t)+             -> SomeBVExpr t++-- | The sign of a bitvector -- this indicates whether it is to be interpreted+-- as a signed 'Int' or an unsigned 'Word'.+data BVSign = Signed | Unsigned++-- | If the inner expression can be viewed as a bitvector, we project out a view+-- of it as such.+asBVExpr :: XExpr t -> Maybe (SomeBVExpr t)+asBVExpr xe = case xe of+  XInt8 e -> Just (SomeBVExpr e knownNat Signed XInt8)+  XInt16 e -> Just (SomeBVExpr e knownNat Signed XInt16)+  XInt32 e -> Just (SomeBVExpr e knownNat Signed XInt32)+  XInt64 e -> Just (SomeBVExpr e knownNat Signed XInt64)+  XWord8 e -> Just (SomeBVExpr e knownNat Unsigned XWord8)+  XWord16 e -> Just (SomeBVExpr e knownNat Unsigned XWord16)+  XWord32 e -> Just (SomeBVExpr e knownNat Unsigned XWord32)+  XWord64 e -> Just (SomeBVExpr e knownNat Unsigned XWord64)+  _ -> Nothing++-- | Translate a constant expression by creating a what4 literal and packaging+-- it up into an 'XExpr'.+translateConstExpr :: forall a t st fs .+                      WB.ExprBuilder t st fs+                   -> CT.Type a+                   -> a+                   -> IO (XExpr t)+translateConstExpr sym tp a = case tp of+  CT.Bool -> case a of+    True  -> return $ XBool (WI.truePred sym)+    False -> return $ XBool (WI.falsePred sym)+  CT.Int8 -> XInt8 <$> WI.bvLit sym knownNat (BV.int8 a)+  CT.Int16 -> XInt16 <$> WI.bvLit sym knownNat (BV.int16 a)+  CT.Int32 -> XInt32 <$> WI.bvLit sym knownNat (BV.int32 a)+  CT.Int64 -> XInt64 <$> WI.bvLit sym knownNat (BV.int64 a)+  CT.Word8 -> XWord8 <$> WI.bvLit sym knownNat (BV.word8 a)+  CT.Word16 -> XWord16 <$> WI.bvLit sym knownNat (BV.word16 a)+  CT.Word32 -> XWord32 <$> WI.bvLit sym knownNat (BV.word32 a)+  CT.Word64 -> XWord64 <$> WI.bvLit sym knownNat (BV.word64 a)+  CT.Float -> XFloat <$> WI.floatLit sym knownRepr (toRational a)+  CT.Double -> XDouble <$> WI.floatLit sym knownRepr (toRational a)+  CT.Array tp -> do+    elts <- traverse (translateConstExpr sym tp) (CT.arrayelems a)+    Just (Some n) <- return $ someNat (length elts)+    case isZeroOrGT1 n of+      Left Refl -> return XEmptyArray+      Right LeqProof -> do+        let Just v = V.fromList n elts+        return $ XArray v+  CT.Struct _ -> do+    elts <- forM (CT.toValues a) $ \(CT.Value tp (CT.Field a)) ->+      translateConstExpr sym tp a+    return $ XStruct elts++arrayLen :: KnownNat n => CT.Type (CT.Array n t) -> NatRepr n+arrayLen _ = knownNat++-- | Generate a fresh constant for a given copilot type. This will be called+-- whenever we attempt to get the constant for a given external variable or+-- stream variable, but that variable has not been accessed yet and therefore+-- has no constant allocated.+freshCPConstant :: forall t st fs a .+                   WB.ExprBuilder t st fs+                -> String+                -> CT.Type a+                -> IO (XExpr t)+freshCPConstant sym nm tp = case tp of+  CT.Bool -> XBool <$> WI.freshConstant sym (WI.safeSymbol nm) knownRepr+  CT.Int8 -> XInt8 <$> WI.freshConstant sym (WI.safeSymbol nm) knownRepr+  CT.Int16 -> XInt16 <$> WI.freshConstant sym (WI.safeSymbol nm) knownRepr+  CT.Int32 -> XInt32 <$> WI.freshConstant sym (WI.safeSymbol nm) knownRepr+  CT.Int64 -> XInt64 <$> WI.freshConstant sym (WI.safeSymbol nm) knownRepr+  CT.Word8 -> XWord8 <$> WI.freshConstant sym (WI.safeSymbol nm) knownRepr+  CT.Word16 -> XWord16 <$> WI.freshConstant sym (WI.safeSymbol nm) knownRepr+  CT.Word32 -> XWord32 <$> WI.freshConstant sym (WI.safeSymbol nm) knownRepr+  CT.Word64 -> XWord64 <$> WI.freshConstant sym (WI.safeSymbol nm) knownRepr+  CT.Float -> XFloat <$> WI.freshConstant sym (WI.safeSymbol nm) knownRepr+  CT.Double -> XDouble <$> WI.freshConstant sym (WI.safeSymbol nm) knownRepr+  atp@(CT.Array itp) -> do+    n <- return $ arrayLen atp+    case isZeroOrGT1 n of+      Left Refl -> return XEmptyArray+      Right LeqProof -> do+        Refl <- return $ minusPlusCancel n (knownNat @1)+        elts :: V.Vector n (XExpr t) <- V.generateM (decNat n) (const (freshCPConstant sym "" itp))+        return $ XArray elts+  CT.Struct stp -> do+    elts <- forM (CT.toValues stp) $ \(CT.Value ftp _) -> freshCPConstant sym "" ftp+    return $ XStruct elts++-- | Get the constant for a given stream id and some offset into the past. This+-- should only be called with a strictly negative offset. When this function+-- gets called for the first time for a given (streamId, offset) pair, it+-- generates a fresh constant and stores it in an internal map. Thereafter, this+-- function will just return that constant when called with the same pair.+getStreamConstant :: WB.ExprBuilder t st fs -> CE.Id -> Int -> TransM t (XExpr t)+getStreamConstant sym streamId offset = do+  scs <- gets streamConstants+  case Map.lookup (streamId, offset) scs of+    Just xe -> return xe+    Nothing -> do+      CS.Stream _ _ _ tp <- getStreamDef streamId+      let nm = show streamId ++ "_" ++ show offset+      xe <- liftIO $ freshCPConstant sym nm tp+      modify (\st -> st { streamConstants = Map.insert (streamId, offset) xe scs })+      return xe++-- | Get the constant for a given external variable and some offset into the+-- past. This should only be called with a strictly negative offset. When this+-- function gets called for the first time for a given (var, offset) pair, it+-- generates a fresh constant and stores it in an internal map. Thereafter, this+-- function will just return that constant when called with the same pair.+getExternConstant :: WB.ExprBuilder t st fs+                  -> CT.Type a+                  -> CE.Name+                  -> Int+                  -> TransM t (XExpr t)+getExternConstant sym tp var offset = do+  es <- gets externVars+  case Map.lookup (var, offset) es of+    Just xe -> return xe+    Nothing -> do+      xe <- liftIO $ freshCPConstant sym var tp+      modify (\st -> st { externVars = Map.insert (var, offset) xe es} )+      return xe++-- | Get the constant for a given external variable at some specific timestep.+getExternConstantAt :: WB.ExprBuilder t st fs+                    -> CT.Type a+                    -> CE.Name+                    -> Int+                    -> TransM t (XExpr t)+getExternConstantAt sym tp var ix = do+  es <- gets externVarsAt+  case Map.lookup (var, ix) es of+    Just xe -> return xe+    Nothing -> do+      xe <- liftIO $ freshCPConstant sym var tp+      modify (\st -> st { externVarsAt = Map.insert (var, ix) xe es} )+      return xe++-- | Retrieve a stream definition given its id.+getStreamDef :: CE.Id -> TransM t CS.Stream+getStreamDef streamId = fromJust <$> gets (Map.lookup streamId . streams)++-- | Translate an expression into a what4 representation. The int offset keeps+-- track of how many timesteps into the past each variable is referring to.+-- Initially the value should be zero, but when we translate a stream, the+-- offset is recomputed based on the length of that stream's prefix (subtracted)+-- and the drop index (added).+translateExpr :: WB.ExprBuilder t st fs+              -> Int+              -- ^ number of timesteps in the past we are currently looking+              -- (must always be <= 0)+              -> CE.Expr a+              -> TransM t (XExpr t)+translateExpr sym offset e = case e of+  CE.Const tp a -> liftIO $ translateConstExpr sym tp a+  CE.Drop _tp ix streamId+    -- If we are referencing a past value of this stream, just return an+    -- unconstrained constant.+    | offset + fromIntegral ix < 0 ->+        getStreamConstant sym streamId (offset + fromIntegral ix)+    -- If we are referencing a current or future value of this stream, we need+    -- to translate the stream's expression, using an offset computed based on+    -- the current offset (negative or 0), the drop index (positive or 0), and+    -- the length of the stream's buffer (subtracted).+    | otherwise -> do+      CS.Stream _ buf e _ <- getStreamDef streamId+      translateExpr sym (offset + fromIntegral ix - length buf) e+  CE.Local _ _ _ _ _ -> error "translateExpr: Local unimplemented"+  CE.Var _ _ -> error "translateExpr: Var unimplemented"+  CE.ExternVar tp nm _prefix -> getExternConstant sym tp nm offset+  CE.Op1 op e -> liftIO . translateOp1 sym op =<< translateExpr sym offset e+  CE.Op2 op e1 e2 -> do+    xe1 <- translateExpr sym offset e1+    xe2 <- translateExpr sym offset e2+    powFn <- gets pow+    logbFn <- gets logb+    liftIO $ translateOp2 sym powFn logbFn op xe1 xe2+  CE.Op3 op e1 e2 e3 -> do+    xe1 <- translateExpr sym offset e1+    xe2 <- translateExpr sym offset e2+    xe3 <- translateExpr sym offset e3+    liftIO $ translateOp3 sym op xe1 xe2 xe3+  CE.Label _ _ _ -> error "translateExpr: Label unimplemented"++-- | Translate an expression into a what4 representation at a /specific/+-- timestep, rather than "at some indeterminate point in the future."+translateExprAt :: WB.ExprBuilder t st fs+                -> Int+                -- ^ Index of timestep+                -> CE.Expr a+                -- ^ stream expression+                -> TransM t (XExpr t)+translateExprAt sym k e = do+  case e of+    CE.Const tp a -> liftIO $ translateConstExpr sym tp a+    CE.Drop _tp ix streamId -> do+      CS.Stream _ buf e tp <- getStreamDef streamId+      if k' < length buf+        then liftIO $ translateConstExpr sym tp (buf !! k')+        else translateExprAt sym (k' - length buf) e+      where k' = k + fromIntegral ix+    CE.Local _ _ _ _ _ -> error "translateExpr: Local unimplemented"+    CE.Var _ _ -> error "translateExpr: Var unimplemented"+    CE.ExternVar tp nm _prefix -> getExternConstantAt sym tp nm k+    CE.Op1 op e -> liftIO . translateOp1 sym op =<< translateExprAt sym k e+    CE.Op2 op e1 e2 -> do+      xe1 <- translateExprAt sym k e1+      xe2 <- translateExprAt sym k e2+      powFn <- gets pow+      logbFn <- gets logb+      liftIO $ translateOp2 sym powFn logbFn op xe1 xe2+    CE.Op3 op e1 e2 e3 -> do+      xe1 <- translateExprAt sym k e1+      xe2 <- translateExprAt sym k e2+      xe3 <- translateExprAt sym k e3+      liftIO $ translateOp3 sym op xe1 xe2 xe3+    CE.Label _ _ _ -> error "translateExpr: Label unimplemented"++type BVOp1 w t = (KnownNat w, 1 <= w) => WB.BVExpr t w -> IO (WB.BVExpr t w)++type FPOp1 fpp t = KnownRepr WT.FloatPrecisionRepr fpp => WB.Expr t (WT.BaseFloatType fpp) -> IO (WB.Expr t (WT.BaseFloatType fpp))++type RealOp1 t = WB.Expr t WT.BaseRealType -> IO (WB.Expr t WT.BaseRealType)++fieldName :: KnownSymbol s => CT.Field s a -> SymbolRepr s+fieldName _ = knownSymbol++valueName :: CT.Value a -> Some SymbolRepr+valueName (CT.Value _ f) = Some (fieldName f)++translateOp1 :: forall t st fs a b .+                WB.ExprBuilder t st fs+             -> CE.Op1 a b+             -> XExpr t+             -> IO (XExpr t)+translateOp1 sym op xe = case (op, xe) of+  (CE.Not, XBool e) -> XBool <$> WI.notPred sym e+  (CE.Not, _) -> panic+  (CE.Abs _, xe) -> numOp bvAbs fpAbs xe+    where bvAbs :: BVOp1 w t+          bvAbs e = do zero <- WI.bvLit sym knownNat (BV.zero knownNat)+                       e_neg <- WI.bvSlt sym e zero+                       neg_e <- WI.bvSub sym zero e+                       WI.bvIte sym e_neg neg_e e+          fpAbs :: FPOp1 fpp t+          fpAbs e = do zero <- WI.floatLit sym knownRepr 0+                       e_neg <- WI.floatLt sym e zero+                       neg_e <- WI.floatSub sym fpRM zero e+                       WI.floatIte sym e_neg neg_e e+  (CE.Sign _, xe) -> numOp bvSign fpSign xe+    where bvSign :: BVOp1 w t+          bvSign e = do zero <- WI.bvLit sym knownRepr (BV.zero knownNat)+                        neg_one <- WI.bvLit sym knownNat (BV.mkBV knownNat (-1))+                        pos_one <- WI.bvLit sym knownNat (BV.mkBV knownNat 1)+                        e_zero <- WI.bvEq sym e zero+                        e_neg <- WI.bvSlt sym e zero+                        t <- WI.bvIte sym e_neg neg_one pos_one+                        WI.bvIte sym e_zero zero t+          fpSign :: FPOp1 fpp t+          fpSign e = do zero <- WI.floatLit sym knownRepr 0+                        neg_one <- WI.floatLit sym knownRepr (-1)+                        pos_one <- WI.floatLit sym knownRepr 1+                        e_zero <- WI.floatEq sym e zero+                        e_neg <- WI.floatLt sym e zero+                        t <- WI.floatIte sym e_neg neg_one pos_one+                        WI.floatIte sym e_zero zero t+  (CE.Recip _, xe) -> fpOp recip xe+    where recip :: FPOp1 fpp t+          recip e = do one <- WI.floatLit sym knownRepr 1+                       WI.floatDiv sym fpRM one e+  (CE.Exp _, xe) -> realOp (WI.realExp sym) xe+  (CE.Sqrt _, xe) -> fpOp (WI.floatSqrt sym fpRM) xe+  (CE.Log _, xe) -> realOp (WI.realLog sym) xe+  (CE.Sin _, xe) -> realOp (WI.realSin sym) xe+  (CE.Cos _, xe) -> realOp (WI.realCos sym) xe+  (CE.Tan _, xe) -> realOp (WI.realTan sym) xe+  (CE.Asin _, xe) -> realOp (realRecip <=< WI.realSin sym) xe+  (CE.Acos _, xe) -> realOp (realRecip <=< WI.realCos sym) xe+  (CE.Atan _, xe) -> realOp (realRecip <=< WI.realTan sym) xe+  (CE.Sinh _, xe) -> realOp (WI.realSinh sym) xe+  (CE.Cosh _, xe) -> realOp (WI.realCosh sym) xe+  (CE.Tanh _, xe) -> realOp (WI.realTanh sym) xe+  (CE.Asinh _, xe) -> realOp (realRecip <=< WI.realSinh sym) xe+  (CE.Acosh _, xe) -> realOp (realRecip <=< WI.realCosh sym) xe+  (CE.Atanh _, xe) -> realOp (realRecip <=< WI.realTanh sym) xe+  (CE.BwNot _, xe) -> case xe of+    XBool e -> XBool <$> WI.notPred sym e+    _ -> bvOp (WI.bvNotBits sym) xe+  (CE.Cast _ tp, xe) -> case (xe, tp) of+    (XBool e, CT.Bool) -> return $ XBool e+    (XBool e, CT.Word8) -> XWord8 <$> WI.predToBV sym e knownNat+    (XBool e, CT.Word16) -> XWord16 <$> WI.predToBV sym e knownNat+    (XBool e, CT.Word32) -> XWord32 <$> WI.predToBV sym e knownNat+    (XBool e, CT.Word64) -> XWord64 <$> WI.predToBV sym e knownNat+    (XBool e, CT.Int8) -> XInt8 <$> WI.predToBV sym e knownNat+    (XBool e, CT.Int16) -> XInt16 <$> WI.predToBV sym e knownNat+    (XBool e, CT.Int32) -> XInt32 <$> WI.predToBV sym e knownNat+    (XBool e, CT.Int64) -> XInt64 <$> WI.predToBV sym e knownNat+    (XInt8 e, CT.Int8) -> return $ XInt8 e+    (XInt8 e, CT.Int16) -> XInt16 <$> WI.bvSext sym knownNat e+    (XInt8 e, CT.Int32) -> XInt32 <$> WI.bvSext sym knownNat e+    (XInt8 e, CT.Int64) -> XInt64 <$> WI.bvSext sym knownNat e+    (XInt16 e, CT.Int16) -> return $ XInt16 e+    (XInt16 e, CT.Int32) -> XInt32 <$> WI.bvSext sym knownNat e+    (XInt16 e, CT.Int64) -> XInt64 <$> WI.bvSext sym knownNat e+    (XInt32 e, CT.Int32) -> return $ XInt32 e+    (XInt32 e, CT.Int64) -> XInt64 <$> WI.bvSext sym knownNat e+    (XInt64 e, CT.Int64) -> return $ XInt64 e+    (XWord8 e, CT.Int16) -> XInt16 <$> WI.bvZext sym knownNat e+    (XWord8 e, CT.Int32) -> XInt32 <$> WI.bvZext sym knownNat e+    (XWord8 e, CT.Int64) -> XInt64 <$> WI.bvZext sym knownNat e+    (XWord8 e, CT.Word8) -> return $ XWord8 e+    (XWord8 e, CT.Word16) -> XWord16 <$> WI.bvZext sym knownNat e+    (XWord8 e, CT.Word32) -> XWord32 <$> WI.bvZext sym knownNat e+    (XWord8 e, CT.Word64) -> XWord64 <$> WI.bvZext sym knownNat e+    (XWord16 e, CT.Int32) -> XInt32 <$> WI.bvZext sym knownNat e+    (XWord16 e, CT.Int64) -> XInt64 <$> WI.bvZext sym knownNat e+    (XWord16 e, CT.Word16) -> return $ XWord16 e+    (XWord16 e, CT.Word32) -> XWord32 <$> WI.bvZext sym knownNat e+    (XWord16 e, CT.Word64) -> XWord64 <$> WI.bvZext sym knownNat e+    (XWord32 e, CT.Int64) -> XInt64 <$> WI.bvZext sym knownNat e+    (XWord32 e, CT.Word32) -> return $ XWord32 e+    (XWord32 e, CT.Word64) -> XWord64 <$> WI.bvZext sym knownNat e+    (XWord64 e, CT.Word64) -> return $ XWord64 e+    _ -> panic+  (CE.GetField (CT.Struct s) _ftp extractor, XStruct xes) -> do+    let fieldNameRepr = fieldName (extractor undefined)+    let structFieldNameReprs = valueName <$> CT.toValues s+    let mIx = elemIndex (Some fieldNameRepr) structFieldNameReprs+    case mIx of+      Just ix -> return $ xes !! ix+      Nothing -> panic+  _ -> panic+  where numOp :: (forall w . BVOp1 w t)+              -> (forall fpp . FPOp1 fpp t)+              -> XExpr t+              -> IO (XExpr t)+        numOp bvOp fpOp xe = case xe of+          XInt8 e -> XInt8 <$> bvOp e+          XInt16 e -> XInt16 <$> bvOp e+          XInt32 e -> XInt32 <$> bvOp e+          XInt64 e -> XInt64 <$> bvOp e+          XWord8 e -> XWord8 <$> bvOp e+          XWord16 e -> XWord16 <$> bvOp e+          XWord32 e -> XWord32 <$> bvOp e+          XWord64 e -> XWord64 <$> bvOp e+          XFloat e -> XFloat <$> fpOp e+          XDouble e -> XDouble <$> fpOp e+          _ -> panic++        bvOp :: (forall w . BVOp1 w t) -> XExpr t -> IO (XExpr t)+        bvOp f xe = case xe of+          XInt8 e -> XInt8 <$> f e+          XInt16 e -> XInt16 <$> f e+          XInt32 e -> XInt32 <$> f e+          XInt64 e -> XInt64 <$> f e+          XWord8 e -> XWord8 <$> f e+          XWord16 e -> XWord16 <$> f e+          XWord32 e -> XWord32 <$> f e+          XWord64 e -> XWord64 <$> f e+          _ -> panic++        fpOp :: (forall fpp . FPOp1 fpp t) -> XExpr t -> IO (XExpr t)+        fpOp g xe = case xe of+          XFloat e -> XFloat <$> g e+          XDouble e -> XDouble <$> g e+          _ -> panic++        realOp :: RealOp1 t -> XExpr t -> IO (XExpr t)+        realOp h xe = fpOp hf xe+          where hf :: (forall fpp . FPOp1 fpp t)+                hf e = do re <- WI.floatToReal sym e+                          hre <- h re+                          WI.realToFloat sym knownRepr fpRM hre++        realRecip :: RealOp1 t+        realRecip e = do one <- WI.realLit sym 1+                         WI.realDiv sym one e++type BVOp2 w t = (KnownNat w, 1 <= w) => WB.BVExpr t w -> WB.BVExpr t w -> IO (WB.BVExpr t w)++type FPOp2 fpp t = KnownRepr WT.FloatPrecisionRepr fpp => WB.Expr t (WT.BaseFloatType fpp) -> WB.Expr t (WT.BaseFloatType fpp) -> IO (WB.Expr t (WT.BaseFloatType fpp))++type RealOp2 t = WB.Expr t WT.BaseRealType -> WB.Expr t WT.BaseRealType -> IO (WB.Expr t WT.BaseRealType)++type BoolCmp2 t = WB.BoolExpr t -> WB.BoolExpr t -> IO (WB.BoolExpr t)++type BVCmp2 w t = (KnownNat w, 1 <= w) => WB.BVExpr t w -> WB.BVExpr t w -> IO (WB.BoolExpr t)++type FPCmp2 fpp t = KnownRepr WT.FloatPrecisionRepr fpp => WB.Expr t (WT.BaseFloatType fpp) -> WB.Expr t (WT.BaseFloatType fpp) -> IO (WB.BoolExpr t)++translateOp2 :: forall t st fs a b c .+                WB.ExprBuilder t st fs+             -> (WB.ExprSymFn t (WB.Expr t)+                 (EmptyCtx ::> WT.BaseRealType ::> WT.BaseRealType)+                 WT.BaseRealType)+             -- ^ Pow function+             -> (WB.ExprSymFn t (WB.Expr t)+                 (EmptyCtx ::> WT.BaseRealType ::> WT.BaseRealType)+                 WT.BaseRealType)+             -- ^ Logb function+             -> CE.Op2 a b c+             -> XExpr t+             -> XExpr t+             -> IO (XExpr t)+translateOp2 sym powFn logbFn op xe1 xe2 = case (op, xe1, xe2) of+  (CE.And, XBool e1, XBool e2) -> XBool <$> WI.andPred sym e1 e2+  (CE.Or, XBool e1, XBool e2) -> XBool <$> WI.orPred sym e1 e2+  (CE.Add _, xe1, xe2) -> numOp (WI.bvAdd sym) (WI.floatAdd sym fpRM) xe1 xe2+  (CE.Sub _, xe1, xe2) -> numOp (WI.bvSub sym) (WI.floatSub sym fpRM) xe1 xe2+  (CE.Mul _, xe1, xe2) -> numOp (WI.bvMul sym) (WI.floatMul sym fpRM) xe1 xe2+  (CE.Mod _, xe1, xe2) -> bvOp (WI.bvSrem sym) (WI.bvUrem sym) xe1 xe2+  (CE.Div _, xe1, xe2) -> bvOp (WI.bvSdiv sym) (WI.bvUdiv sym) xe1 xe2+  (CE.Fdiv _, xe1, xe2) -> fpOp (WI.floatDiv sym fpRM) xe1 xe2+  (CE.Pow _, xe1, xe2) -> fpOp powFn' xe1 xe2+    where powFn' :: FPOp2 fpp t+          powFn' e1 e2 = do re1 <- WI.floatToReal sym e1+                            re2 <- WI.floatToReal sym e2+                            let args = (Empty :> re1 :> re2)+                            rpow <- WI.applySymFn sym powFn args+                            WI.realToFloat sym knownRepr fpRM rpow+  (CE.Logb _, xe1, xe2) -> fpOp logbFn' xe1 xe2+    where logbFn' :: FPOp2 fpp t+          logbFn' e1 e2 = do re1 <- WI.floatToReal sym e1+                             re2 <- WI.floatToReal sym e2+                             let args = (Empty :> re1 :> re2)+                             rpow <- WI.applySymFn sym logbFn args+                             WI.realToFloat sym knownRepr fpRM rpow+  (CE.Eq _, xe1, xe2) -> cmp (WI.eqPred sym) (WI.bvEq sym) (WI.floatEq sym) xe1 xe2+  (CE.Ne _, xe1, xe2) -> cmp neqPred bvNeq fpNeq xe1 xe2+    where neqPred :: BoolCmp2 t+          neqPred e1 e2 = do e <- WI.eqPred sym e1 e2+                             WI.notPred sym e+          bvNeq :: forall w . BVCmp2 w t+          bvNeq e1 e2 = do e <- WI.bvEq sym e1 e2+                           WI.notPred sym e+          fpNeq :: forall fpp . FPCmp2 fpp t+          fpNeq e1 e2 = do e <- WI.floatEq sym e1 e2+                           WI.notPred sym e+  (CE.Le _, xe1, xe2) -> numCmp (WI.bvSle sym) (WI.bvUle sym) (WI.floatLe sym) xe1 xe2+  (CE.Ge _, xe1, xe2) -> numCmp (WI.bvSge sym) (WI.bvUge sym) (WI.floatGe sym) xe1 xe2+  (CE.Lt _, xe1, xe2) -> numCmp (WI.bvSlt sym) (WI.bvUlt sym) (WI.floatLt sym) xe1 xe2+  (CE.Gt _, xe1, xe2) -> numCmp (WI.bvSgt sym) (WI.bvUgt sym) (WI.floatGt sym) xe1 xe2+  (CE.BwAnd _, xe1, xe2) -> bvOp (WI.bvAndBits sym) (WI.bvAndBits sym) xe1 xe2+  (CE.BwOr _, xe1, xe2) -> bvOp (WI.bvOrBits sym) (WI.bvOrBits sym) xe1 xe2+  (CE.BwXor _, xe1, xe2) -> bvOp (WI.bvXorBits sym) (WI.bvXorBits sym) xe1 xe2+  -- Note: For both shift operators, we are interpreting the shifter as an+  -- unsigned bitvector regardless of whether it is a word or an int.+  (CE.BwShiftL _ _, xe1, xe2) -> do+    Just (SomeBVExpr e1 w1 _ ctor1) <- return $ asBVExpr xe1+    Just (SomeBVExpr e2 w2 _ _    ) <- return $ asBVExpr xe2+    e2' <- case testNatCases w1 w2 of+      NatCaseLT LeqProof -> WI.bvTrunc sym w1 e2+      NatCaseEQ -> return e2+      NatCaseGT LeqProof -> WI.bvZext sym w1 e2+    ctor1 <$> WI.bvShl sym e1 e2'+  (CE.BwShiftR _ _, xe1, xe2) -> do+    Just (SomeBVExpr e1 w1 sgn1 ctor1) <- return $ asBVExpr xe1+    Just (SomeBVExpr e2 w2 _    _    ) <- return $ asBVExpr xe2+    e2' <- case testNatCases w1 w2 of+      NatCaseLT LeqProof -> WI.bvTrunc sym w1 e2+      NatCaseEQ -> return e2+      NatCaseGT LeqProof -> WI.bvZext sym w1 e2+    ctor1 <$> case sgn1 of+      Signed -> WI.bvAshr sym e1 e2'+      Unsigned -> WI.bvLshr sym e1 e2'+  -- Note: Currently, copilot does not check if array indices are out of bounds,+  -- even for constant expressions. The method of translation we are using+  -- simply creates a nest of if-then-else expression to check the index+  -- expression against all possible indices. If the index expression is known+  -- by the solver to be out of bounds (for instance, if it is a constant 5 for+  -- an array of 5 elements), then the if-then-else will trivially resolve to+  -- true.+  (CE.Index _, xe1, xe2) -> do+    case (xe1, xe2) of+      (XArray xes, XWord32 ix) -> buildIndexExpr sym 0 ix xes+      _ -> panic+  _ -> panic+  where numOp :: (forall w . BVOp2 w t)+              -> (forall fpp . FPOp2 fpp t)+              -> XExpr t+              -> XExpr t+              -> IO (XExpr t)+        numOp bvOp fpOp xe1 xe2 = case (xe1, xe2) of+          (XInt8 e1, XInt8 e2) -> XInt8 <$> bvOp e1 e2+          (XInt16 e1, XInt16 e2) -> XInt16 <$> bvOp e1 e2+          (XInt32 e1, XInt32 e2)-> XInt32 <$> bvOp e1 e2+          (XInt64 e1, XInt64 e2)-> XInt64 <$> bvOp e1 e2+          (XWord8 e1, XWord8 e2)-> XWord8 <$> bvOp e1 e2+          (XWord16 e1, XWord16 e2)-> XWord16 <$> bvOp e1 e2+          (XWord32 e1, XWord32 e2)-> XWord32 <$> bvOp e1 e2+          (XWord64 e1, XWord64 e2)-> XWord64 <$> bvOp e1 e2+          (XFloat e1, XFloat e2)-> XFloat <$> fpOp e1 e2+          (XDouble e1, XDouble e2)-> XDouble <$> fpOp e1 e2+          _ -> panic++        bvOp :: (forall w . BVOp2 w t)+             -> (forall w . BVOp2 w t)+             -> XExpr t+             -> XExpr t+             -> IO (XExpr t)+        bvOp opS opU xe1 xe2 = case (xe1, xe2) of+          (XInt8 e1, XInt8 e2) -> XInt8 <$> opS e1 e2+          (XInt16 e1, XInt16 e2) -> XInt16 <$> opS e1 e2+          (XInt32 e1, XInt32 e2) -> XInt32 <$> opS e1 e2+          (XInt64 e1, XInt64 e2) -> XInt64 <$> opS e1 e2+          (XWord8 e1, XWord8 e2) -> XWord8 <$> opU e1 e2+          (XWord16 e1, XWord16 e2) -> XWord16 <$> opU e1 e2+          (XWord32 e1, XWord32 e2) -> XWord32 <$> opU e1 e2+          (XWord64 e1, XWord64 e2) -> XWord64 <$> opU e1 e2+          _ -> panic++        fpOp :: (forall fpp . FPOp2 fpp t)+             -> XExpr t+             -> XExpr t+             -> IO (XExpr t)+        fpOp op xe1 xe2 = case (xe1, xe2) of+          (XFloat e1, XFloat e2) -> XFloat <$> op e1 e2+          (XDouble e1, XDouble e2) -> XDouble <$> op e1 e2+          _ -> panic++        cmp :: BoolCmp2 t+            -> (forall w . BVCmp2 w t)+            -> (forall fpp . FPCmp2 fpp t)+            -> XExpr t+            -> XExpr t+            -> IO (XExpr t)+        cmp boolOp bvOp fpOp xe1 xe2 = case (xe1, xe2) of+          (XBool e1, XBool e2) -> XBool <$> boolOp e1 e2+          (XInt8 e1, XInt8 e2) -> XBool <$> bvOp e1 e2+          (XInt16 e1, XInt16 e2) -> XBool <$> bvOp e1 e2+          (XInt32 e1, XInt32 e2)-> XBool <$> bvOp e1 e2+          (XInt64 e1, XInt64 e2)-> XBool <$> bvOp e1 e2+          (XWord8 e1, XWord8 e2)-> XBool <$> bvOp e1 e2+          (XWord16 e1, XWord16 e2)-> XBool <$> bvOp e1 e2+          (XWord32 e1, XWord32 e2)-> XBool <$> bvOp e1 e2+          (XWord64 e1, XWord64 e2)-> XBool <$> bvOp e1 e2+          (XFloat e1, XFloat e2)-> XBool <$> fpOp e1 e2+          (XDouble e1, XDouble e2)-> XBool <$> fpOp e1 e2+          _ -> panic++        numCmp :: (forall w . BVCmp2 w t)+               -> (forall w . BVCmp2 w t)+               -> (forall fpp . FPCmp2 fpp t)+               -> XExpr t+               -> XExpr t+               -> IO (XExpr t)+        numCmp bvSOp bvUOp fpOp xe1 xe2 = case (xe1, xe2) of+          (XInt8 e1, XInt8 e2) -> XBool <$> bvSOp e1 e2+          (XInt16 e1, XInt16 e2) -> XBool <$> bvSOp e1 e2+          (XInt32 e1, XInt32 e2)-> XBool <$> bvSOp e1 e2+          (XInt64 e1, XInt64 e2)-> XBool <$> bvSOp e1 e2+          (XWord8 e1, XWord8 e2)-> XBool <$> bvUOp e1 e2+          (XWord16 e1, XWord16 e2)-> XBool <$> bvUOp e1 e2+          (XWord32 e1, XWord32 e2)-> XBool <$> bvUOp e1 e2+          (XWord64 e1, XWord64 e2)-> XBool <$> bvUOp e1 e2+          (XFloat e1, XFloat e2)-> XBool <$> fpOp e1 e2+          (XDouble e1, XDouble e2)-> XBool <$> fpOp e1 e2+          _ -> panic++        buildIndexExpr :: 1 <= n+                       => WB.ExprBuilder t st fs+                       -> Word32+                       -- ^ Index+                       -> WB.Expr t (WT.BaseBVType 32)+                       -- ^ Index+                       -> V.Vector n (XExpr t)+                       -- ^ Elements+                       -> IO (XExpr t)+        buildIndexExpr sym curIx ix xelts = case V.uncons xelts of+          (xe, Left Refl) -> return xe+          (xe, Right xelts') -> do+            LeqProof <- return $ V.nonEmpty xelts'+            rstExpr <- buildIndexExpr sym (curIx+1) ix xelts'+            curIxExpr <- WI.bvLit sym knownNat (BV.word32 curIx)+            ixEq <- WI.bvEq sym curIxExpr ix+            mkIte sym ixEq xe rstExpr++        mkIte :: WB.ExprBuilder t st fs+              -> WB.Expr t WT.BaseBoolType+              -> XExpr t+              -> XExpr t+              -> IO (XExpr t)+        mkIte sym pred xe1 xe2 = case (xe1, xe2) of+              (XBool e1, XBool e2) -> XBool <$> WI.itePred sym pred e1 e2+              (XInt8 e1, XInt8 e2) -> XInt8 <$> WI.bvIte sym pred e1 e2+              (XInt16 e1, XInt16 e2) -> XInt16 <$> WI.bvIte sym pred e1 e2+              (XInt32 e1, XInt32 e2) -> XInt32 <$> WI.bvIte sym pred e1 e2+              (XInt64 e1, XInt64 e2) -> XInt64 <$> WI.bvIte sym pred e1 e2+              (XWord8 e1, XWord8 e2) -> XWord8 <$> WI.bvIte sym pred e1 e2+              (XWord16 e1, XWord16 e2) -> XWord16 <$> WI.bvIte sym pred e1 e2+              (XWord32 e1, XWord32 e2) -> XWord32 <$> WI.bvIte sym pred e1 e2+              (XWord64 e1, XWord64 e2) -> XWord64 <$> WI.bvIte sym pred e1 e2+              (XFloat e1, XFloat e2) -> XFloat <$> WI.floatIte sym pred e1 e2+              (XDouble e1, XDouble e2) -> XDouble <$> WI.floatIte sym pred e1 e2+              (XStruct xes1, XStruct xes2) ->+                XStruct <$> zipWithM (mkIte sym pred) xes1 xes2+              (XArray xes1, XArray xes2) ->+                case V.length xes1 `testEquality` V.length xes2 of+                  Just Refl -> XArray <$> V.zipWithM (mkIte sym pred) xes1 xes2+                  Nothing -> panic+              _ -> panic+++translateOp3 :: forall t st fs a b c d .+                WB.ExprBuilder t st fs+             -> CE.Op3 a b c d+             -> XExpr t+             -> XExpr t+             -> XExpr t+             -> IO (XExpr t)+translateOp3 sym (CE.Mux _) (XBool te) xe1 xe2 = case (xe1, xe2) of+  (XBool e1, XBool e2) -> XBool <$> WI.itePred sym te e1 e2+  (XInt8 e1, XInt8 e2) -> XInt8 <$> WI.bvIte sym te e1 e2+  (XInt16 e1, XInt16 e2) -> XInt16 <$> WI.bvIte sym te e1 e2+  (XInt32 e1, XInt32 e2) -> XInt32 <$> WI.bvIte sym te e1 e2+  (XInt64 e1, XInt64 e2) -> XInt64 <$> WI.bvIte sym te e1 e2+  (XWord8 e1, XWord8 e2) -> XWord8 <$> WI.bvIte sym te e1 e2+  (XWord16 e1, XWord16 e2) -> XWord16 <$> WI.bvIte sym te e1 e2+  (XWord32 e1, XWord32 e2) -> XWord32 <$> WI.bvIte sym te e1 e2+  (XWord64 e1, XWord64 e2) -> XWord64 <$> WI.bvIte sym te e1 e2+  (XFloat e1, XFloat e2) -> XFloat <$> WI.floatIte sym te e1 e2+  (XDouble e1, XDouble e2) -> XDouble <$> WI.floatIte sym te e1 e2+  _ -> panic+translateOp3 _ _ _ _ _ = panic