@@ -70,7 +70,7 @@ import Booster.Pattern.Pretty
70
70
import Booster.Pattern.Util
71
71
import Booster.Prettyprinter
72
72
import Booster.SMT.Interface qualified as SMT
73
- import Booster.Syntax.Json.Externalise (externaliseTerm )
73
+ import Booster.Syntax.Json.Externalise (externalisePredicate , externaliseSort , externaliseTerm )
74
74
import Booster.Util (Flag (.. ))
75
75
76
76
{- | The @'RewriteT'@ monad encapsulates the effects needed to make a single rewrite step.
@@ -127,6 +127,9 @@ throw = RewriteT . lift . lift . throwE
127
127
getDefinition :: LoggerMIO io => RewriteT io KoreDefinition
128
128
getDefinition = RewriteT $ definition <$> ask
129
129
130
+ getSolver :: Monad m => RewriteT m SMT. SMTContext
131
+ getSolver = RewriteT $ (. smtSolver) <$> ask
132
+
130
133
invalidateRewriterEquationsCache :: LoggerMIO io => RewriteT io ()
131
134
invalidateRewriterEquationsCache =
132
135
RewriteT . lift . modify $ \ s@ RewriteState {} ->
@@ -173,54 +176,125 @@ rewriteStep cutLabels terminalLabels pat = do
173
176
processGroups [] =
174
177
pure $ RewriteStuck pat
175
178
processGroups (rules : rest) = do
179
+ logMessage (" Trying rules with priority " <> (Text. pack . show $ ruleGroupPriority rules))
176
180
-- try all rules of the priority group. This will immediately
177
181
-- fail the rewrite if anything is uncertain (unification,
178
182
-- definedness, rule conditions)
179
183
results <-
180
- filter (/= NotApplied )
181
- <$> forM
182
- rules
183
- ( \ rule -> do
184
- result <- applyRule pat rule
185
- pure (fmap (rule,) result)
186
- )
187
-
188
- -- simplify and filter out bottom states
189
-
190
- -- At the moment, there is no point in calling simplify on the conditions of the
191
- -- resulting patterns again, since we already pruned any rule applications
192
- -- which resulted in one of the conditions being bottom.
184
+ zip rules
185
+ <$> mapM (applyRule pat) rules
193
186
194
187
let labelOf = fromMaybe " " . (. ruleLabel) . (. attributes)
195
188
ruleLabelOrLocT = renderOneLineText . ruleLabelOrLoc
196
189
uniqueId = (. uniqueId) . (. attributes)
197
190
198
- case results of
199
- -- no rules in this group were applicable
200
- [] -> processGroups rest
201
- _ -> case concatMap (\ case Applied x -> [x]; _ -> [] ) results of
202
- [] ->
203
- -- all remaining branches are trivial, i.e. rules which did apply had an ensures condition which evaluated to false
204
- -- if, all the other groups only generate a not applicable or trivial rewrites,
205
- -- then we return a `RewriteTrivial`.
206
- processGroups rest >>= \ case
207
- RewriteStuck {} -> pure $ RewriteTrivial pat
208
- other -> pure other
209
- -- all branches but one were either not applied or trivial
210
- [(r, (x, _remainder, _subst))]
211
- | labelOf r `elem` cutLabels ->
212
- pure $ RewriteCutPoint (labelOf r) (uniqueId r) pat x
213
- | labelOf r `elem` terminalLabels ->
214
- pure $ RewriteTerminal (labelOf r) (uniqueId r) x
215
- | otherwise ->
216
- pure $ RewriteFinished (Just $ ruleLabelOrLocT r) (Just $ uniqueId r) x
217
- -- at this point, there were some Applied rules and potentially some Trivial ones.
218
- -- here, we just return all the applied rules in a `RewriteBranch`
219
- rxs ->
191
+ case postProcessRuleAttempts results of
192
+ OnlyTrivial ->
193
+ -- all branches in this priority group are trivial,
194
+ -- i.e. rules which did apply had an ensures condition which evaluated to false.
195
+ -- if all the other groups only generate a not applicable or trivial rewrites,
196
+ -- then we return a `RewriteTrivial`.
197
+ processGroups rest >>= \ case
198
+ RewriteStuck {} -> pure $ RewriteTrivial pat
199
+ other -> pure other
200
+ AppliedRules ([] , _remainder) -> processGroups rest
201
+ AppliedRules ([(rule, newPat, _subst)], _remainder)
202
+ -- a single rule applies, see if it's special and return an appropriate result
203
+ | labelOf rule `elem` cutLabels ->
204
+ pure $ RewriteCutPoint (labelOf rule) (uniqueId rule) pat newPat
205
+ | labelOf rule `elem` terminalLabels ->
206
+ pure $ RewriteTerminal (labelOf rule) (uniqueId rule) newPat
207
+ | otherwise ->
208
+ pure $ RewriteFinished (Just $ ruleLabelOrLocT rule) (Just $ uniqueId rule) newPat
209
+ AppliedRules (xs, remainder)
210
+ -- multiple rules apply, analyse brunching and remainders
211
+ | any isFalse remainder -> do
212
+ withContext CtxRemainder $ logMessage (" remainder is UNSAT" :: Text )
213
+ -- the remainder predicate is trivially false, return the branching result
220
214
pure $
221
215
RewriteBranch pat $
222
216
NE. fromList $
223
- map (\ (r, (p, _remainder, _subst)) -> (ruleLabelOrLocT r, uniqueId r, p)) rxs
217
+ map (\ (r, p, _subst) -> (ruleLabelOrLocT r, uniqueId r, p)) xs
218
+ | otherwise -> do
219
+ -- otherwise, we need to check the remainder predicate with the SMT solver
220
+ -- and construct an additional remainder branch if needed
221
+ solver <- getSolver
222
+ SMT. isSat solver (Set. toList $ pat. constraints <> remainder) >>= \ case
223
+ SMT. IsUnsat -> do
224
+ -- the remainder condition is unsatisfiable: no need to consider the remainder branch.
225
+ withContext CtxRemainder $ logMessage (" remainder is UNSAT" :: Text )
226
+ -- pure resultsWithoutRemainders
227
+ pure $
228
+ RewriteBranch pat $
229
+ NE. fromList $
230
+ map (\ (r, p, _subst) -> (ruleLabelOrLocT r, uniqueId r, p)) xs
231
+ satRes@ (SMT. IsSat {}) -> do
232
+ -- the remainder condition is satisfiable.
233
+ -- Have to construct the remainder branch and consider it
234
+ -- To construct the "remainder pattern",
235
+ -- we add the remainder condition to the predicates of the @pattr@
236
+ -- (resultsWithoutRemainders <>) <$> processGroups lowerPriorityRules
237
+ let rs = map (\ (r, _p, _subst) -> r) xs
238
+ throw $
239
+ RewriteRemainderPredicate rs satRes . coerce . foldl1 AndTerm $
240
+ map coerce . Set. toList $
241
+ remainder
242
+ satRes@ SMT. IsUnknown {} -> do
243
+ -- solver cannot solve the remainder. Descend into the remainder branch anyway
244
+ -- (resultsWithoutRemainders <>) <$> processGroups lowerPriorityRules
245
+ let rs = map (\ (r, _p, _subst) -> r) xs
246
+ throw $
247
+ RewriteRemainderPredicate rs satRes . coerce . foldl1 AndTerm $
248
+ map coerce . Set. toList $
249
+ remainder
250
+
251
+ -- if any isFalse remainder -- no need to call SMT if any of the conditions is trivially false
252
+ -- then do
253
+ -- -- setRemainder mempty
254
+ -- pure xs
255
+ -- else do
256
+ -- solver <- getSolver
257
+ -- SMT.isSat solver (pat.constraints <> remainder) >>= \case
258
+ -- SMT.IsUnsat -> do
259
+ -- -- the remainder condition is unsatisfiable: no need to consider the remainder branch.
260
+ -- setRemainder mempty
261
+ -- withContext CtxRemainder $ logMessage ("remainder is UNSAT" :: Text)
262
+ -- pure resultsWithoutRemainders
263
+ -- SMT.IsSat _ -> do
264
+ -- withContext CtxRemainder $ logMessage ("remainder is SAT" :: Text)
265
+ -- -- the remainder condition is satisfiable.
266
+ -- -- Have to construct the remainder branch and consider it
267
+ -- -- To construct the "remainder pattern",
268
+ -- -- we add the remainder condition to the predicates of the @pattr@
269
+ -- (resultsWithoutRemainders <>) <$> processGroups lowerPriorityRules
270
+ -- SMT.IsUnknown{} -> do
271
+ -- withContext CtxRemainder $ logMessage ("remainder is UNKNWON" :: Text)
272
+ -- -- solver cannot solve the remainder. Descend into the remainder branch anyway
273
+ -- (resultsWithoutRemainders <>) <$> processGroups lowerPriorityRules
274
+
275
+ -- _ -> case concatMap (\case Applied x -> [x]; _ -> []) results of
276
+ -- [] ->
277
+ -- -- all remaining branches are trivial, i.e. rules which did apply had an ensures condition which evaluated to false
278
+ -- -- if, all the other groups only generate a not applicable or trivial rewrites,
279
+ -- -- then we return a `RewriteTrivial`.
280
+ -- processGroups rest >>= \case
281
+ -- RewriteStuck{} -> pure $ RewriteTrivial pat
282
+ -- other -> pure other
283
+ -- -- all branches but one were either not applied or trivial
284
+ -- [(r, (x, _remainder, _subst))]
285
+ -- | labelOf r `elem` cutLabels ->
286
+ -- pure $ RewriteCutPoint (labelOf r) (uniqueId r) pat x
287
+ -- | labelOf r `elem` terminalLabels ->
288
+ -- pure $ RewriteTerminal (labelOf r) (uniqueId r) x
289
+ -- | otherwise ->
290
+ -- pure $ RewriteFinished (Just $ ruleLabelOrLocT r) (Just $ uniqueId r) x
291
+ -- -- at this point, there were some Applied rules and potentially some Trivial ones.
292
+ -- -- here, we just return all the applied rules in a `RewriteBranch`
293
+ -- rxs ->
294
+ -- pure $
295
+ -- RewriteBranch pat $
296
+ -- NE.fromList $
297
+ -- map (\(r, (p, _remainder, _subst)) -> (ruleLabelOrLocT r, uniqueId r, p)) rxs
224
298
225
299
-- | Rewrite rule application transformer: may throw exceptions on non-applicable or trivial rule applications
226
300
type RewriteRuleAppT m a = ExceptT RewriteRuleAppException m a
@@ -249,11 +323,11 @@ returnNotApplied = throwE RewriteRuleNotApplied
249
323
* Unifies the LHS term with the pattern term
250
324
* Ensures that the unification is a _match_ (one-sided substitution)
251
325
* prunes any rules that turn out to have trivially-false side conditions
252
- * returns the rule and the resulting pattern if successful, otherwise Nothing
326
+ * returns the resulting pattern if successful, otherwise Nothing
253
327
254
- If it cannot be determined whether the rule can be applied or not, an
255
- exception is thrown which indicates the exact reason why (this will
256
- abort the entire rewrite) .
328
+ If it cannot be determined whether the rule can be applied or not, the second component
329
+ of the result will contain a non-trivial /remainder predicate/, i.e. the indeterminate
330
+ subset of the rule's side condition .
257
331
-}
258
332
applyRule ::
259
333
forall io .
@@ -364,13 +438,6 @@ applyRule pat@Pattern{ceilConditions} rule =
364
438
case unclearRequiresAfterSmt of
365
439
[] -> withPatternContext rewritten $ pure (rewritten, Predicate FalseBool , subst)
366
440
_ -> do
367
- failRewrite $
368
- RuleConditionUnclear rule . coerce . foldl1 AndTerm $
369
- map coerce unclearRequiresAfterSmt
370
- -- TODO the following code is intentionally dead and should be enabled to get rewrite rule remainders
371
- -- when unclearRequiresAfterSmt is non-empty, we need to add it as a rule remainder predicate, which means:
372
- -- - the resulting patten will have it conjoined to its constraints TODO is this right?
373
- -- - its negation, i.e. the remainder predicate, will be returned as the second component of the result
374
441
let rewritten' = rewritten{constraints = rewritten. constraints <> Set. fromList unclearRequiresAfterSmt}
375
442
in withPatternContext rewritten' $
376
443
pure
@@ -494,6 +561,38 @@ applyRule pat@Pattern{ceilConditions} rule =
494
561
RuleConditionUnclear rule . coerce . foldl1 AndTerm $
495
562
map coerce predicates
496
563
564
+ data RuleGroupApplication a = OnlyTrivial | AppliedRules a
565
+
566
+ ruleGroupPriority :: [RewriteRule a ] -> Maybe Priority
567
+ ruleGroupPriority = \ case
568
+ [] -> Nothing
569
+ (rule : _) -> Just rule. attributes. priority
570
+
571
+ {- | Given a list of rule application attempts, i.e. a result of applying a priority group of rules in parallel,
572
+ post process them:
573
+ - filter-out trivial and failed applications
574
+ - extract (possibly trivial) remainder predicates of every rule
575
+ and return them as a set relating to the whole group
576
+ -}
577
+ postProcessRuleAttempts ::
578
+ [(RewriteRule " Rewrite" , RewriteRuleAppResult (Pattern , Predicate , Substitution ))] ->
579
+ RuleGroupApplication ([(RewriteRule " Rewrite" , Pattern , Substitution )], Set. Set Predicate )
580
+ postProcessRuleAttempts = \ case
581
+ [] -> AppliedRules ([] , mempty )
582
+ apps -> case filter ((/= NotApplied ) . snd ) apps of
583
+ [] -> AppliedRules ([] , mempty )
584
+ xs
585
+ | all ((== Trivial ) . snd ) xs -> OnlyTrivial
586
+ | otherwise -> go ([] , mempty ) xs
587
+ where
588
+ go acc@ (accPatterns, accRemainders) = \ case
589
+ [] -> AppliedRules (reverse accPatterns, accRemainders)
590
+ ((rule, appRes) : xs) ->
591
+ case appRes of
592
+ Applied (pat, remainder, subst) -> go ((rule, pat, subst) : accPatterns, Set. singleton remainder <> accRemainders) xs
593
+ NotApplied -> go acc xs
594
+ Trivial -> go acc xs
595
+
497
596
{- | Reason why a rewrite did not produce a result. Contains additional
498
597
information for logging what happened during the rewrite.
499
598
-}
@@ -504,6 +603,8 @@ data RewriteFailed k
504
603
RuleApplicationUnclear (RewriteRule k ) Term (NonEmpty (Term , Term ))
505
604
| -- | A rule condition is indeterminate
506
605
RuleConditionUnclear (RewriteRule k ) Predicate
606
+ | -- | After applying multiple rewrite rules there is a satisfiable or unknown remainder condition
607
+ RewriteRemainderPredicate [RewriteRule k ] (SMT. IsSatResult () ) Predicate
507
608
| -- | A rewrite rule does not preserve definedness
508
609
DefinednessUnclear (RewriteRule k ) Pattern [NotPreservesDefinednessReason ]
509
610
| -- | A sort error was detected during m,atching
@@ -538,6 +639,14 @@ instance FromModifiersT mods => Pretty (PrettyWithModifiers mods (RewriteFailed
538
639
, " : "
539
640
, pretty' @ mods predicate
540
641
]
642
+ RewriteRemainderPredicate rules satResult predicate ->
643
+ hsep
644
+ [ pretty (SMT. showIsSatResult satResult)
645
+ , " remainder predicate after applying rules"
646
+ , hsep $ punctuate comma $ map ruleLabelOrLoc rules
647
+ , " : "
648
+ , pretty' @ mods predicate
649
+ ]
541
650
DefinednessUnclear rule _pat reasons ->
542
651
hsep $
543
652
[ " Uncertain about definedness of rule "
@@ -921,6 +1030,20 @@ performRewrite rewriteConfig pat = do
921
1030
emitRewriteTrace $ RewriteStepFailed failure
922
1031
logMessage $ " Aborted after " <> showCounter counter
923
1032
pure (RewriteAborted failure pat')
1033
+ Left failure@ (RewriteRemainderPredicate _rules _satResult remainderPredicate) -> do
1034
+ emitRewriteTrace $ RewriteStepFailed failure
1035
+ withPatternContext pat' . withContext CtxRemainder . withContext CtxAbort $
1036
+ getPrettyModifiers >>= \ case
1037
+ ModifiersRep (_ :: FromModifiersT mods => Proxy mods ) ->
1038
+ logMessage
1039
+ $ WithJsonMessage
1040
+ ( object
1041
+ [" remainder" .= externalisePredicate (externaliseSort $ sortOfPattern pat) remainderPredicate]
1042
+ )
1043
+ $ renderOneLineText
1044
+ $ pretty' @ mods failure
1045
+ logMessage $ " Aborted after " <> showCounter counter
1046
+ pure (RewriteAborted failure pat')
924
1047
Left failure -> do
925
1048
emitRewriteTrace $ RewriteStepFailed failure
926
1049
let msg = " Aborted after " <> showCounter counter
0 commit comments