Open goodlyrottenapple opened 8 months ago
If we're adding boolean requires clauses by default, then we're changing the users transition system, which I don't think is a good idea. I think better is:
requires
clause when loading the rule, ideally store it as the original Ceil
condition, which then can be discharged dynamically before attempting the side-condition evaluation. We would have to have yet another separate bunch of Ceil
conditions associated with any potential non-defindeness in the requires
clause as well.I think storing the computed condition separately is probably the way to go. I'm not sure the user should be required to add this condition manually. This could result in rules which contain partial functions on the RHS to have huge custom written requires clauses which manually copy the ceil conditions for that partial symbol.
Computing these conditions at load time and then storing them separately would also produce better error messages, If the evaluation of such condition is indeterminate or false. I.e. rather than saying the rule did not apply, we return something along the lines of, the rule is applicable but we could not determine whether definiteness would be preserved or worse, this rule somehow leads to an undefined state.
The use of
#Ceil
in the old backend stems from partial functions in the semantics. The three main issues with the design of partiality checking in the old backend are:As a result of the last two points especially, we want to implement the ceil/definedness checker to be a (somewhat) separate pass during rewriting, which should make it easier to scrutinise and maintain. We also want to shift to a partially static definedness analysis (already implemented for rewrite rules in runtimeverification/hs-backend-booster#402), with the hope that a semantics writer could eventually make the ceil checks fully static. What we mean by this is that instead of computing the definedness for every instance of a partial function call, we could instead insert the definedness conditions of partial functions at call sities in rewrite rules. For example, given the rule
and given the definedness condition for
A mod B
beingB > 0
, the static checker would transform the above rule intoThe goal of making the dynamic ceil check a separate pass is more difficult, since the way we will compute #Ceil for a given partial function will either be to:
#Ceil(X mod Y) = Y > 0
. If this rule exists, we continue checking whether the predicateY > 0
holds.#Ceil
is defined, otherwise, recursively check definedness of all new partial function calls.See runtimeverification/haskell-backend#3772 for a discussion on how to refactor the different simplification/evaluation procedures in the booster in such a way as to avoid one giant mutually recursive ball of functions (as is the case in the old backend).
We already internalise and store ceil conditions separately from the configuration and the predicates, and once the old backend is removed, the user should never receive a
#Ceil
predicate as a response. Instead, we should augment the RPC types with a suitable set of errors, indicating that a partial function application could not be determined to be preserving definedness.