Skip to content

Commit bc44927

Browse files
committed
Extend Unknown type, abort on unsafe unknown ensures
1 parent 748c998 commit bc44927

File tree

5 files changed

+73
-217
lines changed

5 files changed

+73
-217
lines changed

booster/library/Booster/JsonRpc.hs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -365,7 +365,7 @@ respond stateVar request =
365365
withContext CtxGetModel $
366366
withContext CtxSMT $
367367
logMessage ("No predicates or substitutions given, returning Unknown" :: Text)
368-
pure $ SMT.IsUnknown "No predicates or substitutions given"
368+
pure $ SMT.IsUnknown (SMT.SMTUnknownReason "No predicates or substitutions given")
369369
else do
370370
solver <- SMT.initSolver def smtOptions
371371
result <- SMT.getModelFor solver boolPs suppliedSubst
@@ -409,7 +409,7 @@ respond stateVar request =
409409
, substitution = Nothing
410410
}
411411
SMT.IsUnknown reason -> do
412-
logMessage $ "SMT result: Unknown - " <> reason
412+
logMessage $ "SMT result: Unknown - " <> show reason
413413
pure . Right . RpcTypes.GetModel $
414414
RpcTypes.GetModelResult
415415
{ satisfiable = RpcTypes.Unknown

booster/library/Booster/Pattern/Rewrite.hs

Lines changed: 28 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -307,7 +307,7 @@ applyRule pat@Pattern{ceilConditions} rule =
307307
MatchSuccess matchingSubstitution -> do
308308
-- existential variables may be present in rule.rhs and rule.ensures,
309309
-- need to strip prefixes and freshen their names with respect to variables already
310-
-- present in the input pattern and in the unification substitution
310+
-- present in the input pattern and in the matching substitution
311311
varsFromInput <- lift . RewriteT $ asks (.varsToAvoid)
312312
let varsFromPattern = freeVariables pat.term <> (Set.unions $ Set.map (freeVariables . coerce) pat.constraints)
313313
varsFromSubst = Set.unions . map freeVariables . Map.elems $ matchingSubstitution
@@ -498,18 +498,11 @@ applyRule pat@Pattern{ceilConditions} rule =
498498

499499
-- check unclear requires-clauses in the context of known constraints (priorKnowledge)
500500
solver <- lift $ RewriteT $ (.smtSolver) <$> ask
501-
let smtUnclear = do
502-
withContext CtxConstraint . withContext CtxAbort . logMessage $
503-
WithJsonMessage (object ["conditions" .= (externaliseTerm . coerce <$> stillUnclear)]) $
504-
renderOneLineText $
505-
"Uncertain about condition(s) in a rule:"
506-
<+> (hsep . punctuate comma . map (pretty' @mods) $ stillUnclear)
507-
failRewrite $
508-
RuleConditionUnclear rule . coerce . foldl1 AndTerm $
509-
map coerce stillUnclear
510501
SMT.checkPredicates solver pat.constraints pat.substitution (Set.fromList stillUnclear) >>= \case
511-
SMT.IsUnknown{} ->
512-
smtUnclear -- abort rewrite if a solver result was Unknown
502+
SMT.IsUnknown reason -> do
503+
-- abort rewrite if a solver result was Unknown
504+
withContext CtxAbort $ logMessage reason
505+
smtUnclear stillUnclear
513506
SMT.IsInvalid -> do
514507
-- requires is actually false given the prior
515508
withContext CtxFailure $ logMessage ("Required clauses evaluated to #Bottom." :: Text)
@@ -534,15 +527,38 @@ applyRule pat@Pattern{ceilConditions} rule =
534527

535528
-- check all new constraints together with the known side constraints
536529
solver <- lift $ RewriteT $ (.smtSolver) <$> ask
530+
-- TODO it is probably enough to establish satisfiablity (rather than validity) of the ensured conditions.
531+
-- For now, we check validity to be safe and admit indeterminate result (i.e. (P, not P) is (Sat, Sat)).
537532
(lift $ SMT.checkPredicates solver pat.constraints pat.substitution (Set.fromList newConstraints)) >>= \case
533+
SMT.IsUnknown SMT.ImplicationIndeterminate -> do
534+
pure ()
535+
SMT.IsUnknown SMT.InconsistentGroundTruth -> do
536+
withContext CtxWarn $ logMessage ("Ground truth is #Bottom." :: Text)
537+
RewriteRuleAppT $ pure Trivial
538538
SMT.IsInvalid -> do
539539
withContext CtxSuccess $ logMessage ("New constraints evaluated to #Bottom." :: Text)
540540
RewriteRuleAppT $ pure Trivial
541+
SMT.IsUnknown reason -> do
542+
-- abort rewrite if a solver result was Unknown for reason other then SMT.ImplicationIndeterminate
543+
withContext CtxAbort $ logMessage reason
544+
smtUnclear newConstraints
541545
_other ->
542546
pure ()
543547

544548
pure newConstraints
545549

550+
smtUnclear :: [Predicate] -> RewriteRuleAppT (RewriteT io) ()
551+
smtUnclear predicates = do
552+
ModifiersRep (_ :: FromModifiersT mods => Proxy mods) <- getPrettyModifiers
553+
withContext CtxConstraint . withContext CtxAbort . logMessage $
554+
WithJsonMessage (object ["conditions" .= (externaliseTerm . coerce <$> predicates)]) $
555+
renderOneLineText $
556+
"Uncertain about condition(s) in a rule:"
557+
<+> (hsep . punctuate comma . map (pretty' @mods) $ predicates)
558+
failRewrite $
559+
RuleConditionUnclear rule . coerce . foldl1 AndTerm $
560+
map coerce predicates
561+
546562
{- | Reason why a rewrite did not produce a result. Contains additional
547563
information for logging what happened during the rewrite.
548564
-}

booster/library/Booster/SMT/Interface.hs

Lines changed: 34 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ module Booster.SMT.Interface (
1212
SMTOptions (..), -- re-export
1313
defaultSMTOptions, -- re-export
1414
SMTError (..),
15+
UnkwnonReason (..),
1516
initSolver,
1617
noSolver,
1718
finaliseSolver,
@@ -33,6 +34,7 @@ import Control.Monad
3334
import Control.Monad.IO.Class
3435
import Control.Monad.Trans.State
3536
import Data.Aeson (object, (.=))
37+
import Data.Aeson.Types (FromJSON (..), ToJSON (..))
3638
import Data.ByteString.Char8 qualified as BS
3739
import Data.Coerce
3840
import Data.Data (Proxy)
@@ -44,6 +46,14 @@ import Data.Map qualified as Map
4446
import Data.Set (Set)
4547
import Data.Set qualified as Set
4648
import Data.Text as Text (Text, pack, unlines, unwords)
49+
import Deriving.Aeson (
50+
CamelToKebab,
51+
CustomJSON (..),
52+
FieldLabelModifier,
53+
OmitNothingFields,
54+
StripPrefix,
55+
)
56+
import GHC.Generics (Generic)
4757
import Prettyprinter (Pretty, backslash, hsep, punctuate, slash, (<+>))
4858
import SMTLIB.Backends.Process qualified as Backend
4959

@@ -188,12 +198,28 @@ finaliseSolver ctxt = do
188198
Log.logMessage ("Closing SMT solver" :: Text)
189199
destroyContext ctxt
190200

191-
pattern IsUnknown :: unknown -> Either unknown b
201+
data UnkwnonReason
202+
= -- | SMT prelude is UNSAT
203+
InconsistentGroundTruth
204+
| -- | (P, not P) is (SAT, SAT)
205+
ImplicationIndeterminate
206+
| -- | SMT solver returned unknown
207+
SMTUnknownReason Text
208+
deriving (Show, Eq, Generic)
209+
deriving
210+
(FromJSON, ToJSON)
211+
via CustomJSON '[OmitNothingFields, FieldLabelModifier '[CamelToKebab, StripPrefix "_"]] UnkwnonReason
212+
213+
instance Log.ToLogFormat UnkwnonReason where
214+
toTextualLog = pack . show
215+
toJSONLog = toJSON
216+
217+
pattern IsUnknown :: UnkwnonReason -> Either UnkwnonReason b
192218
pattern IsUnknown u = Left u
193219

194220
newtype IsSat' a = IsSat' (Maybe a) deriving (Functor)
195221

196-
type IsSatResult a = Either Text (IsSat' a)
222+
type IsSatResult a = Either UnkwnonReason (IsSat' a)
197223

198224
pattern IsSat :: a -> IsSatResult a
199225
pattern IsSat a = Right (IsSat' (Just a))
@@ -243,7 +269,7 @@ isSatReturnTransState ctxt ps subst
243269
SMT.runCmd CheckSat >>= \case
244270
Sat -> pure $ IsSat transState
245271
Unsat -> pure IsUnsat
246-
Unknown reason -> retry (solve smtToCheck transState) (pure $ IsUnknown reason)
272+
Unknown reason -> retry (solve smtToCheck transState) (pure $ IsUnknown (SMTUnknownReason reason))
247273
other -> do
248274
let msg = "Unexpected result while calling 'check-sat': " <> show other
249275
Log.withContext Log.CtxAbort $ Log.logMessage $ Text.pack msg
@@ -347,7 +373,7 @@ mkComment = BS.pack . Pretty.renderDefault . pretty' @'[Decoded]
347373

348374
newtype IsValid' = IsValid' Bool
349375

350-
type IsValidResult = Either (Maybe Text) IsValid'
376+
type IsValidResult = Either UnkwnonReason IsValid'
351377

352378
pattern IsValid, IsInvalid :: IsValidResult
353379
pattern IsValid = Right (IsValid' True)
@@ -418,14 +444,14 @@ checkPredicates ctxt givenPs givenSubst psToCheck
418444
hsep ("Predicates to check:" : map (pretty' @mods) (Set.toList psToCheck))
419445
result <- interactWithSolver smtGiven sexprsToCheck
420446
case result of
421-
(Unsat, Unsat) -> pure $ IsUnknown Nothing -- defensive choice for inconsistent ground truth
447+
(Unsat, Unsat) -> pure $ IsUnknown InconsistentGroundTruth
422448
(Sat, Sat) -> do
423449
Log.logMessage ("Implication not determined" :: Text)
424-
pure $ IsUnknown Nothing
450+
pure $ IsUnknown ImplicationIndeterminate
425451
(Sat, Unsat) -> pure IsValid
426452
(Unsat, Sat) -> pure IsInvalid
427-
(Unknown reason, _) -> retry (solve smtGiven sexprsToCheck transState) (pure $ IsUnknown $ Just reason)
428-
(_, Unknown reason) -> retry (solve smtGiven sexprsToCheck transState) (pure $ IsUnknown $ Just reason)
453+
(Unknown reason, _) -> retry (solve smtGiven sexprsToCheck transState) (pure . IsUnknown . SMTUnknownReason $ reason)
454+
(_, Unknown reason) -> retry (solve smtGiven sexprsToCheck transState) (pure . IsUnknown . SMTUnknownReason $ reason)
429455
other ->
430456
throwSMT $
431457
"Unexpected result while checking a condition: " <> Text.pack (show other)

booster/test/rpc-integration/test-substitutions/response-symbolic-bottom-predicate.booster-dev

Lines changed: 0 additions & 151 deletions
This file was deleted.

0 commit comments

Comments
 (0)