-
Notifications
You must be signed in to change notification settings - Fork 44
Implies endpoint in booster #3846
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 12 commits
b33755c
0a40b69
f4fcc3b
b45ffa4
908544c
e10e47f
2eefa9e
fa0d73b
6be8498
e5b55ba
a5f740c
e7ac98a
5154be2
4d7a127
b1408eb
9c21c9c
bf0ea9f
d6666b7
bd01cae
40c0488
9bef22d
7ef58ee
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -37,7 +37,7 @@ import Data.Text qualified as Text | |
import Data.Text.Encoding qualified as Text | ||
import GHC.Records | ||
import Numeric.Natural | ||
import Prettyprinter (pretty) | ||
import Prettyprinter (pretty, vsep) | ||
import System.Clock (Clock (Monotonic), diffTimeSpec, getTime, toNanoSecs) | ||
|
||
import Booster.Definition.Attributes.Base (UniqueId, getUniqueId, uniqueId) | ||
|
@@ -48,14 +48,21 @@ import Booster.Log | |
import Booster.Pattern.ApplyEquations qualified as ApplyEquations | ||
import Booster.Pattern.Base (Pattern (..), Sort (SortApp), Term, Variable) | ||
import Booster.Pattern.Base qualified as Pattern | ||
import Booster.Pattern.Bool (pattern TrueBool) | ||
import Booster.Pattern.Match (FailReason (..), MatchResult (..), MatchType (..), matchTerms) | ||
import Booster.Pattern.Rewrite ( | ||
RewriteFailed (..), | ||
RewriteResult (..), | ||
RewriteTrace (..), | ||
performRewrite, | ||
) | ||
import Booster.Pattern.Util (sortOfPattern, substituteInPredicate, substituteInTerm) | ||
import Booster.Prettyprinter (renderText) | ||
import Booster.Pattern.Util ( | ||
freeVariables, | ||
sortOfPattern, | ||
substituteInPredicate, | ||
substituteInTerm, | ||
) | ||
import Booster.Prettyprinter (renderDefault, renderText) | ||
import Booster.SMT.Base qualified as SMT | ||
import Booster.SMT.Interface qualified as SMT | ||
import Booster.Syntax.Json (KoreJson (..), addHeader, prettyPattern, sortOfJson) | ||
|
@@ -73,7 +80,11 @@ import Booster.Syntax.Json.Internalise ( | |
import Booster.Syntax.ParsedKore (parseKoreModule) | ||
import Booster.Syntax.ParsedKore.Base hiding (ParsedModule) | ||
import Booster.Syntax.ParsedKore.Base qualified as ParsedModule (ParsedModule (..)) | ||
import Booster.Syntax.ParsedKore.Internalise (addToDefinitions, definitionErrorToRpcError) | ||
import Booster.Syntax.ParsedKore.Internalise ( | ||
addToDefinitions, | ||
definitionErrorToRpcError, | ||
extractExistentials, | ||
) | ||
import Booster.Util (Flag (..), constructorName) | ||
import Kore.JsonRpc.Error qualified as RpcError | ||
import Kore.JsonRpc.Server (ErrorObj (..), JsonRpcHandler (..), Respond) | ||
|
@@ -455,11 +466,109 @@ respond stateVar = | |
{ satisfiable = RpcTypes.Sat | ||
, substitution | ||
} | ||
RpcTypes.Implies req -> withModule req._module $ \(def, mLlvmLibrary, mSMTOptions) -> Booster.Log.withContext "implies" $ do | ||
-- internalise given constrained term | ||
let internalised = | ||
runExcept . internalisePattern DisallowAlias CheckSubsorts Nothing def . fst . extractExistentials | ||
|
||
case (internalised req.antecedent.term, internalised req.consequent.term) of | ||
(Left patternError, _) -> do | ||
Log.logDebug $ "Error internalising cterm" <> Text.pack (show patternError) | ||
pure $ | ||
Left $ | ||
RpcError.backendError $ | ||
RpcError.CouldNotVerifyPattern | ||
[ patternErrorToRpcError patternError | ||
] | ||
(_, Left patternError) -> do | ||
Log.logDebug $ "Error internalising cterm" <> Text.pack (show patternError) | ||
goodlyrottenapple marked this conversation as resolved.
Show resolved
Hide resolved
|
||
pure $ | ||
Left $ | ||
RpcError.backendError $ | ||
RpcError.CouldNotVerifyPattern | ||
[ patternErrorToRpcError patternError | ||
] | ||
(Right (patL, substitutionL, unsupportedL), Right (patR, substitutionR, unsupportedR)) -> do | ||
unless (null unsupportedL && null unsupportedR) $ do | ||
Log.logWarnNS | ||
"booster" | ||
"Implies: aborting due to unsupported predicate parts" | ||
unless (null unsupportedL) $ | ||
Log.logOtherNS | ||
"booster" | ||
(Log.LevelOther "ErrorDetails") | ||
(Text.unlines $ map prettyPattern unsupportedL) | ||
unless (null unsupportedR) $ | ||
Log.logOtherNS | ||
"booster" | ||
(Log.LevelOther "ErrorDetails") | ||
(Text.unlines $ map prettyPattern unsupportedR) | ||
let doTracing = | ||
Flag $ | ||
any | ||
(fromMaybe False) | ||
[ req.logSuccessfulSimplifications | ||
, req.logFailedSimplifications | ||
] | ||
goodlyrottenapple marked this conversation as resolved.
Show resolved
Hide resolved
|
||
-- apply the given substitution before doing anything else | ||
substPatL = | ||
Pattern | ||
{ term = substituteInTerm substitutionL patL.term | ||
, constraints = Set.map (substituteInPredicate substitutionL) patL.constraints | ||
, ceilConditions = patL.ceilConditions | ||
} | ||
substPatR = | ||
Pattern | ||
{ term = substituteInTerm substitutionR patR.term | ||
, constraints = Set.map (substituteInPredicate substitutionR) patR.constraints | ||
, ceilConditions = patR.ceilConditions | ||
} | ||
|
||
case matchTerms Booster.Pattern.Match.Implies def substPatR.term substPatL.term of | ||
MatchFailed (SubsortingError sortError) -> | ||
pure . Left . RpcError.backendError . RpcError.ImplicationCheckError . RpcError.ErrorOnly . pack $ | ||
show sortError | ||
MatchFailed{} -> | ||
doesNotImply (sortOfPattern substPatL) req.antecedent.term req.consequent.term | ||
Comment on lines
+524
to
+525
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The legacy backend would first simplify both patterns and return the simplified implication. Not sure whether we would want to keep that behaviour (did it matter in the tests?) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i guess this would matter if e.g. the antecedent goes to #bottom? i guess here i am assuming things are defined and the constraints don't contradict each other |
||
MatchIndeterminate remainder -> | ||
pure . Left . RpcError.backendError . RpcError.ImplicationCheckError . RpcError.ErrorOnly . pack $ | ||
"match remainder: " | ||
<> renderDefault (pretty remainder) | ||
MatchSuccess subst -> do | ||
-- check it is a "matching" substitution (substitutes variables | ||
-- from the subject term only). Return does not imply if not. | ||
let violatingItems = Map.restrictKeys subst (Map.keysSet subst `Set.difference` freeVariables substPatR.term) | ||
if not $ null violatingItems | ||
then do | ||
pure | ||
. Left | ||
. RpcError.backendError | ||
. RpcError.ImplicationCheckError | ||
. RpcError.ErrorWithContext "does-not-imply2" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is the "2" in "does-not-imply2" spurious? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ah, that's now a pointless check. thanks for pointing out. After the matching code re-factor, we already throw this error earlier via |
||
$ [ "not matching substitution" | ||
, pack $ renderDefault $ vsep [pretty k <> "->" <> pretty v | (k, v) <- Map.toList violatingItems] | ||
] | ||
else -- doesNotImply (sortOfPattern substPatL) req.antecedent.term req.consequent.term | ||
do | ||
let filteredConsequentPreds = | ||
Set.map (substituteInPredicate subst) substPatR.constraints `Set.difference` substPatL.constraints | ||
solver <- traverse (SMT.initSolver def) mSMTOptions | ||
|
||
if null filteredConsequentPreds | ||
then implies (sortOfPattern substPatL) req.antecedent.term req.consequent.term subst | ||
else -- pure . Left . RpcError.backendError . RpcError.ImplicationCheckError . RpcError.ErrorOnly $ "implies" | ||
goodlyrottenapple marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
ApplyEquations.evaluateConstraints doTracing def mLlvmLibrary solver mempty filteredConsequentPreds >>= \case | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would have expected that the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yeah i was surprised even this most naive version seems to work reasonably well especially in KEVM. don't know if we should try more complex versions in this PR or follow up later. What do you think? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So we are essentially throwing the known constraints away. We should definitely have a follow-up PR to see if considering the |
||
(Right newPreds, _) -> | ||
if all (== Pattern.Predicate TrueBool) newPreds | ||
then implies (sortOfPattern substPatL) req.antecedent.term req.consequent.term subst | ||
else -- pure . Left . RpcError.backendError . RpcError.ImplicationCheckError . RpcError.ErrorOnly $ "implies" | ||
goodlyrottenapple marked this conversation as resolved.
Show resolved
Hide resolved
|
||
pure . Left . RpcError.backendError $ RpcError.Aborted "unknown constrains" | ||
(Left other, _) -> | ||
pure . Left . RpcError.backendError $ RpcError.Aborted (Text.pack . constructorName $ other) | ||
|
||
-- this case is only reachable if the cancel appeared as part of a batch request | ||
RpcTypes.Cancel -> pure $ Left RpcError.cancelUnsupportedInBatchMode | ||
-- using "Method does not exist" error code | ||
_ -> pure $ Left RpcError.notImplemented | ||
where | ||
withModule :: | ||
Maybe Text -> | ||
|
@@ -474,6 +583,39 @@ respond stateVar = | |
Nothing -> pure $ Left $ RpcError.backendError $ RpcError.CouldNotFindModule mainName | ||
Just d -> action (d, state.mLlvmLibrary, state.mSMTOptions) | ||
|
||
doesNotImply s l r = | ||
pure $ | ||
Right $ | ||
RpcTypes.Implies | ||
RpcTypes.ImpliesResult | ||
{ implication = addHeader $ Syntax.KJImplies (externaliseSort s) l r | ||
, valid = False | ||
, condition = Nothing | ||
, logs = Nothing | ||
} | ||
|
||
implies s' l r subst = | ||
let s = externaliseSort s' | ||
in pure $ | ||
Right $ | ||
RpcTypes.Implies | ||
RpcTypes.ImpliesResult | ||
{ implication = addHeader $ Syntax.KJImplies s l r | ||
, valid = True | ||
, condition = | ||
Just | ||
RpcTypes.Condition | ||
{ predicate = addHeader $ Syntax.KJTop s | ||
, substitution = | ||
addHeader | ||
$ (\xs -> if null xs then Syntax.KJTop s else Syntax.KJAnd s xs) | ||
. map (uncurry $ externaliseSubstitution s) | ||
. Map.toList | ||
$ subst | ||
} | ||
, logs = Nothing | ||
} | ||
|
||
handleSmtError :: JsonRpcHandler | ||
handleSmtError = JsonRpcHandler $ \case | ||
SMT.GeneralSMTError err -> runtimeError "problem" err | ||
|
@@ -764,7 +906,7 @@ mkLogRewriteTrace | |
| logSuccessfulRewrites -> | ||
Just $ | ||
singleton $ | ||
Rewrite | ||
Kore.JsonRpc.Types.Log.Rewrite | ||
{ result = | ||
Success | ||
{ rewrittenTerm = Nothing | ||
|
@@ -778,7 +920,7 @@ mkLogRewriteTrace | |
| logFailedRewrites -> | ||
Just $ | ||
singleton $ | ||
Rewrite | ||
Kore.JsonRpc.Types.Log.Rewrite | ||
{ result = case reason of | ||
NoApplicableRules{} -> Failure{reason = "No applicable rules found", _ruleId = Nothing} | ||
TermIndexIsNone{} -> Failure{reason = "Term index is None for term", _ruleId = Nothing} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -30,6 +30,7 @@ module Booster.Pattern.ApplyEquations ( | |
simplifyConstraints, | ||
SimplifierCache, | ||
logWarn, | ||
evaluateConstraints, | ||
) where | ||
|
||
import Control.Applicative (Alternative (..)) | ||
|
@@ -560,7 +561,7 @@ evaluatePattern doTracing def mLlvmLibrary smtSolver cache = | |
|
||
-- version for internal nested evaluation | ||
evaluatePattern' :: | ||
MonadLoggerIO io => | ||
LoggerMIO io => | ||
Pattern -> | ||
EquationT io Pattern | ||
evaluatePattern' pat@Pattern{term, constraints, ceilConditions} = withPatternContext pat $ do | ||
|
@@ -572,14 +573,38 @@ evaluatePattern' pat@Pattern{term, constraints, ceilConditions} = withPatternCon | |
-- this may yield additional new constraints, left unevaluated | ||
evaluatedConstraints <- predicates <$> getState | ||
pure Pattern{constraints = evaluatedConstraints, term = newTerm, ceilConditions} | ||
where | ||
-- evaluate the given predicate assuming all others | ||
simplifyAssumedPredicate p = withContext "constraint" $ do | ||
allPs <- predicates <$> getState | ||
let otherPs = Set.delete p allPs | ||
eqState $ modify $ \s -> s{predicates = otherPs} | ||
newP <- simplifyConstraint' True $ coerce p | ||
pushConstraints $ Set.singleton $ coerce newP | ||
|
||
-- evaluate the given predicate assuming all others | ||
simplifyAssumedPredicate :: LoggerMIO io => Predicate -> EquationT io () | ||
simplifyAssumedPredicate p = do | ||
allPs <- predicates <$> getState | ||
let otherPs = Set.delete p allPs | ||
eqState $ modify $ \s -> s{predicates = otherPs} | ||
newP <- simplifyConstraint' True $ coerce p | ||
pushConstraints $ Set.singleton $ coerce newP | ||
|
||
evaluateConstraints :: | ||
LoggerMIO io => | ||
Flag "CollectEquationTraces" -> | ||
KoreDefinition -> | ||
Maybe LLVM.API -> | ||
Maybe SMT.SMTContext -> | ||
SimplifierCache -> | ||
Set Predicate -> | ||
io (Either EquationFailure (Set Predicate), SimplifierCache) | ||
evaluateConstraints doTracing def mLlvmLibrary smtSolver cache = | ||
runEquationT doTracing def mLlvmLibrary smtSolver cache . evaluateConstraints' | ||
|
||
evaluateConstraints' :: | ||
LoggerMIO io => | ||
Set Predicate -> | ||
EquationT io (Set Predicate) | ||
evaluateConstraints' constraints = do | ||
pushConstraints constraints | ||
-- evaluate all existing constraints, once | ||
traverse_ simplifyAssumedPredicate . predicates =<< getState | ||
-- this may yield additional new constraints, left unevaluated | ||
predicates <$> getState | ||
Comment on lines
+610
to
+615
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Wrt. the comment above in |
||
|
||
---------------------------------------- | ||
|
||
|
Uh oh!
There was an error while loading. Please reload this page.