Ceil simplifier#4157
Conversation
Collects all maximal sub-terms of a term that are rooted at a partial (non-total, non-constructor) symbol. These sub-terms represent the definedness conditions that must hold for the enclosing rule to be applied soundly — i.e. each collected sub-term must not evaluate to bottom. Used by ApplyEquations for runtime definedness discharge. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> (cherry picked from commit 9b7b65ce2b5ed3139c4326b92f1bdbc1aa8e679a)
…s discharge Adds an evaluateCeils flag to EquationConfig controlling whether the equation engine attempts to discharge definedness conditions for rules whose RHS contains partial-function applications. When evaluateCeils=True (via runEquationTWithCeils / evaluatePatternWithCeils): - Rules with notPreservesDefinednessReasons=[] proceed unconditionally (already guaranteed by the preserves-definedness attribute). - Rules with no undefined sub-terms in the RHS also proceed unconditionally. - Rules that have undefined sub-terms require runtime discharge: each partial- function sub-term is evaluated with evaluateCeils=False; if the result changed (i.e. the term was defined), the condition is considered discharged. All conditions must discharge for the rule to apply. The tryEvaluate heuristic — "if evaluating with evaluateCeils=False changes the term, it was defined" — is an under-approximation: it may miss some cases (returning false negatives), but never unsoundly accepts an undefined term. Also exports evaluatePatternWithCeils for use in the implies checker. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> (cherry picked from commit b37c3cdafcc077a82e389a9988d9cfc47660c961)
…aluatePatternWithCeils When matching during implies returns MatchIndeterminate, the handler simplifies the LHS pattern and retries; switch that simplification call from 'evaluatePattern' to 'evaluatePatternWithCeils' so the LHS-simplify pass can discharge runtime definedness side-conditions (the capability the prior commit added). No structural change to the retry logic. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> (cherry picked from commit 1c7164d5d1734fd71bf437939bc446d691613c9b)
In the MatchIndeterminate branch: after failing to change the LHS with evaluatePatternWithCeils, also attempt simplifying the RHS with the LHS constraints added to its context. If the RHS term changes, retry matching. This handles cases like hashLoc(...) => keccak(buf(...) +Bytes buf(32, 0)) where the consequent can be simplified to match the antecedent once LHS constraints are propagated. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> (cherry picked from commit 373d1446e66559bda7e860307e3f67bd9e210b10)
| -- notPreservingReasons≠[] + conditions≠[] + evaluateCeils=True → defer to runtime check after match | ||
| let notPreservingReasons = rule.computedAttributes.notPreservesDefinednessReasons | ||
| definednessConditions = collectUndefinedSubterms rule.rhs | ||
| preservedByAttr = null notPreservingReasons |
There was a problem hiding this comment.
Detail: The list being empty does not imply that the attribute is present (the other way round yes, but not this way round). There is an explicit attribute for preserves-definedness:
haskell-backend/booster/library/Booster/Definition/Attributes/Base.hs
Lines 91 to 98 in 7ca9fd6
and if it is set we don't start computing the list, but the list can also just be empty.
| preservedByAttr = null notPreservingReasons | |
| preservedByAttr = coerce rule.attributes.preserving |
| definednessConditions = collectUndefinedSubterms rule.rhs | ||
| preservedByAttr = null notPreservingReasons | ||
| hasConditions = not (null definednessConditions) | ||
| case (preservedByAttr, hasConditions) of |
There was a problem hiding this comment.
Given the above, maybe pull the preserves-definedness logging out to cut this short (and make the logic of the case easier to understand).
if (coerce rule.attributes.preserving)
then logMessage ...
else
case (notPreservingReasons, definednesConditions) of
([], []) -> pure () -- proceed silently
(reasons, []) -> -- no conditions to check at runtime (but reasons present), conservatively reject
(reasons, ts) -> -- conditions present, reject unless evaluateCeils
The case that was previously logged (no reasons but definednessConditions) falls into the last case here but is not reached when the rule is marked.
| collectUndefinedSubterms t@(SymbolApplication sym _ args) | ||
| | not (isDefinedSymbol sym) = [t] | ||
| | otherwise = concatMap collectUndefinedSubterms args | ||
| collectUndefinedSubterms (AndTerm l r) = collectUndefinedSubterms l <> collectUndefinedSubterms r | ||
| collectUndefinedSubterms (Injection _ _ inner) = collectUndefinedSubterms inner | ||
| collectUndefinedSubterms _ = [] |
There was a problem hiding this comment.
There are missing cases: known elements of a KList or KSet and keys as well as values of a KMap must be collected as well.
No description provided.