packages feed

crux-0.7: src/Crux/FormatOut.hs

{-# LANGUAGE GADTs #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE MultiWayIf #-}
{-# LANGUAGE OverloadedStrings #-}
-- | This module provides various default formatters for outputting
-- information in human readable form.  Alternative versions should be
-- used where appropriate.

module Crux.FormatOut
  (
    sayWhatResultStatus
  , sayWhatFailedGoals
  )
where

import qualified Data.BitVector.Sized as BV
import           Data.Foldable ( toList )
import           Data.Sequence (Seq)
import qualified Data.Text as Text (Text)
import           Prettyprinter ( (<+>) )
import qualified Prettyprinter as PP
import qualified Prettyprinter.Render.Text as PPR

import           What4.Expr (GroundValueWrapper(..), GroundValue)
import           What4.BaseTypes
import           What4.ProgramLoc

import           Lang.Crucible.Backend (CrucibleEvent(..))
import qualified Lang.Crucible.Simulator.SimError as CSE

import           Crux.Types

sayWhatResultStatus :: CruxSimulationResult -> SayWhat
sayWhatResultStatus (CruxSimulationResult cmpl gls) =
  let tot        = sum (totalProcessedGoals . fst <$> gls)
      proved     = sum (provedGoals . fst <$> gls)
      disproved  = sum (disprovedGoals . fst <$> gls)
      incomplete = sum (incompleteGoals . fst <$> gls)
      unknown    = tot - (proved + disproved + incomplete)
      goalSummary = if tot == 0 then
                      "All goals discharged through internal simplification."
                    else
                      PP.nest 2 $
                      PP.vcat [ "Goal status:"
                              , "Total:" <+> PP.pretty tot
                              , "Proved:" <+> PP.pretty proved
                              , "Disproved:" <+> PP.pretty disproved
                              , "Incomplete:" <+> PP.pretty incomplete
                              , "Unknown:" <+> PP.pretty unknown
                              ]
  in SayMore
     (SayWhat Simply "Crux" $ ppToText goalSummary)
     $ if | disproved > 0 ->
              SayWhat Fail "Crux" "Overall status: Invalid."
          | incomplete > 0 || cmpl == ProgramIncomplete ->
              SayWhat Warn "Crux" "Overall status: Unknown (incomplete)."
          | unknown > 0 -> SayWhat Warn "Crux" "Overall status: Unknown."
          | proved == tot -> SayWhat OK "Crux" "Overall status: Valid."
          | otherwise ->
              SayWhat Fail "Crux" "Internal error computing overall status."

sayWhatFailedGoals :: Bool -> Bool -> Seq ProvedGoals -> SayWhat
sayWhatFailedGoals skipIncompl showVars allGls =
  if null allDocs
     then SayNothing
     else SayWhat Fail "Crux" $ ppToText $ PP.vsep allDocs
 where
  failedDoc = \case
    Branch gls1 gls2 -> failedDoc gls1 <> failedDoc gls2
    ProvedGoal{} -> []
    NotProvedGoal _asmps err ex _locs mdl s ->
      if | skipIncompl, CSE.SimError _ (CSE.ResourceExhausted _) <- err -> []
         | Just (_m,evs) <- mdl ->
             [ PP.nest 2 $ PP.vcat (
               [ "Found counterexample for verification goal"
               -- n.b. prefer the prepared pretty explanation, but
               -- if not available, use the NotProved information.
               -- Don't show both: they tend to be duplications.
               , if null (show ex) then PP.viaShow err else ex
               ] -- if `showVars` is set, print the sequence of symbolic
                 -- variable events that led to this failure
                 ++ if showVars then
                      ["Symbolic variables:", PP.indent 2 (PP.vcat (ppVars evs))]
                    else []
                 -- print abducts, if any
                 ++ if s /= [] then
                      let numFacts = length s
                          -- NB: If you update the contents of this error
                          -- message, make sure to update the corresponding
                          -- regexes that check for this in
                          -- crux-llvm/test/Test.hs.
                          herald = PP.plural
                                     "The following fact"
                                     ("One of the following"
                                        PP.<+> PP.viaShow numFacts
                                        PP.<+> "facts")
                                     numFacts PP.<+> "would entail the goal" in
                      herald : (map (\x -> PP.pretty ('*' : ' ' : x)) s)
                    else [])]
         | otherwise ->
           [ PP.nest 2 $ PP.vcat [ "Failed to prove verification goal", ex ] ]

  ppVars evs =
    do CreateVariableEvent loc nm tpr (GVW v) <- evs
       pure (ppVarEvent loc nm tpr v)

  ppVarEvent loc nm tpr v =
    PP.pretty nm PP.<+> "=" PP.<+> ppVal tpr v PP.<+> "at" PP.<+> PP.pretty (plSourceLoc loc)

  ppVal :: BaseTypeRepr tp -> GroundValue tp -> PP.Doc ann
  ppVal tpr v = case tpr of
    BaseBVRepr _w -> PP.viaShow (BV.asUnsigned v)
    BaseFloatRepr _fpp -> PP.viaShow v
    BaseRealRepr -> PP.viaShow v
    _ -> "<unknown>" PP.<+> PP.viaShow tpr

  allDocs = mconcat (failedDoc <$> toList allGls)



ppToText :: PP.Doc ann -> Text.Text
ppToText = PPR.renderStrict . PP.layoutPretty PP.defaultLayoutOptions