-
Notifications
You must be signed in to change notification settings - Fork 161
Description
Depends on: #4726
Suppose we have a function production
syntax Foo ::= foo ( <params> )
The general structure of a function rule is the following:
rule foo ( <args_i> ) => <rhs_i> requires <req_i> [priority(<prio_i>)]
First, we order rules by priority. This allows the following defintion:
def foo <params> := foo_pgroup_1 <params> <|> foo_pgroup_2 <params> <|> ...
where foo_pgroup_i
is a function representing all rules in priority group i
. These helpers are combined using <|>
in increasing order of priority, i.e., if a group with lower priority fails to yield a result, then the next group is tried, etc.
In order for the function to be well-defined, within each priority group, for a given concrete input, there must be at most one matching rule. There are three cases.
No requires
clauses
def foo_pgroup_i <params> :=
| <args_1> => some <rhs_1>
| <args_2> => some <rhs_2>
...
| _ => none
Strictly speaking, this mapping is faithful only if the patterns are non-overlapping, as the order in which patterns are defined matters in Lean. In the presence of overlapping patterns, the K function is only well defined if there's confluence of values, thus it might happen that a non-well-defined K function is translated to a well-defined Lean function.
Only a single pattern on the LHS (modulo alpha renaming)
In this case, it has to be ensured that requires
clauses are non-overlapping. We can generate a theorem
stub (or multiple ones, for each pair of clauses) to encode the proof obligation. Then the function definition can be implemented using if-then-else
:
def foo_pgroup_i <params> :=
| <args> =>
if <req_1> then some <rhs_1>
else if <req_2> then some <rhs2>
else if ...
else none
| _ => none
The general case
I.e., there are requires
clasues, and there are various patterns on the left hand sides of rules. Such a function can be well defined (even if the LHS patterns are overlapping), consider
rule foo ( X, 0 ) => 0 requires X =/=Int 0
rule foo ( 0, Y ) => 1 requires Y =/=Int 0
Group rules by LHS pattern. Then generate a helper function as per the previous case for each group. Combine the cases with <|>
as for priority groups.
Similarly to the previous case though, a theorem
stub (or multiple ones, for each pair of helpers) has to be generated that states well-definedness (i.e. at most one of the helpers returns some
).