packages feed

crucible 0.7 → 0.7.1

raw patch · 17 files changed

+1565/−269 lines, 17 filesdep +asyncdep ~basedep ~th-abstractiondep ~what4new-uploaderPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

Dependencies added: async

Dependency ranges changed: base, th-abstraction, what4

API changes (from Hackage documentation)

- Lang.Crucible.Backend: instance GHC.Base.Monoid (Lang.Crucible.Backend.CrucibleAssumptions e)
- Lang.Crucible.Backend: instance GHC.Base.Semigroup (Lang.Crucible.Backend.CrucibleAssumptions e)
- Lang.Crucible.Types: [DoubleDoubleFloatRepr] :: FloatInfoRepr 'DoubleDoubleFloat
- Lang.Crucible.Types: [DoubleFloatRepr] :: FloatInfoRepr 'DoubleFloat
- Lang.Crucible.Types: [HalfFloatRepr] :: FloatInfoRepr 'HalfFloat
- Lang.Crucible.Types: [QuadFloatRepr] :: FloatInfoRepr 'QuadFloat
- Lang.Crucible.Types: [SingleFloatRepr] :: FloatInfoRepr 'SingleFloat
- Lang.Crucible.Types: [X86_80FloatRepr] :: FloatInfoRepr 'X86_80Float
- Lang.Crucible.Types: data () => FloatInfo
- Lang.Crucible.Types: data () => FloatInfoRepr (fi :: FloatInfo)
- Lang.Crucible.Types: floatInfoToBVTypeRepr :: forall (fi :: FloatInfo). FloatInfoRepr fi -> BaseTypeRepr (BaseBVType (FloatInfoToBitWidth fi))
- Lang.Crucible.Types: type DoubleDoubleFloat = 'DoubleDoubleFloat
- Lang.Crucible.Types: type DoubleFloat = 'DoubleFloat
- Lang.Crucible.Types: type HalfFloat = 'HalfFloat
- Lang.Crucible.Types: type QuadFloat = 'QuadFloat
- Lang.Crucible.Types: type SingleFloat = 'SingleFloat
- Lang.Crucible.Types: type X86_80Float = 'X86_80Float
- Lang.Crucible.Types: type family FloatInfoToBitWidth (fi :: FloatInfo) :: Nat
+ Lang.Crucible.Analysis.Fixpoint.Components: parentWTOComponent :: Ord n => [WTOComponent n] -> Map n n
+ Lang.Crucible.Backend: convertProofObligationsAsImplications :: IsSymInterface sym => sym -> ProofObligations sym -> IO [Pred sym]
+ Lang.Crucible.Backend: pathConditionUninterpConstants :: IsSymBackend sym bak => bak -> IO (Set (Some (BoundVar sym)))
+ Lang.Crucible.Backend: proofObligationsAsImplications :: IsSymBackend sym bak => bak -> IO [Pred sym]
+ Lang.Crucible.Backend: proofObligationsUninterpConstants :: IsSymBackend sym bak => bak -> IO (Set (Some (BoundVar sym)))
+ Lang.Crucible.Backend: runCHC :: (IsSymBackend sym bak, sym ~ ExprBuilder t st fs, MonadIO m) => bak -> [SomeSymFn sym] -> m (MapF (SymFnWrapper sym) (SymFnWrapper sym))
+ Lang.Crucible.Backend.ProofGoals: instance GHC.Show.Show Lang.Crucible.Backend.ProofGoals.FrameIdentifier
+ Lang.Crucible.Backend.Prove: Combiner :: (m (SubgoalResult r) -> m (SubgoalResult r) -> m (SubgoalResult r)) -> Combiner m r
+ Lang.Crucible.Backend.Prove: Disproved :: GroundEvalFn t -> Maybe (ExprRangeBindings t) -> ProofResult sym t
+ Lang.Crucible.Backend.Prove: ProofConsumer :: (ProofObligation sym -> ProofResult sym t -> IO r) -> ProofConsumer sym t r
+ Lang.Crucible.Backend.Prove: ProofStrategy :: {-# UNPACK #-} !Prover sym m t r -> Combiner m r -> ProofStrategy sym m t r
+ Lang.Crucible.Backend.Prove: Proved :: ProofResult sym t
+ Lang.Crucible.Backend.Prove: Prover :: (Assumptions sym -> Assertion sym -> ProofConsumer sym t r -> m (SubgoalResult r)) -> (Assumptions sym -> m (SubgoalResult r) -> m (SubgoalResult r)) -> Prover sym m t r
+ Lang.Crucible.Backend.Prove: SubgoalResult :: !Bool -> !r -> SubgoalResult r
+ Lang.Crucible.Backend.Prove: Unknown :: ProofResult sym t
+ Lang.Crucible.Backend.Prove: [getCombiner] :: Combiner m r -> m (SubgoalResult r) -> m (SubgoalResult r) -> m (SubgoalResult r)
+ Lang.Crucible.Backend.Prove: [proverAssume] :: Prover sym m t r -> Assumptions sym -> m (SubgoalResult r) -> m (SubgoalResult r)
+ Lang.Crucible.Backend.Prove: [proverProve] :: Prover sym m t r -> Assumptions sym -> Assertion sym -> ProofConsumer sym t r -> m (SubgoalResult r)
+ Lang.Crucible.Backend.Prove: [stratCombine] :: ProofStrategy sym m t r -> Combiner m r
+ Lang.Crucible.Backend.Prove: [stratProver] :: ProofStrategy sym m t r -> {-# UNPACK #-} !Prover sym m t r
+ Lang.Crucible.Backend.Prove: [subgoalResult] :: SubgoalResult r -> !r
+ Lang.Crucible.Backend.Prove: [subgoalWasProved] :: SubgoalResult r -> !Bool
+ Lang.Crucible.Backend.Prove: data ProofResult sym t
+ Lang.Crucible.Backend.Prove: data ProofStrategy sym m t r
+ Lang.Crucible.Backend.Prove: data Prover sym m t r
+ Lang.Crucible.Backend.Prove: data SubgoalResult r
+ Lang.Crucible.Backend.Prove: failFast :: Monad m => Semigroup r => Combiner m r
+ Lang.Crucible.Backend.Prove: instance GHC.Base.Functor Lang.Crucible.Backend.Prove.SubgoalResult
+ Lang.Crucible.Backend.Prove: keepGoing :: Monad m => Semigroup r => Combiner m r
+ Lang.Crucible.Backend.Prove: newtype Combiner m r
+ Lang.Crucible.Backend.Prove: newtype ProofConsumer sym t r
+ Lang.Crucible.Backend.Prove: offlineProve :: MonadIO m => sym ~ ExprBuilder t st fs => IsSymExprBuilder sym => sym -> LogData -> SolverAdapter st -> Assumptions sym -> Assertion sym -> ProofConsumer sym t r -> m (SubgoalResult r)
+ Lang.Crucible.Backend.Prove: offlineProveWithTimeout :: MonadError TimedOut m => MonadIO m => sym ~ ExprBuilder t st fs => IsSymExprBuilder sym => Timeout -> sym -> LogData -> SolverAdapter st -> Assumptions sym -> Assertion sym -> ProofConsumer sym t r -> m (SubgoalResult r)
+ Lang.Crucible.Backend.Prove: offlineProver :: MonadError TimedOut m => MonadIO m => sym ~ ExprBuilder t st fs => Timeout -> IsSymExprBuilder sym => sym -> LogData -> SolverAdapter st -> Prover sym m t r
+ Lang.Crucible.Backend.Prove: onlineProve :: MonadIO m => SMTReadWriter solver => sym ~ ExprBuilder t st fs => IsSymExprBuilder sym => SolverProcess t solver -> Assumptions sym -> Assertion sym -> ProofConsumer sym t r -> m (SubgoalResult r)
+ Lang.Crucible.Backend.Prove: onlineProver :: MonadIO m => MonadMask m => SMTReadWriter solver => sym ~ ExprBuilder t st fs => IsSymExprBuilder sym => sym -> SolverProcess t solver -> Prover sym m t r
+ Lang.Crucible.Backend.Prove: proveCurrentObligations :: MonadIO m => Monoid r => sym ~ ExprBuilder t st fs => IsSymBackend sym bak => bak -> ProofStrategy sym m t r -> ProofConsumer sym t r -> m r
+ Lang.Crucible.Backend.Prove: proveGoals :: Functor m => ProofStrategy sym m t r -> Goals (Assumptions sym) (Assertion sym) -> ProofConsumer sym t r -> m r
+ Lang.Crucible.Backend.Prove: proveObligations :: Applicative m => Monoid r => sym ~ ExprBuilder t st fs => ProofStrategy sym m t r -> ProofObligations sym -> ProofConsumer sym t r -> m r
+ Lang.Crucible.Concretize: ConcAnyValue :: TypeRepr tp -> ConcRV' sym tp -> ConcAnyValue sym
+ Lang.Crucible.Concretize: ConcCtx :: GroundEvalFn t -> MapF SymbolRepr (IntrinsicConcFn t) -> ConcCtx sym t
+ Lang.Crucible.Concretize: ConcRV' :: ConcRegValue sym tp -> ConcRV' sym tp
+ Lang.Crucible.Concretize: IntrinsicConcFn :: (forall sym ctx. SymExpr sym ~ Expr t => IsExprBuilder sym => ConcCtx sym t -> Assignment TypeRepr ctx -> Intrinsic sym nm ctx -> IO (ConcRegValue sym (IntrinsicType nm ctx))) -> IntrinsicConcFn t nm
+ Lang.Crucible.Concretize: IntrinsicConcToSymFn :: (forall sym ctx. IsExprBuilder sym => sym -> Assignment TypeRepr ctx -> ConcIntrinsic nm ctx -> IO (RegValue sym (IntrinsicType nm ctx))) -> IntrinsicConcToSymFn nm
+ Lang.Crucible.Concretize: [intrinsicConcFuns] :: ConcCtx sym t -> MapF SymbolRepr (IntrinsicConcFn t)
+ Lang.Crucible.Concretize: [model] :: ConcCtx sym t -> GroundEvalFn t
+ Lang.Crucible.Concretize: [unConcRV'] :: ConcRV' sym tp -> ConcRegValue sym tp
+ Lang.Crucible.Concretize: concRegEntry :: SymExpr sym ~ Expr t => IsExprBuilder sym => ConcCtx sym t -> RegEntry sym tp -> IO (ConcRegValue sym tp)
+ Lang.Crucible.Concretize: concRegMap :: SymExpr sym ~ Expr t => IsExprBuilder sym => ConcCtx sym t -> RegMap sym tps -> IO (Assignment (ConcRV' sym) tps)
+ Lang.Crucible.Concretize: concRegValue :: SymExpr sym ~ Expr t => IsExprBuilder sym => ConcCtx sym t -> TypeRepr tp -> RegValue sym tp -> IO (ConcRegValue sym tp)
+ Lang.Crucible.Concretize: concToSym :: sym ~ ExprBuilder scope st (Flags fm) => sym -> MapF SymbolRepr IntrinsicConcToSymFn -> FloatModeRepr fm -> TypeRepr tp -> ConcRegValue sym tp -> IO (RegValue sym tp)
+ Lang.Crucible.Concretize: data ConcAnyValue sym
+ Lang.Crucible.Concretize: data ConcCtx sym t
+ Lang.Crucible.Concretize: newtype ConcRV' sym tp
+ Lang.Crucible.Concretize: newtype IntrinsicConcFn t nm
+ Lang.Crucible.Concretize: newtype IntrinsicConcToSymFn nm
+ Lang.Crucible.Concretize: type family ConcIntrinsic nm ctx
+ Lang.Crucible.FunctionHandle: updateHandleMap :: (f args ret -> f args ret) -> FnHandle args ret -> FnHandleMap f -> FnHandleMap f
+ Lang.Crucible.Simulator.SymSequence: concretizeSymSequence :: (Pred sym -> IO Bool) -> (a -> IO b) -> SymSequence sym a -> IO (Seq b)
+ Lang.Crucible.Simulator.SymSequence: fromListSymSequence :: sym -> [a] -> IO (SymSequence sym a)
+ Lang.Crucible.Utils.Seconds: data Seconds
+ Lang.Crucible.Utils.Seconds: instance GHC.Classes.Eq Lang.Crucible.Utils.Seconds.Seconds
+ Lang.Crucible.Utils.Seconds: instance GHC.Classes.Ord Lang.Crucible.Utils.Seconds.Seconds
+ Lang.Crucible.Utils.Seconds: instance GHC.Num.Num Lang.Crucible.Utils.Seconds.Seconds
+ Lang.Crucible.Utils.Seconds: instance GHC.Show.Show Lang.Crucible.Utils.Seconds.Seconds
+ Lang.Crucible.Utils.Seconds: secondsFromInt :: Int -> Seconds
+ Lang.Crucible.Utils.Seconds: secondsToInt :: Seconds -> Int
+ Lang.Crucible.Utils.Seconds: secondsToMicroseconds :: Seconds -> Int
+ Lang.Crucible.Utils.Timeout: TimedOut :: TimedOut
+ Lang.Crucible.Utils.Timeout: Timeout :: Seconds -> Timeout
+ Lang.Crucible.Utils.Timeout: [getTimeout] :: Timeout -> Seconds
+ Lang.Crucible.Utils.Timeout: data TimedOut
+ Lang.Crucible.Utils.Timeout: instance GHC.Classes.Eq Lang.Crucible.Utils.Timeout.Timeout
+ Lang.Crucible.Utils.Timeout: instance GHC.Classes.Ord Lang.Crucible.Utils.Timeout.Timeout
+ Lang.Crucible.Utils.Timeout: instance GHC.Show.Show Lang.Crucible.Utils.Timeout.TimedOut
+ Lang.Crucible.Utils.Timeout: instance GHC.Show.Show Lang.Crucible.Utils.Timeout.Timeout
+ Lang.Crucible.Utils.Timeout: newtype Timeout
+ Lang.Crucible.Utils.Timeout: withTimeout :: Timeout -> IO a -> IO (Either TimedOut a)

Files

CHANGELOG.md view
@@ -1,3 +1,9 @@+# 0.7.1 -- 2024-08-30++* Add support for GHC 9.8++* Deprecate `concreteizeSymSequence` in favor of `concretizeSymSequence`+ # 0.7 -- 2024-02-05  * Add `TypedOverride`, `SomeTypedOverride`, and `runTypedOverride` to
crucible.cabal view
@@ -1,6 +1,6 @@ Cabal-version: 2.2 Name:          crucible-Version:       0.7+Version:       0.7.1 Author:        Galois Inc. Maintainer:    rscott@galois.com, kquick@galois.com, langston@galois.com Copyright:     (c) Galois, Inc 2014-2022@@ -44,7 +44,8 @@ library   import: bldflags   build-depends:-    base >= 4.13 && < 4.19,+    async,+    base >= 4.13 && < 4.20,     bimap,     bv-sized >= 1.0.0 && < 1.1,     containers >= 0.5.9.0,@@ -60,7 +61,7 @@     template-haskell,     text,     time >= 1.8 && < 2.0,-    th-abstraction >=0.1 && <0.6,+    th-abstraction >=0.1 && <0.7,     transformers,     unordered-containers,     vector,@@ -72,6 +73,9 @@    hs-source-dirs: src +  other-modules:+    Lang.Crucible.Backend.Assumptions+   exposed-modules:     Lang.Crucible.Analysis.DFS     Lang.Crucible.Analysis.ForwardDataflow@@ -83,7 +87,9 @@     Lang.Crucible.Backend.AssumptionStack     Lang.Crucible.Backend.ProofGoals     Lang.Crucible.Backend.Online+    Lang.Crucible.Backend.Prove     Lang.Crucible.Backend.Simple+    Lang.Crucible.Concretize     Lang.Crucible.CFG.Common     Lang.Crucible.CFG.Core     Lang.Crucible.CFG.Expr@@ -124,6 +130,8 @@     Lang.Crucible.Utils.MuxTree     Lang.Crucible.Utils.PrettyPrint     Lang.Crucible.Utils.RegRewrite+    Lang.Crucible.Utils.Seconds+    Lang.Crucible.Utils.Timeout     Lang.Crucible.Utils.StateContT     Lang.Crucible.Utils.Structural 
src/Lang/Crucible/Analysis/Fixpoint/Components.hs view
@@ -21,6 +21,7 @@   weakTopologicalOrdering,   WTOComponent(..),   SCC(..),+  parentWTOComponent,   -- * Special cases   cfgWeakTopologicalOrdering,   cfgSuccessors,@@ -238,6 +239,23 @@  maxLabel :: Label maxLabel = Label maxBound++-- | Construct a map from each vertex to the head of its parent WTO component.+-- In particular, the head of a component is not in the map. The vertices that+-- are not in any component are not in the map.+parentWTOComponent :: (Ord n) => [WTOComponent n] -> M.Map n n+parentWTOComponent = F.foldMap' $ \case+  SCC scc' -> parentWTOComponent' scc'+  Vertex{} -> M.empty++parentWTOComponent' :: (Ord n) => SCC n -> M.Map n n+parentWTOComponent' scc =+  F.foldMap'+    (\case+      SCC scc' -> parentWTOComponent' scc'+      Vertex v -> M.singleton v $ wtoHead scc)+    (wtoComps scc)+  {- Note [Bourdoncle Components] 
src/Lang/Crucible/Backend.hs view
@@ -36,42 +36,22 @@ {-# LANGUAGE RankNTypes #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE TypeOperators #-} {-# LANGUAGE ViewPatterns #-} module Lang.Crucible.Backend   ( IsSymBackend(..)   , IsSymInterface   , HasSymInterface(..)-  , SomeBackend(..)--    -- * Assumption management-  , CrucibleAssumption(..)-  , CrucibleEvent(..)-  , CrucibleAssumptions(..)-  , Assumption   , Assertion-  , Assumptions--  , concretizeEvents-  , ppEvent-  , singleEvent-  , singleAssumption-  , trivialAssumption-  , impossibleAssumption-  , ppAssumption-  , assumptionLoc-  , eventLoc-  , mergeAssumptions-  , assumptionPred-  , forgetAssumption-  , assumptionsPred-  , flattenAssumptions-  , assumptionsTopLevelLocs+  , SomeBackend(..)   , ProofObligation   , ProofObligations   , AssumptionState   , assert+  , impossibleAssumption      -- ** Reexports+  , module Lang.Crucible.Backend.Assumptions   , LabeledPred(..)   , labeledPred   , labeledPredMsg@@ -95,222 +75,45 @@   , addFailedAssertion   , assertIsInteger   , readPartExpr+  , runCHC+  , proofObligationsAsImplications+  , convertProofObligationsAsImplications+  , proofObligationsUninterpConstants+  , pathConditionUninterpConstants   , ppProofObligation   , backendOptions   , assertThenAssumeConfigOption   ) where  import           Control.Exception(Exception(..), throwIO)-import           Control.Lens ((^.), Traversal, folded)+import           Control.Lens ((^.)) import           Control.Monad import           Control.Monad.IO.Class-import           Data.Kind (Type) import           Data.Foldable (toList)-import           Data.Functor.Identity-import           Data.Functor.Const-import qualified Data.Sequence as Seq-import           Data.Sequence (Seq)+import           Data.Set (Set) import qualified Prettyprinter as PP import           GHC.Stack +import           Data.Parameterized.Map (MapF)+ import           What4.Concrete import           What4.Config+import           What4.Expr.Builder import           What4.Interface import           What4.InterpretedFloatingPoint import           What4.LabeledPred import           What4.Partial import           What4.ProgramLoc-import           What4.Expr (GroundValue, GroundValueWrapper(..)) +import           What4.Solver+import qualified What4.Solver.Z3 as Z3++import           Lang.Crucible.Backend.Assumptions import qualified Lang.Crucible.Backend.AssumptionStack as AS import qualified Lang.Crucible.Backend.ProofGoals as PG import           Lang.Crucible.Simulator.SimError --- | This type describes assumptions made at some point during program execution.-data CrucibleAssumption (e :: BaseType -> Type)-  = GenericAssumption ProgramLoc String (e BaseBoolType)-    -- ^ An unstructured description of the source of an assumption.--  | BranchCondition ProgramLoc (Maybe ProgramLoc) (e BaseBoolType)-    -- ^ This arose because we want to explore a specific path.-    -- The first location is the location of the branch predicate.-    -- The second one is the location of the branch target.--  | AssumingNoError SimError (e BaseBoolType)-    -- ^ An assumption justified by a proof of the impossibility of-    -- a certain simulator error.---- | This type describes events we can track during program execution.-data CrucibleEvent (e :: BaseType -> Type) where-  -- | This event describes the creation of a symbolic variable.-  CreateVariableEvent ::-    ProgramLoc {- ^ location where the variable was created -} ->-    String {- ^ user-provided name for the variable -} ->-    BaseTypeRepr tp {- ^ type of the variable -} ->-    e tp {- ^ the variable expression -} ->-    CrucibleEvent e--  -- | This event describes reaching a particular program location.-  LocationReachedEvent ::-    ProgramLoc ->-    CrucibleEvent e---- | Pretty print an event-ppEvent :: IsExpr e => CrucibleEvent e -> PP.Doc ann-ppEvent (CreateVariableEvent loc nm _tpr v) =-  "create var" PP.<+> PP.pretty nm PP.<+> "=" PP.<+> printSymExpr v PP.<+> "at" PP.<+> PP.pretty (plSourceLoc loc)-ppEvent (LocationReachedEvent loc) =-  "reached" PP.<+> PP.pretty (plSourceLoc loc) PP.<+> "in" PP.<+> PP.pretty (plFunction loc)---- | Return the program location associated with an event-eventLoc :: CrucibleEvent e -> ProgramLoc-eventLoc (CreateVariableEvent loc _ _ _) = loc-eventLoc (LocationReachedEvent loc) = loc---- | Return the program location associated with an assumption-assumptionLoc :: CrucibleAssumption e -> ProgramLoc-assumptionLoc r =-  case r of-    GenericAssumption l _ _ -> l-    BranchCondition  l _ _   -> l-    AssumingNoError s _    -> simErrorLoc s---- | Get the predicate associated with this assumption-assumptionPred :: CrucibleAssumption e -> e BaseBoolType-assumptionPred (AssumingNoError _ p) = p-assumptionPred (BranchCondition _ _ p) = p-assumptionPred (GenericAssumption _ _ p) = p---- | If an assumption is clearly impossible, return an abort reason---   that can be used to unwind the execution of this branch.-impossibleAssumption :: IsExpr e => CrucibleAssumption e -> Maybe AbortExecReason-impossibleAssumption (AssumingNoError err p)-  | Just False <- asConstantPred p = Just (AssertionFailure err)-impossibleAssumption (BranchCondition loc _ p)-  | Just False <- asConstantPred p = Just (InfeasibleBranch loc)-impossibleAssumption (GenericAssumption loc _ p)-  | Just False <- asConstantPred p = Just (InfeasibleBranch loc)-impossibleAssumption _ = Nothing--forgetAssumption :: CrucibleAssumption e -> CrucibleAssumption (Const ())-forgetAssumption = runIdentity . traverseAssumption (\_ -> Identity (Const ()))--traverseAssumption :: Traversal (CrucibleAssumption e) (CrucibleAssumption e') (e BaseBoolType) (e' BaseBoolType)-traverseAssumption f = \case-  GenericAssumption loc msg p -> GenericAssumption loc msg <$> f p-  BranchCondition l t p -> BranchCondition l t <$> f p-  AssumingNoError err p -> AssumingNoError err <$> f p---- | This type tracks both logical assumptions and program events---   that are relevant when evaluating proof obligations arising---   from simulation.-data CrucibleAssumptions (e :: BaseType -> Type) where-  SingleAssumption :: CrucibleAssumption e -> CrucibleAssumptions e-  SingleEvent      :: CrucibleEvent e -> CrucibleAssumptions e-  ManyAssumptions  :: Seq (CrucibleAssumptions e) -> CrucibleAssumptions e-  MergeAssumptions ::-    e BaseBoolType {- ^ branch condition -} ->-    CrucibleAssumptions e {- ^ "then" assumptions -} ->-    CrucibleAssumptions e {- ^ "else" assumptions -} ->-    CrucibleAssumptions e--instance Semigroup (CrucibleAssumptions e) where-  ManyAssumptions xs <> ManyAssumptions ys = ManyAssumptions (xs <> ys)-  ManyAssumptions xs <> y = ManyAssumptions (xs Seq.|> y)-  x <> ManyAssumptions ys = ManyAssumptions (x Seq.<| ys)-  x <> y = ManyAssumptions (Seq.fromList [x,y])--instance Monoid (CrucibleAssumptions e) where-  mempty = ManyAssumptions mempty--singleAssumption :: CrucibleAssumption e -> CrucibleAssumptions e-singleAssumption x = SingleAssumption x--singleEvent :: CrucibleEvent e -> CrucibleAssumptions e-singleEvent x = SingleEvent x---- | Collect the program locations of all assumptions and---   events that did not occur in the context of a symbolic branch.---   These are locations that every program path represented by---   this @CrucibleAssumptions@ structure must have passed through.-assumptionsTopLevelLocs :: CrucibleAssumptions e -> [ProgramLoc]-assumptionsTopLevelLocs (SingleEvent e)      = [eventLoc e]-assumptionsTopLevelLocs (SingleAssumption a) = [assumptionLoc a]-assumptionsTopLevelLocs (ManyAssumptions as) = concatMap assumptionsTopLevelLocs as-assumptionsTopLevelLocs MergeAssumptions{}   = []---- | Compute the logical predicate corresponding to this collection of assumptions.-assumptionsPred :: IsExprBuilder sym => sym -> Assumptions sym -> IO (Pred sym)-assumptionsPred sym (SingleEvent _) =-  return (truePred sym)-assumptionsPred _sym (SingleAssumption a) =-  return (assumptionPred a)-assumptionsPred sym (ManyAssumptions xs) =-  andAllOf sym folded =<< traverse (assumptionsPred sym) xs-assumptionsPred sym (MergeAssumptions c xs ys) =-  do xs' <- assumptionsPred sym xs-     ys' <- assumptionsPred sym ys-     itePred sym c xs' ys'--traverseEvent :: Applicative m =>-  (forall tp. e tp -> m (e' tp)) ->-  CrucibleEvent e -> m (CrucibleEvent e')-traverseEvent f (CreateVariableEvent loc nm tpr v) = CreateVariableEvent loc nm tpr <$> f v-traverseEvent _ (LocationReachedEvent loc) = pure (LocationReachedEvent loc)---- | Given a ground evaluation function, compute a linear, ground-valued---   sequence of events corresponding to this program run.-concretizeEvents ::-  IsExpr e =>-  (forall tp. e tp -> IO (GroundValue tp)) ->-  CrucibleAssumptions e ->-  IO [CrucibleEvent GroundValueWrapper]-concretizeEvents f = loop-  where-    loop (SingleEvent e) =-      do e' <- traverseEvent (\v -> GVW <$> f v) e-         return [e']-    loop (SingleAssumption _) = return []-    loop (ManyAssumptions as) = concat <$> traverse loop as-    loop (MergeAssumptions p xs ys) =-      do b <- f p-         if b then loop xs else loop ys---- | Given a @CrucibleAssumptions@ structure, flatten all the muxed assumptions into---   a flat sequence of assumptions that have been appropriately weakened.---   Note, once these assumptions have been flattened, their order might no longer---   strictly correspond to any concrete program run.-flattenAssumptions :: IsExprBuilder sym => sym -> Assumptions sym -> IO [Assumption sym]-flattenAssumptions sym = loop Nothing-  where-    loop _mz (SingleEvent _) = return []-    loop mz (SingleAssumption a) =-      do a' <- maybe (pure a) (\z -> traverseAssumption (impliesPred sym z) a) mz-         if trivialAssumption a' then return [] else return [a']-    loop mz (ManyAssumptions as) =-      concat <$> traverse (loop mz) as-    loop mz (MergeAssumptions p xs ys) =-      do pnot <- notPred sym p-         px <- maybe (pure p) (andPred sym p) mz-         py <- maybe (pure pnot) (andPred sym pnot) mz-         xs' <- loop (Just px) xs-         ys' <- loop (Just py) ys-         return (xs' <> ys')---- | Merge the assumptions collected from the branches of a conditional.-mergeAssumptions ::-  IsExprBuilder sym =>-  sym ->-  Pred sym ->-  Assumptions sym ->-  Assumptions sym ->-  IO (Assumptions sym)-mergeAssumptions _sym p thens elses =-  return (MergeAssumptions p thens elses)--type Assertion sym  = LabeledPred (Pred sym) SimError-type Assumption sym = CrucibleAssumption (SymExpr sym)-type Assumptions sym = CrucibleAssumptions (SymExpr sym)+type Assertion sym = LabeledPred (Pred sym) SimError type ProofObligation sym = AS.ProofGoal (Assumptions sym) (Assertion sym) type ProofObligations sym = Maybe (AS.Goals (Assumptions sym) (Assertion sym)) type AssumptionState sym = PG.GoalCollector (Assumptions sym) (Assertion sym)@@ -338,6 +141,17 @@ instance Exception AbortExecReason  +-- | If an assumption is clearly impossible, return an abort reason+--   that can be used to unwind the execution of this branch.+impossibleAssumption :: IsExpr e => CrucibleAssumption e -> Maybe AbortExecReason+impossibleAssumption (AssumingNoError err p)+  | Just False <- asConstantPred p = Just (AssertionFailure err)+impossibleAssumption (BranchCondition loc _ p)+  | Just False <- asConstantPred p = Just (InfeasibleBranch loc)+impossibleAssumption (GenericAssumption loc _ p)+  | Just False <- asConstantPred p = Just (InfeasibleBranch loc)+impossibleAssumption _ = Nothing+ ppAbortExecReason :: AbortExecReason -> PP.Doc ann ppAbortExecReason e =   case e of@@ -349,47 +163,21 @@       ]     VariantOptionsExhausted l -> ppLocated l "Variant options exhausted."     EarlyExit l -> ppLocated l "Program exited early."+  where+    ppLocated :: ProgramLoc -> PP.Doc ann -> PP.Doc ann+    ppLocated l x = "in" PP.<+> ppFn l PP.<+> ppLoc l PP.<> ":" PP.<+> x -ppAssumption :: (forall tp. e tp -> PP.Doc ann) -> CrucibleAssumption e -> PP.Doc ann-ppAssumption ppDoc e =-  case e of-    GenericAssumption l msg p ->-      PP.vsep [ ppLocated l (PP.pretty msg)-              , ppDoc p-              ]-    BranchCondition l Nothing p ->-      PP.vsep [ "The branch in" PP.<+> ppFn l PP.<+> "at" PP.<+> ppLoc l-              , ppDoc p-              ]-    BranchCondition l (Just t) p ->-      PP.vsep [ "The branch in" PP.<+> ppFn l PP.<+> "from" PP.<+> ppLoc l PP.<+> "to" PP.<+> ppLoc t-              , ppDoc p-              ]-    AssumingNoError simErr p ->-      PP.vsep [ "Assuming the following error does not occur:"-              , PP.indent 2 (ppSimError simErr)-              , ppDoc p-              ]+    ppFn :: ProgramLoc -> PP.Doc ann+    ppFn l = PP.pretty (plFunction l) +    ppLoc :: ProgramLoc -> PP.Doc ann+    ppLoc l = PP.pretty (plSourceLoc l)+ throwUnsupported :: (IsExprBuilder sym, MonadIO m, HasCallStack) => sym -> String -> m a throwUnsupported sym msg = liftIO $   do loc <- getCurrentProgramLoc sym      throwIO $ SimError loc $ Unsupported callStack msg ---- | Check if an assumption is trivial (always true)-trivialAssumption :: IsExpr e => CrucibleAssumption e -> Bool-trivialAssumption a = asConstantPred (assumptionPred a) == Just True--ppLocated :: ProgramLoc -> PP.Doc ann -> PP.Doc ann-ppLocated l x = "in" PP.<+> ppFn l PP.<+> ppLoc l PP.<> ":" PP.<+> x--ppFn :: ProgramLoc -> PP.Doc ann-ppFn l = PP.pretty (plFunction l)--ppLoc :: ProgramLoc -> PP.Doc ann-ppLoc l = PP.pretty (plSourceLoc l)- type IsSymInterface sym =   ( IsSymExprBuilder sym   , IsInterpretedFloatSymExprBuilder sym@@ -612,6 +400,59 @@   loc <- getCurrentProgramLoc sym   addAssertion bak (LabeledPred p (SimError loc msg))   return v+++-- | Run the CHC solver on the current proof obligations, and return the+-- solution as a substitution from the uninterpreted functions to their+-- definitions.+runCHC ::+  (IsSymBackend sym bak, sym ~ ExprBuilder t st fs, MonadIO m) =>+  bak ->+  [SomeSymFn sym] ->+  m (MapF (SymFnWrapper sym) (SymFnWrapper sym))+runCHC bak uninterp_inv_fns  = liftIO $ do+  let sym = backendGetSym bak++  implications <- proofObligationsAsImplications bak+  clearProofObligations bak++  -- log to stdout+  let logData = defaultLogData+        { logCallbackVerbose = \_ -> putStrLn+        , logReason = "Crucible inv"+        }+  Z3.runZ3Horn sym True logData uninterp_inv_fns implications >>= \case+    Sat sub -> return sub+    Unsat{} -> fail "Prover returned Unsat"+    Unknown -> fail "Prover returned Unknown"+++-- | Get proof obligations as What4 implications.+proofObligationsAsImplications :: IsSymBackend sym bak => bak -> IO [Pred sym]+proofObligationsAsImplications bak = do+  let sym = backendGetSym bak+  convertProofObligationsAsImplications sym =<< getProofObligations bak++-- | Convert proof obligations to What4 implications.+convertProofObligationsAsImplications :: IsSymInterface sym => sym -> ProofObligations sym -> IO [Pred sym]+convertProofObligationsAsImplications sym goals = do+  let obligations = maybe [] PG.goalsToList goals+  forM obligations $ \(AS.ProofGoal hyps (LabeledPred concl _err)) -> do+    hyp <- assumptionsPred sym hyps+    impliesPred sym hyp concl++-- | Get the set of uninterpreted constants that appear in the path condition.+pathConditionUninterpConstants :: IsSymBackend sym bak => bak -> IO (Set (Some (BoundVar sym)))+pathConditionUninterpConstants bak = do+  let sym = backendGetSym bak+  exprUninterpConstants sym <$> getPathCondition bak++-- | Get the set of uninterpreted constants that appear in the proof obligations.+proofObligationsUninterpConstants :: IsSymBackend sym bak => bak -> IO (Set (Some (BoundVar sym)))+proofObligationsUninterpConstants bak = do+  let sym = backendGetSym bak+  foldMap (exprUninterpConstants sym) <$> proofObligationsAsImplications bak+  ppProofObligation :: IsExprBuilder sym => sym -> ProofObligation sym -> IO (PP.Doc ann) ppProofObligation sym (AS.ProofGoal asmps gl) =
+ src/Lang/Crucible/Backend/Assumptions.hs view
@@ -0,0 +1,268 @@+{-|+Module      : Lang.Crucible.Backend.Assumptions+Copyright   : (c) Galois, Inc 2014-2024+License     : BSD3+Maintainer  : Langston Barrett <langston@galois.com>+-}++{-# LANGUAGE ConstraintKinds #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE DeriveFunctor #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FunctionalDependencies #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE PolyKinds #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE ViewPatterns #-}++module Lang.Crucible.Backend.Assumptions+  ( CrucibleAssumption(..)+  , CrucibleEvent(..)+  , CrucibleAssumptions(..)+  , Assumption+  , Assumptions++  , concretizeEvents+  , ppEvent+  , singleEvent+  , singleAssumption+  , trivialAssumption+  , ppAssumption+  , assumptionLoc+  , eventLoc+  , mergeAssumptions+  , assumptionPred+  , forgetAssumption+  , assumptionsPred+  , flattenAssumptions+  , assumptionsTopLevelLocs+  ) where+++import           Control.Lens (Traversal, folded)+import           Data.Kind (Type)+import           Data.Functor.Identity+import           Data.Functor.Const+import qualified Data.Sequence as Seq+import           Data.Sequence (Seq)+import qualified Prettyprinter as PP++import           What4.Expr.Builder+import           What4.Interface+import           What4.ProgramLoc+import           What4.Expr (GroundValue, GroundValueWrapper(..))++import           Lang.Crucible.Simulator.SimError++type Assumption sym = CrucibleAssumption (SymExpr sym)+type Assumptions sym = CrucibleAssumptions (SymExpr sym)++-- | This type describes assumptions made at some point during program execution.+data CrucibleAssumption (e :: BaseType -> Type)+  = GenericAssumption ProgramLoc String (e BaseBoolType)+    -- ^ An unstructured description of the source of an assumption.++  | BranchCondition ProgramLoc (Maybe ProgramLoc) (e BaseBoolType)+    -- ^ This arose because we want to explore a specific path.+    -- The first location is the location of the branch predicate.+    -- The second one is the location of the branch target.++  | AssumingNoError SimError (e BaseBoolType)+    -- ^ An assumption justified by a proof of the impossibility of+    -- a certain simulator error.++-- | This type describes events we can track during program execution.+data CrucibleEvent (e :: BaseType -> Type) where+  -- | This event describes the creation of a symbolic variable.+  CreateVariableEvent ::+    ProgramLoc {- ^ location where the variable was created -} ->+    String {- ^ user-provided name for the variable -} ->+    BaseTypeRepr tp {- ^ type of the variable -} ->+    e tp {- ^ the variable expression -} ->+    CrucibleEvent e++  -- | This event describes reaching a particular program location.+  LocationReachedEvent ::+    ProgramLoc ->+    CrucibleEvent e++-- | Pretty print an event+ppEvent :: IsExpr e => CrucibleEvent e -> PP.Doc ann+ppEvent (CreateVariableEvent loc nm _tpr v) =+  "create var" PP.<+> PP.pretty nm PP.<+> "=" PP.<+> printSymExpr v PP.<+> "at" PP.<+> PP.pretty (plSourceLoc loc)+ppEvent (LocationReachedEvent loc) =+  "reached" PP.<+> PP.pretty (plSourceLoc loc) PP.<+> "in" PP.<+> PP.pretty (plFunction loc)++-- | Return the program location associated with an event+eventLoc :: CrucibleEvent e -> ProgramLoc+eventLoc (CreateVariableEvent loc _ _ _) = loc+eventLoc (LocationReachedEvent loc) = loc++-- | Return the program location associated with an assumption+assumptionLoc :: CrucibleAssumption e -> ProgramLoc+assumptionLoc r =+  case r of+    GenericAssumption l _ _ -> l+    BranchCondition  l _ _   -> l+    AssumingNoError s _    -> simErrorLoc s++-- | Get the predicate associated with this assumption+assumptionPred :: CrucibleAssumption e -> e BaseBoolType+assumptionPred (AssumingNoError _ p) = p+assumptionPred (BranchCondition _ _ p) = p+assumptionPred (GenericAssumption _ _ p) = p++forgetAssumption :: CrucibleAssumption e -> CrucibleAssumption (Const ())+forgetAssumption = runIdentity . traverseAssumption (\_ -> Identity (Const ()))++-- | Check if an assumption is trivial (always true)+trivialAssumption :: IsExpr e => CrucibleAssumption e -> Bool+trivialAssumption a = asConstantPred (assumptionPred a) == Just True++traverseAssumption :: Traversal (CrucibleAssumption e) (CrucibleAssumption e') (e BaseBoolType) (e' BaseBoolType)+traverseAssumption f = \case+  GenericAssumption loc msg p -> GenericAssumption loc msg <$> f p+  BranchCondition l t p -> BranchCondition l t <$> f p+  AssumingNoError err p -> AssumingNoError err <$> f p++-- | This type tracks both logical assumptions and program events+--   that are relevant when evaluating proof obligations arising+--   from simulation.+data CrucibleAssumptions (e :: BaseType -> Type) where+  SingleAssumption :: CrucibleAssumption e -> CrucibleAssumptions e+  SingleEvent      :: CrucibleEvent e -> CrucibleAssumptions e+  ManyAssumptions  :: Seq (CrucibleAssumptions e) -> CrucibleAssumptions e+  MergeAssumptions ::+    e BaseBoolType {- ^ branch condition -} ->+    CrucibleAssumptions e {- ^ "then" assumptions -} ->+    CrucibleAssumptions e {- ^ "else" assumptions -} ->+    CrucibleAssumptions e++instance Semigroup (CrucibleAssumptions e) where+  ManyAssumptions xs <> ManyAssumptions ys = ManyAssumptions (xs <> ys)+  ManyAssumptions xs <> y = ManyAssumptions (xs Seq.|> y)+  x <> ManyAssumptions ys = ManyAssumptions (x Seq.<| ys)+  x <> y = ManyAssumptions (Seq.fromList [x,y])++instance Monoid (CrucibleAssumptions e) where+  mempty = ManyAssumptions mempty++singleAssumption :: CrucibleAssumption e -> CrucibleAssumptions e+singleAssumption x = SingleAssumption x++singleEvent :: CrucibleEvent e -> CrucibleAssumptions e+singleEvent x = SingleEvent x++-- | Collect the program locations of all assumptions and+--   events that did not occur in the context of a symbolic branch.+--   These are locations that every program path represented by+--   this @CrucibleAssumptions@ structure must have passed through.+assumptionsTopLevelLocs :: CrucibleAssumptions e -> [ProgramLoc]+assumptionsTopLevelLocs (SingleEvent e)      = [eventLoc e]+assumptionsTopLevelLocs (SingleAssumption a) = [assumptionLoc a]+assumptionsTopLevelLocs (ManyAssumptions as) = concatMap assumptionsTopLevelLocs as+assumptionsTopLevelLocs MergeAssumptions{}   = []++-- | Compute the logical predicate corresponding to this collection of assumptions.+assumptionsPred :: IsExprBuilder sym => sym -> Assumptions sym -> IO (Pred sym)+assumptionsPred sym (SingleEvent _) =+  return (truePred sym)+assumptionsPred _sym (SingleAssumption a) =+  return (assumptionPred a)+assumptionsPred sym (ManyAssumptions xs) =+  andAllOf sym folded =<< traverse (assumptionsPred sym) xs+assumptionsPred sym (MergeAssumptions c xs ys) =+  do xs' <- assumptionsPred sym xs+     ys' <- assumptionsPred sym ys+     itePred sym c xs' ys'++traverseEvent :: Applicative m =>+  (forall tp. e tp -> m (e' tp)) ->+  CrucibleEvent e -> m (CrucibleEvent e')+traverseEvent f (CreateVariableEvent loc nm tpr v) = CreateVariableEvent loc nm tpr <$> f v+traverseEvent _ (LocationReachedEvent loc) = pure (LocationReachedEvent loc)++-- | Given a ground evaluation function, compute a linear, ground-valued+--   sequence of events corresponding to this program run.+concretizeEvents ::+  IsExpr e =>+  (forall tp. e tp -> IO (GroundValue tp)) ->+  CrucibleAssumptions e ->+  IO [CrucibleEvent GroundValueWrapper]+concretizeEvents f = loop+  where+    loop (SingleEvent e) =+      do e' <- traverseEvent (\v -> GVW <$> f v) e+         return [e']+    loop (SingleAssumption _) = return []+    loop (ManyAssumptions as) = concat <$> traverse loop as+    loop (MergeAssumptions p xs ys) =+      do b <- f p+         if b then loop xs else loop ys++-- | Given a @CrucibleAssumptions@ structure, flatten all the muxed assumptions into+--   a flat sequence of assumptions that have been appropriately weakened.+--   Note, once these assumptions have been flattened, their order might no longer+--   strictly correspond to any concrete program run.+flattenAssumptions :: IsExprBuilder sym => sym -> Assumptions sym -> IO [Assumption sym]+flattenAssumptions sym = loop Nothing+  where+    loop _mz (SingleEvent _) = return []+    loop mz (SingleAssumption a) =+      do a' <- maybe (pure a) (\z -> traverseAssumption (impliesPred sym z) a) mz+         if trivialAssumption a' then return [] else return [a']+    loop mz (ManyAssumptions as) =+      concat <$> traverse (loop mz) as+    loop mz (MergeAssumptions p xs ys) =+      do pnot <- notPred sym p+         px <- maybe (pure p) (andPred sym p) mz+         py <- maybe (pure pnot) (andPred sym pnot) mz+         xs' <- loop (Just px) xs+         ys' <- loop (Just py) ys+         return (xs' <> ys')++-- | Merge the assumptions collected from the branches of a conditional.+mergeAssumptions ::+  IsExprBuilder sym =>+  sym ->+  Pred sym ->+  Assumptions sym ->+  Assumptions sym ->+  IO (Assumptions sym)+mergeAssumptions _sym p thens elses =+  return (MergeAssumptions p thens elses)++ppAssumption :: (forall tp. e tp -> PP.Doc ann) -> CrucibleAssumption e -> PP.Doc ann+ppAssumption ppDoc e =+  case e of+    GenericAssumption l msg p ->+      PP.vsep [ ppLocated l (PP.pretty msg)+              , ppDoc p+              ]+    BranchCondition l Nothing p ->+      PP.vsep [ "The branch in" PP.<+> ppFn l PP.<+> "at" PP.<+> ppLoc l+              , ppDoc p+              ]+    BranchCondition l (Just t) p ->+      PP.vsep [ "The branch in" PP.<+> ppFn l PP.<+> "from" PP.<+> ppLoc l PP.<+> "to" PP.<+> ppLoc t+              , ppDoc p+              ]+    AssumingNoError simErr p ->+      PP.vsep [ "Assuming the following error does not occur:"+              , PP.indent 2 (ppSimError simErr)+              , ppDoc p+              ]+  where+    ppLocated :: ProgramLoc -> PP.Doc ann -> PP.Doc ann+    ppLocated l x = "in" PP.<+> ppFn l PP.<+> ppLoc l PP.<> ":" PP.<+> x++    ppFn :: ProgramLoc -> PP.Doc ann+    ppFn l = PP.pretty (plFunction l)++    ppLoc :: ProgramLoc -> PP.Doc ann+    ppLoc l = PP.pretty (plSourceLoc l)
src/Lang/Crucible/Backend/ProofGoals.hs view
@@ -166,7 +166,7 @@ --   primarily a debugging aid, to ensure that stack management --   remains well-bracketed. newtype FrameIdentifier = FrameIdentifier Word64- deriving(Eq,Ord)+ deriving(Eq,Ord,Show)   -- | A data-strucutre that can incrementally collect goals in context.
+ src/Lang/Crucible/Backend/Prove.hs view
@@ -0,0 +1,455 @@+{-|+Module      : Lang.Crucible.Backend.Prove+Description : Proving goals under assumptions+Copyright   : (c) Galois, Inc 2024+License     : BSD3++This module contains helpers to dispatch the proof obligations arising from+symbolic execution using SMT solvers. There are several dimensions of+configurability, encapsulated in a 'ProofStrategy':++* Offline vs. online: Offline solvers ('offlineProver') are simpler to manage+  and more easily parallelized, but starting processes adds overhead, and online+  solvers ('onlineProver') can share state as assumptions are added. See the+  top-level README for What4 for further discussion of this choice.+* Failing fast ('failFast') vs. keeping going ('keepGoing')+* Timeouts: Proving with timeouts ('offlineProveWithTimeout') vs. without+  ('offlineProve')+* Parallelism: Not yet available via helpers in this module, but may be added to+  a 'ProofStrategy' by clients.++Once an appropriate strategy has been selected, it can be passed to entrypoints+such as 'proveObligations' to dispatch proof obligations.++When proving a single goal, the overall approach is:++* Gather all of the assumptions ('Assumptions') currently in scope (e.g.,+  from branch conditions).+* Negate the goal ('CB.Assertion') that we are trying to prove.+* Attempt to prove the conjunction of the assumptions and the negated goal.++If this goal is satisfiable ('W4R.Sat'), then there exists a counterexample+that makes the original goal false, so we have disproven the goal. If the+negated goal is unsatisfiable ('W4R.Unsat'), on the other hand, then the+original goal is proven.++Another way to think of this is as the negated material conditional+(implication) @not (assumptions -> assertion)@. This formula is equivalent+to @not ((not assumptions) and assertion)@, i.e., @assumptions and (not+assertion)@.+-}++{-# LANGUAGE DeriveFunctor #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE TupleSections #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE ScopedTypeVariables #-}++module Lang.Crucible.Backend.Prove+  ( -- * Strategy+    ProofResult(..)+  , ProofConsumer(..)+  , ProofStrategy(..)+    -- ** Combiner+  , SubgoalResult(..)+  , Combiner(..)+  , keepGoing+  , failFast+    -- ** Prover+  , Prover(..)+    -- *** Offline+  , offlineProve+  , offlineProveWithTimeout+  , offlineProver+    -- *** Online+  , onlineProve+  , onlineProver+    -- * Proving goals+  , proveGoals+  , proveObligations+  , proveCurrentObligations+  ) where++import           Control.Lens ((^.))+import           Control.Monad.Catch (MonadMask)+import           Control.Monad.Error.Class (MonadError, liftEither)+import           Control.Monad.IO.Class (MonadIO(liftIO))+import qualified Control.Monad.Reader as Reader++import qualified What4.Interface as W4+import qualified What4.Expr as WE+import qualified What4.Protocol.Online as WPO+import qualified What4.Protocol.SMTWriter as W4SMT+import qualified What4.SatResult as W4R+import qualified What4.Solver.Adapter as WSA++import qualified Lang.Crucible.Backend as CB+import           Lang.Crucible.Backend.Assumptions (Assumptions)+import           Lang.Crucible.Utils.Timeout (Timeout, TimedOut)+import qualified Lang.Crucible.Utils.Timeout as CTO++-- | Local helper+consumeGoals ::+  -- | Consume an 'Assuming'+  (asmp -> a -> a) ->+  -- | Consume a 'Prove'+  (goal -> a) ->+  -- | Consume a 'ProveConj'+  (a -> a -> a) ->+  CB.Goals asmp goal ->+  a+consumeGoals onAssumption onGoal onConj = go+  where+  go (CB.Prove gl) = onGoal gl+  go (CB.Assuming as gl) = onAssumption as (go gl)+  go (CB.ProveConj g1 g2) = onConj (go g1) (go g2)++-- | Local helper+consumeGoalsWithAssumptions ::+  forall asmp goal a.+  Monoid asmp =>+  -- | Consume a 'Prove'+  (asmp -> goal -> a) ->+  -- | Consume a 'ProveConj'+  (a -> a -> a) ->+  CB.Goals asmp goal ->+  a+consumeGoalsWithAssumptions onGoal onConj goals =+  Reader.runReader (go goals) mempty+  where+  go :: CB.Goals asmp goal -> Reader.Reader asmp a+  go =+    consumeGoals+      (\asmp gl -> Reader.local (<> asmp) gl)+      (\gl -> Reader.asks (\asmps -> onGoal asmps gl))+      (\g1 g2 -> onConj <$> g1 <*> g2)++---------------------------------------------------------------------+-- * Strategy++-- | The result of attempting to prove a goal with an SMT solver.+--+-- The constructors of this type correspond to those of 'W4R.SatResult'.+--+-- * @sym@ is the symbolic backend, usually 'WE.ExprBuilder'+-- * @t@ is the \"brand\" parameter to 'WE.Expr' (/not/ a base type)+data ProofResult sym t+   = -- | The goal was proved.+     --+     -- Corresponds to 'W4R.Unsat'.+     Proved+     -- | The goal was disproved, and a model that falsifies it is available.+     --+     -- The 'WE.GroundEvalFn' is only available for use during the execution of+     -- a 'ProofConsumer'. See 'WSA.SolverAdapter'.+     --+     -- The @'Maybe' 'WE.ExprRangeBindings'@ are 'Just' when using+     -- 'offlineProve' and 'Nothing' when using 'onlineProve'.+     --+     -- Corresponds to 'W4R.Sat'.+   | Disproved (WE.GroundEvalFn t) (Maybe (WE.ExprRangeBindings t))+     -- | The SMT solver returned \"unknown\".+     --+     -- Corresponds to 'W4R.Unknown'.+   | Unknown++-- | A 'ProofStrategy' dictates how results are proved.+--+-- * @sym@ is the symbolic backend, usually 'WE.ExprBuilder'+-- * @m@ is the monad in which the 'Prover' and 'Combiner' run+-- * @t@ is the \"brand\" parameter to 'WE.Expr' (/not/ a base type)+-- * @r@ is the return type of the eventual 'ProofConsumer'+data ProofStrategy sym m t r+  = ProofStrategy+    { -- | Generally 'offlineProver' or 'onlineProver'+      stratProver :: {-# UNPACK #-} !(Prover sym m t r)+    , stratCombine :: Combiner m r+    }++-- | A callback used to consume a 'ProofResult'.+--+-- If the result is 'Disproved', then this function must consume the+-- 'WE.GroundEvalFn' before returning. See 'WSA.SolverAdapter'.+--+-- * @sym@ is the symbolic backend, usually 'WE.ExprBuilder'+-- * @t@ is the \"brand\" parameter to 'WE.Expr' (/not/ a base type)+-- * @r@ is the return type of the callback+newtype ProofConsumer sym t r+  = ProofConsumer (CB.ProofObligation sym -> ProofResult sym t -> IO r)++---------------------------------------------------------------------+-- *** Combiner++-- | Whether or not a subgoal was proved, together with the result from a+-- 'ProofConsumer'.+data SubgoalResult r+  = SubgoalResult+    { subgoalWasProved :: !Bool+    , subgoalResult :: !r+    }+  deriving Functor++-- | How to combine results of proofs, used as part of a 'ProofStrategy'.+--+-- * @m@ is the monad in which the 'Prover' and 'Combiner' run+-- * @r@ is the return type of the eventual 'ProofConsumer'+newtype Combiner m r+  = Combiner+    { getCombiner ::+        m (SubgoalResult r) -> m (SubgoalResult r) -> m (SubgoalResult r)+    }++-- | Combine 'SubgoalResult's using the '<>' operator. Keep going when subgoals+-- fail.+keepGoing :: Monad m => Semigroup r => Combiner m r+keepGoing = Combiner $ \a1 a2 -> subgoalAnd <$> a1 <*> a2+  where+  subgoalAnd ::+    Semigroup r =>+    SubgoalResult r ->+    SubgoalResult r ->+    SubgoalResult r+  subgoalAnd (SubgoalResult ok1 r1) (SubgoalResult ok2 r2) =+    SubgoalResult (ok1 && ok2) (r1 <> r2)++-- | Combine 'SubgoalResult's using the '<>' operator. After the first subgoal+-- fails, stop trying to prove further goals.+failFast :: Monad m => Semigroup r => Combiner m r+failFast = Combiner $ \sr1 sr2 -> do+  SubgoalResult ok1 r1 <- sr1+  if ok1+  then do+    SubgoalResult ok2 r2 <- sr2+    pure (SubgoalResult ok2 (r1 <> r2))+  else pure (SubgoalResult False r1)++isProved :: ProofResult sym t -> Bool+isProved =+  \case+    Proved {} -> True+    Disproved {} -> False+    Unknown {} -> False++---------------------------------------------------------------------+-- ** Prover++-- | A collection of functions used to prove goals as part of a 'ProofStrategy'.+data Prover sym m t r+  = Prover+    { -- | Prove a single goal under some 'Assumptions'.+      proverProve ::+        Assumptions sym ->+        CB.Assertion sym ->+        ProofConsumer sym t r ->+        m (SubgoalResult r)+      -- | Assume some 'Assumptions' in the scope of a subgoal.+    , proverAssume ::+        Assumptions sym ->+        m (SubgoalResult r) ->+        m (SubgoalResult r)+    }++---------------------------------------------------------------------+-- *** Offline++-- Not exported+offlineProveIO ::+  (sym ~ WE.ExprBuilder t st fs) =>+  W4.IsSymExprBuilder sym =>+  sym ->+  WSA.LogData ->+  WSA.SolverAdapter st ->+  Assumptions sym ->+  CB.Assertion sym ->+  ProofConsumer sym t r ->+  IO (SubgoalResult r)+offlineProveIO sym ld adapter asmps goal (ProofConsumer k) = do+  let goalPred = goal ^. CB.labeledPred+  asmsPred <- CB.assumptionsPred sym asmps+  notGoal <- W4.notPred sym goalPred+  WSA.solver_adapter_check_sat adapter sym ld [asmsPred, notGoal] $ \r ->+    let r' =+          case r of+            W4R.Sat (gfn, binds) -> Disproved gfn binds+            W4R.Unsat () -> Proved+            W4R.Unknown -> Unknown+    in SubgoalResult (isProved r') <$> k (CB.ProofGoal asmps goal) r'++-- | Prove a goal using an \"offline\" solver (i.e., one process per goal).+--+-- See 'offlineProveWithTimeout' for a version that integrates 'Timeout's.+--+-- See the module-level documentation for further discussion of offline vs.+-- online solving.+offlineProve ::+  MonadIO m =>+  (sym ~ WE.ExprBuilder t st fs) =>+  W4.IsSymExprBuilder sym =>+  sym ->+  WSA.LogData ->+  WSA.SolverAdapter st ->+  Assumptions sym ->+  CB.Assertion sym ->+  ProofConsumer sym t r ->+  m (SubgoalResult r)+offlineProve sym ld adapter asmps goal k =+  liftIO (offlineProveIO sym ld adapter asmps goal k)++-- | Prove a goal using an \"offline\" solver, with a timeout.+--+-- See 'offlineProveWithTimeout' for a version without 'Timeout's.+--+-- See the module-level documentation for further discussion of offline vs.+-- online solving.+offlineProveWithTimeout ::+  MonadError TimedOut m =>+  MonadIO m =>+  (sym ~ WE.ExprBuilder t st fs) =>+  W4.IsSymExprBuilder sym =>+  Timeout ->+  sym ->+  WSA.LogData ->+  WSA.SolverAdapter st ->+  Assumptions sym ->+  CB.Assertion sym ->+  ProofConsumer sym t r ->+  m (SubgoalResult r)+offlineProveWithTimeout to sym ld adapter asmps goal k = do+  r <- liftIO (CTO.withTimeout to (offlineProveIO sym ld adapter asmps goal k))+  liftEither r++-- | Prove goals using 'offlineProveWithTimeout'.+--+-- See the module-level documentation for further discussion of offline vs.+-- online solving.+offlineProver ::+  MonadError TimedOut m =>+  MonadIO m =>+  (sym ~ WE.ExprBuilder t st fs) =>+  Timeout ->+  W4.IsSymExprBuilder sym =>+  sym ->+  WSA.LogData ->+  WSA.SolverAdapter st ->+  Prover sym m t r+offlineProver to sym ld adapter =+  Prover+  { proverProve = offlineProveWithTimeout to sym ld adapter+  , proverAssume = \_asmps a -> a+  }++---------------------------------------------------------------------+-- *** Online++-- | Prove a goal using an \"online\" solver (i.e., one process for all goals).+--+-- See the module-level documentation for further discussion of offline vs.+-- online solving.+onlineProve ::+  MonadIO m =>+  W4SMT.SMTReadWriter solver =>+  (sym ~ WE.ExprBuilder t st fs) =>+  W4.IsSymExprBuilder sym =>+  WPO.SolverProcess t solver ->+  Assumptions sym ->+  CB.Assertion sym ->+  ProofConsumer sym t r ->+  m (SubgoalResult r)+onlineProve sProc asmps goal (ProofConsumer k) =+  liftIO $ WPO.checkSatisfiableWithModel sProc "prove" (goal ^. CB.labeledPred) $ \r ->+    let r' =+          case r of+            W4R.Sat gfn -> Disproved gfn Nothing+            W4R.Unsat () -> Proved+            W4R.Unknown -> Unknown+    in SubgoalResult (isProved r') <$> k (CB.ProofGoal asmps goal) r'++-- | Add an assumption by @push@ing a new frame ('WPO.inNewFrame').+onlineAssume :: +  MonadIO m =>+  MonadMask m =>+  W4SMT.SMTReadWriter solver =>+  W4.IsSymExprBuilder sym =>+  (W4.SymExpr sym ~ WE.Expr t) =>+  sym ->+  WPO.SolverProcess t solver ->+  Assumptions sym ->+  m r ->+  m r+onlineAssume sym sProc asmps a =+  WPO.inNewFrame sProc $ do+    liftIO $ do+      let conn = WPO.solverConn sProc+      asmpsPred <- CB.assumptionsPred sym asmps+      term <- W4SMT.mkFormula conn asmpsPred+      W4SMT.assumeFormula conn term+      pure ()+    a++-- | Prove goals using 'onlineProve' and 'onlineAssume'.+--+-- See the module-level documentation for further discussion of offline vs.+-- online solving.+onlineProver ::+  MonadIO m =>+  MonadMask m =>+  W4SMT.SMTReadWriter solver =>+  (sym ~ WE.ExprBuilder t st fs) =>+  W4.IsSymExprBuilder sym =>+  sym ->+  WPO.SolverProcess t solver ->+  Prover sym m t r+onlineProver sym sProc =+  Prover+  { proverProve = onlineProve sProc+  , proverAssume = onlineAssume sym sProc+  }++---------------------------------------------------------------------+-- * Proving goals++-- | Prove a collection of 'CB.Goals' using the specified 'ProofStrategy'.+proveGoals ::+  Functor m =>+  ProofStrategy sym m t r ->+  CB.Goals (CB.Assumptions sym) (CB.Assertion sym) ->+  ProofConsumer sym t r ->+  m r+proveGoals (ProofStrategy prover (Combiner comb)) goals k =+  fmap subgoalResult $+    consumeGoalsWithAssumptions+      (\asmps gl -> proverProve prover asmps gl k)+      comb+      goals++-- | Prove a collection of 'CB.ProofObligations' using a 'ProofStrategy'.+proveObligations ::+  Applicative m =>+  Monoid r =>+  (sym ~ WE.ExprBuilder t st fs) =>+  ProofStrategy sym m t r ->+  CB.ProofObligations sym ->+  ProofConsumer sym t r ->+  m r+proveObligations strat obligations k =+  case obligations of+    Nothing -> pure mempty+    Just goals -> proveGoals strat goals k++-- | Prove a the current collection of 'CB.ProofObligations' associated with the+-- symbolic backend (retrieved via 'CB.getProofObligations').+proveCurrentObligations ::+  MonadIO m =>+  Monoid r =>+  (sym ~ WE.ExprBuilder t st fs) =>+  CB.IsSymBackend sym bak =>+  bak ->+  ProofStrategy sym m t r ->+  ProofConsumer sym t r ->+  m r+proveCurrentObligations bak strat k = do+  obligations <- liftIO (CB.getProofObligations bak)+  proveObligations strat obligations k
src/Lang/Crucible/CFG/Expr.hs view
@@ -75,7 +75,6 @@ import           Data.Parameterized.TraversableFC  import           What4.Interface (RoundingMode(..),StringLiteral(..), stringLiteralInfo)-import           What4.InterpretedFloatingPoint (X86_80Val(..))  import           Lang.Crucible.CFG.Extension import           Lang.Crucible.FunctionHandle
+ src/Lang/Crucible/Concretize.hs view
@@ -0,0 +1,607 @@+-----------------------------------------------------------------------+-- |+-- Module           : Lang.Crucible.Concretize+-- Description      : Get feasible concrete values from a model+-- Copyright        : (c) Galois, Inc 2024+-- License          : BSD3+-- Maintainer       : Langston Barrett <langston@galois.com>+-- Stability        : provisional+--+-- This module defines 'concRegValue', a function that takes a 'RegValue' (i.e.,+-- a symbolic value), and a model from the SMT solver ('W4GE.GroundEvalFn'), and+-- returns the concrete value that the symbolic value takes in the model.+--+-- This can be used to report specific values that lead to violations of+-- assertions, including safety assertions.+------------------------------------------------------------------------++{-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE StandaloneKindSignatures #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE UndecidableInstances #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeApplications #-}++module Lang.Crucible.Concretize+  ( ConcRegValue+  , ConcRV'(..)+  , ConcAnyValue(..)+  , ConcIntrinsic+  , IntrinsicConcFn(..)+  , ConcCtx(..)+  , concRegValue+  , concRegEntry+  , concRegMap+    -- * There and back again+  , IntrinsicConcToSymFn(..)+  , concToSym+  ) where++import qualified Data.Foldable as F+import           Data.Kind (Type)+import           Data.List.NonEmpty (NonEmpty)+import qualified Data.List.NonEmpty as NE+import           Data.Map (Map)+import qualified Data.Map as Map+import           Data.Sequence (Seq)+import           Data.Text (Text)+import qualified Data.Text as Text+import qualified Data.Vector as V+import           Data.Word (Word16)++import qualified Data.Parameterized.Context as Ctx+import           Data.Parameterized.Map (MapF)+import qualified Data.Parameterized.Map as MapF+import           Data.Parameterized.TraversableFC (traverseFC)++import           What4.Expr (Expr, ExprBuilder, Flags, FloatModeRepr(..))+import qualified What4.Expr.GroundEval as W4GE+import           What4.Interface (SymExpr)+import qualified What4.Interface as W4I+import qualified What4.Partial as W4P++import           Lang.Crucible.FunctionHandle (FnHandle, RefCell)+import           Lang.Crucible.Simulator.Intrinsics (Intrinsic)+import           Lang.Crucible.Simulator.RegMap (RegEntry, RegMap)+import qualified Lang.Crucible.Simulator.RegMap as RM+import           Lang.Crucible.Simulator.RegValue (RegValue, FnVal)+import qualified Lang.Crucible.Simulator.RegValue as RV+import qualified Lang.Crucible.Simulator.SymSequence as SymSeq+import qualified Lang.Crucible.Utils.MuxTree as MuxTree+import           Lang.Crucible.Types+import           Lang.Crucible.Panic (panic)++-- | Newtype to allow partial application of 'ConcRegValue'.+--+-- Type families cannot appear partially applied.+type ConcRV' :: Type -> CrucibleType -> Type+newtype ConcRV' sym tp = ConcRV' { unConcRV' :: ConcRegValue sym tp }++-- | Defines the \"concrete\" interpretations of 'CrucibleType' (as opposed+-- to the \"symbolic\" interpretations, which are defined by 'RegValue'), as+-- returned by 'concRegValue'.+--+-- Unlike What4\'s 'W4GE.GroundValue', this type family is parameterized+-- by @sym@, the symbolic backend. This is because Crucible makes use of+-- \"interpreted\" floating point numbers ('SymInterpretedFloatType'). What4\'s+-- @SymFloat@ always uses an IEEE-754 interpretation of symbolic floats, whereas+-- 'SymInterpretedFloatType' can use IEEE-754, real numbers, or uninterpreted+-- functions depending on how the symbolic backend is configured.+type ConcRegValue :: Type -> CrucibleType -> Type+type family ConcRegValue sym tp where+  ConcRegValue sym (BaseToType bt) = W4GE.GroundValue bt+  ConcRegValue sym (FloatType fi) = W4GE.GroundValue (SymInterpretedFloatType sym fi)+  ConcRegValue sym AnyType = ConcAnyValue sym+  ConcRegValue sym UnitType = ()+  ConcRegValue sym NatType = Integer+  ConcRegValue sym CharType = Word16+  ConcRegValue sym (FunctionHandleType a r) = ConcFnVal sym a r+  ConcRegValue sym (MaybeType tp) = Maybe (ConcRegValue sym tp)+  ConcRegValue sym (VectorType tp) = V.Vector (ConcRV' sym tp)+  ConcRegValue sym (SequenceType tp) = Seq (ConcRV' sym tp)+  ConcRegValue sym (StructType ctx) = Ctx.Assignment (ConcRV' sym) ctx+  ConcRegValue sym (VariantType ctx) = Ctx.Assignment (ConcVariantBranch sym) ctx+  ConcRegValue sym (ReferenceType tp) = NonEmpty (RefCell tp)+  ConcRegValue sym (WordMapType w tp) = ()  -- TODO: possible to do something meaningful?+  ConcRegValue sym (RecursiveType nm ctx) = ConcRegValue sym (UnrollType nm ctx)+  ConcRegValue sym (IntrinsicType nm ctx) = ConcIntrinsic nm ctx+  ConcRegValue sym (StringMapType tp) = Map Text (ConcRV' sym tp)++---------------------------------------------------------------------+-- * ConcCtx++-- | Context needed for 'concRegValue'+--+-- The @t@ parameter matches that on 'W4GE.GroundEvalFn' and 'Expr', namely, it+-- is a phantom type brand used to relate nonces to a specific nonce generator+-- (similar to the @s@ parameter of the @ST@ monad). It also appears as the+-- first argument to 'ExprBuilder'.+data ConcCtx sym t+  = ConcCtx+  { -- | Model returned from SMT solver+    model :: W4GE.GroundEvalFn t+    -- | How to ground intrinsics+  , intrinsicConcFuns :: MapF SymbolRepr (IntrinsicConcFn t)+  }++-- | Helper, not exported+ground ::+  ConcCtx sym t ->+  Expr t tp ->+  IO (ConcRegValue sym (BaseToType tp))+ground (ConcCtx (W4GE.GroundEvalFn ge) _) = ge++---------------------------------------------------------------------+-- * Helpers++-- | Helper, not exported+ite ::+  (SymExpr sym ~ Expr t) =>+  ConcCtx sym t ->+  W4I.Pred sym ->+  a ->+  a ->+  IO a+ite ctx p t f = do+  b <- ground ctx p+  pure (if b then t else f)++-- | Helper, not exported+iteIO ::+  (SymExpr sym ~ Expr t) =>+  ConcCtx sym t ->+  W4I.Pred sym ->+  IO a ->+  IO a ->+  IO a+iteIO ctx p t f = do+  b <- ground ctx p+  if b then t else f++-- | Helper, not exported+concPartial ::+  (SymExpr sym ~ Expr t) =>+  W4I.IsExprBuilder sym =>+  ConcCtx sym t ->+  TypeRepr tp ->+  W4P.Partial (W4I.Pred sym) (RegValue sym tp) ->+  IO (Maybe (ConcRegValue sym tp))+concPartial ctx tp (W4P.Partial p v) =+  iteIO ctx p (Just <$> concRegValue ctx tp v) (pure Nothing)++-- | Helper, not exported+concPartialWithErr ::+  (SymExpr sym ~ Expr t) =>+  W4I.IsExprBuilder sym =>+  ConcCtx sym t ->+  TypeRepr tp ->+  W4P.PartialWithErr e (W4I.Pred sym) (RegValue sym tp) ->+  IO (Maybe (ConcRegValue sym tp))+concPartialWithErr ctx tp =+  \case+    W4P.Err _ -> pure Nothing+    W4P.NoErr pv -> concPartial ctx tp pv++---------------------------------------------------------------------+-- * Intrinsics++-- | Open type family for defining how intrinsics are concretized+type ConcIntrinsic :: Symbol -> Ctx CrucibleType -> Type+type family ConcIntrinsic nm ctx++-- | Function for concretizing an intrinsic type+type IntrinsicConcFn :: Type -> Symbol -> Type+newtype IntrinsicConcFn t nm+  = IntrinsicConcFn+    (forall sym ctx.+      SymExpr sym ~ Expr t =>+      W4I.IsExprBuilder sym =>+      ConcCtx sym t ->+      Ctx.Assignment TypeRepr ctx ->+      Intrinsic sym nm ctx ->+      IO (ConcRegValue sym (IntrinsicType nm ctx)))++-- | Helper, not exported+tryConcIntrinsic ::+  forall sym nm ctx t.+  SymExpr sym ~ Expr t =>+  W4I.IsExprBuilder sym =>+  ConcCtx sym t ->+  SymbolRepr nm ->+  Ctx.Assignment TypeRepr ctx ->+  RegValue sym (IntrinsicType nm ctx) ->+  Maybe (IO (ConcRegValue sym (IntrinsicType nm ctx)))+tryConcIntrinsic ctx nm tyCtx v = do+    case MapF.lookup nm (intrinsicConcFuns ctx) of+      Nothing -> Nothing+      Just (IntrinsicConcFn f) -> Just (f @sym @ctx ctx tyCtx v)++---------------------------------------------------------------------+-- * Any++-- | An 'AnyValue' concretized by 'concRegValue'+data ConcAnyValue sym = forall tp. ConcAnyValue (TypeRepr tp) (ConcRV' sym tp)++---------------------------------------------------------------------+-- * FnVal++-- | A 'FnVal' concretized by 'concRegValue'+data ConcFnVal (sym :: Type) (args :: Ctx CrucibleType) (res :: CrucibleType) where+  ConcClosureFnVal ::+    !(ConcFnVal sym (args ::> tp) ret) ->+    !(TypeRepr tp) ->+    !(ConcRV' sym tp) ->+    ConcFnVal sym args ret++  ConcVarargsFnVal ::+    !(FnHandle (args ::> VectorType AnyType) ret) ->+    !(CtxRepr addlArgs) ->+    ConcFnVal sym (args <+> addlArgs) ret++  ConcHandleFnVal ::+    !(FnHandle a r) ->+    ConcFnVal sym a r++-- | Helper, not exported+concFnVal ::+  (SymExpr sym ~ Expr t) =>+  W4I.IsExprBuilder sym =>+  ConcCtx sym t ->+  CtxRepr args ->+  TypeRepr ret ->+  FnVal sym args ret ->+  IO (ConcFnVal sym args ret)+concFnVal ctx args ret =+  \case+    RV.ClosureFnVal fv t v -> do+      concV <- concFnVal ctx (args Ctx.:> t) ret fv+      v' <- concRegValue ctx t v+      pure (ConcClosureFnVal concV t (ConcRV' v'))+    RV.VarargsFnVal hdl extra ->+      pure (ConcVarargsFnVal hdl extra)+    RV.HandleFnVal hdl ->+      pure (ConcHandleFnVal hdl)++---------------------------------------------------------------------+-- * Reference++-- | Helper, not exported+concMux ::+  (SymExpr sym ~ Expr t) =>+  W4I.IsExprBuilder sym =>+  ConcCtx sym t ->+  MuxTree.MuxTree sym a ->+  IO (NonEmpty a)+concMux ctx mt = do+  l <- go (MuxTree.viewMuxTree mt)+  case NE.nonEmpty l of+    -- This is impossible because the only way to make a MuxTree is with+    -- `toMuxTree`, which uses `truePred`.+    Nothing ->+      panic "Lang.Crucible.Concretize.concMux"+        [ "Impossible: Mux tree had no feasible branches?" ]+    Just ne -> pure ne+  where+    go [] = pure []+    go ((val, p):xs) = do+      f <- ite ctx p (val:) id+      f <$> go xs++---------------------------------------------------------------------+-- * Sequence++-- | Helper, not exported+concSymSequence ::+  (SymExpr sym ~ Expr t) =>+  W4I.IsExprBuilder sym =>+  ConcCtx sym t ->+  TypeRepr tp ->+  SymSeq.SymSequence sym (RegValue sym tp) ->+  IO (Seq (ConcRV' sym tp))+concSymSequence ctx tp =+  SymSeq.concretizeSymSequence+    (ground ctx)+    (fmap ConcRV' . concRegValue ctx tp)++---------------------------------------------------------------------+-- * StringMap++-- | Helper, not exported+concStringMap ::+  (SymExpr sym ~ Expr t) =>+  W4I.IsExprBuilder sym =>+  ConcCtx sym t ->+  TypeRepr tp ->+  RegValue sym (StringMapType tp) ->+  IO (Map Text (ConcRV' sym tp))+concStringMap ctx tp v = Map.fromList <$> go (Map.toList v)+  where+    go [] = pure []+    go ((t, v'):xs) =+      concPartialWithErr ctx tp v' >>=+        \case+          Nothing -> go xs+          Just v'' -> ((t, ConcRV' v''):) <$> go xs++---------------------------------------------------------------------+-- * Variant++-- | Note that we do not attempt to \"normalize\" variants in 'concRegValue'+-- in any way. If the model reports that multiple branches of a variant are+-- plausible, then multiple branches might be included as 'Just's.+newtype ConcVariantBranch sym tp+  = ConcVariantBranch (Maybe (ConcRV' sym tp))++-- | Helper, not exported+concVariant ::+  forall sym variants t.+  (SymExpr sym ~ Expr t) =>+  W4I.IsExprBuilder sym =>+  ConcCtx sym t ->+  Ctx.Assignment TypeRepr variants ->+  RegValue sym (VariantType variants) ->+  IO (ConcRegValue sym (VariantType variants))+concVariant ctx tps vs = Ctx.zipWithM concBranch tps vs+  where+    concBranch :: forall tp. TypeRepr tp -> RV.VariantBranch sym tp -> IO (ConcVariantBranch sym tp)+    concBranch tp (RV.VB v) = do+      v' <- concPartialWithErr ctx tp v+      case v' of+        Just v'' -> pure (ConcVariantBranch (Just (ConcRV' v'')))+        Nothing -> pure (ConcVariantBranch Nothing)++---------------------------------------------------------------------+-- * 'concRegValue'++-- | Pick a feasible concrete value from the model+--+-- This function does not attempt to \"normalize\" variants nor mux trees in any+-- way. If the model reports that multiple branches of a variant or mux tree are+-- plausible, then multiple branches might be included in the result.+concRegValue ::+  (SymExpr sym ~ Expr t) =>+  W4I.IsExprBuilder sym =>+  ConcCtx sym t ->+  TypeRepr tp ->+  RegValue sym tp ->+  IO (ConcRegValue sym tp)+concRegValue ctx tp v =+  case (tp, v) of+    -- Base types+    (BoolRepr, _) -> ground ctx v+    (BVRepr _width, _) -> ground ctx v+    (ComplexRealRepr, _) -> ground ctx v+    (FloatRepr _fpp, _) -> ground ctx v+    (IEEEFloatRepr _fpp, _) -> ground ctx v+    (IntegerRepr, _) -> ground ctx v+    (NatRepr, _) -> ground ctx (W4I.natToIntegerPure v)+    (RealValRepr, _) -> ground ctx v+    (StringRepr _, _) -> ground ctx v+    (SymbolicArrayRepr _idxs _tp', _) -> ground ctx v+    (SymbolicStructRepr _tys, _) -> ground ctx v++    -- Trivial cases+    (UnitRepr, ()) -> pure ()+    (CharRepr, _) -> pure v++    -- Simple recursive cases+    (AnyRepr, RV.AnyValue tp' v') ->+      ConcAnyValue tp' . ConcRV' <$> concRegValue ctx tp' v'+    (RecursiveRepr symb tyCtx, RV.RolledType v') ->+      concRegValue ctx (unrollType symb tyCtx) v'+    (StructRepr tps, _) ->+      Ctx.zipWithM (\tp' (RV.RV v') -> ConcRV' <$> concRegValue ctx tp' v') tps v+    (VectorRepr tp', _) ->+      traverse (fmap ConcRV' . concRegValue ctx tp') v++    -- Cases with helper functions+    (MaybeRepr tp', _) ->+      concPartialWithErr ctx tp' v+    (FunctionHandleRepr args ret, _) ->+      concFnVal ctx args ret v+    (IntrinsicRepr nm tyCtx, _) ->+      case tryConcIntrinsic ctx nm tyCtx v of+        Nothing ->+          let strNm = Text.unpack (symbolRepr nm) in+          fail ("Missing concretization function for intrinsic: " ++ strNm)+        Just r -> r+    (ReferenceRepr _, _) ->+      concMux ctx v+    (SequenceRepr tp', _) ->+      concSymSequence ctx tp' v+    (StringMapRepr tp', _) ->+      concStringMap ctx tp' v+    (VariantRepr tps, _) ->+      concVariant ctx tps v++    -- Incomplete cases+    (WordMapRepr _ _, _) -> pure ()++-- | Like 'concRegValue', but for 'RegEntry'+concRegEntry ::+  (SymExpr sym ~ Expr t) =>+  W4I.IsExprBuilder sym =>+  ConcCtx sym t ->+  RegEntry sym tp ->+  IO (ConcRegValue sym tp)+concRegEntry ctx e = concRegValue ctx (RM.regType e) (RM.regValue e)++-- | Like 'concRegEntry', but for a whole 'RegMap'+concRegMap ::+  (SymExpr sym ~ Expr t) =>+  W4I.IsExprBuilder sym =>+  ConcCtx sym t ->+  RegMap sym tps ->+  IO (Ctx.Assignment (ConcRV' sym) tps)+concRegMap ctx (RM.RegMap m) = traverseFC (fmap ConcRV' . concRegEntry ctx) m++---------------------------------------------------------------------+-- * concToSym++-- | Function for re-symbolizing an intrinsic type+type IntrinsicConcToSymFn :: Symbol -> Type+newtype IntrinsicConcToSymFn nm+  = IntrinsicConcToSymFn+    (forall sym ctx.+      W4I.IsExprBuilder sym =>+      sym ->+      Ctx.Assignment TypeRepr ctx ->+      ConcIntrinsic nm ctx ->+      IO (RegValue sym (IntrinsicType nm ctx)))++-- | Helper, not exported+concToSymAny ::+  (sym ~ ExprBuilder scope st (Flags fm)) =>+  sym ->+  MapF SymbolRepr IntrinsicConcToSymFn ->+  FloatModeRepr fm ->+  ConcRegValue sym AnyType ->+  IO (RegValue sym AnyType)+concToSymAny sym iFns fm (ConcAnyValue tp' (ConcRV' v')) =+  RV.AnyValue tp' <$> concToSym sym iFns fm tp' v'++-- | Helper, not exported+concToSymFn ::+  (sym ~ ExprBuilder scope st (Flags fm)) =>+  sym ->+  MapF SymbolRepr IntrinsicConcToSymFn ->+  FloatModeRepr fm ->+  Ctx.Assignment (TypeRepr) as ->+  TypeRepr r ->+  ConcRegValue sym (FunctionHandleType as r) ->+  IO (RegValue sym (FunctionHandleType as r))+concToSymFn sym iFns fm as r f =+  case f of+    ConcClosureFnVal clos vtp (ConcRV' v) -> do+      v' <- concToSym sym iFns fm vtp v+      clos' <- concToSymFn sym iFns fm (as Ctx.:> vtp) r clos+      pure (RV.ClosureFnVal clos' vtp v')++    ConcVarargsFnVal hdl extra ->+      pure (RV.VarargsFnVal hdl extra)++    ConcHandleFnVal hdl ->+      pure (RV.HandleFnVal hdl)++-- | Helper, not exported+concToSymIntrinsic ::+  W4I.IsExprBuilder sym =>+  sym ->+  MapF SymbolRepr IntrinsicConcToSymFn ->+  SymbolRepr nm ->+  CtxRepr ctx ->+  ConcRegValue sym (IntrinsicType nm ctx) ->+  IO (RegValue sym (IntrinsicType nm ctx))+concToSymIntrinsic sym iFns nm tyCtx v =+  case MapF.lookup nm iFns of+    Nothing ->+      let strNm = Text.unpack (symbolRepr nm) in+      fail ("Missing concretization function for intrinsic: " ++ strNm)+    Just (IntrinsicConcToSymFn f) -> f sym tyCtx v++-- | Helper, not exported+concToSymMaybe ::+  (sym ~ ExprBuilder scope st (Flags fm)) =>+  sym ->+  MapF SymbolRepr IntrinsicConcToSymFn ->+  FloatModeRepr fm ->+  TypeRepr tp ->+  ConcRegValue sym (MaybeType tp) ->+  IO (RegValue sym (MaybeType tp))+concToSymMaybe sym iFns fm tp =+  \case+    Nothing -> pure (W4P.Err ())+    Just v ->+      W4P.justPartExpr sym <$> concToSym sym iFns fm tp v++-- | Helper, not exported+concToSymRef ::+  W4I.IsExprBuilder sym =>+  sym ->+  ConcRegValue sym (ReferenceType tp) ->+  IO (RegValue sym (ReferenceType tp))+concToSymRef sym (v NE.:| _) = pure (MuxTree.toMuxTree sym v)++-- | Helper, not exported+concToSymVariant ::+  forall sym tps scope st fm.+  (sym ~ ExprBuilder scope st (Flags fm)) =>+  sym ->+  MapF SymbolRepr IntrinsicConcToSymFn ->+  FloatModeRepr fm ->+  CtxRepr tps ->+  ConcRegValue sym (VariantType tps) ->+  IO (RegValue sym (VariantType tps))+concToSymVariant sym iFns fm tps v = Ctx.zipWithM go tps v+  where+    go :: forall tp. TypeRepr tp -> ConcVariantBranch sym tp -> IO (RV.VariantBranch sym tp)+    go tp (ConcVariantBranch b) =+      case b of+        Nothing -> pure (RV.VB (W4P.Err ()))+        Just (ConcRV' v') ->+          RV.VB . W4P.justPartExpr sym <$> concToSym sym iFns fm tp v'++-- | Inject a 'ConcRegValue' back into a 'RegValue'.+concToSym ::+  (sym ~ ExprBuilder scope st (Flags fm)) =>+  sym ->+  MapF SymbolRepr IntrinsicConcToSymFn ->+  FloatModeRepr fm ->+  TypeRepr tp ->+  ConcRegValue sym tp ->+  IO (RegValue sym tp)+concToSym sym iFns fm tp v =+  case tp of+    -- Base types+    BoolRepr -> W4GE.groundToSym sym BaseBoolRepr v+    BVRepr width -> W4GE.groundToSym sym (BaseBVRepr width) v+    ComplexRealRepr -> W4GE.groundToSym sym BaseComplexRepr v+    FloatRepr fi ->+      case fm of+        FloatIEEERepr ->+          W4I.floatLit sym (floatInfoToPrecisionRepr fi) v+        FloatUninterpretedRepr -> do+          sv <- W4GE.groundToSym sym (floatInfoToBVTypeRepr fi) v+          iFloatFromBinary sym fi sv+        FloatRealRepr ->+          iFloatLitRational sym fi v+    IEEEFloatRepr fpp -> W4GE.groundToSym sym (BaseFloatRepr fpp) v+    IntegerRepr -> W4GE.groundToSym sym BaseIntegerRepr v+    NatRepr -> W4I.integerToNat sym =<< W4GE.groundToSym sym BaseIntegerRepr v+    RealValRepr -> W4GE.groundToSym sym BaseRealRepr v+    StringRepr si -> W4GE.groundToSym sym (BaseStringRepr si) v+    SymbolicArrayRepr idxs tp' -> W4GE.groundToSym sym (BaseArrayRepr idxs tp') v+    SymbolicStructRepr tys -> W4GE.groundToSym sym (BaseStructRepr tys) v++    -- Trivial cases+    UnitRepr -> pure ()+    CharRepr -> pure v++    -- Simple recursive cases+    RecursiveRepr symb tyCtx ->+      RV.RolledType <$> concToSym sym iFns fm (unrollType symb tyCtx) v+    SequenceRepr tp' -> do+      l <- traverse (concToSym sym iFns fm tp' . unConcRV') (F.toList v)+      SymSeq.fromListSymSequence sym l+    StringMapRepr tp' ->+      traverse (fmap (W4P.justPartExpr sym) . concToSym sym iFns fm tp' . unConcRV') v+    StructRepr tps ->+      Ctx.zipWithM (\tp' (ConcRV' v') -> RV.RV <$> concToSym sym iFns fm tp' v') tps v+    VectorRepr tp' ->+      traverse (concToSym sym iFns fm tp' . unConcRV') v++    -- Cases with helper functions+    AnyRepr -> concToSymAny sym iFns fm v+    MaybeRepr tp' -> concToSymMaybe sym iFns fm tp' v+    FunctionHandleRepr args ret -> concToSymFn sym iFns fm args ret v+    IntrinsicRepr nm tyCtx -> concToSymIntrinsic sym iFns nm tyCtx v+    ReferenceRepr _tp' -> concToSymRef sym v+    VariantRepr tps -> concToSymVariant sym iFns fm tps v++    -- Incomplete cases+    WordMapRepr _ _ -> fail "concToSym does not yet support WordMap"
src/Lang/Crucible/FunctionHandle.hs view
@@ -34,6 +34,7 @@   , emptyHandleMap   , insertHandleMap   , lookupHandleMap+  , updateHandleMap   , searchHandleMap   , handleMapToHandles     -- * Reference cells@@ -44,6 +45,7 @@  import           Data.Hashable import           Data.Kind+import           Data.Functor.Identity import qualified Data.List as List import           Data.Ord (comparing) @@ -216,6 +218,19 @@   case MapF.lookup (handleID hdl) m of      Just (HandleElt _ x) -> Just x      Nothing -> Nothing++-- | Update the entry of the function handle in the map.+updateHandleMap :: (f args ret -> f args ret)+                -> FnHandle args ret+                -> FnHandleMap f+                -> FnHandleMap f+updateHandleMap f hdl (FnHandleMap m) =+  FnHandleMap $ MapF.updatedValue $ runIdentity $+    MapF.updateAtKey+      (handleID hdl)+      (Identity Nothing)+      (\(HandleElt hdl' x) -> Identity $ MapF.Set $ HandleElt hdl' $ f x)+      m  -- | Lookup the function name in the map by a linear scan of all -- entries.  This will be much slower than using 'lookupHandleMap' to
src/Lang/Crucible/Simulator/EvalStmt.hs view
@@ -60,7 +60,6 @@  import           What4.Config import           What4.Interface-import           What4.InterpretedFloatingPoint (freshFloatConstant) import           What4.Partial import           What4.ProgramLoc 
src/Lang/Crucible/Simulator/Evaluation.hs view
@@ -792,8 +792,8 @@       bvSlt sym x y     BoolToBV w xe -> do       x <- evalSub xe-      one <- bvLit sym w (BV.one w)-      zro <- bvLit sym w (BV.zero w)+      one <- bvOne sym w+      zro <- bvZero sym w       bvIte sym x one zro     BVNonzero _ xe -> do       x <- evalSub xe
src/Lang/Crucible/Simulator/Profiling.hs view
@@ -76,6 +76,7 @@  import           Lang.Crucible.Backend import           Lang.Crucible.CFG.Core+import           Lang.Crucible.Panic (panic) import           Lang.Crucible.Simulator.CallFrame import           Lang.Crucible.Simulator.EvalStmt import           Lang.Crucible.Simulator.ExecutionTree@@ -290,7 +291,11 @@  go xs (e Seq.:<| es) =    case cgEvent_type e of      ENTER -> go (e:xs) es-     EXIT  -> go (tail xs) es+     EXIT  -> case xs of+                (_:xss) -> go xss es+                _ -> panic+                       "openEventFrames"+                       ["Encountered an EXIT without a preceding ENTER"]      _     -> go xs es  openToCloseEvent :: UTCTime -> Metrics Identity -> CGEvent -> CGEvent
src/Lang/Crucible/Simulator/SymSequence.hs view
@@ -14,6 +14,7 @@ ( SymSequence(..) , nilSymSequence , consSymSequence+, fromListSymSequence , appendSymSequence , muxSymSequence , isNilSymSequence@@ -23,6 +24,7 @@ , unconsSymSequence , traverseSymSequence , concreteizeSymSequence+, concretizeSymSequence , prettySymSequence    -- * Low-level evaluation primitives@@ -40,6 +42,8 @@ import qualified Data.Map as Map import           Data.Parameterized.Nonce import qualified Data.Parameterized.Map as MapF+import           Data.Sequence (Seq)+import qualified Data.Sequence as Seq import           Prettyprinter (Doc) import qualified Prettyprinter as PP @@ -167,6 +171,12 @@   do n <- freshNonce globalNonceGenerator      pure (SymSequenceCons n x xs) +fromListSymSequence :: sym -> [a] -> IO (SymSequence sym a)+fromListSymSequence sym =+  \case+    [] -> nilSymSequence sym+    (x:xs) -> consSymSequence sym x =<< fromListSymSequence sym xs+ -- | Append two sequences appendSymSequence ::   sym ->@@ -400,6 +410,24 @@     loop SymSequenceNil = pure []     loop (SymSequenceCons _ v tl) = (:) <$> eval v <*> loop tl     loop (SymSequenceAppend _ xs ys) = (++) <$> loop xs <*> loop ys+    loop (SymSequenceMerge _ p xs ys) =+      do b <- conc p+         if b then loop xs else loop ys+{-# DEPRECATED concreteizeSymSequence "Use concretizeSymSequence instead" #-} ++-- | Using the given evaluation function for booleans, and an evaluation+--   function for values, compute a concrete sequence corresponding+--   to the given symbolic sequence.+concretizeSymSequence ::+  (Pred sym -> IO Bool) {- ^ evaluation for booleans -} ->+  (a -> IO b) {- ^ evaluation for values -} ->+  SymSequence sym a ->+  IO (Seq b)+concretizeSymSequence conc eval = loop+  where+    loop SymSequenceNil = pure Seq.empty+    loop (SymSequenceCons _ v tl) = (Seq.<|) <$> eval v <*> loop tl+    loop (SymSequenceAppend _ xs ys) = (Seq.><) <$> loop xs <*> loop ys     loop (SymSequenceMerge _ p xs ys) =       do b <- conc p          if b then loop xs else loop ys
src/Lang/Crucible/Types.hs view
@@ -95,16 +95,7 @@   , module Data.Parameterized.NatRepr   , module Data.Parameterized.SymbolRepr   , module What4.BaseTypes-  , FloatInfo-  , HalfFloat-  , SingleFloat-  , DoubleFloat-  , QuadFloat-  , X86_80Float-  , DoubleDoubleFloat-  , FloatInfoRepr(..)-  , FloatInfoToBitWidth-  , floatInfoToBVTypeRepr+  , module What4.InterpretedFloatingPoint   ) where  import           Data.Hashable
+ src/Lang/Crucible/Utils/Seconds.hs view
@@ -0,0 +1,18 @@+{-# LANGUAGE GeneralizedNewtypeDeriving #-}++module Lang.Crucible.Utils.Seconds+  ( Seconds+  , secondsToInt+  , secondsFromInt+  , secondsToMicroseconds+  ) where++newtype Seconds = Seconds { secondsToInt :: Int }+  deriving (Eq, Num, Ord, Show)++-- | Inverse of 'secondsToInt'+secondsFromInt :: Int -> Seconds+secondsFromInt = Seconds++secondsToMicroseconds :: Seconds -> Int+secondsToMicroseconds = (* 1000000) . secondsToInt
+ src/Lang/Crucible/Utils/Timeout.hs view
@@ -0,0 +1,38 @@+module Lang.Crucible.Utils.Timeout+  ( Timeout(..)+  , TimedOut(..)+  , withTimeout+  ) where++import qualified Control.Concurrent as CC+import qualified Control.Concurrent.Async as CCA++import qualified Lang.Crucible.Utils.Seconds as Secs++-- | A timeout, in seconds.+newtype Timeout = Timeout { getTimeout :: Secs.Seconds }+  deriving (Eq, Ord, Show)++-- Private, not exported+timeoutToMicros :: Timeout -> Int+timeoutToMicros = Secs.secondsToMicroseconds . getTimeout++-- | A task timed out.+data TimedOut = TimedOut+  deriving Show++-- | Execute a task with a timeout.+--+-- Implemented via 'CCA.race', so re-throws exceptions that occur during the+-- task (if it completes before the timeout).+withTimeout ::+  -- | Timeout duration (seconds)+  Timeout ->+  -- | Task to attempt+  IO a ->+  IO (Either TimedOut a)+withTimeout to task = do+  let timeout = do+        CC.threadDelay (timeoutToMicros to)+        pure TimedOut+  CCA.race timeout task